scripting: support for zone based routing in application classes
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / classify.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 #define _GNU_SOURCE
21
22 #include <stdio.h>
23 #include <string.h>
24
25 #include <pulsecore/pulsecore-config.h>
26
27 #include <pulsecore/sink.h>
28 #include <pulsecore/card.h>
29 #include <pulsecore/device-port.h>
30 #include <pulsecore/core-util.h>
31
32 #include "classify.h"
33 #include "node.h"
34 #include "utils.h"
35
36
37 void pa_classify_node_by_card(mir_node        *node,
38                               pa_card         *card,
39                               pa_card_profile *prof,
40                               pa_device_port  *port)
41 {
42     const char *bus;
43     const char *form;
44     /*
45     const char *desc;
46     */
47
48     pa_assert(node);
49     pa_assert(card);
50
51     bus  = pa_utils_get_card_bus(card);
52     form = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_FORM_FACTOR);
53     /*
54     desc = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
55     */
56
57     node->type = mir_node_type_unknown;
58
59     if (form) {
60         if (!strcasecmp(form, "internal")) {
61             node->location = mir_external;
62             if (port && !strcasecmp(bus, "pci")) {
63                 pa_classify_guess_device_node_type_and_name(node, port->name,
64                                                             port->description);
65             }
66         }
67         else if (!strcasecmp(form, "speaker") || !strcasecmp(form, "car")) {
68             if (node->direction == mir_output) {
69                 node->location = mir_internal;
70                 node->type = mir_speakers;
71             }
72         }
73         else if (!strcasecmp(form, "handset")) {
74             node->location = mir_external;
75             node->type = mir_phone;
76             node->privacy = mir_private;
77         }
78         else if (!strcasecmp(form, "headset")) {
79             node->location = mir_external;
80             if (bus) {
81                 if (!strcasecmp(bus,"usb")) {
82                     node->type = mir_usb_headset;
83                 }
84                 else if (!strcasecmp(bus,"bluetooth")) {
85                     if (prof && !strcmp(prof->name, "a2dp"))
86                         node->type = mir_bluetooth_a2dp;
87                     else
88                         node->type = mir_bluetooth_sco;
89                 }
90                 else {
91                     node->type = mir_wired_headset;
92                 }
93             }
94         }
95         else if (!strcasecmp(form, "headphone")) {
96             if (node->direction == mir_output) {
97                 node->location = mir_external;
98                 if (bus) {
99                     if (!strcasecmp(bus,"usb"))
100                         node->type = mir_usb_headphone;
101                     else if (strcasecmp(bus,"bluetooth"))
102                         node->type = mir_wired_headphone;
103                 }
104             }
105         }
106         else if (!strcasecmp(form, "microphone")) {
107             if (node->direction == mir_input) {
108                 node->location = mir_external;
109                 node->type = mir_microphone;
110             }
111         }
112         else if (!strcasecmp(form, "phone")) {
113             if (bus && !strcasecmp(bus,"bluetooth") && prof) {
114                 if (!strcmp(prof->name, "a2dp"))
115                     node->type = mir_bluetooth_a2dp;
116                 else if (!strcmp(prof->name, "hsp"))
117                     node->type = mir_bluetooth_sco;
118                 else if (!strcmp(prof->name, "hfgw"))
119                     node->type = mir_bluetooth_carkit;
120                 else if (!strcmp(prof->name, "a2dp_source"))
121                     node->type = mir_bluetooth_source;
122                 else if (!strcmp(prof->name, "a2dp_sink"))
123                     node->type = mir_bluetooth_sink;
124
125                 if (node->type != mir_node_type_unknown)
126                     node->location = mir_external;
127             }
128         }
129     }
130     else {
131         if (port && !strcasecmp(bus, "pci")) {
132             pa_classify_guess_device_node_type_and_name(node, port->name,
133                                                         port->description);
134         }
135         else if (prof && !strcasecmp(bus, "bluetooth")) {
136             if (!strcmp(prof->name, "a2dp"))
137                 node->type = mir_bluetooth_a2dp;
138             else if (!strcmp(prof->name, "hsp"))
139                 node->type = mir_bluetooth_sco;
140             else if (!strcmp(prof->name, "hfgw"))
141                 node->type = mir_bluetooth_carkit;
142             else if (!strcmp(prof->name, "a2dp_source"))
143                 node->type = mir_bluetooth_source;
144             else if (!strcmp(prof->name, "a2dp_sink"))
145                 node->type = mir_bluetooth_sink;
146         }
147     }
148
149     if (!node->amname[0]) {
150         if (node->type != mir_node_type_unknown)
151             node->amname = (char *)mir_node_type_str(node->type);
152         else if (port && port->description)
153             node->amname = port->description;
154         else if (port && port->name)
155             node->amname = port->name;
156         else
157             node->amname = node->paname;
158     }
159
160
161     if (node->direction == mir_input)
162         node->privacy = mir_privacy_unknown;
163     else {
164         switch (node->type) {
165             /* public */
166         default:
167         case mir_speakers:
168         case mir_front_speakers:
169         case mir_rear_speakers:
170             node->privacy = mir_public;
171             break;
172             
173             /* private */
174         case mir_phone:
175         case mir_wired_headset:
176         case mir_wired_headphone:
177         case mir_usb_headset:
178         case mir_usb_headphone:
179         case mir_bluetooth_sco:
180         case mir_bluetooth_a2dp:
181             node->privacy = mir_private;
182             break;
183             
184             /* unknown */
185         case mir_null:
186         case mir_jack:
187         case mir_spdif:
188         case mir_hdmi:
189         case mir_bluetooth_sink:
190             node->privacy = mir_privacy_unknown;
191             break;
192         } /* switch */
193     }
194 }
195
196 pa_bool_t pa_classify_node_by_property(mir_node *node, pa_proplist *pl)
197 {
198     typedef struct {
199         const char *name;
200         mir_node_type value;
201     } type_mapping_t;
202
203     static type_mapping_t  map[] = {
204         {"speakers"      , mir_speakers         },
205         {"front-speakers", mir_front_speakers   },
206         {"rear-speakers" , mir_rear_speakers    },
207         {"microphone"    , mir_microphone       },
208         {"jack"          , mir_jack             },
209         {"hdmi"          , mir_hdmi             },
210         {"spdif"         , mir_spdif            },
211         { NULL           , mir_node_type_unknown}
212     };
213
214     const char *type;
215     int i;
216
217     pa_assert(node);
218     pa_assert(pl);
219
220     if ((type = pa_proplist_gets(pl, PA_PROP_NODE_TYPE))) {
221         for (i = 0; map[i].name;  i++) {
222             if (pa_streq(type, map[i].name)) {
223                 node->type = map[i].value;
224                 return TRUE;
225             }
226         }
227     }
228
229     return FALSE;
230 }
231
232 /* data->direction must be set */
233 void pa_classify_guess_device_node_type_and_name(mir_node   *node,
234                                                  const char *name,
235                                                  const char *desc)
236 {
237     pa_assert(node);
238     pa_assert(name);
239     pa_assert(desc);
240
241     if (node->direction == mir_output && strcasestr(name, "headphone")) {
242         node->type = mir_wired_headphone;
243         node->amname = (char *)desc;
244     }
245     else if (strcasestr(name, "headset")) {
246         node->type = mir_wired_headset;
247         node->amname = (char *)desc;
248     }
249     else if (strcasestr(name, "line")) {
250         node->type = mir_jack;
251         node->amname = (char *)desc;
252     }
253     else if (strcasestr(name, "spdif")) {
254         node->type = mir_spdif;
255         node->amname = (char *)desc;
256     }
257     else if (strcasestr(name, "hdmi")) {
258         node->type = mir_hdmi;
259         node->amname = (char *)desc;
260     }
261     else if (node->direction == mir_input && strcasestr(name, "microphone")) {
262         node->type = mir_microphone;
263         node->amname = (char *)desc;
264     }
265     else if (node->direction == mir_output && strcasestr(name,"analog-output"))
266         node->type = mir_speakers;
267     else if (node->direction == mir_input && strcasestr(name, "analog-input"))
268         node->type = mir_jack;
269     else {
270         node->type = mir_node_type_unknown;
271     }
272 }
273
274
275 mir_node_type pa_classify_guess_stream_node_type(struct userdata *u,
276                                                  pa_proplist *pl,
277                                                  pa_nodeset_resdef **resdef)
278 {
279     pa_nodeset_map *map;
280     const char     *role;
281     const char     *bin;
282
283     pa_assert(u);
284     pa_assert(pl);
285
286
287     do {
288         if ((bin = pa_proplist_gets(pl, PA_PROP_APPLICATION_PROCESS_BINARY)) &&
289             (map = pa_nodeset_get_map_by_binary(u, bin)))
290             break;
291
292         if ((role = pa_proplist_gets(pl, PA_PROP_MEDIA_ROLE)) &&
293             (map = pa_nodeset_get_map_by_role(u, role)))
294             break;
295
296         if (resdef)
297             *resdef = NULL;
298
299         return role ? mir_node_type_unknown : mir_player;
300
301     } while (0);
302
303     if (resdef)
304         *resdef = map->resdef;
305
306     return map->type;
307 }
308
309 mir_node_type pa_classify_guess_application_class(mir_node *node)
310 {
311     mir_node_type class;
312
313     pa_assert(node);
314
315     if (node->implement == mir_stream)
316         class = node->type;
317     else {
318         if (node->direction == mir_output)
319             class = mir_node_type_unknown;
320         else {
321             switch (node->type) {
322             default:                    class = mir_node_type_unknown;   break;
323             case mir_bluetooth_carkit:  class = mir_phone;               break;
324             case mir_bluetooth_source:  class = mir_player;              break;
325             }
326         }
327     }
328
329     return class;
330 }
331
332
333 pa_bool_t pa_classify_multiplex_stream(mir_node *node)
334 {
335     static pa_bool_t multiplex[mir_application_class_end] = {
336         [ mir_player  ] = TRUE,
337         [ mir_game    ] = TRUE,
338     };
339
340     mir_node_type class;
341
342     pa_assert(node);
343
344     if (node->implement == mir_stream && node->direction == mir_input) {
345         class = node->type;
346
347         if (class > mir_application_class_begin &&
348             class < mir_application_class_end)
349         {
350             return multiplex[class];
351         }
352     }
353
354     return FALSE;
355 }
356
357 const char *pa_classify_loopback_stream(mir_node *node)
358 {
359     const char *role[mir_device_class_end - mir_device_class_begin] = {
360         [ mir_bluetooth_carkit - mir_device_class_begin ] = "phone",
361         [ mir_bluetooth_source - mir_device_class_begin ] = "music" ,
362     };
363
364     int class;
365
366     pa_assert(node);
367
368     if (node->implement == mir_device) {
369         class = node->type;
370
371         if (class >= mir_device_class_begin && class < mir_device_class_end) {
372             return role[class - mir_device_class_begin];
373         }
374     }
375
376     return NULL;
377 }
378
379 /*
380  * Local Variables:
381  * c-basic-offset: 4
382  * indent-tabs-mode: nil
383  * End:
384  *
385  */