routing: set correct routing class when reinstating a default stream
[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, "hfgw"))
115                 node->type = mir_bluetooth_carkit;
116             else if (!strcmp(prof->name, "a2dp_source"))
117                 node->type = mir_bluetooth_source;
118             else if (!strcmp(prof->name, "a2dp_sink"))
119                 node->type = mir_bluetooth_sink;
120         }
121     }
122
123     if (!node->amname[0]) {
124         if (node->type != mir_node_type_unknown)
125             node->amname = (char *)mir_node_type_str(node->type);
126         else if (port && port->description)
127             node->amname = port->description;
128         else if (port && port->name)
129             node->amname = port->name;
130         else
131             node->amname = node->paname;
132     }
133
134
135     if (node->direction == mir_input)
136         node->privacy = mir_privacy_unknown;
137     else {
138         switch (node->type) {
139             /* public */
140         default:
141         case mir_speakers:
142         case mir_front_speakers:
143         case mir_rear_speakers:
144             node->privacy = mir_public;
145             break;
146             
147             /* private */
148         case mir_phone:
149         case mir_wired_headset:
150         case mir_wired_headphone:
151         case mir_usb_headset:
152         case mir_usb_headphone:
153         case mir_bluetooth_sco:
154         case mir_bluetooth_a2dp:
155             node->privacy = mir_private;
156             break;
157             
158             /* unknown */
159         case mir_null:
160         case mir_jack:
161         case mir_spdif:
162         case mir_hdmi:
163         case mir_bluetooth_sink:
164             node->privacy = mir_privacy_unknown;
165             break;
166         } /* switch */
167     }
168 }
169
170
171 /* data->direction must be set */
172 void pa_classify_guess_device_node_type_and_name(mir_node   *node,
173                                                  const char *name,
174                                                  const char *desc)
175 {
176     pa_assert(node);
177     pa_assert(name);
178     pa_assert(desc);
179
180     if (node->direction == mir_output && strcasestr(name, "headphone")) {
181         node->type = mir_wired_headphone;
182         node->amname = (char *)desc;
183     }
184     else if (strcasestr(name, "headset")) {
185         node->type = mir_wired_headset;
186         node->amname = (char *)desc;
187     }
188     else if (strcasestr(name, "line")) {
189         node->type = mir_jack;
190         node->amname = (char *)desc;
191     }
192     else if (strcasestr(name, "spdif")) {
193         node->type = mir_spdif;
194         node->amname = (char *)desc;
195     }
196     else if (strcasestr(name, "hdmi")) {
197         node->type = mir_hdmi;
198         node->amname = (char *)desc;
199     }
200     else if (node->direction == mir_input && strcasestr(name, "microphone")) {
201         node->type = mir_microphone;
202         node->amname = (char *)desc;
203     }
204     else if (node->direction == mir_output && strcasestr(name,"analog-output"))
205         node->type = mir_speakers;
206     else if (node->direction == mir_input && strcasestr(name, "analog-input"))
207         node->type = mir_jack;
208     else {
209         node->type = mir_node_type_unknown;
210     }
211 }
212
213
214 mir_node_type pa_classify_guess_stream_node_type(pa_proplist *pl)
215 {
216     typedef struct {
217         char *id;
218         mir_node_type type;
219     } map_t;
220
221     static map_t role_map[] = {
222         {"video"    , mir_player    },
223         {"music"    , mir_player    },
224         {"game"     , mir_game      },
225         {"event"    , mir_event     },
226         {"navigator", mir_navigator },
227         {"phone"    , mir_phone     },
228         {"carkit"   , mir_phone     },
229         {"animation", mir_browser   },
230         {"test"     , mir_player    },
231         {NULL, mir_node_type_unknown}
232     };
233
234     static map_t bin_map[] = {
235         {"rhytmbox"    , mir_player },
236         {"firefox"     , mir_browser},
237         {"chrome"      , mir_browser},
238         {"sound-juicer", mir_player },
239         {NULL, mir_node_type_unknown}
240     };
241
242
243     mir_node_type  rtype, btype;
244     const char    *role;
245     const char    *bin;
246     map_t         *m;
247
248     pa_assert(pl);
249
250     rtype = btype = mir_node_type_unknown;
251
252     role = pa_proplist_gets(pl, PA_PROP_MEDIA_ROLE);
253     bin  = pa_proplist_gets(pl, PA_PROP_APPLICATION_PROCESS_BINARY);
254
255     if (role) {
256         for (m = role_map;  m->id;  m++) {
257             if (pa_streq(role, m->id))
258                 break;
259         }
260         rtype = m->type;
261     }
262
263     if (rtype != mir_node_type_unknown && rtype != mir_player)
264         return rtype;
265
266     if (bin) {
267         for (m = bin_map;  m->id;  m++) {
268             if (pa_streq(bin, m->id))
269                 break;
270         }
271         btype = m->type;
272     }
273
274     if (btype == mir_node_type_unknown)
275         return rtype;
276
277     return btype;
278 }
279
280 mir_node_type pa_classify_guess_application_class(mir_node *node)
281 {
282     mir_node_type class;
283
284     pa_assert(node);
285
286     if (node->implement == mir_stream)
287         class = node->type;
288     else {
289         if (node->direction == mir_output)
290             class = mir_node_type_unknown;
291         else {
292             switch (node->type) {
293             default:                    class = mir_node_type_unknown;   break;
294             case mir_bluetooth_carkit:  class = mir_phone;               break;
295             case mir_bluetooth_source:  class = mir_player;              break;
296             }
297         }
298     }
299
300     return class;
301 }
302
303
304 pa_bool_t pa_classify_multiplex_stream(mir_node *node)
305 {
306     static pa_bool_t multiplex[mir_application_class_end] = {
307         [ mir_player  ] = TRUE,
308         [ mir_game    ] = TRUE,
309         [ mir_browser ] = TRUE,
310     };
311
312     mir_node_type class;
313
314     pa_assert(node);
315
316     if (node->implement == mir_stream && node->direction == mir_input) {
317         class = node->type;
318
319         if (class > mir_application_class_begin &&
320             class < mir_application_class_end)
321         {
322             return multiplex[class];
323         }
324     }
325
326     return FALSE;
327 }
328
329 const char *pa_classify_loopback_stream(mir_node *node)
330 {
331     const char *role[mir_device_class_end - mir_device_class_begin] = {
332         /*
333         [ mir_bluetooth_sco    - mir_device_class_begin ] = "phone" ,
334         [ mir_bluetooth_carkit - mir_device_class_begin ] = "carkit",
335         */
336         [ mir_bluetooth_source - mir_device_class_begin ] = "music" ,
337     };
338
339     int class;
340
341     pa_assert(node);
342
343     if (node->implement == mir_device && node->direction == mir_input) {
344         class = node->type;
345
346         if (class >= mir_device_class_begin && class < mir_device_class_end) {
347             return role[class - mir_device_class_begin];
348         }
349     }
350
351     return NULL;
352 }
353