• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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