widen the name field in database to accommodate longer strings
[profile/ivi/murphy.git] / src / plugins / system-controller / resource-manager / audio.c
1 /*
2  * Copyright (c) 2012, 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 "audio.h"
44 #include "notifier.h"
45 #include "wayland/wayland.h"
46 #include "application/application.h"
47
48 #define ANY_ZONE  (~((uint32_t)0))
49
50 #ifdef BIT
51 #undef BIT
52 #endif
53 #ifdef MASK
54 #undef MASK
55 #endif
56 #ifdef MAX
57 #undef MAX
58 #endif
59
60 #define BIT(i)                ((uint32_t)1 << (i))
61 #define MASK(w)               (((uint32_t)1 << (w)) - 1)
62 #define MAX(w)                (((uint32_t)1 << (w)))
63
64 #define STAMP_BITS            13
65 #define PRIORITY_BITS         8
66 #define CLASSPRI_BITS         8
67 #define ACQUIRE_BITS          1
68 #define SHARE_BITS            1
69 #define INTERRUPT_BITS        1
70
71 #define STAMP_POSITION        0
72 #define PRIORITY_POSITION     (STAMP_POSITION + STAMP_BITS)
73 #define CLASSPRI_POSITION     (PRIORITY_POSITION + PRIORITY_BITS)
74 #define ACQUIRE_POSITION      (CLASSPRI_POSITION + CLASSPRI_BITS)
75 #define SHARE_POSITION        (ACQUIRE_POSITION + ACQUIRE_BITS)
76 #define INTERRUPT_POSITION    (SHARE_POSITION + SHARE_BITS)
77
78 #define STAMP_MASK            MASK(STAMP_BITS)
79 #define PRIORITY_MASK         MASK(PRIORITY_BITS)
80 #define CLASSPRI_MASK         MASK(CLASSPRI_BITS)
81 #define ACQUIRE_MASK          MASK(ACQUIRE_BITS)
82 #define SHARE_MASK            MASK(SHARE_BITS)
83 #define INTERRUPT_MASK        MASK(INTERRUPT_BITS)
84
85 #define STAMP_MAX             MAX(STAMP_BITS)
86 #define PRIORITY_MAX          MAX(PRIORITY_BITS)
87 #define CLASSPRI_MAX          MAX(CLASSPRI_BITS)
88 #define ACQUIRE_MAX           MAX(ACQUIRE_BITS)
89 #define SHARE_MAX             MAX(SHARE_BITS)
90 #define INTERRUPT_MAX         MAX(INTERRUPT_BITS)
91
92
93 #define ATTRIBUTE(n,t,v)    {n, MRP_RESOURCE_RW, mqi_##t, {.t=v}}
94 #define ATTR_END            {NULL, 0, 0, {.string=NULL}}
95
96 typedef struct audio_resource_s    audio_resource_t;
97 typedef struct disable_iterator_s  disable_iterator_t;
98
99 struct audio_resource_s {
100     mrp_list_hook_t link;
101     mrp_resmgr_audio_t *audio;
102     mrp_resource_t *res;
103     uint32_t zoneid;
104     uint32_t audioid; /* unique id. Not to confuse with resource-set id */
105     bool interrupt;
106     uint32_t classpri;
107     uint32_t key;
108     bool acquire;
109     bool grant;
110     uint32_t grantid;
111     mrp_application_requisite_t requisite;
112     mrp_resmgr_disable_t disable;
113 };
114
115 struct disable_iterator_s {
116     uint32_t zoneid;
117     const char *appclass;
118     bool disable;
119     mrp_resmgr_disable_t type;
120     uint32_t mask;
121     union {
122         const char *appid;
123         mrp_application_requisite_t req;
124     };
125     uint32_t zones;
126     int counter;
127 };
128
129 static int hash_compare(const void *, const void *);
130 static uint32_t hash_function(const void *);
131
132 static const char *get_appid_for_resource(mrp_resource_t *);
133 static mrp_application_t *get_application_for_resource(mrp_resource_t *);
134 static uint32_t get_priority_for_resource(mrp_resource_t *);
135 static uint32_t get_class_priority_for_resource(mrp_resource_t *);
136
137
138 static audio_resource_t *audio_resource_create(mrp_resmgr_audio_t *,
139                                                  mrp_zone_t *,mrp_resource_t *,
140                                                  mrp_application_class_t *);
141 static void audio_resource_destroy(mrp_resmgr_audio_t *,  mrp_zone_t *,
142                                     mrp_resource_t *);
143 static audio_resource_t *audio_resource_lookup(mrp_resmgr_audio_t *,
144                                                  mrp_resource_t *);
145
146 static void audio_insert_resource(audio_resource_t *);
147 static void audio_grant_resources(mrp_resmgr_audio_t *, mrp_zone_t *);
148 static void audio_queue_events(mrp_resmgr_audio_t *, mrp_zone_t *);
149
150 static uint32_t resource_key(audio_resource_t *);
151
152 static void audio_notify(mrp_resource_event_t, mrp_zone_t *,
153                           mrp_application_class_t *, mrp_resource_t *, void *);
154 static void audio_init(mrp_zone_t *, void *);
155 static bool audio_allocate(mrp_zone_t *, mrp_resource_t *, void *);
156 static void audio_free(mrp_zone_t *, mrp_resource_t *, void *);
157 static bool audio_advice(mrp_zone_t *, mrp_resource_t *, void *);
158 static void audio_commit(mrp_zone_t *, void *);
159
160 #define PRIORITY_ATTRIDX  0
161 #define CLASSPRI_ATTRIDX  1
162 #define APPID_ATTRIDX     2
163 #define ROLE_ATTRIDX      3
164 #define PID_ATTRIDX       4
165 #define POLICY_ATTRIDX    5
166
167 static mrp_attr_def_t audio_attrs[] = {
168     ATTRIBUTE("priority" , integer,       0      ),
169     ATTRIBUTE("classpri" , integer,      -1      ),
170     ATTRIBUTE("appid"    , string , "<undefined>"),
171     ATTRIBUTE("role"     , string , "music"      ),
172     ATTRIBUTE("pid"      , string , "<unknown>"  ),
173     ATTRIBUTE("policy"   , string , "relaxed"    ),
174     ATTRIBUTE("name"     , string , "<unknown>"  ),
175     ATTR_END
176 };
177
178 static mrp_resource_mgr_ftbl_t audio_ftbl = {
179     audio_notify,
180     audio_init,
181     audio_allocate,
182     audio_free,
183     audio_advice,
184     audio_commit
185 };
186
187 mrp_resmgr_audio_t *mrp_resmgr_audio_create(mrp_resmgr_t *resmgr)
188 {
189     mrp_resmgr_audio_t *audio;
190     mrp_htbl_config_t cfg;
191     uint32_t resid;
192     size_t i;
193
194     if ((audio = mrp_allocz(sizeof(mrp_resmgr_audio_t)))) {
195         resid = mrp_resource_definition_create(MRP_SYSCTL_AUDIO_RESOURCE,
196                                                true,audio_attrs,
197                                                &audio_ftbl, audio);
198         mrp_lua_resclass_create_from_c(resid);
199
200         cfg.nentry = MRP_RESMGR_RESOURCE_MAX;
201         cfg.comp = hash_compare;
202         cfg.hash = hash_function;
203         cfg.free = NULL;
204         cfg.nbucket = MRP_RESMGR_RESOURCE_BUCKETS;
205
206         audio->resmgr = resmgr;
207         audio->resid = resid;
208         audio->resources = mrp_htbl_create(&cfg);
209
210         for (i = 0;  i < MRP_ZONE_MAX;  i++)
211             mrp_list_init(audio->zones + i);
212     }
213
214     return audio;
215 }
216
217 void mrp_resmgr_audio_destroy(mrp_resmgr_audio_t *audio)
218 {
219     if (audio) {
220         mrp_free(audio);
221     }
222 }
223
224 static int audio_disable_cb(void *key, void *object, void *user_data)
225 {
226     audio_resource_t *ar = (audio_resource_t *)object;
227     disable_iterator_t *it = (disable_iterator_t *)user_data;
228     const char *appid;
229     const char *appclass;
230     uint32_t disable;
231
232     MRP_UNUSED(key);
233
234     MRP_ASSERT(ar && it, "invalid argument");
235
236     if ((appclass = mrp_resource_get_application_class(ar->res))) {
237         if ((  it->zoneid == ANY_ZONE    ||     ar->zoneid == it->zoneid   ) &&
238             (!strcmp(it->appclass, "*")  || !strcmp(appclass, it->appclass))  )
239         {
240             switch (it->type) {
241
242             case MRP_RESMGR_DISABLE_REQUISITE:
243                 if (it->req && (it->req & ar->requisite) == it->req)
244                     goto disable;
245                 break;
246                 
247             case MRP_RESMGR_DISABLE_APPID:
248                 if (it->appid) {
249                     if (!strcmp(it->appid, "*"))
250                         goto disable;
251                     appid = get_appid_for_resource(ar->res);
252                     if (appid && !strcmp(it->appid, appid))
253                         goto disable;
254                 }
255                 break;
256                 
257             disable:
258                 disable = ar->disable & it->mask;
259                 if (it->disable) {
260                     if (disable)
261                         break;
262                     ar->disable |= it->mask;
263                 }
264                 else {
265                     if (!disable)
266                     break;
267                     ar->disable &= ~it->mask;
268                 }
269                 it->counter++;
270                 it->zones |= (((uint32_t)1) << ar->zoneid);
271                 break;
272                 
273             default:
274                 return MRP_HTBL_ITER_STOP;
275             }
276         }
277     }
278     
279     return MRP_HTBL_ITER_MORE;
280 }
281
282 int mrp_resmgr_audio_disable(mrp_resmgr_audio_t *audio,
283                              const char *zone_name,
284                              const char *application_class,
285                              bool disable,
286                              mrp_resmgr_disable_t type,
287                              void *data,
288                              bool recalc_owner)
289 {
290     const char *zone_names[MRP_ZONE_MAX + 1];
291     disable_iterator_t it;
292     uint32_t i;
293     uint32_t zone_id = ANY_ZONE;
294     uint32_t mask;
295     uint32_t z;
296
297     MRP_ASSERT(audio && data && zone_name && application_class,
298                "invalid argument");
299
300     mrp_debug("zone_name='%s' application_class='%s' %s type=0x%02x data=%p"
301               "recalc_owner=%s",
302               zone_name ? zone_name : "<any zone>",
303               application_class ? application_class : "<any class>",
304               disable ? "disable" : "enable",
305               type, data, recalc_owner ? "true" : "false");
306
307     if (zone_name && strcmp(zone_name, "*")) {
308         if (mrp_zone_get_all_names(MRP_ZONE_MAX + 1, zone_names)) {
309             for (i = 0;  zone_names[i];  i++) {
310                 if (!strcmp(zone_name, zone_names[i])) {
311                     zone_id = i;
312                     break;
313                 }
314             }
315         }
316         if (zone_id == ANY_ZONE) {
317             mrp_log_error("system-controller: failed to disable audio: "
318                           "can't find zone '%s'", zone_name);
319             return -1;
320         }
321     }
322
323     memset(&it, 0, sizeof(it));
324     it.zoneid = zone_id;
325     it.appclass = application_class;
326     it.disable = disable;
327     it.type = type;
328     it.mask = BIT(type - 1);
329     it.zones = 0;
330     it.counter = 0;
331     
332     switch (type) {
333
334     case MRP_RESMGR_DISABLE_REQUISITE:
335         it.req = *(mrp_application_requisite_t *)data;
336         break;
337
338     case MRP_RESMGR_DISABLE_APPID:
339         it.appid = (const char *)data;
340         break;
341
342     default:
343         mrp_log_error("system-controller: invalid type %d of "
344                       "audio disable", type);
345         return -1;
346     }
347
348     mrp_htbl_foreach(audio->resources, audio_disable_cb, &it);
349
350     if (recalc_owner) {
351         for (z = 0;   it.zones && z < MRP_ZONE_MAX;   z++) {
352             mask = (((uint32_t)1) << z);
353             
354             if ((mask & it.zones)) {
355                 it.zones &= ~mask;
356                 mrp_resource_owner_recalc(z);
357             }
358         }
359     }
360
361     return it.counter;
362 }
363
364
365 int mrp_resmgr_audio_print(mrp_resmgr_audio_t *audio,
366                            uint32_t zoneid,
367                            char *buf, int len)
368 {
369 #define PRINT(...)                              \
370     do {                                        \
371         p += snprintf(p, e-p, __VA_ARGS__);     \
372         if (p >= e)                             \
373             return p - buf;                     \
374     } while (0)
375
376     char *p, *e;
377     uint32_t grantid;
378     mrp_list_hook_t *resources, *rentry, *rn;
379     audio_resource_t *ar;
380     mrp_attr_t a;
381     size_t i;
382     char disable[256];
383     char requisite[1024];
384
385     MRP_ASSERT(audio && buf && len > 0, "invalid argument");
386
387     e = (p = buf) + len;
388     *p = 0;
389     
390     if (zoneid < MRP_ZONE_MAX) {
391         resources = audio->zones + zoneid;
392         grantid = audio->grantids[zoneid];
393     }
394     else {
395         resources = NULL;
396         grantid = 0;
397     }
398
399     PRINT("      Resource '%s' - grantid:%u\n",
400           MRP_SYSCTL_AUDIO_RESOURCE, grantid);
401
402     if (!resources || mrp_list_empty(resources))
403         PRINT("         No resources\n");
404     else {
405         mrp_list_foreach_back(resources, rentry, rn) {
406             ar = mrp_list_entry(rentry, audio_resource_t, link);
407
408             mrp_resmgr_disable_print(ar->disable, disable,
409                                      sizeof(disable));
410             mrp_application_requisite_print(ar->requisite, requisite,
411                                             sizeof(requisite));
412
413             PRINT("            "
414                   "key:0x%08x %s %s grantid:%u requisite:%s disable:%s",
415                   ar->key,
416                   ar->interrupt ? "interrupt" : "base",
417                   ar->acquire ? "acquire":"release",
418                   ar->grantid,
419                   requisite,
420                   disable);
421
422             for (i = 0;  i < MRP_ARRAY_SIZE(audio_attrs) - 1;  i++) {
423                 if ((mrp_resource_read_attribute(ar->res, i, &a))) {
424                     PRINT(" %s:", a.name);
425
426                     switch (a.type) {
427                     case mqi_string:   PRINT("'%s'",a.value.string); break;
428                     case mqi_integer:  PRINT("%d",a.value.integer);  break;
429                     case mqi_unsignd:  PRINT("%u",a.value.unsignd);  break;
430                     case mqi_floating: PRINT("%lf",a.value.floating);break;
431                     default:           PRINT("<unsupported type>");  break;
432                     }
433                 }
434             }
435
436             PRINT("\n");
437         } /* mrp_list_foreach_back - resources */
438     }
439
440     return p - buf;
441 }
442
443
444 static int hash_compare(const void *key1, const void *key2)
445 {
446     if (key1 < key2)
447         return -1;
448     if (key1 > key2)
449         return 1;
450     return 0;
451 }
452
453 static uint32_t hash_function(const void *key)
454 {
455     return (uint32_t)(key - (const void *)0);
456 }
457
458 static const char *get_appid_for_resource(mrp_resource_t *res)
459 {
460     mrp_attr_t attr;
461     const char *appid;
462
463     if (!mrp_resource_read_attribute(res, APPID_ATTRIDX, &attr) ||
464         attr.type != mqi_string || !(appid = attr.value.string)  )
465         appid = NULL;
466
467     return appid;
468 }
469
470 static mrp_application_t *get_application_for_resource(mrp_resource_t *res)
471 {
472     const char        *appid = get_appid_for_resource(res);
473     mrp_application_t *app   = NULL;
474
475     if (!appid || !(app = mrp_application_find(appid)))
476         app = mrp_application_find(MRP_SYSCTL_APPID_DEFAULT);
477
478     return app;
479 }
480
481 static uint32_t get_priority_for_resource(mrp_resource_t *res)
482 {
483     mrp_attr_t attr;
484     uint32_t priority = 0;
485
486     if (mrp_resource_read_attribute(res, PRIORITY_ATTRIDX, &attr)) {
487         if (attr.type == mqi_integer && attr.value.integer >= 0)
488             priority = attr.value.integer;
489     }
490
491     return priority;
492 }
493
494 static uint32_t get_class_priority_for_resource(mrp_resource_t *res)
495 {
496     mrp_attr_t attr;
497     uint32_t priority = 0;
498
499     if (mrp_resource_read_attribute(res, CLASSPRI_ATTRIDX, &attr)) {
500         if (attr.type == mqi_integer && attr.value.integer >= 0)
501             priority = attr.value.integer < 0 ? 0 : attr.value.integer;
502     }
503
504     return priority;
505 }
506
507 static audio_resource_t *audio_resource_create(mrp_resmgr_audio_t *audio,
508                                                mrp_zone_t *zone,
509                                                mrp_resource_t *res,
510                                                mrp_application_class_t *ac)
511 {
512     static uint32_t audioid;
513
514     const char *zonename;
515     const char *appid;
516     const char *class_name;
517     mrp_resmgr_t *resmgr;
518     mrp_application_t *app;
519     audio_resource_t *ar;
520     void *hk;
521
522     MRP_ASSERT(audio && zone && res && ac, "invalid argument");
523     MRP_ASSERT(audio->resmgr, "confused with data structures");
524
525     resmgr = audio->resmgr;
526     zonename = mrp_zone_get_name(zone);
527     appid = get_appid_for_resource(res);
528     class_name = mrp_application_class_get_name(ac);
529     ar = NULL;
530
531     if (!(app = get_application_for_resource(res))) {
532         mrp_log_error("system-controller: failed to create audio resource: "
533                       "can't find app");
534         return NULL;
535     }
536             
537     if (!(ar = mrp_allocz(sizeof(*ar)))) {
538         mrp_log_error("system-controller: failed to create audio resource: "
539                       "can't allocate memory");
540         return NULL;
541     }
542
543     mrp_list_init(&ar->link);
544     ar->audio     = audio;
545     ar->res       = res;
546     ar->zoneid    = mrp_zone_get_id(zone);
547     ar->audioid   = audioid++;
548     ar->interrupt = strcmp(class_name, "player") && strcmp(class_name, "base");
549     ar->key       = resource_key(ar);
550     ar->requisite = app->requisites.audio;
551
552     audio_insert_resource(ar);
553                     
554     mrp_debug("inserting resource to hash table: key=%p value=%p", res, ar);
555     mrp_resmgr_insert_resource(resmgr, zone, res, ar);
556
557     hk = NULL + ar->audioid;
558     mrp_debug("inserting audio to hash table: key=%p value=%p", hk, ar);
559     mrp_htbl_insert(audio->resources, hk, ar);
560
561     mrp_resmgr_notifier_queue_audio_event(audio->resmgr, ar->zoneid, zonename,
562                                           MRP_RESMGR_EVENTID_CREATE,
563                                           appid, ar->audioid);
564     mrp_resmgr_notifier_flush_audio_events(audio->resmgr, ar->zoneid);
565
566     return ar;
567 }
568
569
570 static void audio_resource_destroy(mrp_resmgr_audio_t *audio,
571                                     mrp_zone_t *zone,
572                                     mrp_resource_t *res)
573 {
574     audio_resource_t *ar;
575     const char *zonename;
576     const char *appid;
577
578     MRP_ASSERT(audio && res && zone, "invalid argument");
579     MRP_ASSERT(audio->resmgr, "confused with data structures");
580
581     if ((ar = mrp_resmgr_remove_resource(audio->resmgr, zone, res))) {
582         zonename = mrp_zone_get_name(zone);
583         appid = get_appid_for_resource(res);
584
585         mrp_resmgr_notifier_queue_audio_event(audio->resmgr,
586                                               ar->zoneid, zonename,
587                                               MRP_RESMGR_EVENTID_DESTROY,
588                                               appid, ar->audioid);
589
590         mrp_htbl_remove(audio->resources, NULL + ar->audioid, false);
591
592         mrp_list_delete(&ar->link);
593
594         mrp_resmgr_notifier_flush_audio_events(audio->resmgr, ar->zoneid);
595         mrp_free(ar);
596     }
597 }
598
599
600 static audio_resource_t *audio_resource_lookup(mrp_resmgr_audio_t *audio,
601                                                mrp_resource_t *res)
602 {
603     audio_resource_t *ar;
604
605     MRP_ASSERT(audio && res, "invalid argument");
606     MRP_ASSERT(audio->resmgr, "confused with data structures");
607
608     ar = mrp_resmgr_lookup_resource(audio->resmgr, res);
609
610     return ar;
611 }
612
613 static void audio_insert_resource(audio_resource_t *resource)
614 {
615     mrp_resmgr_audio_t *audio;
616     mrp_list_hook_t *resources, *insert_after, *n;
617     audio_resource_t *ar;
618     uint32_t key;
619
620     mrp_list_delete(&resource->link);
621
622     audio = resource->audio;
623     resources = audio->zones + resource->zoneid;
624     key = resource->key;
625     insert_after = resources;   /* keep the compiler happy: foreach below
626                                    will do it anyways */
627
628     mrp_list_foreach_back(resources, insert_after, n) {
629         ar = mrp_list_entry(insert_after, audio_resource_t, link);
630         if (key >= ar->key)
631             break;
632     }
633
634     mrp_list_insert_after(insert_after, &resource->link);
635 }
636
637 static void audio_grant_resources(mrp_resmgr_audio_t *audio,
638                                   mrp_zone_t *zone)
639 {
640     uint32_t zoneid;
641     const char *zonename;
642     uint32_t grantid;
643     mrp_list_hook_t *resources, *rentry , *rn;
644     audio_resource_t *ar;
645     const char *appid;
646
647     zoneid = mrp_zone_get_id(zone);
648     zonename = mrp_zone_get_name(zone);
649
650     /* We got a nonsense zone id */
651     if (zoneid >= MRP_ZONE_MAX) {
652         mrp_debug("invalid zoneid '%" PRIu32 "' is larger than MRP_ZONE_MAX (%d), "
653                   "bailing", zoneid, MRP_ZONE_MAX);
654         return;
655     }
656
657     if (!zonename)
658         zonename = "<unknown>";
659
660     resources = audio->zones + zoneid;
661     grantid = ++(audio->grantids[zoneid]);
662
663     mrp_list_foreach_back(resources, rentry, rn) {
664         ar = mrp_list_entry(rentry, audio_resource_t, link);
665
666         MRP_ASSERT(ar->res, "confused with data structures");
667
668         if (ar->acquire && !ar->disable) {
669             if (!(appid = get_appid_for_resource(ar->res)))
670                 appid = "<unknown>";
671
672             mrp_debug("preallocate audio resource for '%s' in zone '%s'",
673                       appid, zonename);
674
675             ar->grantid = grantid;
676
677             mrp_resmgr_notifier_queue_audio_event(audio->resmgr,
678                                                 zoneid, zonename,
679                                                 MRP_RESMGR_EVENTID_PREALLOCATE,
680                                                 appid, ar->audioid);
681
682             if (!mrp_resource_is_shared(ar->res))
683                 break;
684         }
685     }
686 }
687
688 static void audio_queue_events(mrp_resmgr_audio_t *audio, mrp_zone_t *zone)
689 {
690     uint32_t zoneid;
691     const char *zonename;
692     uint32_t grantid;
693     mrp_list_hook_t *resources, *rentry, *rn;
694     audio_resource_t *ar;
695     bool grant;
696     mrp_resmgr_eventid_t eventid;
697     const char *appid;
698
699     zoneid    = mrp_zone_get_id(zone);
700     zonename  = mrp_zone_get_name(zone);
701
702     /* We got a nonsense zone id */
703     if (zoneid >= MRP_ZONE_MAX) {
704         mrp_debug("invalid zoneid '%" PRIu32 "' is larger than MRP_ZONE_MAX (%d), "
705                   "bailing", zoneid, MRP_ZONE_MAX);
706         return;
707     }
708
709     resources = audio->zones + zoneid;
710     grantid   = audio->grantids[zoneid];
711     
712     mrp_list_foreach_back(resources, rentry, rn) {
713         ar = mrp_list_entry(rentry, audio_resource_t, link);
714         grant = (grantid == ar->grantid);
715
716         if ((grant && !ar->grant) || (!grant && ar->grant)) {
717             eventid = grant ? MRP_RESMGR_EVENTID_GRANT :
718                               MRP_RESMGR_EVENTID_REVOKE;
719             appid = get_appid_for_resource(ar->res);
720
721             mrp_resmgr_notifier_queue_audio_event(audio->resmgr,
722                                                   zoneid, zonename,
723                                                   eventid,
724                                                   appid, ar->audioid);
725         }
726
727         ar->grant = grant;
728     }
729 }
730
731
732 static uint32_t resource_key(audio_resource_t *ar)
733 {
734     uint32_t stamp;
735     uint32_t priority;
736     uint32_t classpri;
737     uint32_t acquire;
738     uint32_t share;
739     uint32_t interrupt;
740     uint32_t key = 0;
741
742     if (ar) {
743         stamp     = 0; /* for now */
744         priority  = get_priority_for_resource(ar->res);
745         classpri  = get_class_priority_for_resource(ar->res);
746         acquire   = ar->acquire ? 1 : 0;
747         share     = mrp_resource_is_shared(ar->res) ? 1 : 0;
748         interrupt = ar->interrupt ? 1 : 0;
749
750         key = ((stamp     & STAMP_MASK    ) << STAMP_POSITION    ) |
751               ((priority  & PRIORITY_MASK ) << PRIORITY_POSITION ) |
752               ((classpri  & CLASSPRI_MASK ) << CLASSPRI_POSITION ) |
753               ((acquire   & ACQUIRE_MASK  ) << ACQUIRE_POSITION  ) |
754               ((share     & SHARE_MASK    ) << SHARE_POSITION    ) |
755               ((interrupt & INTERRUPT_MASK) << INTERRUPT_POSITION) ;
756     }
757
758     return key;
759 }
760
761 static void audio_notify(mrp_resource_event_t event,
762                           mrp_zone_t *zone,
763                           mrp_application_class_t *ac,
764                           mrp_resource_t *res,
765                           void *userdata)
766 {
767     mrp_resmgr_audio_t *audio = (mrp_resmgr_audio_t *)userdata;
768     const char *zonename = mrp_zone_get_name(zone);
769     audio_resource_t *ar;
770
771     MRP_ASSERT(zone && ac && res && audio, "invalid argument");
772
773     switch (event) {
774
775     case MRP_RESOURCE_EVENT_CREATED:
776         mrp_debug("audio resource in zone '%s' created", zonename);
777         audio_resource_create(audio, zone, res, ac);
778         break;
779
780     case MRP_RESOURCE_EVENT_DESTROYED:
781         mrp_debug("audio resource in zone '%s' destroyed", zonename);
782         audio_resource_destroy(audio, zone, res);
783         break;
784
785     case MRP_RESOURCE_EVENT_ACQUIRE:
786         mrp_debug("audio resource in zone '%s' is acquiring", zonename);
787         if (!(ar = audio_resource_lookup(audio, res)))
788             goto no_audio_resource;
789         else
790             if (!ar->acquire) {
791                 ar->acquire = true;
792                 ar->key = resource_key(ar);
793                 audio_insert_resource(ar);
794             }
795         break;
796
797     case MRP_RESOURCE_EVENT_RELEASE:
798         mrp_debug("audio resource in zone '%s' is released", zonename);
799         if (!(ar = audio_resource_lookup(audio, res)))
800             goto no_audio_resource;
801         else
802             if (ar->acquire) {
803                 ar->acquire = false;
804                 ar->key = resource_key(ar);
805                 audio_insert_resource(ar);
806             }
807         break;
808
809     no_audio_resource:
810         mrp_debug("resource lookup in hash table failed: key=%p", res);
811         mrp_log_error("system-controller: can't find audio resource "
812                       "in zone '%s'", zonename);
813         break;
814
815     default:
816         mrp_log_error("system-controller: invalid event %d at audio "
817                       "notification (zone '%s')", event, zonename);
818         break;
819     }
820 }
821
822 static void audio_init(mrp_zone_t *zone, void *userdata)
823 {
824     mrp_resmgr_audio_t *audio = (mrp_resmgr_audio_t *)userdata;
825     uint32_t zoneid;
826     const char *zonename;
827
828     MRP_ASSERT(zone && audio, "invalid argument");
829
830     zoneid   = mrp_zone_get_id(zone);
831     zonename = mrp_zone_get_name(zone);
832
833     /* We got a nonsense zone id */
834     if (zoneid >= MRP_ZONE_MAX) {
835         mrp_debug("invalid zoneid '%" PRIu32 "' is larger than MRP_ZONE_MAX (%d), "
836                   "bailing", zoneid, MRP_ZONE_MAX);
837         return;
838     }
839
840     if (!zonename)
841         zonename = "<unknown>";
842
843     mrp_debug("audio init in zone '%s'", zonename);
844
845     mrp_resmgr_notifier_queue_audio_event(audio->resmgr, zoneid, zonename,
846                                           MRP_RESMGR_EVENTID_INIT,
847                                           "<unknown>", -1);
848     audio_grant_resources(audio, zone);
849
850     mrp_resmgr_notifier_flush_audio_events(audio->resmgr, zoneid);
851 }
852
853 static bool audio_allocate(mrp_zone_t *zone,
854                             mrp_resource_t *res,
855                             void *userdata)
856 {
857     mrp_resmgr_audio_t *audio = (mrp_resmgr_audio_t *)userdata;
858     uint32_t zoneid;
859     const char *zonename;
860     const char *appid;
861     audio_resource_t *ar;
862     uint32_t grantid;
863     bool allocated;
864
865     MRP_ASSERT(zone && res && audio && audio->resmgr, "invalid argument");
866
867     zoneid  = mrp_zone_get_id(zone);
868
869     /* We got a nonsense zone id */
870     if (zoneid >= MRP_ZONE_MAX) {
871         mrp_debug("invalid zoneid '%" PRIu32 "' is larger than MRP_ZONE_MAX (%d), "
872                   "bailing", zoneid, MRP_ZONE_MAX);
873         return FALSE;
874     }
875
876     grantid = audio->grantids[zoneid];
877
878     if (!(zonename = mrp_zone_get_name(zone)))
879         zonename = "<unknown>";
880     if (!(appid = get_appid_for_resource(res)))
881         appid = "<unknown>";
882
883     if ((ar = audio_resource_lookup(audio, res))) {
884         allocated = (ar->grantid == grantid);
885
886         mrp_debug("audio allocation for '%s' in zone '%s' %s",
887                   zonename, appid, allocated ? "succeeded":"failed");
888
889         return allocated;
890     }
891
892     mrp_log_error("system-controller: attempt to allocate untracked "
893                   "resource '%s' in zone '%s'", appid, zonename);
894
895     return FALSE;
896 }
897
898 static void audio_free(mrp_zone_t *zone, mrp_resource_t *res, void *userdata)
899 {
900     mrp_resmgr_audio_t *audio = (mrp_resmgr_audio_t *)userdata;
901     const char *zonename;
902     const char *appid;
903     audio_resource_t *ar;
904
905     MRP_ASSERT(zone && res && audio, "invalid argument");
906
907     if (!(zonename = mrp_zone_get_name(zone)))
908         zonename = "<unknown>";
909     if (!(appid = get_appid_for_resource(res)))
910         appid = "<unknown>";
911
912     mrp_debug("free audio of '%s' in zone '%s'", appid, zonename);
913
914     if ((ar = audio_resource_lookup(audio, res)))
915         ar->grantid = 0;
916 }
917
918 static bool audio_advice(mrp_zone_t *zone,mrp_resource_t *res,void *userdata)
919 {
920     mrp_resmgr_audio_t *audio = (mrp_resmgr_audio_t *)userdata;
921     const char *zonename;
922     const char *appid;
923
924     MRP_ASSERT(zone && res && audio, "invalid argument");
925
926     if (!(zonename = mrp_zone_get_name(zone)))
927         zonename = "<unknown>";
928     if (!(appid = get_appid_for_resource(res)))
929         appid = "<unknown>";
930
931     mrp_debug("audio advice for '%s' in zone '%s'", appid, zonename);
932
933     return TRUE;
934 }
935
936 static void audio_commit(mrp_zone_t *zone, void *userdata)
937 {
938     mrp_resmgr_audio_t *audio = (mrp_resmgr_audio_t *)userdata;
939     const char *zonename;
940     uint32_t zoneid;
941
942     MRP_ASSERT(zone && audio && audio->resmgr, "invalid argument");
943
944     zoneid  = mrp_zone_get_id(zone);
945
946     /* We got a nonsense zone id */
947     if (zoneid >= MRP_ZONE_MAX) {
948         mrp_debug("invalid zoneid '%" PRIu32 "' is larger than MRP_ZONE_MAX (%d), "
949                   "bailing", zoneid, MRP_ZONE_MAX);
950         return;
951     }
952
953     if (!(zonename = mrp_zone_get_name(zone)))
954         zonename = "<unknown>";
955
956     mrp_debug("audio commit in zone '%s'", zonename);
957
958     audio_queue_events(audio, zone);
959     mrp_resmgr_notifier_flush_audio_events(audio->resmgr, zoneid);
960 }