f49800f3a95b867ece72b29734a85fa6d13ed708
[profile/ivi/murphy.git] / src / plugins / gam-resource-manager / backend.c
1 /*
2  * Copyright (c) 2014, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *  * Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *  * Neither the name of Intel Corporation nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34
35 #include <murphy/common.h>
36
37 #include <murphy-db/mqi.h>
38
39 #include <murphy/resource/config-api.h>
40 #include <murphy/resource/manager-api.h>
41 #include <murphy/resource/client-api.h>
42
43 #include "backend.h"
44 #include "source.h"
45 #include "sink.h"
46 #include "usecase.h"
47
48 #define ANY_ZONE  (~((uint32_t)0))
49
50
51 #define ATTRIBUTE(n,t,v)    {n, MRP_RESOURCE_RW, mqi_##t, {.t=v}}
52 #define ATTR_END            {NULL, 0, 0, {.string=NULL}}
53
54
55 typedef enum   decision_value_e  decision_value_t;
56 typedef enum   state_value_e     state_value_t;
57 typedef struct resource_s        resource_t;
58 typedef struct resource_type_s   resource_type_t;
59 typedef struct decision_s        decision_t;
60 typedef struct state_s           state_t;
61
62 enum decision_value_e {
63     STATE_ERROR = -1,
64
65     STATE_STOP = 0,
66     STATE_PAUSE,
67     STATE_PLAY,
68
69     STATE_MAX
70 };
71
72 enum state_value_e {
73     DECISION_ERROR = -1,
74
75     DECISION_TEARDOWN = 0,
76     DECISION_DISCONNECTED,
77     DECISION_CONNECTED,
78     DECISION_SUSPENDED,
79
80     DECISION_MAX
81 };
82
83 struct resource_type_s {
84     int id;
85     const char *name;
86     uint32_t resid;
87 };
88
89 struct mrp_resmgr_backend_s {
90     mrp_resmgr_t *resmgr;
91     resource_type_t types[MRP_RESMGR_RESOURCE_TYPE_MAX];
92     struct {
93         mrp_htbl_t *by_pointer;
94         mrp_htbl_t *by_connid;
95     } resources;
96 };
97
98 struct decision_s {
99     int32_t new;
100     int32_t current;
101 };
102
103 struct state_s {
104     int32_t new;
105     int32_t current;
106 };
107
108 struct mrp_resmgr_resource_s {
109     const char *name;
110     mrp_resmgr_backend_t *backend;
111     mrp_resource_t *res;
112     resource_type_t *type;
113     mrp_resmgr_source_t *source;
114     mrp_resmgr_sink_t *sink;
115     mrp_list_hook_t source_link;
116     uint32_t zoneid;
117     uint32_t connid;
118     uint32_t connno;
119     decision_t decision;
120     state_t state;
121 };
122
123
124 static void make_resource_definition(mrp_resmgr_backend_t *, int, const char*);
125 static resource_type_t *find_resource_definition_by_id(mrp_resmgr_backend_t *,
126                                                        int);
127
128 static int hash_compare(const void *, const void *);
129 static uint32_t ptr_hash_function(const void *);
130 static uint32_t id_hash_function(const void *);
131
132 //static const char *get_resource_appid(mrp_resource_t *);
133 static int32_t get_resource_sourceid(mrp_resource_t *);
134 static int32_t get_resource_sinkid(mrp_resource_t *);
135 static int32_t get_resource_connid(mrp_resource_t *);
136 static int32_t get_resource_connno(mrp_resource_t *);
137 static int32_t get_resource_stamp(mrp_resource_t *);
138
139 static bool set_resource_source_and_sink(mrp_resource_t *,
140                                          mrp_resmgr_source_t *,
141                                          mrp_resmgr_sink_t *);
142 static bool set_resource_stamp(mrp_resource_t *, int32_t);
143 static bool set_resource_decision(mrp_resource_t *, int32_t);
144
145
146 static void resource_create(mrp_resmgr_backend_t *, mrp_application_class_t *,
147                             mrp_zone_t *, mrp_resource_t *);
148 static void resource_destroy(mrp_resmgr_backend_t *, mrp_zone_t *,
149                              mrp_resource_t *);
150 static bool resource_acquire(mrp_resmgr_backend_t *, mrp_zone_t *,
151                              mrp_resource_t *);
152 static bool resource_release(mrp_resmgr_backend_t *, mrp_zone_t *,
153                              mrp_resource_t *);
154
155 static bool resource_register_by_id(mrp_resmgr_backend_t *,
156                                     mrp_resmgr_resource_t *);
157 static mrp_resmgr_resource_t *resource_lookup_by_pointer(mrp_resmgr_backend_t*,
158                                                          mrp_resource_t *);
159
160 static size_t resource_print_name(mrp_resmgr_source_t *,uint32_t,char *,size_t);
161
162
163 static void make_decisions(mrp_resmgr_backend_t *);
164 static void commit_decisions(mrp_resmgr_backend_t *);
165 static size_t print_decision(mrp_resmgr_resource_t *, char *, size_t);
166 static size_t print_commit(mrp_resmgr_resource_t *, char *, size_t);
167
168 static void backend_notify(mrp_resource_event_t, mrp_zone_t *,
169                            mrp_application_class_t *, mrp_resource_t *, void*);
170 static void backend_init(mrp_zone_t *, void *);
171 static bool backend_allocate(mrp_zone_t *, mrp_resource_t *, void *);
172 static void backend_free(mrp_zone_t *, mrp_resource_t *, void *);
173 static bool backend_advice(mrp_zone_t *, mrp_resource_t *, void *);
174 static void backend_commit(mrp_zone_t *, void *);
175
176
177
178 #define APPID_ATTRIDX      0
179 #define ROLE_ATTRIDX       1
180 #define PID_ATTRIDX        2
181 #define POLICY_ATTRIDX     3
182 #define SRCNAM_ATTRIDX     4
183 #define SRCID_ATTRIDX      5
184 #define SINKNAM_ATTRIDX    6
185 #define SINKID_ATTRIDX     7
186 #define CONNID_ATTRIDX     8
187 #define CONNNO_ATTRIDX     9
188 #define STAMP_ATTRIDX     10
189 #define DECISION_ATTRIDX  11
190
191 #define ATTR_MAX          12
192
193 static mrp_attr_def_t audio_attrs[] = {
194     ATTRIBUTE( "appid"      , string ,  "<undefined>" ),
195     ATTRIBUTE( "role"       , string ,  "music"       ),
196     ATTRIBUTE( "pid"        , string ,  "<unknown>"   ),
197     ATTRIBUTE( "policy"     , string ,  "relaxed"     ),
198     ATTRIBUTE( "source_name", string ,  "<undefined>" ),
199     ATTRIBUTE( "source_id"  , integer,  0             ),
200     ATTRIBUTE( "sink_name"  , string ,  "<undefined>" ),
201     ATTRIBUTE( "sink_id"    , integer,  0             ),
202     ATTRIBUTE( "connid"     , integer,  0             ),
203     ATTRIBUTE( "connno"     , integer,  0             ),
204     ATTRIBUTE( "stamp"      , integer,  0             ),
205     ATTRIBUTE( "decision"   , string ,  "<not yet>"   ),
206     ATTR_END
207 };
208
209 static mrp_resource_mgr_ftbl_t playback_ftbl = {
210     backend_notify,             /* notify   */
211     backend_init,               /* init     */
212     backend_allocate,           /* allocate */
213     backend_free,               /* free     */
214     backend_advice,             /* advice   */
215     NULL                        /* commit   */
216 };
217
218 static mrp_resource_mgr_ftbl_t recording_ftbl = {
219     backend_notify,             /* notify   */
220     NULL,                       /* init     */
221     backend_allocate,           /* allocate */
222     backend_free,               /* free     */
223     backend_advice,             /* advice   */
224     backend_commit              /* commit   */
225 };
226
227 static mrp_resource_mgr_ftbl_t *backend_ftbl[MRP_RESMGR_RESOURCE_TYPE_MAX] = {
228     [ MRP_RESMGR_RESOURCE_TYPE_PLAYBACK  ] = &playback_ftbl ,
229     [ MRP_RESMGR_RESOURCE_TYPE_RECORDING ] = &recording_ftbl,
230 };
231
232
233 static const char *decision_names[DECISION_MAX + 1] = {
234     [ DECISION_TEARDOWN     ] = "teardown"    ,
235     [ DECISION_DISCONNECTED ] = "disconnected",
236     [ DECISION_CONNECTED    ] = "connected"   ,
237     [ DECISION_SUSPENDED    ] = "suspended"   ,
238 };
239
240 static const char *state_names[STATE_MAX + 1] = {
241     [ STATE_STOP  ] = "stop" ,
242     [ STATE_PAUSE ] = "pause",
243     [ STATE_PLAY  ] = "play" ,
244 };
245
246 static state_value_t decision2state[DECISION_MAX] = {
247     [ DECISION_TEARDOWN     ] = STATE_STOP ,
248     [ DECISION_DISCONNECTED ] = STATE_STOP ,
249     [ DECISION_CONNECTED    ] = STATE_PLAY ,
250     [ DECISION_SUSPENDED    ] = STATE_PAUSE,
251 };
252
253
254 mrp_resmgr_backend_t *mrp_resmgr_backend_create(mrp_resmgr_t *resmgr)
255 {
256     mrp_resmgr_backend_t *backend;
257     mrp_htbl_config_t pcfg, icfg;
258
259     MRP_ASSERT(resmgr, "invalid argument");
260
261     if ((backend = mrp_allocz(sizeof(mrp_resmgr_backend_t)))) {
262         pcfg.nentry = MRP_RESMGR_RESOURCE_MAX;
263         pcfg.comp = hash_compare;
264         pcfg.hash = ptr_hash_function;
265         pcfg.free = NULL;
266         pcfg.nbucket = MRP_RESMGR_RESOURCE_BUCKETS;
267
268         icfg.nentry = MRP_RESMGR_RESOURCE_MAX;
269         icfg.comp = hash_compare;
270         icfg.hash = id_hash_function;
271         icfg.free = NULL;
272         icfg.nbucket = MRP_RESMGR_RESOURCE_BUCKETS;
273
274         backend->resmgr = resmgr;
275         backend->resources.by_pointer = mrp_htbl_create(&pcfg);
276         backend->resources.by_connid = mrp_htbl_create(&icfg);
277
278         make_resource_definition(backend, MRP_RESMGR_RESOURCE_TYPE_PLAYBACK,
279                                  MRP_RESMGR_PLAYBACK_RESOURCE);
280         make_resource_definition(backend, MRP_RESMGR_RESOURCE_TYPE_RECORDING,
281                                  MRP_RESMGR_RECORDING_RESOURCE);
282     }
283
284     return backend;
285 }
286
287 void mrp_resmgr_backend_destroy(mrp_resmgr_backend_t *backend)
288 {
289     if (backend) {
290         mrp_free(backend);
291     }
292 }
293
294 const char **mrp_resmgr_backend_get_decision_names(void)
295 {
296     return decision_names;
297 }
298
299 uint32_t mrp_resmgr_backend_get_resource_connid(mrp_resmgr_resource_t *ar)
300 {
301     MRP_ASSERT(ar, "invalid argument");
302
303     return ar->connid;
304 }
305
306 uint32_t mrp_resmgr_backend_get_resource_connno(mrp_resmgr_resource_t *ar)
307 {
308     MRP_ASSERT(ar, "invalid argument");
309
310     return ar->connno;
311 }
312
313 int32_t mrp_resmgr_backend_get_resource_state(mrp_resmgr_resource_t *ar)
314 {
315     MRP_ASSERT(ar, "invalid argument");
316
317     if (get_resource_stamp(ar->res) == 0)
318         return 0;
319
320     return ar->state.current;
321 }
322
323 int32_t mrp_resmgr_backend_get_resource_decision_id(mrp_resmgr_resource_t *ar)
324 {
325     MRP_ASSERT(ar, "invalid argument");
326
327     if (get_resource_stamp(ar->res) == 0)
328         return 0;
329
330     return mrp_resmgr_sink_get_decision_id(ar->sink, ar->source);
331 }
332
333
334 uint32_t mrp_resmgr_backend_get_attribute_index(const char *name,
335                                                 mqi_data_type_t type)
336 {
337     mrp_attr_def_t *attrd;
338     uint32_t idx;
339
340     if (name) {
341         for (idx = 0;  (attrd = audio_attrs + idx)->name;  idx++) {
342             if (!strcmp(name, attrd->name)) {
343                 if (attrd->type == type)
344                     return idx;
345                 break;
346             }
347         } /* for attrd */
348     }
349
350     return MRP_RESMGR_RESOURCE_FIELD_INVALID;
351 }
352
353
354 int32_t mrp_resmgr_backend_get_integer_attribute(mrp_resmgr_resource_t *ar,
355                                                  uint32_t idx)
356 {
357     mrp_attr_t attr;
358
359     if (mrp_resource_read_attribute(ar->res, idx, &attr)) {
360         if (attr.type == mqi_integer)
361             return attr.value.integer;
362     }
363
364     return 0;
365 }
366
367 const char *mrp_resmgr_backend_get_string_attribute(mrp_resmgr_resource_t *ar,
368                                                     uint32_t idx)
369 {
370     mrp_attr_t attr;
371
372     if (mrp_resource_read_attribute(ar->res, idx, &attr)) {
373         if (attr.type == mqi_string)
374             return attr.value.string;
375     }
376
377     return "";
378 }
379
380
381 mrp_resmgr_resource_t *mrp_resmgr_backend_resource_list_entry(
382                                                    mrp_list_hook_t *entry)
383 {
384     MRP_ASSERT(entry, "invalid argument");
385
386     return mrp_list_entry(entry, mrp_resmgr_resource_t, source_link);
387 }
388
389
390
391
392
393
394 #if 0
395 int mrp_resmgr_backend_print(mrp_resmgr_backend_t *backend,
396                            uint32_t zoneid,
397                            char *buf, int len)
398 {
399 #define PRINT(...)                              \
400     do {                                        \
401         p += snprintf(p, e-p, __VA_ARGS__);     \
402         if (p >= e)                             \
403             return p - buf;                     \
404     } while (0)
405
406     char *p, *e;
407     uint32_t grantid;
408     mrp_list_hook_t *resources, *rentry, *rn;
409     mrp_resmgr_resource_t *ar;
410     mrp_attr_t a;
411     size_t i;
412     char disable[256];
413     char requisite[1024];
414
415     MRP_ASSERT(backend && buf && len > 0, "invalid argument");
416
417     e = (p = buf) + len;
418     *p = 0;
419
420     if (zoneid < MRP_ZONE_MAX) {
421         resources = backend->zones + zoneid;
422         grantid = backend->grantids[zoneid];
423     }
424     else {
425         resources = NULL;
426         grantid = 0;
427     }
428
429     PRINT("      Resource '%s' - grantid:%u\n",
430           MRP_SYSCTL_AUDIO_RESOURCE, grantid);
431
432     if (!resources || mrp_list_empty(resources))
433         PRINT("         No resources\n");
434     else {
435         mrp_list_foreach_back(resources, rentry, rn) {
436             ar = mrp_list_entry(rentry, mrp_resmgr_resource_t, link);
437
438             mrp_resmgr_disable_print(ar->disable, disable,
439                                      sizeof(disable));
440             mrp_application_requisite_print(ar->requisite, requisite,
441                                             sizeof(requisite));
442
443             PRINT("            "
444                   "key:0x%08x %s %s grantid:%u requisite:%s disable:%s",
445                   ar->key,
446                   ar->interrupt ? "interrupt" : "base",
447                   ar->acquire ? "acquire":"release",
448                   ar->grantid,
449                   requisite,
450                   disable);
451
452             for (i = 0;  i < MRP_ARRAY_SIZE(audio_attrs) - 1;  i++) {
453                 if ((mrp_resource_read_attribute(ar->res, i, &a))) {
454                     PRINT(" %s:", a.name);
455
456                     switch (a.type) {
457                     case mqi_string:   PRINT("'%s'",a.value.string); break;
458                     case mqi_integer:  PRINT("%d",a.value.integer);  break;
459                     case mqi_unsignd:  PRINT("%u",a.value.unsignd);  break;
460                     case mqi_floating: PRINT("%lf",a.value.floating);break;
461                     default:           PRINT("<unsupported type>");  break;
462                     }
463                 }
464             }
465
466             PRINT("\n");
467         } /* mrp_list_foreach_back - resources */
468     }
469
470     return p - buf;
471 }
472 #endif
473
474 static void make_resource_definition(mrp_resmgr_backend_t *backend,
475                                      int id,
476                                      const char *name)
477 {
478     resource_type_t *type = backend->types + id;
479
480     MRP_ASSERT(backend && id >= 0 && id < MRP_RESMGR_RESOURCE_TYPE_MAX,
481                "invalid attribute");
482
483     type->id = id;
484     type->name = mrp_strdup(name);
485     type->resid = mrp_resource_definition_create(type->name, true, /* share */
486                                                  audio_attrs, backend_ftbl[id],
487                                                  backend);
488
489     mrp_lua_resclass_create_from_c(type->resid);
490 }
491
492 static resource_type_t *find_resource_definition_by_id(
493                                              mrp_resmgr_backend_t *backend,
494                                              int id)
495 {
496     resource_type_t *type;
497     int i;
498
499     MRP_ASSERT(backend, "invalid argument");
500
501     for (i = 0;  i < MRP_RESMGR_RESOURCE_TYPE_MAX;  i++) {
502         type = backend->types + i;
503
504         if (type->id == id)
505             return type;
506     }
507
508     return NULL;
509 }
510
511 static int hash_compare(const void *key1, const void *key2)
512 {
513     if (key1 < key2)
514         return -1;
515     if (key1 > key2)
516         return 1;
517     return 0;
518 }
519
520 static uint32_t ptr_hash_function(const void *key)
521 {
522     return (uint32_t)(((size_t)key >> 4) & 0xffffffff);
523 }
524
525 static uint32_t id_hash_function(const void *key)
526 {
527     return (uint32_t)(key - (const void *)0);
528 }
529
530
531 #if 0
532 static const char *get_resource_appid(mrp_resource_t *res)
533 {
534     mrp_attr_t attr;
535     const char *appid;
536
537     if (!mrp_resource_read_attribute(res, APPID_ATTRIDX, &attr) ||
538         attr.type != mqi_string || !(appid = attr.value.string)  )
539         appid = NULL;
540
541     return appid;
542 }
543 #endif
544
545 static int32_t get_resource_sourceid(mrp_resource_t *res)
546 {
547     mrp_attr_t attr;
548
549     if (!mrp_resource_read_attribute(res, SRCID_ATTRIDX, &attr) ||
550         attr.type != mqi_integer)
551     {
552         return 0;
553     }
554
555     return attr.value.integer;
556 }
557
558 static int32_t get_resource_sinkid(mrp_resource_t *res)
559 {
560     mrp_attr_t attr;
561
562     if (!mrp_resource_read_attribute(res, SINKID_ATTRIDX, &attr) ||
563         attr.type != mqi_integer)
564     {
565         return 0;
566     }
567
568     return attr.value.integer;
569 }
570
571
572 static int32_t get_resource_connid(mrp_resource_t *res)
573 {
574     mrp_attr_t attr;
575
576     if (!mrp_resource_read_attribute(res, CONNID_ATTRIDX, &attr) ||
577         attr.type != mqi_integer)
578     {
579         return 0;
580     }
581
582     return attr.value.integer;
583 }
584
585 static int32_t get_resource_connno(mrp_resource_t *res)
586 {
587     mrp_attr_t attr;
588
589     if (!mrp_resource_read_attribute(res, CONNNO_ATTRIDX, &attr) ||
590         attr.type != mqi_integer)
591     {
592         return 0;
593     }
594
595     return attr.value.integer;
596 }
597
598 static int32_t get_resource_stamp(mrp_resource_t *res)
599 {
600     mrp_attr_t attr;
601
602     if (!mrp_resource_read_attribute(res, STAMP_ATTRIDX, &attr) ||
603         attr.type != mqi_integer)
604     {
605         return 0;
606     }
607
608     return attr.value.integer;
609 }
610
611 static bool set_resource_source_and_sink(mrp_resource_t *res,
612                                          mrp_resmgr_source_t *source,
613                                          mrp_resmgr_sink_t *sink)
614 {
615     const char *source_name;
616     const char *sink_name;
617     mrp_attr_t attrs[ATTR_MAX+1];
618
619     memset(attrs, 0, sizeof(attrs));
620
621     if (!mrp_resource_read_all_attributes(res, ATTR_MAX+1, attrs))
622         return false;
623
624     if (source && (source_name = mrp_resmgr_source_get_name(source)))
625         attrs[SRCNAM_ATTRIDX].value.string = source_name;
626
627     if (sink && (sink_name = mrp_resmgr_sink_get_name(sink)))
628         attrs[SINKNAM_ATTRIDX].value.string = sink_name;
629
630     if (mrp_resource_write_attributes(res, attrs) < 0)
631         return false;
632
633     return true;
634 }
635
636
637 static bool set_resource_stamp(mrp_resource_t *res, int32_t stamp)
638 {
639     mrp_attr_t attrs[ATTR_MAX+1];
640
641     memset(attrs, 0, sizeof(attrs));
642
643     if (!mrp_resource_read_all_attributes(res, ATTR_MAX+1, attrs))
644         return false;
645
646     attrs[STAMP_ATTRIDX].value.integer = stamp;
647
648     if (mrp_resource_write_attributes(res, attrs) < 0)
649         return false;
650
651     return true;
652 }
653
654 static bool set_resource_decision(mrp_resource_t *res, int32_t decision)
655 {
656     const char *decision_name;
657     mrp_attr_t attrs[ATTR_MAX+1];
658
659     if (decision < 0 || decision >= DECISION_MAX)
660         decision_name = "<error>";
661     else
662         decision_name = decision_names[decision];
663
664     memset(attrs, 0, sizeof(attrs));
665
666     if (!mrp_resource_read_all_attributes(res, ATTR_MAX+1, attrs))
667         return false;
668
669     attrs[DECISION_ATTRIDX].value.string = decision_name;
670
671     if (mrp_resource_write_attributes(res, attrs) < 0)
672         return false;
673
674     return true;
675 }
676
677
678 static void resource_create(mrp_resmgr_backend_t *backend,
679                             mrp_application_class_t *ac,
680                             mrp_zone_t *zone,
681                             mrp_resource_t *res)
682 {
683     mrp_resmgr_t *resmgr;
684     mrp_resmgr_resource_t *ar;
685     uint32_t zoneid;
686     uint32_t resid;
687     resource_type_t *type;
688     uint32_t srcid, sinkid;
689     mrp_resmgr_source_t *src;
690     mrp_resmgr_sink_t *sink;
691     int32_t connid;
692     int32_t connno;
693     char name[256];
694
695     MRP_UNUSED(ac);
696     MRP_UNUSED(zone);
697
698     MRP_ASSERT(backend && backend->resmgr && res, "invalid argument");
699
700     resmgr = backend->resmgr;
701     zoneid = mrp_zone_get_id(zone);
702     resid  = mrp_resource_get_id(res);
703     type   = find_resource_definition_by_id(backend, resid);
704     srcid  = get_resource_sourceid(res);
705     sinkid = get_resource_sinkid(res);
706     src    = (srcid  > 0) ? mrp_resmgr_source_find_by_id(resmgr, srcid):NULL;
707     sink   = (sinkid > 0) ? mrp_resmgr_sink_find_by_gam_id(resmgr,sinkid):NULL;
708     connid = get_resource_connid(res);
709     connno = get_resource_connno(res);
710
711     if (!type)
712         return;
713
714     resource_print_name(src, connno, name, sizeof(name));
715
716
717     if (!(ar = mrp_allocz(sizeof(mrp_resmgr_resource_t))))
718         return;
719
720     ar->name    = mrp_strdup(name);
721     ar->backend = backend;
722     ar->res     = res;
723     ar->type    = type;
724     ar->source  = src;
725     ar->sink    = sink;
726     ar->zoneid  = zoneid;
727     ar->connid  = connid;
728     ar->connno  = connno;
729
730     mrp_list_init(&ar->source_link);
731
732     if (!mrp_htbl_insert(backend->resources.by_pointer, res, ar)) {
733         mrp_log_error("gam-resource-manager: can't add resource %s"
734                       "to hash (by pointer)", ar->name);
735         mrp_free(ar);
736         return;
737     }
738
739     resource_register_by_id(backend, ar);
740
741     set_resource_source_and_sink(ar->res, ar->source, ar->sink);
742 }
743
744 static void resource_destroy(mrp_resmgr_backend_t *backend,
745                              mrp_zone_t *zone,
746                              mrp_resource_t *res)
747 {
748     mrp_resmgr_usecase_t *usecase;
749     mrp_resmgr_resource_t *ar;
750     mrp_htbl_t *hash;
751
752     MRP_ASSERT(backend && backend->resmgr && zone && res,"invalid argument");
753
754     if (!(hash = backend->resources.by_pointer) ||
755         !(ar = mrp_htbl_remove(hash, res, false)))
756     {
757         mrp_debug("failed to destroy audio resource: can't find it");
758         return;
759     }
760
761     mrp_debug("%s resource '%s' going to be destroyed",
762               ar->type->name, ar->name);
763
764     if (ar->connid > 0 && (hash = backend->resources.by_connid)) {
765         if (ar != mrp_htbl_remove(hash, NULL + ar->connid, false)) {
766             mrp_log_error("gam-resource-manager: confused with data "
767                           "structures when attempting to remove "
768                           "resource '%s' from ID hash", ar->name);
769         }
770     }
771
772     mrp_list_delete(&ar->source_link);
773
774     mrp_free((void *)ar->name);
775
776     mrp_free(ar);
777
778     usecase = mrp_resmgr_get_usecase(backend->resmgr);
779     mrp_resmgr_usecase_update(usecase);
780 }
781
782 static bool resource_acquire(mrp_resmgr_backend_t *backend,
783                              mrp_zone_t *zone,
784                              mrp_resource_t *res)
785 {
786     static int32_t stamp;
787
788     mrp_resmgr_resource_t *ar;
789
790     MRP_ASSERT(backend && zone && res, "invalid argument");
791
792     if (!(ar = resource_lookup_by_pointer(backend, res))) {
793         mrp_debug("failed to acquire audio resource: can't find it");
794         return false;
795     }
796
797     if (!set_resource_stamp(ar->res, ++stamp)) {
798         mrp_log_error("gam-resource-manager: failed to set 'stamp' "
799                       "property for '%s' when acquiring", ar->name);
800         return false;
801     }
802
803     return true;
804 }
805
806
807 static bool resource_release(mrp_resmgr_backend_t *backend,
808                              mrp_zone_t *zone,
809                              mrp_resource_t *res)
810 {
811     mrp_resmgr_resource_t *ar;
812
813     MRP_ASSERT(backend && zone && res, "invalid argument");
814
815     if (!(ar = resource_lookup_by_pointer(backend, res))) {
816         mrp_debug("failed to release audio resource: can't find it");
817         return false;
818     }
819
820     if (!set_resource_stamp(ar->res, 0)) {
821         mrp_log_error("gam-resource-manager: failed to set 'stamp' "
822                       "property for '%s' when releasing", ar->name);
823         return false;
824     }
825
826     return true;
827 }
828
829
830 static bool resource_register_by_id(mrp_resmgr_backend_t *backend,
831                                     mrp_resmgr_resource_t *ar)
832 {
833     if (ar->connid < 1)
834         return false;
835
836     if (!mrp_htbl_insert(backend->resources.by_connid, NULL + ar->connid, ar)) {
837         mrp_log_error("gam-resource-manager: can't add resource '%s'"
838                       "to hash (by connid)", ar->name);
839         return false;
840     }
841
842     if (!mrp_resmgr_source_add_resource(ar->source, &ar->source_link)) {
843         mrp_log_error("gam-resource-manager: can't add resource '%s'"
844                       "to source", ar->name);
845         return false;
846     }
847
848     return true;
849 }
850
851 static mrp_resmgr_resource_t *resource_lookup_by_pointer(mrp_resmgr_backend_t *backend,
852                                                          mrp_resource_t *res)
853 {
854     mrp_htbl_t *htbl;
855
856     if (!backend || !(htbl = backend->resources.by_pointer) || !res)
857         return NULL;
858
859     return mrp_htbl_lookup(htbl, res);
860 }
861
862 static size_t resource_print_name(mrp_resmgr_source_t *src,
863                                   uint32_t connno,
864                                   char *name,
865                                   size_t len)
866 {
867     size_t ret;
868
869     if (!src)
870         ret = snprintf(name, len, "<invalid>");
871     else if (connno < 2)
872         ret = snprintf(name, len, "%s", mrp_resmgr_source_get_name(src));
873     else {
874         ret = snprintf(name, len, "%s%d", mrp_resmgr_source_get_name(src),
875                        connno);
876     }
877
878     return ret;
879 }
880
881
882 static int decision_cb(void *key, void *object, void *user_data)
883 {
884     mrp_resmgr_backend_t  *backend = (mrp_resmgr_backend_t *)user_data;
885     mrp_resmgr_resource_t *ar = (mrp_resmgr_resource_t *)object;
886     mrp_resmgr_t *resmgr;
887     mrp_resmgr_usecase_t *usecase;
888     bool sink_available, source_available;
889     int32_t decision_new, state_new;
890     mrp_attr_t attrs[ATTR_MAX+1];
891     uint16_t src_id, sink_id;
892     uint32_t connid;
893     mrp_resmgr_source_t *src;
894     bool need_update;
895     char buf[512];
896
897     MRP_UNUSED(key);
898     MRP_UNUSED(user_data);
899
900     MRP_ASSERT(ar && ar->backend == backend, "confused with data structures");
901
902     if (!ar->sink || !ar->source || !ar->connno) {
903         memset(attrs, 0, sizeof(attrs));
904         need_update = false;
905
906         if (mrp_resource_read_all_attributes(ar->res, ATTR_MAX+1, attrs)) {
907             src_id = attrs[SRCID_ATTRIDX].value.integer;
908             sink_id = attrs[SINKID_ATTRIDX].value.integer;
909             connid = attrs[CONNID_ATTRIDX].value.integer;
910
911             if (!ar->source && src_id > 0) {
912                 src = mrp_resmgr_source_find_by_id(backend->resmgr, src_id);
913
914                 if (src && mrp_resmgr_source_add_resource(src,&ar->source_link)) {
915                     resource_print_name(src, ar->connno, buf, sizeof(buf));
916                     mrp_free(ar->name);
917
918                     ar->name = mrp_strdup(buf);
919                     ar->source = src;
920
921                     mrp_debug("update resource %s source to '%s'",
922                               ar->name, mrp_resmgr_source_get_name(src));
923
924                     need_update = true;
925                 }
926             }
927
928             if (!ar->sink && sink_id > 0) {
929                 ar->sink = mrp_resmgr_sink_find_by_gam_id(backend->resmgr,
930                                                           sink_id);
931                 mrp_debug("update resource %s sink to '%s'",
932                           ar->name, mrp_resmgr_sink_get_name(ar->sink));
933
934                 need_update = true;
935             }
936
937             if (!ar->connid && connid > 0) {
938                 ar->connid = connid;
939                 mrp_debug("update resource %s connid to %u",
940                           ar->name, connid);
941
942                 need_update = true;
943             }
944
945             if (need_update) {
946                 resmgr = backend->resmgr;
947
948                 if ((usecase = mrp_resmgr_get_usecase(resmgr)))
949                     mrp_resmgr_usecase_update(usecase);
950             }
951         }
952     }
953
954     source_available = mrp_resmgr_source_get_availability(ar->source);
955     sink_available = mrp_resmgr_sink_get_availability(ar->sink);
956
957     if (!source_available || !sink_available)
958         decision_new = DECISION_DISCONNECTED; /* or teardown ? */
959     else if (ar->connno != 0 || ar->connid < 1)
960         decision_new = DECISION_DISCONNECTED;
961     else
962         decision_new = mrp_resmgr_source_make_decision(ar->source);
963
964     if (decision_new < 0 || decision_new >= DECISION_MAX)
965         decision_new = ar->decision.current;
966
967     if (decision_new < 0 || decision_new >= DECISION_MAX)
968         decision_new = 0;
969
970     state_new = decision2state[decision_new];
971
972     ar->decision.new = decision_new;
973     ar->state.new = state_new;
974
975     print_decision(ar, buf, sizeof(buf));
976     mrp_debug("   %s", buf);
977
978     return MRP_HTBL_ITER_MORE;
979 }
980
981 static void make_decisions(mrp_resmgr_backend_t *backend)
982 {
983     mrp_resmgr_usecase_t *usecase;
984
985     usecase = mrp_resmgr_get_usecase(backend->resmgr);
986     mrp_resmgr_usecase_update(usecase);
987
988     mrp_htbl_foreach(backend->resources.by_pointer, decision_cb, backend);
989 }
990
991 static int commit_cb(void *key, void *object, void *user_data)
992 {
993     mrp_resmgr_backend_t  *backend = (mrp_resmgr_backend_t *)user_data;
994     mrp_resmgr_resource_t *ar = (mrp_resmgr_resource_t *)object;
995     char buf[256];
996
997     MRP_UNUSED(key);
998     MRP_UNUSED(user_data);
999
1000     MRP_ASSERT(ar && ar->backend == backend, "confused with data structures");
1001
1002     if (ar->source) {
1003         print_commit(ar, buf, sizeof(buf));
1004         mrp_debug("   %s", buf);
1005
1006         ar->decision.current = ar->decision.new;
1007         ar->state.current = ar->state.new;
1008
1009         set_resource_decision(ar->res, ar->decision.current);
1010     }
1011
1012     return MRP_HTBL_ITER_MORE;
1013 }
1014
1015 static void commit_decisions(mrp_resmgr_backend_t *backend)
1016 {
1017     mrp_resmgr_usecase_t *usecase;
1018
1019     mrp_htbl_foreach(backend->resources.by_pointer, commit_cb, backend);
1020
1021     usecase = mrp_resmgr_get_usecase(backend->resmgr);
1022     mrp_resmgr_usecase_update(usecase);
1023 }
1024
1025
1026 static size_t print_decision(mrp_resmgr_resource_t *ar, char *buf, size_t len)
1027 {
1028     char name [256];
1029     char decision[256];
1030     char state[256];
1031
1032     snprintf(name, sizeof(name), "%s:", ar->name);
1033
1034     if (ar->decision.new == ar->decision.current) {
1035         snprintf(decision, sizeof(decision), "%s (no change)",
1036                  decision_names[ar->decision.new]);
1037     }
1038     else {
1039         snprintf(decision, sizeof(decision), "%s => %s",
1040                  decision_names[ar->decision.current],
1041                  decision_names[ar->decision.new]);
1042     }
1043
1044     if (ar->state.new == ar->state.current)
1045         state[0] = 0;
1046     else {
1047         snprintf(state, sizeof(state), "%s => %s",
1048                  state_names[ar->state.current],
1049                  state_names[ar->state.new]);
1050     }
1051
1052     return snprintf(buf, len, "%-24s %-28s %s", name, decision, state);
1053 }
1054
1055
1056 static size_t print_commit(mrp_resmgr_resource_t *ar, char *buf, size_t len)
1057 {
1058     const char *decision;
1059     const char *state;
1060     char name[256];
1061
1062     snprintf(name, sizeof(name), "%s:", ar->name);
1063
1064     decision = decision_names[ar->decision.new];
1065     state = state_names[ar->state.new];
1066
1067     return snprintf(buf, len, "%-24s %-12s %s", name, decision, state);
1068 }
1069
1070
1071 static void backend_notify(mrp_resource_event_t event,
1072                            mrp_zone_t *zone,
1073                            mrp_application_class_t *ac,
1074                            mrp_resource_t *res,
1075                            void *userdata)
1076 {
1077     mrp_resmgr_backend_t *backend = (mrp_resmgr_backend_t *)userdata;
1078     const char *zonename = mrp_zone_get_name(zone);
1079
1080     MRP_ASSERT(zone && ac && res && backend, "invalid argument");
1081
1082     switch (event) {
1083
1084     case MRP_RESOURCE_EVENT_CREATED:
1085         mrp_debug("audio resource in zone '%s' created", zonename);
1086         resource_create(backend, ac, zone, res);
1087         break;
1088
1089     case MRP_RESOURCE_EVENT_DESTROYED:
1090         mrp_debug("audio resource in zone '%s' destroyed", zonename);
1091         resource_destroy(backend, zone, res);
1092         break;
1093
1094     case MRP_RESOURCE_EVENT_ACQUIRE:
1095         mrp_debug("audio resource in zone '%s' is acquiring", zonename);
1096         resource_acquire(backend, zone, res);
1097         break;
1098
1099     case MRP_RESOURCE_EVENT_RELEASE:
1100         mrp_debug("audio resource in zone '%s' is released", zonename);
1101         resource_release(backend, zone, res);
1102         break;
1103
1104     default:
1105         mrp_log_error("gam-resource-manager: invalid event %d at audio "
1106                       "notification (zone '%s')", event, zonename);
1107         break;
1108     }
1109 }
1110
1111 static void backend_init(mrp_zone_t *zone, void *userdata)
1112 {
1113     mrp_resmgr_backend_t *backend = (mrp_resmgr_backend_t *)userdata;
1114     // uint32_t zoneid;
1115     const char *zonename;
1116
1117     MRP_ASSERT(zone && backend && backend->resmgr, "invalid argument");
1118
1119     // zoneid   = mrp_zone_get_id(zone);
1120     zonename = mrp_zone_get_name(zone);
1121
1122     if (!zonename)
1123         zonename = "<unknown>";
1124
1125     mrp_debug("audio init in zone '%s'", zonename);
1126
1127     make_decisions(backend);
1128 }
1129
1130 static bool backend_allocate(mrp_zone_t *zone,
1131                             mrp_resource_t *res,
1132                             void *userdata)
1133 {
1134     mrp_resmgr_backend_t *backend = (mrp_resmgr_backend_t *)userdata;
1135     // uint32_t zoneid;
1136     const char *zonename;
1137     mrp_resmgr_resource_t *ar;
1138     bool allocated;
1139
1140     MRP_ASSERT(zone && res && backend && backend->resmgr, "invalid argument");
1141
1142     // zoneid = mrp_zone_get_id(zone);
1143
1144     if (!(zonename = mrp_zone_get_name(zone)))
1145         zonename = "<unknown>";
1146
1147     if ((ar = resource_lookup_by_pointer(backend, res))) {
1148         allocated = (ar->state.new == STATE_PLAY);
1149
1150         mrp_debug("%s allocation for '%s' in zone '%s' %s",
1151                   ar->type->name, ar->name, zonename,
1152                   allocated ? "succeeded":"failed");
1153
1154         return allocated;
1155     }
1156
1157     mrp_log_error("gam-resource-manager: attempt to allocate untracked "
1158                   "resource in zone '%s'", zonename);
1159
1160     return FALSE;
1161 }
1162
1163 static void backend_free(mrp_zone_t *zone, mrp_resource_t *res, void *userdata)
1164 {
1165     mrp_resmgr_backend_t *backend = (mrp_resmgr_backend_t *)userdata;
1166     const char *zonename;
1167     mrp_resmgr_resource_t *ar;
1168
1169     MRP_ASSERT(zone && res && backend, "invalid argument");
1170
1171     if (!(zonename = mrp_zone_get_name(zone)))
1172         zonename = "<unknown>";
1173
1174     if ((ar = resource_lookup_by_pointer(backend, res))) {
1175         ar->decision.new = DECISION_DISCONNECTED;
1176         ar->state.new = decision2state[ar->decision.new];
1177
1178         mrp_debug("free %s of '%s' in zone '%s'",
1179                   ar->type->name, ar->name, zonename);
1180
1181         return;
1182     }
1183
1184     mrp_log_error("gam-resource-manager: attempt to free untracked "
1185                   "resource in zone '%s'", zonename);
1186 }
1187
1188 static bool backend_advice(mrp_zone_t *zone,mrp_resource_t *res,void *userdata)
1189 {
1190 #if 1
1191     MRP_UNUSED(zone);
1192     MRP_UNUSED(res);
1193     MRP_UNUSED(userdata);
1194 #else
1195     mrp_resmgr_backend_t *backend = (mrp_resmgr_backend_t *)userdata;
1196     const char *zonename;
1197     const char *appid;
1198
1199     MRP_ASSERT(zone && res && backend, "invalid argument");
1200
1201     if (!(zonename = mrp_zone_get_name(zone)))
1202         zonename = "<unknown>";
1203     if (!(appid = get_resource_appid(res)))
1204         appid = "<unknown>";
1205
1206     mrp_debug("audio advice for '%s' in zone '%s'", appid, zonename);
1207 #endif
1208
1209     return TRUE;
1210 }
1211
1212 static void backend_commit(mrp_zone_t *zone, void *userdata)
1213 {
1214     mrp_resmgr_backend_t *backend = (mrp_resmgr_backend_t *)userdata;
1215     const char *zonename;
1216     // uint32_t zoneid;
1217
1218     MRP_ASSERT(zone && backend && backend->resmgr, "invalid argument");
1219
1220     // zoneid  = mrp_zone_get_id(zone);
1221
1222     if (!(zonename = mrp_zone_get_name(zone)))
1223         zonename = "<unknown>";
1224
1225     mrp_debug("audio commit in zone '%s'", zonename);
1226
1227     commit_decisions(backend);
1228 }