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