LIRC libraries
LinuxInfraredRemoteControl
 All Classes Files Functions Variables Typedefs Enumerations Macros Modules Pages
drv_enum.c
Go to the documentation of this file.
1 
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12 
13 #include <glob.h>
14 #include <stdbool.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <dirent.h>
18 #include <errno.h>
19 
20 #ifdef HAVE_LIBUDEV_H
21 #include <libudev.h>
22 #endif
23 
24 #ifdef HAVE_USB_H
25 #include <usb.h>
26 #endif
27 
28 #include "drv_enum.h"
29 #include "driver.h"
30 #include "lirc_log.h"
31 
32 static const logchannel_t logchannel = LOG_LIB;
33 
34 
35 
37 static const int GLOB_CHUNK_SIZE = 32;
38 
39 
40 void glob_t_init(glob_t* glob)
41 {
42  memset(glob, 0, sizeof(glob_t));
43  glob->gl_offs = GLOB_CHUNK_SIZE;
44  glob->gl_pathv = (char**) calloc(glob->gl_offs, sizeof(char*));
45 }
46 
47 
48 void glob_t_add_path(glob_t* glob, const char* path)
49 {
50  if (path == NULL)
51  return;
52  if (glob->gl_pathc >= glob->gl_offs) {
53  glob->gl_offs += GLOB_CHUNK_SIZE;
54  glob->gl_pathv = realloc(glob->gl_pathv,
55  glob->gl_offs * sizeof(char*));
56  }
57  glob->gl_pathv[glob->gl_pathc] = strdup(path);
58  glob->gl_pathc += 1;
59 }
60 
61 
63 void drv_enum_free(glob_t* glob)
64 {
65  int i;
66 
67  if (glob == NULL)
68  return;
69  for (i = 0; i < glob->gl_pathc; i += 1)
70  free(glob->gl_pathv[i]);
71  free(glob->gl_pathv);
72 }
73 
74 #ifdef HAVE_LIBUDEV_H
75 
76 static const char* get_sysattr(struct udev_device* device, const char* attr);
77 
78 
80 static struct udev_device* udev_from_dev_path(struct udev* udev,
81  const char* path)
82 {
83  struct stat statbuf;
84  char dev_id[64];
85 
86  if (stat(path, &statbuf) != 0) {
87  log_perror_debug("Cannot stat device %s", path);
88  return NULL;
89  }
90  if (!S_ISCHR(statbuf.st_mode)) {
91  log_debug("Ignoring non-character device %s", path);
92  return NULL;
93  }
94  snprintf(dev_id, sizeof(dev_id), "c%d:%d",
95  major(statbuf.st_rdev), minor(statbuf.st_rdev));
96  return udev_device_new_from_device_id(udev, dev_id);
97 }
98 
99 
104 static struct udev_device* get_some_info(struct udev_device* device,
105  const char** idVendor,
106  const char** idProduct)
107 {
108  struct udev_device* usb_device = NULL;
109  const char* subsystem = udev_device_get_subsystem(device);
110 
111  if (subsystem && (strcmp(subsystem, "usb") != 0)) {
112  usb_device = udev_device_get_parent_with_subsystem_devtype(
113  device, "usb", "usb_device");
114  if (!usb_device) {
115  log_error("Unable to find parent usb device.");
116  }
117  }
118  *idVendor = udev_device_get_sysattr_value(device, "idVendor");
119  *idProduct = udev_device_get_sysattr_value(device, "idProduct");
120  if (!*idProduct && usb_device)
121  *idProduct = get_sysattr(usb_device, "idProduct");
122  if (!*idVendor && usb_device)
123  *idVendor = get_sysattr(usb_device, "idVendor");
124  return usb_device ? usb_device : device;
125 }
126 
127 
129 void drv_enum_add_udev_info(glob_t* oldbuf)
130 {
131  glob_t newbuf;
132  int i;
133  char line[256];
134  char* device_path;
135  struct udev* udev = udev_new();
136  const char* idVendor;
137  const char* idProduct;
138 
139  glob_t_init(&newbuf);
140  for (i = 0; i < oldbuf->gl_pathc; i += 1) {
141  device_path = strdup(oldbuf->gl_pathv[i]);
142  device_path = strtok(device_path, "\n \t");
143  struct udev_device* udev_device =
144  udev_from_dev_path(udev, device_path);
145  if (udev_device == NULL) {
146  glob_t_add_path(&newbuf, oldbuf->gl_pathv[i]);
147  } else {
148  udev_device = get_some_info(udev_device,
149  &idVendor,
150  &idProduct);
151  snprintf(line, sizeof(line),
152  "%s [%s:%s] %s %s version: %s serial: %s",
153  device_path,
154  idVendor,
155  idProduct,
156  get_sysattr(udev_device, "manufacturer"),
157  get_sysattr(udev_device, "product"),
158  get_sysattr(udev_device, "version"),
159  get_sysattr(udev_device, "serial")
160  );
161  if (idVendor == NULL && idProduct == NULL)
162  glob_t_add_path(&newbuf, oldbuf->gl_pathv[i]);
163  else
164  glob_t_add_path(&newbuf, line);
165  }
166  free(device_path);
167  }
168  drv_enum_free(oldbuf);
169  memcpy(oldbuf, &newbuf, sizeof(glob_t));
170 }
171 
172 #else // HAVE_LIBUDEV_H
173 
174 void drv_enum_add_udev_info(glob_t* oldbuf) {}
175 
176 #endif // HAVE_LIBUDEV_H
177 
178 
179 int drv_enum_globs(glob_t* globbuf, const char* const* patterns)
180 {
181  glob_t buff;
182  int i;
183  int flags;
184  int r;
185 
186  if (!patterns)
187  return DRV_ERR_BAD_VALUE;
188  buff.gl_offs = 0;
189  buff.gl_pathc = 0;
190  buff.gl_pathv = NULL;
191  glob_t_init(globbuf);
192 
193  for (flags = 0; *patterns; patterns++) {
194  r = glob(*patterns, flags, NULL, &buff);
195  if (r == GLOB_NOMATCH)
196  continue;
197  if (r != 0) {
198  globfree(&buff);
199  return DRV_ERR_BAD_STATE;
200  }
201  flags = GLOB_APPEND;
202  }
203  for (i = 0; i < buff.gl_pathc; i += 1) {
204  glob_t_add_path(globbuf, buff.gl_pathv[i]);
205  }
206  globfree(&buff);
207  drv_enum_add_udev_info(globbuf);
208  return globbuf->gl_pathc == 0 ? DRV_ERR_ENUM_EMPTY : 0;
209 }
210 
211 
212 int drv_enum_glob(glob_t* globbuf, const char* const pattern)
213 {
214  const char* globs[] = {pattern, NULL};
215 
216  return drv_enum_globs(globbuf, globs);
217 }
218 
219 
220 #ifdef HAVE_USB_H
221 
222 int drv_enum_usb(glob_t* glob,
223  int (*is_device_ok)(uint16_t vendor, uint16_t product))
224 {
225  struct usb_bus* usb_bus;
226  struct usb_device* dev;
227  char device_path[2 * MAXPATHLEN + 32];
228 
229  usb_init();
230  usb_find_busses();
231  usb_find_devices();
232  glob_t_init(glob);
233  for (usb_bus = usb_busses; usb_bus; usb_bus = usb_bus->next) {
234  for (dev = usb_bus->devices; dev; dev = dev->next) {
235  if (!is_device_ok(dev->descriptor.idVendor,
236  dev->descriptor.idProduct))
237  continue;
238  snprintf(device_path, sizeof(device_path),
239  "/dev/bus/usb/%s/%s %04x:%04x",
240  dev->bus->dirname, dev->filename,
241  dev->descriptor.idVendor,
242  dev->descriptor.idProduct);
243  glob_t_add_path(glob, device_path);
244  }
245  }
247  return 0;
248 }
249 
250 #else
251 
252 int drv_enum_usb(glob_t* glob,
253  int (*is_device_ok)(uint16_t vendor, uint16_t product))
254 {
256 }
257 
258 #endif // HAVE_USB_H
259 
260 
261 #ifdef HAVE_LIBUDEV_H
262 
263 static const char* get_sysattr(struct udev_device* device, const char* attr)
264 {
265  const char* s = udev_device_get_sysattr_value(device, attr);
266 
267  return s ? s : "?";
268 }
269 
270 
272 static bool format_udev_entry(struct udev* udev,
273  struct udev_list_entry* device,
274  char* buff,
275  size_t size)
276 {
277  const char* const syspath = udev_list_entry_get_name(device);
278  struct udev_device* udev_device =
279  udev_device_new_from_syspath(udev, syspath);
280  const char* const devnode = udev_device_get_devnode(udev_device);
281  const char* idVendor;
282  const char* idProduct;
283 
284  if (!devnode)
285  return false;
286  udev_device = get_some_info(udev_device, &idVendor, &idProduct);
287  snprintf(buff, size, "%s [%s:%s] %s %s version: %s serial: %s",
288  devnode,
289  idVendor,
290  idProduct,
291  get_sysattr(udev_device, "manufacturer"),
292  get_sysattr(udev_device, "product"),
293  get_sysattr(udev_device, "version"),
294  get_sysattr(udev_device, "serial")
295  );
296  return true;
297 }
298 
299 
301 static void add_links(glob_t* globbuf,
302  struct udev* udev,
303  struct udev_list_entry* target_entry)
304 {
305  char buff[128];
306  char path[128];
307  size_t pathlen;
308 
309  const char* const syspath = udev_list_entry_get_name(target_entry);
310  struct udev_device* target_device =
311  udev_device_new_from_syspath(udev, syspath);
312  struct udev_list_entry* links =
313  udev_device_get_devlinks_list_entry(target_device);
314 
315  while (links != NULL) {
316  pathlen = readlink(udev_list_entry_get_name(links),
317  path,
318  sizeof(path) - 1);
319  path[pathlen] = '\0';
320  snprintf(buff, sizeof(buff), "%s -> %s",
321  udev_list_entry_get_name(links), path);
322  links = udev_list_entry_get_next(links);
323  glob_t_add_path(globbuf, buff);
324  }
325 }
326 
327 
329 static bool is_dup(glob_t* globbuf, const char* buff)
330 {
331  int i;
332 
333  for (i = 0; i < globbuf->gl_pathc; i += 1) {
334  if (strcmp(globbuf->gl_pathv[i], buff) == 0)
335  return true;
336  }
337  return false;
338 }
339 
340 
342 static bool check_parent_subsys(const struct drv_enum_udev_what* what,
343  struct udev* udev,
344  struct udev_list_entry* device_entry)
345 {
346  if (!what->parent_subsys)
347  return true;
348  const char* const syspath = udev_list_entry_get_name(device_entry);
349  struct udev_device* device =
350  udev_device_new_from_syspath(udev, syspath);
351  struct udev_device* parent =
352  udev_device_get_parent_with_subsystem_devtype(device,
353  "rc",
354  NULL);
355 
356  return parent != NULL;
357 }
358 
359 
361 int drv_enum_udev(glob_t* globbuf,
362  const struct drv_enum_udev_what* what)
363 {
364  const struct drv_enum_udev_what SENTINEL = {0};
365  struct udev* udev;
366  struct udev_enumerate* enumerate;
367  struct udev_list_entry* devices;
368  struct udev_list_entry* device;
369  char buff[128];
370 
371  glob_t_init(globbuf);
372  udev = udev_new();
373  if (udev == NULL) {
374  log_error("Cannot run udev_new()");
375  return DRV_ERR_BAD_STATE;
376  }
377  while (memcmp(what,
378  &SENTINEL,
379  sizeof(struct drv_enum_udev_what)) != 0) {
380  enumerate = udev_enumerate_new(udev);
381  if (what->idVendor != NULL)
382  udev_enumerate_add_match_sysattr(
383  enumerate, "idVendor", what->idVendor);
384  if (what->idProduct != NULL)
385  udev_enumerate_add_match_sysattr(
386  enumerate, "idProduct", what->idProduct);
387  if (what->subsystem != NULL)
388  udev_enumerate_add_match_subsystem(enumerate,
389  what->subsystem);
390  udev_enumerate_scan_devices(enumerate);
391  devices = udev_enumerate_get_list_entry(enumerate);
392  udev_list_entry_foreach(device, devices) {
393  if (!check_parent_subsys(what, udev, device))
394  continue;
395  if (!format_udev_entry(udev, device,
396  buff, sizeof(buff)))
397  continue;
398  if (is_dup(globbuf, buff))
399  continue;
400  glob_t_add_path(globbuf, (const char*)buff);
401  add_links(globbuf, udev, device);
402  }
403  udev_enumerate_unref(enumerate);
404  what++;
405  }
406  udev_unref(udev);
407  return 0;
408 }
409 
410 #else // HAVE_LIBUDEV_H
411 
412 int drv_enum_udev(glob_t* globbuf, const struct drv_enum_udev_what* what)
413 {
415 }
416 
417 #endif // HAVE_LIBUDEV_H
#define DRV_ERR_NOT_IMPLEMENTED
drvctl definitions
Definition: driver.h:115
Logging functionality.
#define log_debug(fmt,...)
Log a debug message.
Definition: lirc_log.h:124
#define log_perror_debug(fmt,...)
perror wrapper logging with level LIRC_DEBUG.
Definition: lirc_log.h:99
Interface to the userspace drivers.
dynamic drivers device enumeration support
logchannel_t
Log channels used to filter messages.
Definition: lirc_log.h:53
#define DRV_ERR_BAD_VALUE
drvctl error: arg is bad
Definition: driver.h:124
#define log_error(fmt,...)
Log an error message.
Definition: lirc_log.h:104
void drv_enum_free(glob_t *glob)
Free memory allocated by for a glob_t.
Definition: drv_enum.c:63
void glob_t_init(glob_t *glob)
Setup a glob_t variable to empty state.
Definition: drv_enum.c:40
int drv_enum_udev(glob_t *globbuf, const struct drv_enum_udev_what *what)
List all devices matching any of conditions in {0}-terminated list.
Definition: drv_enum.c:412
void drv_enum_add_udev_info(glob_t *oldbuf)
Try to add udev info to existing entries in glob.
Definition: drv_enum.c:174
int drv_enum_usb(glob_t *glob, int(*is_device_ok)(uint16_t vendor, uint16_t product))
List all available devices matched by is_device_ok() using libusb.
Definition: drv_enum.c:252
const char * parent_subsys
Require a given subsystem parent.
Definition: drv_enum.h:36
const char * subsystem
Require given subsystem.
Definition: drv_enum.h:35
int drv_enum_globs(glob_t *globbuf, const char *const *patterns)
List devices matching any of patterns in null-terminated list.
Definition: drv_enum.c:179
void glob_t_add_path(glob_t *glob, const char *path)
Add a path to glob, allocating memory as necessary.
Definition: drv_enum.c:48
Condition to match in drv_enum_udev().
Definition: drv_enum.h:32
#define DRV_ERR_ENUM_EMPTY
No requested data available.
Definition: driver.h:127
#define DRV_ERR_BAD_STATE
drvctl error: cmd and arg is OK, but other errors.
Definition: driver.h:118
int drv_enum_glob(glob_t *globbuf, const char *const pattern)
List all devices matching glob(3) pattern.
Definition: drv_enum.c:212
const char * device
Name of the device (string).
Definition: driver.h:143