router: fix for bluetooth disappearing causing assert in combine
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / audiomgr.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5
6 #include <pulsecore/pulsecore-config.h>
7
8 #include <pulse/def.h>
9
10 #include <pulsecore/core-util.h>
11
12 #include "userdata.h"
13 #include "audiomgr.h"
14 #include "node.h"
15 #include "discover.h"
16 #include "router.h"
17 #include "dbusif.h"
18
19 #define AUDIOMGR_DOMAIN   "PULSE"
20 #define AUDIOMGR_NODE     "pulsePlugin"
21
22 /*
23  * these must match their counterpart
24  * in audiomanagertypes.h
25  */
26 /* domain status */
27 #define DS_UNKNOWN        0
28 #define DS_CONTROLLED     1
29 #define DS_RUNDOWN        2
30 #define DS_DOWN           255
31
32 /* interrupt state */
33 #define IS_OFF            1
34 #define IS_INTERRUPTED    2
35
36 /* availability status */
37 #define AS_AVAILABLE      1
38 #define AS_UNAVAILABLE    2
39
40 /* availability reason */
41 #define AR_NEWMEDIA       1
42 #define AR_SAMEMEDIA      2
43 #define AR_NOMEDIA        3
44 #define AR_TEMPERATURE    4
45 #define AR_VOLTAGE        5
46 #define AR_ERRORMEDIA     6
47
48 /* mute state */
49 #define MS_MUTED          1
50 #define MS_UNMUTED        2
51
52
53 typedef struct {
54     const char *name;
55     uint16_t    id;
56     uint16_t    state;
57 } domain_t;
58
59
60 struct pa_audiomgr {
61     domain_t      domain;
62     pa_hashmap   *nodes;        /**< nodes ie. sinks and sources */
63     pa_hashmap   *conns;        /**< connections */
64 };
65
66
67 static void *node_hash(mir_direction, uint16_t);
68 static void *conn_hash(uint16_t);
69
70
71 struct pa_audiomgr *pa_audiomgr_init(struct userdata *u)
72 {
73     /* pa_module *m = u->module; */
74     pa_audiomgr *am;
75
76     pa_assert(u);
77     
78     am = pa_xnew0(pa_audiomgr, 1);
79
80     am->domain.id = AM_ID_INVALID;
81     am->domain.state = DS_DOWN;
82     am->nodes = pa_hashmap_new(pa_idxset_trivial_hash_func,
83                                      pa_idxset_trivial_compare_func);
84     am->conns = pa_hashmap_new(pa_idxset_trivial_hash_func,
85                                pa_idxset_trivial_compare_func);
86     return am;
87 }
88
89 void pa_audiomgr_done(struct userdata *u)
90 {
91     pa_audiomgr *am;
92
93     if (u && (am = u->audiomgr)) {
94         if (u->dbusif && am->domain.id != AM_ID_INVALID)
95             pa_policy_dbusif_unregister_domain(u, am->domain.id);
96
97         pa_hashmap_free(am->nodes, NULL,NULL);
98         pa_hashmap_free(am->conns, NULL,NULL);
99         pa_xfree((void *)am->domain.name);
100         pa_xfree(am);
101         u->audiomgr = NULL;
102     }
103 }
104
105
106 void pa_audiomgr_register_domain(struct userdata *u)
107 {
108     pa_audiomgr        *am;
109     am_domainreg_data  *dr;
110
111     pa_assert(u);
112     pa_assert_se((am = u->audiomgr));
113
114     dr = pa_xnew0(am_domainreg_data, 1);
115
116     dr->domain_id = 0;
117     dr->name      = AUDIOMGR_DOMAIN;  /* AM domain name */
118     dr->bus_name  = AUDIOMGR_NODE;    /* AM internal bus name. */
119     dr->node_name = AUDIOMGR_NODE;    /* node name on AM's internal bus */
120     dr->early     = FALSE;
121     dr->complete  = FALSE;
122     dr->state     = 1;
123
124     pa_policy_dbusif_register_domain(u, dr);
125 }
126
127 void pa_audiomgr_domain_registered(struct userdata   *u,
128                                    uint16_t           id,
129                                    uint16_t           state, 
130                                    am_domainreg_data *dr)
131 {
132     pa_audiomgr *am;
133
134     pa_assert(u);
135     pa_assert(dr);
136     pa_assert_se((am = u->audiomgr));
137
138
139     am->domain.name  = pa_xstrdup(dr->name);
140     am->domain.id    = id;
141     am->domain.state = state;
142
143     pa_log_debug("start domain registration for '%s' domain", dr->name);
144     
145     pa_discover_domain_up(u);
146     
147     pa_log_debug("domain registration for '%s' domain is complete", dr->name);
148
149     pa_policy_dbusif_domain_complete(u, id);
150
151     pa_xfree(dr);
152 }
153
154
155 void pa_audiomgr_unregister_domain(struct userdata *u, pa_bool_t send_state)
156 {
157     pa_audiomgr *am;
158     mir_node    *node;
159     const void  *key;
160     void        *state = NULL;
161
162     pa_assert(u);
163     pa_assert_se((am = u->audiomgr));
164
165     pa_log_debug("unregistering domain '%s'", am->domain.name);
166
167     while ((node  = pa_hashmap_iterate(am->nodes, &state, &key))) {
168         pa_log_debug("   unregistering '%s' (%p/%p)", node->amname, key,node);
169         node->amid = AM_ID_INVALID;
170         pa_hashmap_remove(am->nodes, key);
171     }
172
173     am->domain.id = AM_ID_INVALID;
174     am->domain.state = DS_DOWN;
175 }
176
177
178 void pa_audiomgr_register_node(struct userdata *u, mir_node *node)
179 {
180     pa_audiomgr      *am;
181     am_nodereg_data  *rd;
182     const char       *method;
183     pa_bool_t         success;
184
185     pa_assert(u);
186     pa_assert_se((am = u->audiomgr));
187
188     if (am->domain.state == DS_DOWN || am->domain.state == DS_RUNDOWN)
189         pa_log_debug("skip registering nodes while the domain is down");
190     else {
191         if (node->direction == mir_input || node->direction == mir_output) {
192             rd = pa_xnew0(am_nodereg_data, 1);
193             rd->key     = pa_xstrdup(node->key);
194             rd->name    = pa_xstrdup(node->amname);
195             rd->domain  = am->domain.id;
196             rd->class   = 0x43;
197             rd->volume  = 32767;
198             rd->visible = node->visible;
199             rd->avail.status = AS_AVAILABLE;
200             rd->avail.reason = 0;
201             rd->mainvol = 32767;
202             
203             if (node->direction == mir_input) {
204                 rd->interrupt = IS_OFF;
205                 method = AUDIOMGR_REGISTER_SOURCE;
206             } 
207             else {
208                 rd->mute = MS_UNMUTED;
209                 method = AUDIOMGR_REGISTER_SINK;
210             }
211             
212             success = pa_policy_dbusif_register_node(u, method, rd);
213             
214             if (success) {
215                 pa_log_debug("initiate registration node '%s' (%p)"
216                              "to audio manager", rd->name, node);
217             }
218             else {
219                 pa_log("%s: failed to register node '%s' (%p)"
220                        "to audio manager", __FILE__, rd->name, node);
221             }
222         }
223     }
224 }
225
226 void pa_audiomgr_node_registered(struct userdata *u,
227                                  uint16_t         id,
228                                  uint16_t         state,
229                                  am_nodereg_data *rd)
230 {
231     pa_audiomgr *am;
232     mir_node    *node;
233     void        *key;
234
235     pa_assert(u);
236     pa_assert(rd);
237     pa_assert(rd->key);
238     pa_assert_se((am = u->audiomgr));
239
240     if (!(node = pa_discover_find_node(u, rd->key)))
241         pa_log("%s: can't find node with key '%s'", __FILE__, rd->key);
242     else {
243         node->amid = id;
244
245         key = node_hash(node->direction, id);
246
247         pa_log_debug("registering node '%s' (%p/%p)",
248                      node->amname, key, node);
249
250         pa_hashmap_put(am->nodes, key, node);
251     }
252
253     pa_xfree((void *)rd->key);
254     pa_xfree((void *)rd->name);
255     pa_xfree((void *)rd);
256 }
257
258 void pa_audiomgr_unregister_node(struct userdata *u, mir_node *node)
259 {
260     pa_audiomgr       *am;
261     am_nodeunreg_data *ud;
262     const char        *method;
263     mir_node          *removed;
264     pa_bool_t          success;
265     void              *key;
266
267     pa_assert(u);
268     pa_assert_se((am = u->audiomgr));
269
270     if (am->domain.state == DS_DOWN || am->domain.state == DS_RUNDOWN)
271         pa_log_debug("skip unregistering nodes while the domain is down");
272     else if (node->amid == AM_ID_INVALID)
273         pa_log_debug("node '%s' was not registered", node->amname);
274     else if (node->direction == mir_input || node->direction == mir_output) {
275         ud = pa_xnew0(am_nodeunreg_data, 1);
276         ud->id   = node->amid;
277         ud->name = pa_xstrdup(node->amname);
278
279         key = node_hash(node->direction, node->amid);
280         removed = pa_hashmap_remove(am->nodes, key);
281
282         if (node != removed) {
283             if (removed)
284                 pa_log("%s: confused with data structures: key mismatch. "
285                        "attempted to remove '%s' (%p/%p); "
286                        "actually removed '%s' (%p/%p)", __FILE__,
287                        node->amname, key, node, removed->amname,
288                        node_hash(removed->direction, removed->amid), removed);
289             else
290                 pa_log("%s: confused with data structures: node %u (%p)"
291                        "is not in the hash table", __FILE__, node->amid, node);
292         }
293         
294         
295         if (node->direction == mir_input)
296             method = AUDIOMGR_DEREGISTER_SOURCE;
297         else
298             method = AUDIOMGR_DEREGISTER_SINK;
299         
300         
301         success = pa_policy_dbusif_unregister_node(u, method, ud);
302         
303         if (success) {
304             pa_log_debug("sucessfully unregistered node '%s' (%p/%p)"
305                          "from audio manager", node->amname, key, node);
306         }
307         else {
308             pa_log("%s: failed to unregister node '%s' (%p)"
309                    "from audio manager", __FILE__, node->amname, node);
310         }
311     }
312 }
313
314 void pa_audiomgr_node_unregistered(struct userdata   *u,
315                                    am_nodeunreg_data *ud)
316 {
317     (void)u;
318
319     /* can't do too much here anyways,
320        since the node is gone already */
321
322     pa_xfree((void *)ud->name);
323     pa_xfree((void *)ud);
324 }
325
326
327 void pa_audiomgr_connect(struct userdata *u, am_connect_data *cd)
328 {
329     pa_audiomgr    *am;
330     am_ack_data     ad;
331     mir_connection *conn;
332     uint16_t        cid;
333     mir_node       *from = NULL;
334     mir_node       *to   = NULL;
335     int             err  = E_OK;
336
337     pa_assert(u);
338     pa_assert(cd);
339     pa_assert_se((am = u->audiomgr));
340
341     if ((from = pa_hashmap_get(am->nodes, node_hash(mir_input, cd->source))) &&
342         (to   = pa_hashmap_get(am->nodes, node_hash(mir_output, cd->sink))))
343     {
344         cid = cd->connection;
345
346         pa_log_debug("routing '%s' => '%s'", from->amname, to->amname);
347
348         if (!(conn = mir_router_add_explicit_route(u, cid, from, to)))
349             err = E_NOT_POSSIBLE;
350         else {
351             pa_log_debug("registering connection (%u/%p)",
352                          cd->connection, conn);
353             pa_hashmap_put(am->conns, conn_hash(cid), conn);
354         }
355     }
356     else {
357         pa_log_debug("failed to connect: can't find node for %s %u",
358                      from ? "sink" : "source", from ? cd->sink : cd->source);
359         err = E_NON_EXISTENT;
360     }
361
362     memset(&ad, 0, sizeof(ad));
363     ad.handle = cd->handle;
364     ad.param1 = cd->connection;
365     ad.error  = err;
366
367     pa_policy_dbusif_acknowledge(u, AUDIOMGR_CONNECT_ACK, &ad);
368 }
369
370 void pa_audiomgr_disconnect(struct userdata *u, am_connect_data *cd)
371 {
372     pa_audiomgr    *am;
373     mir_connection *conn;
374     uint16_t        cid;
375     am_ack_data     ad;
376     int             err = E_OK;
377
378     pa_assert(u);
379     pa_assert(cd);
380     pa_assert_se((am = u->audiomgr));
381
382     cid = cd->connection;
383
384     if ((conn = pa_hashmap_remove(am->conns, conn_hash(cid))))
385         mir_router_remove_explicit_route(u, conn);
386     else {
387         pa_log_debug("failed to disconnect: can't find connection %u", cid);
388         err = E_NON_EXISTENT;
389     }
390
391     memset(&ad, 0, sizeof(ad));
392     ad.handle = cd->handle;
393     ad.param1 = cd->connection;
394     ad.error  = err;
395
396     pa_policy_dbusif_acknowledge(u, AUDIOMGR_DISCONNECT_ACK, &ad);
397 }
398
399 static void *node_hash(mir_direction direction, uint16_t amid)
400 {
401     return NULL + ((uint32_t)direction << 16 | (uint32_t)amid);
402 }
403
404 static void *conn_hash(uint16_t connid)
405 {
406     return NULL + (uint32_t)connid;
407 }
408
409
410
411 /*
412  * Local Variables:
413  * c-basic-offset: 4
414  * indent-tabs-mode: nil
415  * End:
416  *
417  */
418