2 * module-murphy-ivi -- PulseAudio module for providing audio routing support
3 * Copyright (c) 2012, Intel Corporation.
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.
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.
14 * You should have received a copy of the GNU Lesser General Public License
15 Requires(postun): /sbin/ldconfig
17 * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
23 #include <sys/types.h>
30 #include <pulsecore/pulsecore-config.h>
32 #include <pulsecore/sink.h>
33 #include <pulsecore/card.h>
34 #include <pulsecore/device-port.h>
35 #include <pulsecore/core-util.h>
47 static int pid2exe(pid_t, char *, size_t);
48 static char *pid2appid(pid_t, char *, size_t);
50 void pa_classify_node_by_card(mir_node *node,
52 pa_card_profile *prof,
64 bus = pa_utils_get_card_bus(card);
66 /* bus might be null */
70 form = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_FORM_FACTOR);
72 desc = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
75 node->type = mir_node_type_unknown;
78 if (!strcasecmp(form, "internal")) {
79 node->location = mir_external;
80 if (port && !strcasecmp(bus, "pci")) {
81 pa_classify_guess_device_node_type_and_name(node, port->name,
85 else if (!strcasecmp(form, "speaker") || !strcasecmp(form, "car")) {
86 if (node->direction == mir_output) {
87 node->location = mir_internal;
88 node->type = mir_speakers;
91 else if (!strcasecmp(form, "handset")) {
92 node->location = mir_external;
93 node->type = mir_phone;
94 node->privacy = mir_private;
96 else if (!strcasecmp(form, "headset")) {
97 node->location = mir_external;
99 if (!strcasecmp(bus,"usb")) {
100 node->type = mir_usb_headset;
102 else if (!strcasecmp(bus,"bluetooth")) {
103 if (prof && !strcmp(prof->name, "a2dp"))
104 node->type = mir_bluetooth_a2dp;
106 node->type = mir_bluetooth_sco;
109 node->type = mir_wired_headset;
113 else if (!strcasecmp(form, "headphone")) {
114 if (node->direction == mir_output) {
115 node->location = mir_external;
117 if (!strcasecmp(bus,"usb"))
118 node->type = mir_usb_headphone;
119 else if (strcasecmp(bus,"bluetooth"))
120 node->type = mir_wired_headphone;
124 else if (!strcasecmp(form, "microphone")) {
125 if (node->direction == mir_input) {
126 node->location = mir_external;
127 node->type = mir_microphone;
130 else if (!strcasecmp(form, "phone")) {
131 if (bus && !strcasecmp(bus,"bluetooth") && prof) {
132 if (!strcmp(prof->name, "a2dp"))
133 node->type = mir_bluetooth_a2dp;
134 else if (!strcmp(prof->name, "hsp"))
135 node->type = mir_bluetooth_sco;
136 else if (!strcmp(prof->name, "hfgw"))
137 node->type = mir_bluetooth_carkit;
138 else if (!strcmp(prof->name, "a2dp_source"))
139 node->type = mir_bluetooth_source;
140 else if (!strcmp(prof->name, "a2dp_sink"))
141 node->type = mir_bluetooth_sink;
143 if (node->type != mir_node_type_unknown)
144 node->location = mir_external;
149 if (port && !strcasecmp(bus, "pci")) {
150 pa_classify_guess_device_node_type_and_name(node, port->name,
153 else if (prof && !strcasecmp(bus, "bluetooth")) {
154 if (!strcmp(prof->name, "a2dp"))
155 node->type = mir_bluetooth_a2dp;
156 else if (!strcmp(prof->name, "hsp"))
157 node->type = mir_bluetooth_sco;
158 else if (!strcmp(prof->name, "hfgw"))
159 node->type = mir_bluetooth_carkit;
160 else if (!strcmp(prof->name, "a2dp_source"))
161 node->type = mir_bluetooth_source;
162 else if (!strcmp(prof->name, "a2dp_sink"))
163 node->type = mir_bluetooth_sink;
167 if (!node->amname[0]) {
168 if (node->type != mir_node_type_unknown)
169 node->amname = (char *)mir_node_type_str(node->type);
170 else if (port && port->description)
171 node->amname = port->description;
172 else if (port && port->name)
173 node->amname = port->name;
175 node->amname = node->paname;
179 if (node->direction == mir_input)
180 node->privacy = mir_privacy_unknown;
182 switch (node->type) {
186 case mir_front_speakers:
187 node->privacy = mir_public;
192 case mir_wired_headset:
193 case mir_wired_headphone:
194 case mir_usb_headset:
195 case mir_usb_headphone:
196 case mir_bluetooth_sco:
197 case mir_bluetooth_a2dp:
198 node->privacy = mir_private;
206 case mir_bluetooth_sink:
207 node->privacy = mir_privacy_unknown;
213 bool pa_classify_node_by_property(mir_node *node, pa_proplist *pl)
220 static type_mapping_t map[] = {
221 {"speakers" , mir_speakers },
222 {"front-speakers" , mir_front_speakers },
223 {"rear-speakers" , mir_rear_speakers },
224 {"microphone" , mir_microphone },
225 {"jack" , mir_jack },
226 {"hdmi" , mir_hdmi },
227 {"gateway_source" , mir_gateway_source },
228 {"gateway_sink" , mir_gateway_sink },
229 {"spdif" , mir_spdif },
230 { NULL , mir_node_type_unknown}
239 if ((type = pa_proplist_gets(pl, PA_PROP_NODE_TYPE))) {
240 for (i = 0; map[i].name; i++) {
241 if (pa_streq(type, map[i].name)) {
242 node->type = map[i].value;
251 /* data->direction must be set */
252 void pa_classify_guess_device_node_type_and_name(mir_node *node,
260 if (node->direction == mir_output && strcasestr(name, "headphone")) {
261 node->type = mir_wired_headphone;
262 node->amname = (char *)desc;
264 else if (strcasestr(name, "headset")) {
265 node->type = mir_wired_headset;
266 node->amname = (char *)desc;
268 else if (strcasestr(name, "line")) {
269 node->type = mir_jack;
270 node->amname = (char *)desc;
272 else if (strcasestr(name, "spdif")) {
273 node->type = mir_spdif;
274 node->amname = (char *)desc;
276 else if (strcasestr(name, "hdmi")) {
277 node->type = mir_hdmi;
278 node->amname = (char *)desc;
280 else if (node->direction == mir_input &&
281 (strcasestr(name, "microphone") || strcasestr(desc, "microphone")))
283 node->type = mir_microphone;
284 node->amname = (char *)desc;
286 else if (node->direction == mir_output && strcasestr(name,"analog-output"))
287 node->type = mir_speakers;
288 else if (node->direction == mir_input && strcasestr(name, "analog-input"))
289 node->type = mir_jack;
291 node->type = mir_node_type_unknown;
296 mir_node_type pa_classify_guess_stream_node_type(struct userdata *u,
298 pa_nodeset_resdef **resdef)
300 pa_nodeset_map *map = NULL;
304 char appid[PATH_MAX];
314 if (!(pidstr = pa_proplist_gets(pl, PA_PROP_APPLICATION_PROCESS_ID)) ||
315 (pid = strtol(pidstr, NULL, 10)) < 2)
320 if ((bin = pa_proplist_gets(pl, PA_PROP_APPLICATION_PROCESS_BINARY))) {
321 if (!strcmp(bin, "threaded-ml") ||
322 !strcmp(bin, "WebProcess") ||
323 !strcmp(bin,"wrt_launchpad_daemon"))
329 if (aul_app_get_appid_bypid(pid, buf, sizeof(buf)) < 0 &&
330 pid2exe(pid, buf, sizeof(buf)) < 0)
332 pa_log("can't obtain real application name for wrt '%s' "
333 "(pid %d)", bin, pid);
337 if (pid2exe(pid, buf, sizeof(buf)) < 0) {
338 pa_log("can't obtain real application name for wrt '%s' "
339 "(pid %d)", bin, pid);
343 if ((name = strrchr(buf, '.')))
348 pa_proplist_sets(pl, PA_PROP_APPLICATION_NAME, name);
349 pa_proplist_sets(pl, PA_PROP_APPLICATION_PROCESS_BINARY, buf);
354 if ((map = pa_nodeset_get_map_by_binary(u, bin))) {
356 pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, map->role);
361 if ((role = pa_proplist_gets(pl, PA_PROP_MEDIA_ROLE)) &&
362 (map = pa_nodeset_get_map_by_role(u, role)))
368 return role ? mir_node_type_unknown : mir_player;
372 if (pid2appid(pid, appid, sizeof(appid)))
373 pa_proplist_sets(pl, PA_PROP_RESOURCE_SET_APPID, appid);
376 *resdef = map ? map->resdef : NULL;
378 return map ? map->type : mir_player;
381 static char *get_tag(pid_t pid, const char *tag, char *buf, size_t size)
384 char data[8192], *p, *q;
389 snprintf(path, sizeof(path), "/proc/%u/status", pid);
391 if ((fd = open(path, O_RDONLY)) < 0) {
398 if ((n = read(fd, data, sizeof(data) - 1)) <= 0)
401 data[sizeof(data)-1] = '\0';
409 if (*p != '\n' && p != data) {
410 while (*p && *p != '\n')
420 if (!strncmp(p, tag, tlen) && p[tlen] == ':') {
422 while (*p == ' ' || *p == '\t')
426 while (*p != '\n' && *p && size > 1)
440 static pid_t get_ppid(pid_t pid)
445 if (get_tag(pid, "PPid", buf, sizeof(buf)) != NULL) {
446 ppid = strtol(buf, &end, 10);
456 static int pid2exe(pid_t pid, char *buf, size_t len)
464 if (buf && len > 0) {
465 ppid = get_ppid(pid);
467 snprintf(path, sizeof(path), "/proc/%u/cmdline", ppid);
469 if ((f = fopen(path, "r"))) {
470 if (fgets(buf, (int)len-1, f)) {
471 if ((p = strchr(buf, ' ')))
473 else if ((p = strchr(buf, '\n')))
476 p = buf + strlen(buf);
478 if ((q = strrchr(buf, '/')))
479 memmove(buf, q+1, (size_t)(p-q));
488 pa_log("pid2exe(%u) failed", pid);
490 pa_log_debug("pid2exe(%u) => exe %s", pid, buf);
496 static char *get_binary(pid_t pid, char *buf, size_t size)
501 snprintf(path, sizeof(path), "/proc/%u/exe", pid);
502 if ((len = readlink(path, buf, size - 1)) > 0) {
511 static char *strprev(char *point, char c, char *base)
513 while (point > base && *point != c)
523 static char *pid2appid(pid_t pid, char *buf, size_t size)
525 char binary[PATH_MAX];
526 char path[PATH_MAX], *dir, *p, *base;
529 if (!pid || !get_binary(pid, binary, sizeof(binary)))
532 strncpy(path, binary, sizeof(path) - 1);
533 path[sizeof(path) - 1] = '\0';
536 if ((p = strrchr(path, '/')) == NULL || p == path) {
537 strncpy(buf, binary, size - 1);
538 buf[size - 1] = '\0';
544 /* fetch ../bin/<basename> */
545 if ((p = strprev(p, '/', path)) == NULL || p == path)
548 if (strncmp(p + 1, "bin/", 4) != 0)
553 /* fetch dir name above bin */
554 if ((dir = strprev(p, '/', path)) == NULL || dir == path)
557 len = (size_t)(p - dir);
559 /* fetch 'apps' dir */
562 if ((p = strprev(p, '/', path)) == NULL)
565 if (strncmp(p + 1, "apps/", 5) != 0)
568 if (len + 1 <= size) {
569 strncpy(buf, dir + 1, len);
576 strncpy(buf, base, size - 1);
577 buf[size - 1] = '\0';
582 mir_node_type pa_classify_guess_application_class(mir_node *node)
588 if (node->implement == mir_stream)
591 if (node->direction == mir_output)
592 class = mir_node_type_unknown;
594 switch (node->type) {
595 default: class = mir_node_type_unknown; break;
596 case mir_bluetooth_carkit: class = mir_phone; break;
597 case mir_bluetooth_source: class = mir_player; break;
606 bool pa_classify_multiplex_stream(mir_node *node)
608 static bool multiplex[mir_application_class_end] = {
609 [ mir_player ] = true,
617 if (node->implement == mir_stream && node->direction == mir_input) {
620 if (class > mir_application_class_begin &&
621 class < mir_application_class_end)
623 return multiplex[class];
630 bool pa_classify_ramping_stream(mir_node *node)
632 static bool ramping[mir_application_class_end] = {
633 [ mir_player ] = true,
640 if (node->implement == mir_stream && node->direction == mir_input) {
643 if (class > mir_application_class_begin &&
644 class < mir_application_class_end)
646 return ramping[class];
653 const char *pa_classify_loopback_stream(mir_node *node)
655 const char *role[mir_device_class_end - mir_device_class_begin] = {
656 [ mir_bluetooth_carkit - mir_device_class_begin ] = "phone",
657 [ mir_bluetooth_source - mir_device_class_begin ] = "bt_music",
664 if (node->implement == mir_device) {
667 if (class >= mir_device_class_begin && class < mir_device_class_end) {
668 return role[class - mir_device_class_begin];
678 * indent-tabs-mode: nil