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