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 34static 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 59static 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 72static 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 165int 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 253finish: 254 255 if (card) 256 udev_device_unref(card); 257 258 if (udev) 259 udev_unref(udev); 260 261 return r; 262} 263 264char* 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 290finish: 291 292 if (card) 293 udev_device_unref(card); 294 295 if (udev) 296 udev_unref(udev); 297 298 return r; 299} 300