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>
45 static int pid2exe(pid_t, char *, size_t);
46 static char *pid2appid(pid_t, char *, size_t);
48 void pa_classify_node_by_card(mir_node *node,
50 pa_card_profile *prof,
62 bus = pa_utils_get_card_bus(card);
63 form = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_FORM_FACTOR);
65 desc = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
68 node->type = mir_node_type_unknown;
71 if (!strcasecmp(form, "internal")) {
72 node->location = mir_external;
73 if (port && !strcasecmp(bus, "pci")) {
74 pa_classify_guess_device_node_type_and_name(node, port->name,
78 else if (!strcasecmp(form, "speaker") || !strcasecmp(form, "car")) {
79 if (node->direction == mir_output) {
80 node->location = mir_internal;
81 node->type = mir_speakers;
84 else if (!strcasecmp(form, "handset")) {
85 node->location = mir_external;
86 node->type = mir_phone;
87 node->privacy = mir_private;
89 else if (!strcasecmp(form, "headset")) {
90 node->location = mir_external;
92 if (!strcasecmp(bus,"usb")) {
93 node->type = mir_usb_headset;
95 else if (!strcasecmp(bus,"bluetooth")) {
96 if (prof && !strcmp(prof->name, "a2dp"))
97 node->type = mir_bluetooth_a2dp;
99 node->type = mir_bluetooth_sco;
102 node->type = mir_wired_headset;
106 else if (!strcasecmp(form, "headphone")) {
107 if (node->direction == mir_output) {
108 node->location = mir_external;
110 if (!strcasecmp(bus,"usb"))
111 node->type = mir_usb_headphone;
112 else if (strcasecmp(bus,"bluetooth"))
113 node->type = mir_wired_headphone;
117 else if (!strcasecmp(form, "microphone")) {
118 if (node->direction == mir_input) {
119 node->location = mir_external;
120 node->type = mir_microphone;
123 else if (!strcasecmp(form, "phone")) {
124 if (bus && !strcasecmp(bus,"bluetooth") && prof) {
125 if (!strcmp(prof->name, "a2dp"))
126 node->type = mir_bluetooth_a2dp;
127 else if (!strcmp(prof->name, "hsp"))
128 node->type = mir_bluetooth_sco;
129 else if (!strcmp(prof->name, "hfgw"))
130 node->type = mir_bluetooth_carkit;
131 else if (!strcmp(prof->name, "a2dp_source"))
132 node->type = mir_bluetooth_source;
133 else if (!strcmp(prof->name, "a2dp_sink"))
134 node->type = mir_bluetooth_sink;
136 if (node->type != mir_node_type_unknown)
137 node->location = mir_external;
142 if (port && !strcasecmp(bus, "pci")) {
143 pa_classify_guess_device_node_type_and_name(node, port->name,
146 else if (prof && !strcasecmp(bus, "bluetooth")) {
147 if (!strcmp(prof->name, "a2dp"))
148 node->type = mir_bluetooth_a2dp;
149 else if (!strcmp(prof->name, "hsp"))
150 node->type = mir_bluetooth_sco;
151 else if (!strcmp(prof->name, "hfgw"))
152 node->type = mir_bluetooth_carkit;
153 else if (!strcmp(prof->name, "a2dp_source"))
154 node->type = mir_bluetooth_source;
155 else if (!strcmp(prof->name, "a2dp_sink"))
156 node->type = mir_bluetooth_sink;
160 if (!node->amname[0]) {
161 if (node->type != mir_node_type_unknown)
162 node->amname = (char *)mir_node_type_str(node->type);
163 else if (port && port->description)
164 node->amname = port->description;
165 else if (port && port->name)
166 node->amname = port->name;
168 node->amname = node->paname;
172 if (node->direction == mir_input)
173 node->privacy = mir_privacy_unknown;
175 switch (node->type) {
179 case mir_front_speakers:
180 node->privacy = mir_public;
185 case mir_wired_headset:
186 case mir_wired_headphone:
187 case mir_usb_headset:
188 case mir_usb_headphone:
189 case mir_bluetooth_sco:
190 case mir_bluetooth_a2dp:
191 node->privacy = mir_private;
199 case mir_bluetooth_sink:
200 node->privacy = mir_privacy_unknown;
206 bool pa_classify_node_by_property(mir_node *node, pa_proplist *pl)
213 static type_mapping_t map[] = {
214 {"speakers" , mir_speakers },
215 {"front-speakers", mir_front_speakers },
216 {"rear-speakers" , mir_rear_speakers },
217 {"microphone" , mir_microphone },
218 {"jack" , mir_jack },
219 {"hdmi" , mir_hdmi },
220 {"spdif" , mir_spdif },
221 { NULL , mir_node_type_unknown}
230 if ((type = pa_proplist_gets(pl, PA_PROP_NODE_TYPE))) {
231 for (i = 0; map[i].name; i++) {
232 if (pa_streq(type, map[i].name)) {
233 node->type = map[i].value;
242 /* data->direction must be set */
243 void pa_classify_guess_device_node_type_and_name(mir_node *node,
251 if (node->direction == mir_output && strcasestr(name, "headphone")) {
252 node->type = mir_wired_headphone;
253 node->amname = (char *)desc;
255 else if (strcasestr(name, "headset")) {
256 node->type = mir_wired_headset;
257 node->amname = (char *)desc;
259 else if (strcasestr(name, "line")) {
260 node->type = mir_jack;
261 node->amname = (char *)desc;
263 else if (strcasestr(name, "spdif")) {
264 node->type = mir_spdif;
265 node->amname = (char *)desc;
267 else if (strcasestr(name, "hdmi")) {
268 node->type = mir_hdmi;
269 node->amname = (char *)desc;
271 else if (node->direction == mir_input &&
272 (strcasestr(name, "microphone") || strcasestr(desc, "microphone")))
274 node->type = mir_microphone;
275 node->amname = (char *)desc;
277 else if (node->direction == mir_output && strcasestr(name,"analog-output"))
278 node->type = mir_speakers;
279 else if (node->direction == mir_input && strcasestr(name, "analog-input"))
280 node->type = mir_jack;
282 node->type = mir_node_type_unknown;
287 mir_node_type pa_classify_guess_stream_node_type(struct userdata *u,
289 pa_nodeset_resdef **resdef)
291 pa_nodeset_map *map = NULL;
295 char appid[PATH_MAX];
305 if (!(pidstr = pa_proplist_gets(pl, PA_PROP_APPLICATION_PROCESS_ID)) ||
306 (pid = strtol(pidstr, NULL, 10)) < 2)
311 if ((bin = pa_proplist_gets(pl, PA_PROP_APPLICATION_PROCESS_BINARY))) {
312 if (!strcmp(bin, "threaded-ml") || !strcmp(bin, "WebProcess")) {
316 if (aul_app_get_appid_bypid(pid, buf, sizeof(buf)) < 0 &&
317 pid2exe(pid, buf, sizeof(buf)) < 0)
319 pa_log("can't obtain real application name for wrt '%s' "
320 "(pid %d)", bin, pid);
324 if ((name = strrchr(buf, '.')))
329 pa_proplist_sets(pl, PA_PROP_APPLICATION_NAME, name);
330 pa_proplist_sets(pl, PA_PROP_APPLICATION_PROCESS_BINARY, buf);
335 if ((map = pa_nodeset_get_map_by_binary(u, bin))) {
337 pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, map->role);
342 if ((role = pa_proplist_gets(pl, PA_PROP_MEDIA_ROLE)) &&
343 (map = pa_nodeset_get_map_by_role(u, role)))
349 return role ? mir_node_type_unknown : mir_player;
353 if (pid2appid(pid, appid, sizeof(appid)))
354 pa_proplist_sets(pl, PA_PROP_RESOURCE_SET_APPID, appid);
357 *resdef = map ? map->resdef : NULL;
359 return map ? map->type : mir_player;
362 static char *get_tag(pid_t pid, char *tag, char *buf, size_t size)
365 char data[8192], *p, *q;
369 snprintf(path, sizeof(path), "/proc/%u/status", pid);
371 if ((fd = open(path, O_RDONLY)) < 0) {
378 if ((n = read(fd, data, sizeof(data) - 1)) <= 0)
381 data[sizeof(data)-1] = '\0';
389 if (*p != '\n' && p != data) {
390 while (*p && *p != '\n')
400 if (!strncmp(p, tag, tlen) && p[tlen] == ':') {
402 while (*p == ' ' || *p == '\t')
406 while (*p != '\n' && *p && size > 1)
420 static pid_t get_ppid(pid_t pid)
425 if (get_tag(pid, "PPid", buf, sizeof(buf)) != NULL) {
426 ppid = strtoul(buf, &end, 10);
436 static int pid2exe(pid_t pid, char *buf, size_t len)
444 if (buf && len > 0) {
445 ppid = get_ppid(pid);
447 snprintf(path, sizeof(path), "/proc/%u/cmdline", ppid);
449 if ((f = fopen(path, "r"))) {
450 if (fgets(buf, len-1, f)) {
451 if ((p = strchr(buf, ' ')))
453 else if ((p = strchr(buf, '\n')))
456 p = buf + strlen(buf);
458 if ((q = strrchr(buf, '/')))
459 memmove(buf, q+1, p-q);
468 pa_log("pid2exe(%u) failed", pid);
470 pa_log_debug("pid2exe(%u) => exe %s", pid, buf);
476 static char *get_binary(pid_t pid, char *buf, size_t size)
481 snprintf(path, sizeof(path), "/proc/%u/exe", pid);
482 if ((len = readlink(path, buf, size - 1)) > 0) {
491 static char *strprev(char *point, char c, char *base)
493 while (point > base && *point != c)
503 static char *pid2appid(pid_t pid, char *buf, size_t size)
505 char binary[PATH_MAX];
506 char path[PATH_MAX], *dir, *p, *base;
509 if (!pid || !get_binary(pid, binary, sizeof(binary)))
512 strncpy(path, binary, sizeof(path) - 1);
513 path[sizeof(path) - 1] = '\0';
516 if ((p = strrchr(path, '/')) == NULL || p == path) {
517 strncpy(buf, binary, size - 1);
518 buf[size - 1] = '\0';
524 /* fetch ../bin/<basename> */
525 if ((p = strprev(p, '/', path)) == NULL || p == path)
528 if (strncmp(p + 1, "bin/", 4) != 0)
533 /* fetch dir name above bin */
534 if ((dir = strprev(p, '/', path)) == NULL || dir == path)
539 /* fetch 'apps' dir */
542 if ((p = strprev(p, '/', path)) == NULL)
545 if (strncmp(p + 1, "apps/", 5) != 0)
548 if (len + 1 <= size) {
549 strncpy(buf, dir + 1, len);
556 strncpy(buf, base, size - 1);
557 buf[size - 1] = '\0';
562 mir_node_type pa_classify_guess_application_class(mir_node *node)
568 if (node->implement == mir_stream)
571 if (node->direction == mir_output)
572 class = mir_node_type_unknown;
574 switch (node->type) {
575 default: class = mir_node_type_unknown; break;
576 case mir_bluetooth_carkit: class = mir_phone; break;
577 case mir_bluetooth_source: class = mir_player; break;
586 bool pa_classify_multiplex_stream(mir_node *node)
588 static bool multiplex[mir_application_class_end] = {
589 [ mir_player ] = true,
597 if (node->implement == mir_stream && node->direction == mir_input) {
600 if (class > mir_application_class_begin &&
601 class < mir_application_class_end)
603 return multiplex[class];
610 const char *pa_classify_loopback_stream(mir_node *node)
612 const char *role[mir_device_class_end - mir_device_class_begin] = {
613 [ mir_bluetooth_carkit - mir_device_class_begin ] = "phone",
614 [ mir_bluetooth_source - mir_device_class_begin ] = "bt_music",
621 if (node->implement == mir_device) {
624 if (class >= mir_device_class_begin && class < mir_device_class_end) {
625 return role[class - mir_device_class_begin];
635 * indent-tabs-mode: nil