• 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     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