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 int id;
172
173 pa_assert(p);
174 pa_assert(card_idx >= 0);
175
176 if (!(udev = udev_new())) {
177 pa_log_error("Failed to allocate udev context.");
178 goto finish;
179 }
180
181 t = pa_sprintf_malloc("/sys/class/sound/card%i", card_idx);
182 card = udev_device_new_from_syspath(udev, t);
183 pa_xfree(t);
184
185 if (!card) {
186 pa_log_error("Failed to get card object.");
187 goto finish;
188 }
189
190 if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS_PATH))
191 if (((v = udev_device_get_property_value(card, "ID_PATH")) && *v) ||
192 (v = udev_device_get_devpath(card)))
193 pa_proplist_sets(p, PA_PROP_DEVICE_BUS_PATH, v);
194
195 if (!pa_proplist_contains(p, "sysfs.path"))
196 if ((v = udev_device_get_devpath(card)))
197 pa_proplist_sets(p, "sysfs.path", v);
198
199 if (!pa_proplist_contains(p, "udev.id"))
200 if ((v = udev_device_get_property_value(card, "ID_ID")) && *v)
201 pa_proplist_sets(p, "udev.id", v);
202
203 if (!pa_proplist_contains(p, PA_PROP_DEVICE_BUS))
204 if ((v = udev_device_get_property_value(card, "ID_BUS")) && *v)
205 pa_proplist_sets(p, PA_PROP_DEVICE_BUS, v);
206
207 if (!pa_proplist_contains(p, PA_PROP_DEVICE_VENDOR_ID))
208 if ((id = read_id(card, "ID_VENDOR_ID")) > 0)
209 pa_proplist_setf(p, PA_PROP_DEVICE_VENDOR_ID, "%04x", id);
210
211 if (!pa_proplist_contains(p, PA_PROP_DEVICE_VENDOR_NAME)) {
212 if ((v = udev_device_get_property_value(card, "ID_VENDOR_FROM_DATABASE")) && *v)
213 pa_proplist_sets(p, PA_PROP_DEVICE_VENDOR_NAME, v);
214 else if ((v = udev_device_get_property_value(card, "ID_VENDOR_ENC")) && *v)
215 proplist_sets_unescape(p, PA_PROP_DEVICE_VENDOR_NAME, v);
216 else if ((v = udev_device_get_property_value(card, "ID_VENDOR")) && *v)
217 pa_proplist_sets(p, PA_PROP_DEVICE_VENDOR_NAME, v);
218 }
219
220 if (!pa_proplist_contains(p, PA_PROP_DEVICE_PRODUCT_ID))
221 if ((id = read_id(card, "ID_MODEL_ID")) >= 0)
222 pa_proplist_setf(p, PA_PROP_DEVICE_PRODUCT_ID, "%04x", id);
223
224 if (!pa_proplist_contains(p, PA_PROP_DEVICE_PRODUCT_NAME)) {
225 if ((v = udev_device_get_property_value(card, "ID_MODEL_FROM_DATABASE")) && *v)
226 pa_proplist_sets(p, PA_PROP_DEVICE_PRODUCT_NAME, v);
227 else if ((v = udev_device_get_property_value(card, "ID_MODEL_ENC")) && *v)
228 proplist_sets_unescape(p, PA_PROP_DEVICE_PRODUCT_NAME, v);
229 else if ((v = udev_device_get_property_value(card, "ID_MODEL")) && *v)
230 pa_proplist_sets(p, PA_PROP_DEVICE_PRODUCT_NAME, v);
231 }
232
233 if (!pa_proplist_contains(p, PA_PROP_DEVICE_SERIAL))
234 if ((v = udev_device_get_property_value(card, "ID_SERIAL")) && *v)
235 pa_proplist_sets(p, PA_PROP_DEVICE_SERIAL, v);
236
237 if (!pa_proplist_contains(p, PA_PROP_DEVICE_CLASS))
238 if ((v = udev_device_get_property_value(card, "SOUND_CLASS")) && *v)
239 pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, v);
240
241 if (!pa_proplist_contains(p, PA_PROP_DEVICE_FORM_FACTOR))
242 if ((v = udev_device_get_property_value(card, "SOUND_FORM_FACTOR")) && *v)
243 pa_proplist_sets(p, PA_PROP_DEVICE_FORM_FACTOR, v);
244
245 /* This is normally not set by the udev rules but may be useful to
246 * allow administrators to overwrite the device description.*/
247 if (!pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
248 if ((v = udev_device_get_property_value(card, "SOUND_DESCRIPTION")) && *v)
249 pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, v);
250
251 r = 0;
252
253 finish:
254
255 if (card)
256 udev_device_unref(card);
257
258 if (udev)
259 udev_unref(udev);
260
261 return r;
262 }
263
pa_udev_get_property(int card_idx,const char * name)264 char* pa_udev_get_property(int card_idx, const char *name) {
265 struct udev *udev;
266 struct udev_device *card = NULL;
267 char *t, *r = NULL;
268 const char *v;
269
270 pa_assert(card_idx >= 0);
271 pa_assert(name);
272
273 if (!(udev = udev_new())) {
274 pa_log_error("Failed to allocate udev context.");
275 goto finish;
276 }
277
278 t = pa_sprintf_malloc("/sys/class/sound/card%i", card_idx);
279 card = udev_device_new_from_syspath(udev, t);
280 pa_xfree(t);
281
282 if (!card) {
283 pa_log_error("Failed to get card object.");
284 goto finish;
285 }
286
287 if ((v = udev_device_get_property_value(card, name)) && *v)
288 r = pa_xstrdup(v);
289
290 finish:
291
292 if (card)
293 udev_device_unref(card);
294
295 if (udev)
296 udev_unref(udev);
297
298 return r;
299 }
300