murphyif: Free the connect timer when unloading
[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 static bool find_default_route(struct userdata *, mir_node *,
104                                am_connect_data *);
105
106 static void *node_hash(mir_direction, uint16_t);
107 static void *conn_hash(uint16_t);
108
109
110 struct pa_audiomgr *pa_audiomgr_init(struct userdata *u)
111 {
112     /* pa_module *m = u->module; */
113     pa_audiomgr *am;
114
115     pa_assert(u);
116
117     am = pa_xnew0(pa_audiomgr, 1);
118
119     am->domain.id = AM_ID_INVALID;
120     am->domain.state = DS_DOWN;
121     am->nodes = pa_hashmap_new(pa_idxset_trivial_hash_func,
122                                      pa_idxset_trivial_compare_func);
123     am->conns = pa_hashmap_new(pa_idxset_trivial_hash_func,
124                                pa_idxset_trivial_compare_func);
125     return am;
126 }
127
128 void pa_audiomgr_done(struct userdata *u)
129 {
130     pa_audiomgr *am;
131
132     if (u && (am = u->audiomgr)) {
133         if (u->routerif && am->domain.id != AM_ID_INVALID)
134             pa_routerif_unregister_domain(u, am->domain.id);
135
136         pa_hashmap_free(am->nodes);
137         pa_hashmap_free(am->conns);
138         pa_xfree((void *)am->domain.name);
139         pa_xfree(am);
140         u->audiomgr = NULL;
141     }
142 }
143
144
145 void pa_audiomgr_register_domain(struct userdata *u)
146 {
147     pa_audiomgr        *am;
148     am_domainreg_data  *dr;
149
150     pa_assert(u);
151     pa_assert_se((am = u->audiomgr));
152
153     dr = pa_xnew0(am_domainreg_data, 1);
154
155     dr->domain_id = 0;
156     dr->name      = AUDIOMGR_DOMAIN;  /* AM domain name */
157     dr->bus_name  = AUDIOMGR_NODE;    /* AM internal bus name. */
158     dr->node_name = AUDIOMGR_NODE;    /* node name on AM's internal bus */
159     dr->early     = false;
160     dr->complete  = false;
161     dr->state     = 1;
162
163     pa_routerif_register_domain(u, dr);
164 }
165
166 void pa_audiomgr_domain_registered(struct userdata   *u,
167                                    uint16_t           id,
168                                    uint16_t           state,
169                                    am_domainreg_data *dr)
170 {
171     pa_audiomgr *am;
172
173     pa_assert(u);
174     pa_assert(dr);
175     pa_assert_se((am = u->audiomgr));
176
177
178     am->domain.name  = pa_xstrdup(dr->name);
179     am->domain.id    = id;
180     am->domain.state = state;
181
182     pa_log_debug("start domain registration for '%s' domain", dr->name);
183
184     pa_discover_domain_up(u);
185
186     pa_log_debug("domain registration for '%s' domain is complete", dr->name);
187
188     pa_routerif_domain_complete(u, id);
189
190     pa_xfree(dr);
191 }
192
193
194 void pa_audiomgr_unregister_domain(struct userdata *u, bool send_state)
195 {
196     pa_audiomgr *am;
197     mir_node    *node;
198     const void  *key;
199     void        *state = NULL;
200
201     pa_assert(u);
202     pa_assert_se((am = u->audiomgr));
203
204     pa_log_debug("unregistering domain '%s'", am->domain.name);
205
206     while ((node  = pa_hashmap_iterate(am->nodes, &state, &key))) {
207         pa_log_debug("   unregistering '%s' (%p/%p)", node->amname, key,node);
208         node->amid = AM_ID_INVALID;
209         pa_hashmap_remove(am->nodes, key);
210     }
211
212     am->domain.id = AM_ID_INVALID;
213     am->domain.state = DS_DOWN;
214 }
215
216 void fill_am_data_and_register(struct userdata *u, mir_node *node, pa_audiomgr *am)
217 {
218     am_nodereg_data  *rd;
219     am_method         method;
220     bool              success;
221
222     rd = pa_xnew0(am_nodereg_data, 1);
223     rd->key     = pa_xstrdup(node->key);
224     rd->name    = pa_xstrdup(node->amname);
225     rd->domain  = am->domain.id;
226     rd->class   = 0x43;
227     rd->state   = 1;
228     rd->volume  = 32767;
229     rd->visible = node->visible;
230     rd->avail.status = AS_AVAILABLE;
231     rd->avail.reason = 0;
232     rd->mainvol = 32767;
233
234     if (node->direction == mir_input) {
235         rd->interrupt = IS_OFF;
236         method = audiomgr_register_source;
237     }
238     else {
239         rd->mute = MS_UNMUTED;
240         method = audiomgr_register_sink;
241     }
242
243     success = pa_routerif_register_node(u, method, rd);
244
245     if (success) {
246         pa_log_debug("initiate registration node '%s' (%p)"
247                      "to audio manager", rd->name, node);
248     }
249     else {
250         pa_log("%s: failed to register node '%s' (%p)"
251                "to audio manager", __FILE__, rd->name, node);
252     }
253
254     return;
255 }
256
257 void pa_audiomgr_register_node(struct userdata *u, mir_node *node)
258 {
259     static const char *classes_to_register[] = {
260         "wrtApplication",
261         "icoApplication",
262         "navigator",
263         "phone",
264         "radio",
265         NULL
266     };
267
268     pa_audiomgr      *am;
269     bool              success;
270     const char       *class_to_register;
271     int               i;
272
273     pa_assert(u);
274     pa_assert_se((am = u->audiomgr));
275
276     if (am->domain.state == DS_DOWN || am->domain.state == DS_RUNDOWN) {
277         pa_log_debug("skip registering nodes while the domain is down");
278         return;
279     }
280
281     for (i = 0;   (class_to_register = classes_to_register[i]);   i++) {
282         if (!strcmp(node->amname, class_to_register)) {
283             if (node->direction == mir_input || node->direction == mir_output){
284                 fill_am_data_and_register(u, node, am);
285             }
286
287             return;
288         }
289     } /* for classes_to_register */
290
291     /* ok, register also the gateways */
292     if (strncmp(node->amname, "gw", 2) == 0) {
293         if (node->direction == mir_input || node->direction == mir_output){
294             fill_am_data_and_register(u, node, am);
295         }
296         return;
297     }
298
299     pa_log_debug("skip registration of node '%s' (%p): "
300                  "not known by audio manager", node->amname, node);
301 }
302
303 void pa_audiomgr_node_registered(struct userdata *u,
304                                  uint16_t         id,
305                                  uint16_t         state,
306                                  am_nodereg_data *rd)
307 {
308     pa_audiomgr     *am;
309     mir_node        *node;
310     void            *key;
311     am_connect_data  cd;
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, key, 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, key, node, removed->amname,
374                        node_hash(removed->direction, removed->amid), removed);
375             else
376                 pa_log("%s: confused with data structures: node %u (%p)"
377                        "is not in the hash table", __FILE__, node->amid, node);
378         }
379
380
381         if (node->direction == mir_input)
382             method = audiomgr_deregister_source;
383         else
384             method = audiomgr_deregister_sink;
385
386
387         success = pa_routerif_unregister_node(u, method, ud);
388
389         if (success) {
390             pa_log_debug("sucessfully unregistered node '%s' (%p/%p)"
391                          "from audio manager", node->amname, key, node);
392         }
393         else {
394             pa_log("%s: failed to unregister node '%s' (%p)"
395                    "from audio manager", __FILE__, node->amname, node);
396         }
397     }
398 }
399
400 void pa_audiomgr_node_unregistered(struct userdata   *u,
401                                    am_nodeunreg_data *ud)
402 {
403     (void)u;
404
405     /* can't do too much here anyways,
406        since the node is gone already */
407
408     pa_xfree((void *)ud->name);
409     pa_xfree((void *)ud);
410 }
411
412
413 void pa_audiomgr_delete_default_routes(struct userdata *u)
414 {
415     pa_audiomgr *am;
416     routes_t    *defrts;
417
418     pa_assert(u);
419     pa_assert_se((am = u->audiomgr));
420
421     defrts = &am->defrts;
422
423     defrts->nlink = 0;
424 }
425
426 void pa_audiomgr_add_default_route(struct userdata *u,
427                                    mir_node        *from,
428                                    mir_node        *to)
429 {
430     pa_audiomgr *am;
431     routes_t    *defrts;
432     link_t      *link;
433     size_t       size;
434
435     pa_assert(u);
436     pa_assert(from);
437     pa_assert(to);
438     pa_assert_se((am = u->audiomgr));
439
440     defrts = &am->defrts;
441
442     if (from->paidx == PA_IDXSET_INVALID || to->paidx == PA_IDXSET_INVALID) {
443         pa_log_debug("ignoring default route %s => %s: incomplete "
444                      "input or output", from->amname, to->amname);
445     }
446     else {
447         pa_log_debug("adding default route %s => %s", from->amname,to->amname);
448
449         if (defrts->nlink >= defrts->maxlink) {
450             defrts->maxlink += 16;
451
452             size = sizeof(link_t) * defrts->maxlink;
453             defrts->links = realloc(defrts->links, size);
454             pa_assert(defrts->links);
455         }
456
457         link = defrts->links + defrts->nlink++;
458
459         link->fromidx  = from->index;
460         link->toidx    = to->index;
461         link->channels = from->channels < to->channels ?
462                          from->channels : to->channels;
463     }
464 }
465
466 void pa_audiomgr_send_default_routes(struct userdata *u)
467 {
468 #define MAX_DEFAULT_ROUTES 128
469
470     pa_audiomgr     *am;
471     routes_t        *defrts;
472     link_t          *link;
473     mir_node        *from;
474     mir_node        *to;
475     am_connect_data  cds[MAX_DEFAULT_ROUTES];
476     am_connect_data *cd;
477     int              ncd;
478
479     pa_assert(u);
480     pa_assert_se((am = u->audiomgr));
481
482     defrts = &am->defrts;
483
484     pa_assert(defrts->nlink < MAX_DEFAULT_ROUTES);
485
486     for (ncd = 0;   ncd < defrts->nlink;   ncd++) {
487         link = defrts->links + ncd;
488         cd = cds + ncd;
489
490         if (!(from = mir_node_find_by_index(u, link->fromidx)) ||
491             !(to   = mir_node_find_by_index(u, link->toidx))     )
492         {
493             pa_log_debug("will not send default route: node not found");
494             continue;
495         }
496
497         if (from->amid == AM_ID_INVALID || to->amid == AM_ID_INVALID) {
498             pa_log_debug("wil not send default route: invalid audiomgr ID");
499             continue;
500         }
501
502         cd->handle = 0;
503         cd->connection = 0;
504         cd->source = from->amid;
505         cd->sink = to->amid;
506         cd->format = link->channels >= 2 ? CF_STEREO : CF_MONO;
507     }
508
509     /* we don't want implicit connections register and confuse */
510     /* audio manager. Implicit connections are handled by      */
511     /* creating a resource through murphy */
512     /*
513     if (ncd > 0)
514         pa_routerif_register_implicit_connections(u, ncd, cds);
515     */
516
517 #undef MAX_DEFAULT_ROUTES
518 }
519
520 void pa_audiomgr_connect(struct userdata *u, am_connect_data *cd)
521 {
522     pa_audiomgr    *am;
523     am_ack_data     ad;
524     mir_connection *conn;
525     uint16_t        cid;
526     mir_node       *from = NULL;
527     mir_node       *to   = NULL;
528     int             err  = E_OK;
529     bool            autoconn = false;
530
531     pa_assert(u);
532     pa_assert(cd);
533     pa_assert_se((am = u->audiomgr));
534
535     if (cd->format == CF_AUTO) {
536         autoconn = true;
537         pa_log_debug("automatic connection request received");
538     }
539
540     if (autoconn == false) {
541         if ((from = pa_hashmap_get(am->nodes, node_hash(mir_input, cd->source))) &&
542             (to   = pa_hashmap_get(am->nodes, node_hash(mir_output, cd->sink))))
543             {
544                 cid = cd->connection;
545
546                 pa_log_debug("routing '%s' => '%s'", from->amname, to->amname);
547
548                 if (!(conn = mir_router_add_explicit_route(u, cid, from, to)))
549                     err = E_NOT_POSSIBLE;
550                 else {
551                     pa_log_debug("registering connection (%u/%p)",
552                                  cd->connection, conn);
553                     pa_hashmap_put(am->conns, conn_hash(cid), conn);
554                 }
555             }
556         else {
557             pa_log_debug("failed to connect: can't find node for %s %u",
558                          from ? "sink" : "source", from ? cd->sink : cd->source);
559             err = E_NON_EXISTENT;
560         }
561     }
562
563     memset(&ad, 0, sizeof(ad));
564     ad.handle = cd->handle;
565     ad.param1 = cd->connection;
566     ad.error  = err;
567
568     pa_routerif_acknowledge(u, audiomgr_connect_ack, &ad);
569 }
570
571 void pa_audiomgr_disconnect(struct userdata *u, am_connect_data *cd)
572 {
573     pa_audiomgr    *am;
574     mir_connection *conn;
575     uint16_t        cid;
576     am_ack_data     ad;
577     int             err = E_OK;
578
579     pa_assert(u);
580     pa_assert(cd);
581     pa_assert_se((am = u->audiomgr));
582
583     cid = cd->connection;
584
585     if ((conn = pa_hashmap_remove(am->conns, conn_hash(cid))))
586         mir_router_remove_explicit_route(u, conn);
587     else {
588         pa_log_debug("failed to disconnect: can't find connection %u", cid);
589         err = E_NON_EXISTENT;
590     }
591
592     memset(&ad, 0, sizeof(ad));
593     ad.handle = cd->handle;
594     ad.param1 = cd->connection;
595     ad.error  = err;
596
597     pa_routerif_acknowledge(u, audiomgr_disconnect_ack, &ad);
598 }
599
600 static bool find_default_route(struct userdata *u,
601                                mir_node        *node,
602                                am_connect_data *cd)
603 {
604     pa_audiomgr *am;
605     routes_t    *defrts;
606     link_t      *link;
607     mir_node    *pair;
608     int          i;
609
610     pa_assert(u);
611     pa_assert(node);
612     pa_assert(cd);
613     pa_assert_se((am = u->audiomgr));
614
615     defrts = &am->defrts;
616
617     memset(cd, 0, sizeof(am_connect_data));
618
619     for (i = 0;  i < defrts->nlink;  i++) {
620         link = defrts->links + i;
621
622         cd->format = link->channels >= 2 ? CF_STEREO : CF_MONO;
623
624         if (node->direction == mir_input && link->fromidx == node->index) {
625             return (pair = mir_node_find_by_index(u, link->toidx)) &&
626                    (cd->source = node->amid) != AM_ID_INVALID &&
627                    (cd->sink   = pair->amid) != AM_ID_INVALID;
628         }
629
630         if (node->direction == mir_output && link->toidx == node->index) {
631             return (pair = mir_node_find_by_index(u, link->fromidx)) &&
632                    (cd->source = pair->amid) != AM_ID_INVALID &&
633                    (cd->sink = node->amid) != AM_ID_INVALID;
634         }
635     }
636
637     return false;
638 }
639
640 static void *node_hash(mir_direction direction, uint16_t amid)
641 {
642     return NULL + ((uint32_t)direction << 16 | (uint32_t)amid);
643 }
644
645 static void *conn_hash(uint16_t connid)
646 {
647     return NULL + (uint32_t)connid;
648 }
649
650
651
652 /*
653  * Local Variables:
654  * c-basic-offset: 4
655  * indent-tabs-mode: nil
656  * End:
657  *
658  */