1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2009 Lennart Poettering
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <libudev.h>
25
26 #include <pulse/xmalloc.h>
27 #include <pulse/proplist.h>
28
29 #include <pulsecore/log.h>
30 #include <pulsecore/core-util.h>
31
32 #include "udev-util.h"
33
read_id(struct udev_device * d,const char * n)34 static int read_id(struct udev_device *d, const char *n) {
35 const char *v;
36 unsigned u;
37
38 pa_assert(d);
39 pa_assert(n);
40
41 if (!(v = udev_device_get_property_value(d, n)))
42 return -1;
43
44 if (pa_startswith(v, "0x"))
45 v += 2;
46
47 if (!*v)
48 return -1;
49
50 if (sscanf(v, "%04x", &u) != 1)
51 return -1;
52
53 if (u > 0xFFFFU)
54 return -1;
55
56 return u;
57 }
58
dehex(char x)59 static int dehex(char x) {
60 if (x >= '0' && x <= '9')
61 return x - '0';
62
63 if (x >= 'A' && x <= 'F')
64 return x - 'A' + 10;
65
66 if (x >= 'a' && x <= 'f')
67 return x - 'a' + 10;
68
69 return -1;
70 }
71
proplist_sets_unescape(pa_proplist * p,const char * prop,const char * s)72 static void proplist_sets_unescape(pa_proplist *p, const char *prop, const char *s) {
73 const char *f;
74 char *t, *r;
75 int c = 0;
76
77 enum {
78 TEXT,
79 BACKSLASH,
80 EX,
81 FIRST
82 } state = TEXT;
83
84 /* The resulting string is definitely shorter than the source string */
85 r = pa_xnew(char, strlen(s)+1);
86
87 for (f = s, t = r; *f; f++) {
88
89 switch (state) {
90
91 case TEXT:
92 if (*f == '\\')
93 state = BACKSLASH;
94 else
95 *(t++) = *f;
96 break;
97
98 case BACKSLASH:
99 if (*f == 'x')
100 state = EX;
101 else {
102 *(t++) = '\\';
103 *(t++) = *f;
104 state = TEXT;
105 }
106 break;
107
108 case EX:
109 c = dehex(*f);
110
111 if (c < 0) {
112 *(t++) = '\\';
113 *(t++) = 'x';
114 *(t++) = *f;
115 state = TEXT;
116 } else
117 state = FIRST;
118
119 break;
120
121 case FIRST: {
122 int d = dehex(*f);
123
124 if (d < 0) {
125 *(t++) = '\\';
126 *(t++) = 'x';
127 *(t++) = *(f-1);
128 *(t++) = *f;
129 } else
130 *(t++) = (char) (c << 4) | d;
131
132 state = TEXT;
133 break;
134 }
135 }
136 }
137
138 switch (state) {
139
140 case TEXT:
141 break;
142
143 case BACKSLASH:
144 *(t++) = '\\';
145 break;
146
147 case EX:
148 *(t++) = '\\';
149 *(t++) = 'x';
150 break;
151
152 case FIRST:
153 *(t++) = '\\';
154 *(t++) = 'x';
155 *(t++) = *(f-1);
156 break;
157 }
158
159 *t = 0;
160
161 pa_proplist_sets(p, prop, r);
162 pa_xfree(r);
163 }
164
pa_udev_get_info(int card_idx,pa_proplist * p)165 int pa_udev_get_info(int card_idx, pa_proplist *p) {
166 int r = -1;
167 struct udev *udev;
168 struct udev_device *card = NULL;
169 char *t;
170 const char *v;
171 const char *bus = NULL;
172 int id;
173
174 pa_assert(p);
175 pa_assert(card_idx >= 0);
176
177 if (!(udev = udev_new())) {
178 pa_log_error("Failed to allocate udev context.");
179 goto finish;
180 }
181
182 t = pa_sprintf_malloc("/sys/class/sound/card%i", card_idx);
183 card = udev_device_new_from_syspath(udev, t);
184 pa_xfree(t);
185
186 if (!card) {
187 pa_log_error("Failed to get card object.");
188 goto finish;
189 }
190
191 if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS_PATH))
192 if (((v = udev_device_get_property_value(card, "ID_PATH")) && *v) ||
193 (v = udev_device_get_devpath(card)))
194 pa_proplist_sets(p, PA_PROP_DEVICE_BUS_PATH, v);
195
196 if (!pa_proplist_contains(p, "sysfs.path"))
197 if ((v = udev_device_get_devpath(card)))
198 pa_proplist_sets(p, "sysfs.path", v);
199
200 if (!pa_proplist_contains(p, "udev.id"))
201 if ((v = udev_device_get_property_value(card, "ID_ID")) && *v)
202 pa_proplist_sets(p, "udev.id", v);
203
204 if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS))
205 if ((bus = udev_device_get_property_value(card, "ID_BUS")) && *bus)
206 pa_proplist_sets(p, PA_PROP_DEVICE_BUS, bus);
207
208 if (!pa_proplist_contains(p, PA_PROP_DEVICE_VENDOR_ID))
209 if ((id = read_id(card, "ID_VENDOR_ID")) > 0)
210 pa_proplist_setf(p, PA_PROP_DEVICE_VENDOR_ID, "%04x", id);
211
212 if (!pa_proplist_contains(p, PA_PROP_DEVICE_VENDOR_NAME)) {
213 /* ID_VENDOR_FROM_DATABASE returns the name of IEEE 1394 Phy/Link chipset for FireWire devices */
214 if (!pa_safe_streq(bus, "firewire") && (v = udev_device_get_property_value(card, "ID_VENDOR_FROM_DATABASE")) && *v)
215 pa_proplist_sets(p, PA_PROP_DEVICE_VENDOR_NAME, v);
216 else if ((v = udev_device_get_property_value(card, "ID_VENDOR_ENC")) && *v)
217 proplist_sets_unescape(p, PA_PROP_DEVICE_VENDOR_NAME, v);
218 else if ((v = udev_device_get_property_value(card, "ID_VENDOR")) && *v)
219 pa_proplist_sets(p, PA_PROP_DEVICE_VENDOR_NAME, v);
220 }
221
222 if (!pa_proplist_contains(p, PA_PROP_DEVICE_PRODUCT_ID))
223 if ((id = read_id(card, "ID_MODEL_ID")) >= 0)
224 pa_proplist_setf(p, PA_PROP_DEVICE_PRODUCT_ID, "%04x", id);
225
226 if (!pa_proplist_contains(p, PA_PROP_DEVICE_PRODUCT_NAME)) {
227 /* ID_MODEL_FROM_DATABASE returns the name of IEEE 1394 Phy/Link chipset for FireWire devices */
228 if (!pa_safe_streq(bus, "firewire") && (v = udev_device_get_property_value(card, "ID_MODEL_FROM_DATABASE")) && *v)
229 pa_proplist_sets(p, PA_PROP_DEVICE_PRODUCT_NAME, v);
230 else if ((v = udev_device_get_property_value(card, "ID_MODEL_ENC")) && *v)
231 proplist_sets_unescape(p, PA_PROP_DEVICE_PRODUCT_NAME, v);
232 else if ((v = udev_device_get_property_value(card, "ID_MODEL")) && *v)
233 pa_proplist_sets(p, PA_PROP_DEVICE_PRODUCT_NAME, v);
234 }
235
236 if (!pa_proplist_contains(p, PA_PROP_DEVICE_SERIAL))
237 if ((v = udev_device_get_property_value(card, "ID_SERIAL")) && *v)
238 pa_proplist_sets(p, PA_PROP_DEVICE_SERIAL, v);
239
240 if (!pa_proplist_contains(p, PA_PROP_DEVICE_CLASS))
241 if ((v = udev_device_get_property_value(card, "SOUND_CLASS")) && *v)
242 pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, v);
243
244 if (!pa_proplist_contains(p, PA_PROP_DEVICE_FORM_FACTOR))
245 if ((v = udev_device_get_property_value(card, "SOUND_FORM_FACTOR")) && *v)
246 pa_proplist_sets(p, PA_PROP_DEVICE_FORM_FACTOR, v);
247
248 /* This is normally not set by the udev rules but may be useful to
249 * allow administrators to overwrite the device description.*/
250 if (!pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
251 if ((v = udev_device_get_property_value(card, "SOUND_DESCRIPTION")) && *v)
252 pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, v);
253
254 r = 0;
255
256 finish:
257
258 if (card)
259 udev_device_unref(card);
260
261 if (udev)
262 udev_unref(udev);
263
264 return r;
265 }
266
pa_udev_get_property(int card_idx,const char * name)267 char* pa_udev_get_property(int card_idx, const char *name) {
268 struct udev *udev;
269 struct udev_device *card = NULL;
270 char *t, *r = NULL;
271 const char *v;
272
273 pa_assert(card_idx >= 0);
274 pa_assert(name);
275
276 if (!(udev = udev_new())) {
277 pa_log_error("Failed to allocate udev context.");
278 goto finish;
279 }
280
281 t = pa_sprintf_malloc("/sys/class/sound/card%i", card_idx);
282 card = udev_device_new_from_syspath(udev, t);
283 pa_xfree(t);
284
285 if (!card) {
286 pa_log_error("Failed to get card object.");
287 goto finish;
288 }
289
290 if ((v = udev_device_get_property_value(card, name)) && *v)
291 r = pa_xstrdup(v);
292
293 finish:
294
295 if (card)
296 udev_device_unref(card);
297
298 if (udev)
299 udev_unref(udev);
300
301 return r;
302 }
303