LIRC libraries
Linux Infrared Remote Control
Loading...
Searching...
No Matches
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
32static const logchannel_t logchannel = LOG_LIB;
33
34
35
37static const int GLOB_CHUNK_SIZE = 32;
38
39
40void 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
48void 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
63void 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
76static const char* get_sysattr(struct udev_device* device, const char* attr);
77
78
80static 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
104static 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
129void 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
174void drv_enum_add_udev_info(glob_t* oldbuf) {}
175
176#endif // HAVE_LIBUDEV_H
177
178
179int 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
212int 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
222int 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
252int 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
263static 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
272static 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
301static 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
329static 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
342static 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
361int 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
412int drv_enum_udev(glob_t* globbuf, const struct drv_enum_udev_what* what)
413{
415}
416
417#endif // HAVE_LIBUDEV_H
Interface to the userspace drivers.
void drv_enum_free(glob_t *glob)
Free memory allocated by for a glob_t.
Definition drv_enum.c:63
int drv_enum_glob(glob_t *globbuf, const char *const pattern)
List all devices matching glob(3) pattern.
Definition drv_enum.c:212
void drv_enum_add_udev_info(glob_t *oldbuf)
Try to add udev info to existing entries in glob.
Definition drv_enum.c:174
void glob_t_init(glob_t *glob)
Setup a glob_t variable to empty state.
Definition drv_enum.c:40
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
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
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
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
dynamic drivers device enumeration support
#define DRV_ERR_BAD_VALUE
drvctl error: arg is bad
Definition driver.h:124
#define DRV_ERR_ENUM_EMPTY
No requested data available.
Definition driver.h:127
#define DRV_ERR_NOT_IMPLEMENTED
drvctl definitions
Definition driver.h:51
#define DRV_ERR_BAD_STATE
drvctl error: cmd and arg is OK, but other errors.
Definition driver.h:118
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
#define log_error(fmt,...)
Log an error message.
Definition lirc_log.h:104
logchannel_t
Log channels used to filter messages.
Definition lirc_log.h:53
Condition to match in drv_enum_udev().
Definition drv_enum.h:34
const char * subsystem
Require given subsystem.
Definition drv_enum.h:37
const char * parent_subsys
Require a given subsystem parent.
Definition drv_enum.h:38