pump up version number and update changelog
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / audiomgr.c
1 /*
2  * module-murphy-ivi -- PulseAudio module for providing audio routing support
3  * Copyright (c) 2012, Intel Corporation.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU Lesser General Public License,
7  * version 2.1, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.
12  * See the GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
17  * MA 02110-1301 USA.
18  *
19  */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24
25 #include <pulsecore/pulsecore-config.h>
26
27 #include <pulse/def.h>
28
29 #include <pulsecore/core-util.h>
30
31 #include "userdata.h"
32 #include "audiomgr.h"
33 #include "node.h"
34 #include "discover.h"
35 #include "router.h"
36 #include "routerif.h"
37
38 #define AUDIOMGR_DOMAIN   "PULSE"
39 #define AUDIOMGR_NODE     "pulsePlugin"
40
41 /*
42  * these must match their counterpart
43  * in audiomanagertypes.h
44  */
45 /* domain status */
46 #define DS_UNKNOWN        0
47 #define DS_CONTROLLED     1
48 #define DS_RUNDOWN        2
49 #define DS_DOWN           255
50
51 /* interrupt state */
52 #define IS_OFF            1
53 #define IS_INTERRUPTED    2
54
55 /* availability status */
56 #define AS_AVAILABLE      1
57 #define AS_UNAVAILABLE    2
58
59 /* availability reason */
60 #define AR_NEWMEDIA       1
61 #define AR_SAMEMEDIA      2
62 #define AR_NOMEDIA        3
63 #define AR_TEMPERATURE    4
64 #define AR_VOLTAGE        5
65 #define AR_ERRORMEDIA     6
66
67 /* mute state */
68 #define MS_MUTED          1
69 #define MS_UNMUTED        2
70
71 /* connection format */
72 #define CF_MONO           1
73 #define CF_STEREO         2
74 #define CF_AUTO           4
75
76 typedef struct {
77     const char *name;
78     uint16_t    id;
79     uint16_t    state;
80 } domain_t;
81
82
83 typedef struct {
84     uint16_t    fromidx;
85     uint16_t    toidx;
86     uint32_t    channels;
87 } link_t;
88
89 typedef struct {
90     int          maxlink;
91     int          nlink;
92     link_t      *links;
93 } routes_t;
94
95 struct pa_audiomgr {
96     domain_t      domain;
97     pa_hashmap   *nodes;        /**< nodes ie. sinks and sources */
98     pa_hashmap   *conns;        /**< connections */
99     routes_t      defrts;       /**< default routes */
100 };
101
102
103 /*
104 static bool find_default_route(struct userdata *, mir_node *,
105                                am_connect_data *);
106 */
107
108 static void *node_hash(mir_direction, uint16_t);
109 static void *conn_hash(uint16_t);
110 static void fill_am_data_and_register(struct userdata *, mir_node *, pa_audiomgr *);
111
112 struct pa_audiomgr *pa_audiomgr_init(struct userdata *u)
113 {
114     /* pa_module *m = u->module; */
115     pa_audiomgr *am;
116
117     pa_assert(u);
118
119     am = pa_xnew0(pa_audiomgr, 1);
120
121     am->domain.id = AM_ID_INVALID;
122     am->domain.state = DS_DOWN;
123     am->nodes = pa_hashmap_new(pa_idxset_trivial_hash_func,
124                                      pa_idxset_trivial_compare_func);
125     am->conns = pa_hashmap_new(pa_idxset_trivial_hash_func,
126                                pa_idxset_trivial_compare_func);
127     return am;
128 }
129
130 void pa_audiomgr_done(struct userdata *u)
131 {
132     pa_audiomgr *am;
133
134     if (u && (am = u->audiomgr)) {
135         if (u->routerif && am->domain.id != AM_ID_INVALID)
136             pa_routerif_unregister_domain(u, am->domain.id);
137
138         pa_hashmap_free(am->nodes);
139         pa_hashmap_free(am->conns);
140         pa_xfree((void *)am->domain.name);
141         pa_xfree(am);
142         u->audiomgr = NULL;
143     }
144 }
145
146
147 void pa_audiomgr_register_domain(struct userdata *u)
148 {
149     pa_audiomgr        *am;
150     am_domainreg_data  *dr;
151
152     pa_assert(u);
153     pa_assert_se((am = u->audiomgr));
154
155     dr = pa_xnew0(am_domainreg_data, 1);
156
157     dr->domain_id = 0;
158     dr->name      = AUDIOMGR_DOMAIN;  /* AM domain name */
159     dr->bus_name  = AUDIOMGR_NODE;    /* AM internal bus name. */
160     dr->node_name = AUDIOMGR_NODE;    /* node name on AM's internal bus */
161     dr->early     = false;
162     dr->complete  = false;
163     dr->state     = 1;
164
165     pa_routerif_register_domain(u, dr);
166 }
167
168 void pa_audiomgr_domain_registered(struct userdata   *u,
169                                    uint16_t           id,
170                                    uint16_t           state,
171                                    am_domainreg_data *dr)
172 {
173     pa_audiomgr *am;
174
175     pa_assert(u);
176     pa_assert(dr);
177     pa_assert_se((am = u->audiomgr));
178
179
180     am->domain.name  = pa_xstrdup(dr->name);
181     am->domain.id    = id;
182     am->domain.state = state;
183
184     pa_log_debug("start domain registration for '%s' domain", dr->name);
185
186     pa_discover_domain_up(u);
187
188     pa_log_debug("domain registration for '%s' domain is complete", dr->name);
189
190     pa_routerif_domain_complete(u, id);
191
192     pa_xfree(dr);
193 }
194
195
196 void pa_audiomgr_unregister_domain(struct userdata *u, bool send_state)
197 {
198     pa_audiomgr *am;
199     mir_node    *node;
200     const void  *key;
201     void        *state = NULL;
202
203     pa_assert(u);
204     pa_assert_se((am = u->audiomgr));
205
206     pa_log_debug("unregistering domain '%s'", am->domain.name);
207
208     while ((node  = pa_hashmap_iterate(am->nodes, &state, &key))) {
209         pa_log_debug("   unregistering '%s' (%p/%p)", node->amname, (void *)key, (void *)node);
210         node->amid = AM_ID_INVALID;
211         pa_hashmap_remove(am->nodes, key);
212     }
213
214     am->domain.id = AM_ID_INVALID;
215     am->domain.state = DS_DOWN;
216 }
217
218 void fill_am_data_and_register(struct userdata *u, mir_node *node, pa_audiomgr *am)
219 {
220     am_nodereg_data  *rd;
221     am_method         method;
222     bool              success;
223
224     rd = pa_xnew0(am_nodereg_data, 1);
225     rd->key     = pa_xstrdup(node->key);
226     rd->name    = pa_xstrdup(node->amname);
227     rd->domain  = am->domain.id;
228     rd->class   = 0x43;
229     rd->state   = 1;
230     rd->volume  = 32767;
231     rd->visible = node->visible;
232     rd->avail.status = AS_AVAILABLE;
233     rd->avail.reason = 0;
234     rd->mainvol = 32767;
235
236     if (node->direction == mir_input) {
237         rd->interrupt = IS_OFF;
238         method = audiomgr_register_source;
239     }
240     else {
241         rd->mute = MS_UNMUTED;
242         method = audiomgr_register_sink;
243     }
244
245     success = pa_routerif_register_node(u, method, rd);
246
247     if (success) {
248         pa_log_debug("initiate registration node '%s' (%p)"
249                      "to audio manager", rd->name, (void *)node);
250     }
251     else {
252         pa_log("%s: failed to register node '%s' (%p)"
253                "to audio manager", __FILE__, rd->name, (void *)node);
254     }
255
256     return;
257 }
258
259 void pa_audiomgr_register_node(struct userdata *u, mir_node *node)
260 {
261     static const char *classes_to_register[] = {
262         "wrtApplication",
263         "icoApplication",
264         "navigator",
265         "phone",
266         "radio",
267         NULL
268     };
269
270     pa_audiomgr      *am;
271     const char       *class_to_register;
272     int               i;
273
274     pa_assert(u);
275     pa_assert_se((am = u->audiomgr));
276
277     if (am->domain.state == DS_DOWN || am->domain.state == DS_RUNDOWN) {
278         pa_log_debug("skip registering nodes while the domain is down");
279         return;
280     }
281
282     for (i = 0;   (class_to_register = classes_to_register[i]);   i++) {
283         if (!strcmp(node->amname, class_to_register)) {
284             if (node->direction == mir_input || node->direction == mir_output){
285                 fill_am_data_and_register(u, node, am);
286             }
287
288             return;
289         }
290     } /* for classes_to_register */
291
292     /* ok, register also the gateways */
293     if (strncmp(node->amname, "gw", 2) == 0) {
294         if (node->direction == mir_input || node->direction == mir_output){
295             fill_am_data_and_register(u, node, am);
296         }
297         return;
298     }
299
300     pa_log_debug("skip registration of node '%s' (%p): "
301                  "not known by audio manager", node->amname, (void *)node);
302 }
303
304 void pa_audiomgr_node_registered(struct userdata *u,
305                                  uint16_t         id,
306                                  uint16_t         state,
307                                  am_nodereg_data *rd)
308 {
309     pa_audiomgr     *am;
310     mir_node        *node;
311     void            *key;
312
313     pa_assert(u);
314     pa_assert(rd);
315     pa_assert(rd->key);
316     pa_assert_se((am = u->audiomgr));
317
318     if (!(node = pa_discover_find_node_by_key(u, rd->key)))
319         pa_log("%s: can't find node with key '%s'", __FILE__, rd->key);
320     else {
321         node->amid = id;
322
323         key = node_hash(node->direction, id);
324
325         pa_log_debug("registering node '%s' (%p/%p)",
326                      node->amname, (void *)key, (void *)node);
327
328         pa_hashmap_put(am->nodes, key, node);
329
330         /* we don't want implicit connections register and confuse */
331         /* audio manager. Implicit connections are handled by      */
332         /* creating a resource through murphy */
333         /*
334         if (find_default_route(u, node, &cd))
335             pa_routerif_register_implicit_connection(u, &cd);
336         */
337     }
338
339     pa_xfree((void *)rd->key);
340     pa_xfree((void *)rd->name);
341     pa_xfree((void *)rd);
342 }
343
344 void pa_audiomgr_unregister_node(struct userdata *u, mir_node *node)
345 {
346     pa_audiomgr       *am;
347     am_nodeunreg_data *ud;
348     am_method          method;
349     mir_node          *removed;
350     bool          success;
351     void              *key;
352
353     pa_assert(u);
354     pa_assert_se((am = u->audiomgr));
355
356     if (am->domain.state == DS_DOWN || am->domain.state == DS_RUNDOWN)
357         pa_log_debug("skip unregistering nodes while the domain is down");
358     else if (node->amid == AM_ID_INVALID)
359         pa_log_debug("node '%s' was not registered", node->amname);
360     else if (node->direction == mir_input || node->direction == mir_output) {
361         ud = pa_xnew0(am_nodeunreg_data, 1);
362         ud->id   = node->amid;
363         ud->name = pa_xstrdup(node->amname);
364
365         key = node_hash(node->direction, node->amid);
366         removed = pa_hashmap_remove(am->nodes, key);
367
368         if (node != removed) {
369             if (removed)
370                 pa_log("%s: confused with data structures: key mismatch. "
371                        "attempted to remove '%s' (%p/%p); "
372                        "actually removed '%s' (%p/%p)", __FILE__,
373                        node->amname, (void *)key, (void *)node, removed->amname,
374                        (void *)node_hash(removed->direction, removed->amid), 
375                        (void *)removed);
376             else
377                 pa_log("%s: confused with data structures: node %u (%p)"
378                        "is not in the hash table", __FILE__, node->amid, (void *)node);
379         }
380
381
382         if (node->direction == mir_input)
383             method = audiomgr_deregister_source;
384         else
385             method = audiomgr_deregister_sink;
386
387
388         success = pa_routerif_unregister_node(u, method, ud);
389
390         if (success) {
391             pa_log_debug("sucessfully unregistered node '%s' (%p/%p)"
392                          "from audio manager", node->amname, 
393                          (void *)key, (void *)node);
394         }
395         else {
396             pa_log("%s: failed to unregister node '%s' (%p)"
397                    "from audio manager", __FILE__, node->amname, (void *)node);
398         }
399     }
400 }
401
402 void pa_audiomgr_node_unregistered(struct userdata   *u,
403                                    am_nodeunreg_data *ud)
404 {
405     (void)u;
406
407     /* can't do too much here anyways,
408        since the node is gone already */
409
410     pa_xfree((void *)ud->name);
411     pa_xfree((void *)ud);
412 }
413
414
415 void pa_audiomgr_delete_default_routes(struct userdata *u)
416 {
417     pa_audiomgr *am;
418     routes_t    *defrts;
419
420     pa_assert(u);
421     pa_assert_se((am = u->audiomgr));
422
423     defrts = &am->defrts;
424
425     defrts->nlink = 0;
426 }
427
428 void pa_audiomgr_add_default_route(struct userdata *u,
429                                    mir_node        *from,
430                                    mir_node        *to)
431 {
432     pa_audiomgr *am;
433     routes_t    *defrts;
434     link_t      *link;
435     size_t       size;
436
437     pa_assert(u);
438     pa_assert(from);
439     pa_assert(to);
440     pa_assert_se((am = u->audiomgr));
441
442     defrts = &am->defrts;
443
444     if (from->paidx == PA_IDXSET_INVALID || to->paidx == PA_IDXSET_INVALID) {
445         pa_log_debug("ignoring default route %s => %s: incomplete "
446                      "input or output", from->amname, to->amname);
447     }
448     else {
449         pa_log_debug("adding default route %s => %s", from->amname,to->amname);
450
451         if (defrts->nlink >= defrts->maxlink) {
452             defrts->maxlink += 16;
453
454             size = sizeof(link_t) * (size_t)defrts->maxlink;
455             defrts->links = realloc(defrts->links, size);
456             pa_assert(defrts->links);
457         }
458
459         link = defrts->links + defrts->nlink++;
460
461         link->fromidx  = (uint16_t)from->index;
462         link->toidx    = (uint16_t)to->index;
463         link->channels = from->channels < to->channels ?
464                          from->channels : to->channels;
465     }
466 }
467
468 void pa_audiomgr_send_default_routes(struct userdata *u)
469 {
470 #define MAX_DEFAULT_ROUTES 128
471
472     pa_audiomgr     *am;
473     routes_t        *defrts;
474     link_t          *link;
475     mir_node        *from;
476     mir_node        *to;
477     am_connect_data  cds[MAX_DEFAULT_ROUTES];
478     am_connect_data *cd;
479     int              ncd;
480
481     pa_assert(u);
482     pa_assert_se((am = u->audiomgr));
483
484     defrts = &am->defrts;
485
486     pa_assert(defrts->nlink < MAX_DEFAULT_ROUTES);
487
488     for (ncd = 0;   ncd < defrts->nlink;   ncd++) {
489         link = defrts->links + ncd;
490         cd = cds + ncd;
491
492         if (!(from = mir_node_find_by_index(u, link->fromidx)) ||
493             !(to   = mir_node_find_by_index(u, link->toidx))     )
494         {
495             pa_log_debug("will not send default route: node not found");
496             continue;
497         }
498
499         if (from->amid == AM_ID_INVALID || to->amid == AM_ID_INVALID) {
500             pa_log_debug("wil not send default route: invalid audiomgr ID");
501             continue;
502         }
503
504         cd->handle = 0;
505         cd->connection = 0;
506         cd->source = from->amid;
507         cd->sink = to->amid;
508         cd->format = link->channels >= 2 ? CF_STEREO : CF_MONO;
509     }
510
511     /* we don't want implicit connections register and confuse */
512     /* audio manager. Implicit connections are handled by      */
513     /* creating a resource through murphy */
514     /*
515     if (ncd > 0)
516         pa_routerif_register_implicit_connections(u, ncd, cds);
517     */
518
519 #undef MAX_DEFAULT_ROUTES
520 }
521
522 void pa_audiomgr_connect(struct userdata *u, am_connect_data *cd)
523 {
524     pa_audiomgr    *am;
525     am_ack_data     ad;
526     mir_connection *conn;
527     uint16_t        cid;
528     mir_node       *from = NULL;
529     mir_node       *to   = NULL;
530     int             err  = E_OK;
531     bool            autoconn = false;
532
533     pa_assert(u);
534     pa_assert(cd);
535     pa_assert_se((am = u->audiomgr));
536
537     if (cd->format == CF_AUTO) {
538         autoconn = true;
539         pa_log_debug("automatic connection request received");
540     }
541
542     if (autoconn == false) {
543         if ((from = pa_hashmap_get(am->nodes, node_hash(mir_input, cd->source))) &&
544             (to   = pa_hashmap_get(am->nodes, node_hash(mir_output, cd->sink))))
545             {
546                 cid = cd->connection;
547
548                 pa_log_debug("routing '%s' => '%s'", from->amname, to->amname);
549
550                 if (!(conn = mir_router_add_explicit_route(u, cid, from, to)))
551                     err = E_NOT_POSSIBLE;
552                 else {
553                     pa_log_debug("registering connection (%u/%p)",
554                                  cd->connection, (void *)conn);
555                     pa_hashmap_put(am->conns, conn_hash(cid), conn);
556                 }
557             }
558         else {
559             pa_log_debug("failed to connect: can't find node for %s %u",
560                          from ? "sink" : "source", from ? cd->sink : cd->source);
561             err = E_NON_EXISTENT;
562         }
563     }
564
565     memset(&ad, 0, sizeof(ad));
566     ad.handle = cd->handle;
567     ad.param1 = cd->connection;
568     ad.error  = (am_uint16_t)err;
569
570     pa_routerif_acknowledge(u, audiomgr_connect_ack, &ad);
571 }
572
573 void pa_audiomgr_disconnect(struct userdata *u, am_connect_data *cd)
574 {
575     pa_audiomgr    *am;
576     mir_connection *conn;
577     uint16_t        cid;
578     am_ack_data     ad;
579     int             err = E_OK;
580
581     pa_assert(u);
582     pa_assert(cd);
583     pa_assert_se((am = u->audiomgr));
584
585     cid = cd->connection;
586
587     if ((conn = pa_hashmap_remove(am->conns, conn_hash(cid))))
588         mir_router_remove_explicit_route(u, conn);
589     else {
590         pa_log_debug("failed to disconnect: can't find connection %u", cid);
591         err = E_NON_EXISTENT;
592     }
593
594     memset(&ad, 0, sizeof(ad));
595     ad.handle = cd->handle;
596     ad.param1 = cd->connection;
597     ad.error  = (am_uint16_t)err;
598
599     pa_routerif_acknowledge(u, audiomgr_disconnect_ack, &ad);
600 }
601
602 #if 0
603 static bool find_default_route(struct userdata *u,
604                                mir_node        *node,
605                                am_connect_data *cd)
606 {
607     pa_audiomgr *am;
608     routes_t    *defrts;
609     link_t      *link;
610     mir_node    *pair;
611     int          i;
612
613     pa_assert(u);
614     pa_assert(node);
615     pa_assert(cd);
616     pa_assert_se((am = u->audiomgr));
617
618     defrts = &am->defrts;
619
620     memset(cd, 0, sizeof(am_connect_data));
621
622     for (i = 0;  i < defrts->nlink;  i++) {
623         link = defrts->links + i;
624
625         cd->format = link->channels >= 2 ? CF_STEREO : CF_MONO;
626
627         if (node->direction == mir_input && link->fromidx == node->index) {
628             return (pair = mir_node_find_by_index(u, link->toidx)) &&
629                    (cd->source = node->amid) != AM_ID_INVALID &&
630                    (cd->sink   = pair->amid) != AM_ID_INVALID;
631         }
632
633         if (node->direction == mir_output && link->toidx == node->index) {
634             return (pair = mir_node_find_by_index(u, link->fromidx)) &&
635                    (cd->source = pair->amid) != AM_ID_INVALID &&
636                    (cd->sink = node->amid) != AM_ID_INVALID;
637         }
638     }
639
640     return false;
641 }
642 #endif
643
644 static void *node_hash(mir_direction direction, uint16_t amid)
645 {
646     return (char *)NULL + ((uint32_t)direction << 16 | (uint32_t)amid);
647 }
648
649 static void *conn_hash(uint16_t connid)
650 {
651     return (char *)NULL + (uint32_t)connid;
652 }
653
654
655
656 /*
657  * Local Variables:
658  * c-basic-offset: 4
659  * indent-tabs-mode: nil
660  * End:
661  *
662  */