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