0b616346d024ac4b2d1ee7bbd27fe8380872731b
[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
72 typedef struct {
73     const char *name;
74     uint16_t    id;
75     uint16_t    state;
76 } domain_t;
77
78
79 struct pa_audiomgr {
80     domain_t      domain;
81     pa_hashmap   *nodes;        /**< nodes ie. sinks and sources */
82     pa_hashmap   *conns;        /**< connections */
83 };
84
85
86 static void *node_hash(mir_direction, uint16_t);
87 static void *conn_hash(uint16_t);
88
89
90 struct pa_audiomgr *pa_audiomgr_init(struct userdata *u)
91 {
92     /* pa_module *m = u->module; */
93     pa_audiomgr *am;
94
95     pa_assert(u);
96     
97     am = pa_xnew0(pa_audiomgr, 1);
98
99     am->domain.id = AM_ID_INVALID;
100     am->domain.state = DS_DOWN;
101     am->nodes = pa_hashmap_new(pa_idxset_trivial_hash_func,
102                                      pa_idxset_trivial_compare_func);
103     am->conns = pa_hashmap_new(pa_idxset_trivial_hash_func,
104                                pa_idxset_trivial_compare_func);
105     return am;
106 }
107
108 void pa_audiomgr_done(struct userdata *u)
109 {
110     pa_audiomgr *am;
111
112     if (u && (am = u->audiomgr)) {
113         if (u->routerif && am->domain.id != AM_ID_INVALID)
114             pa_routerif_unregister_domain(u, am->domain.id);
115
116         pa_hashmap_free(am->nodes);
117         pa_hashmap_free(am->conns);
118         pa_xfree((void *)am->domain.name);
119         pa_xfree(am);
120         u->audiomgr = NULL;
121     }
122 }
123
124
125 void pa_audiomgr_register_domain(struct userdata *u)
126 {
127     pa_audiomgr        *am;
128     am_domainreg_data  *dr;
129
130     pa_assert(u);
131     pa_assert_se((am = u->audiomgr));
132
133     dr = pa_xnew0(am_domainreg_data, 1);
134
135     dr->domain_id = 0;
136     dr->name      = AUDIOMGR_DOMAIN;  /* AM domain name */
137     dr->bus_name  = AUDIOMGR_NODE;    /* AM internal bus name. */
138     dr->node_name = AUDIOMGR_NODE;    /* node name on AM's internal bus */
139     dr->early     = false;
140     dr->complete  = false;
141     dr->state     = 1;
142
143     pa_routerif_register_domain(u, dr);
144 }
145
146 void pa_audiomgr_domain_registered(struct userdata   *u,
147                                    uint16_t           id,
148                                    uint16_t           state, 
149                                    am_domainreg_data *dr)
150 {
151     pa_audiomgr *am;
152
153     pa_assert(u);
154     pa_assert(dr);
155     pa_assert_se((am = u->audiomgr));
156
157
158     am->domain.name  = pa_xstrdup(dr->name);
159     am->domain.id    = id;
160     am->domain.state = state;
161
162     pa_log_debug("start domain registration for '%s' domain", dr->name);
163     
164     pa_discover_domain_up(u);
165     
166     pa_log_debug("domain registration for '%s' domain is complete", dr->name);
167
168     pa_routerif_domain_complete(u, id);
169
170     pa_xfree(dr);
171 }
172
173
174 void pa_audiomgr_unregister_domain(struct userdata *u, bool send_state)
175 {
176     pa_audiomgr *am;
177     mir_node    *node;
178     const void  *key;
179     void        *state = NULL;
180
181     pa_assert(u);
182     pa_assert_se((am = u->audiomgr));
183
184     pa_log_debug("unregistering domain '%s'", am->domain.name);
185
186     while ((node  = pa_hashmap_iterate(am->nodes, &state, &key))) {
187         pa_log_debug("   unregistering '%s' (%p/%p)", node->amname, key,node);
188         node->amid = AM_ID_INVALID;
189         pa_hashmap_remove(am->nodes, key);
190     }
191
192     am->domain.id = AM_ID_INVALID;
193     am->domain.state = DS_DOWN;
194 }
195
196
197 void pa_audiomgr_register_node(struct userdata *u, mir_node *node)
198 {
199     pa_audiomgr      *am;
200     am_nodereg_data  *rd;
201     am_method         method;
202     bool         success;
203
204     pa_assert(u);
205     pa_assert_se((am = u->audiomgr));
206
207     if (am->domain.state == DS_DOWN || am->domain.state == DS_RUNDOWN)
208         pa_log_debug("skip registering nodes while the domain is down");
209     else {
210         if (node->direction == mir_input || node->direction == mir_output) {
211             rd = pa_xnew0(am_nodereg_data, 1);
212             rd->key     = pa_xstrdup(node->key);
213             rd->name    = pa_xstrdup(node->amname);
214             rd->domain  = am->domain.id;
215             rd->class   = 0x43;
216             rd->volume  = 32767;
217             rd->visible = node->visible;
218             rd->avail.status = AS_AVAILABLE;
219             rd->avail.reason = 0;
220             rd->mainvol = 32767;
221             
222             if (node->direction == mir_input) {
223                 rd->interrupt = IS_OFF;
224                 method = audiomgr_register_source;
225             } 
226             else {
227                 rd->mute = MS_UNMUTED;
228                 method = audiomgr_register_sink;
229             }
230             
231             success = pa_routerif_register_node(u, method, rd);
232             
233             if (success) {
234                 pa_log_debug("initiate registration node '%s' (%p)"
235                              "to audio manager", rd->name, node);
236             }
237             else {
238                 pa_log("%s: failed to register node '%s' (%p)"
239                        "to audio manager", __FILE__, rd->name, node);
240             }
241         }
242     }
243 }
244
245 void pa_audiomgr_node_registered(struct userdata *u,
246                                  uint16_t         id,
247                                  uint16_t         state,
248                                  am_nodereg_data *rd)
249 {
250     pa_audiomgr *am;
251     mir_node    *node;
252     void        *key;
253
254     pa_assert(u);
255     pa_assert(rd);
256     pa_assert(rd->key);
257     pa_assert_se((am = u->audiomgr));
258
259     if (!(node = pa_discover_find_node_by_key(u, rd->key)))
260         pa_log("%s: can't find node with key '%s'", __FILE__, rd->key);
261     else {
262         node->amid = id;
263
264         key = node_hash(node->direction, id);
265
266         pa_log_debug("registering node '%s' (%p/%p)",
267                      node->amname, key, node);
268
269         pa_hashmap_put(am->nodes, key, node);
270     }
271
272     pa_xfree((void *)rd->key);
273     pa_xfree((void *)rd->name);
274     pa_xfree((void *)rd);
275 }
276
277 void pa_audiomgr_unregister_node(struct userdata *u, mir_node *node)
278 {
279     pa_audiomgr       *am;
280     am_nodeunreg_data *ud;
281     am_method          method;
282     mir_node          *removed;
283     bool          success;
284     void              *key;
285
286     pa_assert(u);
287     pa_assert_se((am = u->audiomgr));
288
289     if (am->domain.state == DS_DOWN || am->domain.state == DS_RUNDOWN)
290         pa_log_debug("skip unregistering nodes while the domain is down");
291     else if (node->amid == AM_ID_INVALID)
292         pa_log_debug("node '%s' was not registered", node->amname);
293     else if (node->direction == mir_input || node->direction == mir_output) {
294         ud = pa_xnew0(am_nodeunreg_data, 1);
295         ud->id   = node->amid;
296         ud->name = pa_xstrdup(node->amname);
297
298         key = node_hash(node->direction, node->amid);
299         removed = pa_hashmap_remove(am->nodes, key);
300
301         if (node != removed) {
302             if (removed)
303                 pa_log("%s: confused with data structures: key mismatch. "
304                        "attempted to remove '%s' (%p/%p); "
305                        "actually removed '%s' (%p/%p)", __FILE__,
306                        node->amname, key, node, removed->amname,
307                        node_hash(removed->direction, removed->amid), removed);
308             else
309                 pa_log("%s: confused with data structures: node %u (%p)"
310                        "is not in the hash table", __FILE__, node->amid, node);
311         }
312         
313         
314         if (node->direction == mir_input)
315             method = audiomgr_deregister_source;
316         else
317             method = audiomgr_deregister_sink;
318         
319         
320         success = pa_routerif_unregister_node(u, method, ud);
321         
322         if (success) {
323             pa_log_debug("sucessfully unregistered node '%s' (%p/%p)"
324                          "from audio manager", node->amname, key, node);
325         }
326         else {
327             pa_log("%s: failed to unregister node '%s' (%p)"
328                    "from audio manager", __FILE__, node->amname, node);
329         }
330     }
331 }
332
333 void pa_audiomgr_node_unregistered(struct userdata   *u,
334                                    am_nodeunreg_data *ud)
335 {
336     (void)u;
337
338     /* can't do too much here anyways,
339        since the node is gone already */
340
341     pa_xfree((void *)ud->name);
342     pa_xfree((void *)ud);
343 }
344
345
346 void pa_audiomgr_connect(struct userdata *u, am_connect_data *cd)
347 {
348     pa_audiomgr    *am;
349     am_ack_data     ad;
350     mir_connection *conn;
351     uint16_t        cid;
352     mir_node       *from = NULL;
353     mir_node       *to   = NULL;
354     int             err  = E_OK;
355
356     pa_assert(u);
357     pa_assert(cd);
358     pa_assert_se((am = u->audiomgr));
359
360     if ((from = pa_hashmap_get(am->nodes, node_hash(mir_input, cd->source))) &&
361         (to   = pa_hashmap_get(am->nodes, node_hash(mir_output, cd->sink))))
362     {
363         cid = cd->connection;
364
365         pa_log_debug("routing '%s' => '%s'", from->amname, to->amname);
366
367         if (!(conn = mir_router_add_explicit_route(u, cid, from, to)))
368             err = E_NOT_POSSIBLE;
369         else {
370             pa_log_debug("registering connection (%u/%p)",
371                          cd->connection, conn);
372             pa_hashmap_put(am->conns, conn_hash(cid), conn);
373         }
374     }
375     else {
376         pa_log_debug("failed to connect: can't find node for %s %u",
377                      from ? "sink" : "source", from ? cd->sink : cd->source);
378         err = E_NON_EXISTENT;
379     }
380
381     memset(&ad, 0, sizeof(ad));
382     ad.handle = cd->handle;
383     ad.param1 = cd->connection;
384     ad.error  = err;
385
386     pa_routerif_acknowledge(u, audiomgr_connect_ack, &ad);
387 }
388
389 void pa_audiomgr_disconnect(struct userdata *u, am_connect_data *cd)
390 {
391     pa_audiomgr    *am;
392     mir_connection *conn;
393     uint16_t        cid;
394     am_ack_data     ad;
395     int             err = E_OK;
396
397     pa_assert(u);
398     pa_assert(cd);
399     pa_assert_se((am = u->audiomgr));
400
401     cid = cd->connection;
402
403     if ((conn = pa_hashmap_remove(am->conns, conn_hash(cid))))
404         mir_router_remove_explicit_route(u, conn);
405     else {
406         pa_log_debug("failed to disconnect: can't find connection %u", cid);
407         err = E_NON_EXISTENT;
408     }
409
410     memset(&ad, 0, sizeof(ad));
411     ad.handle = cd->handle;
412     ad.param1 = cd->connection;
413     ad.error  = err;
414
415     pa_routerif_acknowledge(u, audiomgr_disconnect, &ad);
416 }
417
418 static void *node_hash(mir_direction direction, uint16_t amid)
419 {
420     return NULL + ((uint32_t)direction << 16 | (uint32_t)amid);
421 }
422
423 static void *conn_hash(uint16_t connid)
424 {
425     return NULL + (uint32_t)connid;
426 }
427
428
429
430 /*
431  * Local Variables:
432  * c-basic-offset: 4
433  * indent-tabs-mode: nil
434  * End:
435  *
436  */
437