blob: a249d100d686eb33fed0ccc081211053968f104c [file] [log] [blame]
Harald Welteda432cd2019-12-15 19:13:26 +01001/* libosmocore integration with libusb-1.0
2 *
Harald Welted462e3f2019-12-15 20:04:51 +01003 * (C) 2019-2019 by Harald Welte <laforge@gnumonks.org>
Harald Welteda432cd2019-12-15 19:13:26 +01004 * All Rights Reserved.
5 *
6 * SPDX-License-Identifier: GPL-2.0+
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
Harald Welteda432cd2019-12-15 19:13:26 +010017 */
18#include <errno.h>
19#include <unistd.h>
20#include <stdlib.h>
21#include <stdint.h>
22#include <string.h>
23#include <poll.h>
24
25#include <osmocom/core/utils.h>
26#include <osmocom/core/logging.h>
27#include <osmocom/core/select.h>
28#include <osmocom/core/talloc.h>
29
30#include <libusb.h>
31
Harald Welted462e3f2019-12-15 20:04:51 +010032#include <osmocom/usb/libusb.h>
33
Harald Welteda432cd2019-12-15 19:13:26 +010034/***********************************************************************
35 * logging integration
36 ***********************************************************************/
37
38#define DLUSB DLINP
39
40#ifdef LIBUSB_LOG_CB_CONTEXT /* introduced in 1.0.23 */
41static const int usb2logl[] = {
42 [LIBUSB_LOG_LEVEL_NONE] = LOGL_FATAL,
43 [LIBUSB_LOG_LEVEL_ERROR] = LOGL_ERROR,
44 [LIBUSB_LOG_LEVEL_WARNING] = LOGL_NOTICE,
45 [LIBUSB_LOG_LEVEL_INFO] = LOGL_INFO,
46 [LIBUSB_LOG_LEVEL_DEBUG] = LOGL_DEBUG,
47};
48
49/* called by libusb if it wants to log something */
50static void libosmo_usb_log_cb(libusb_context *luctx, enum libusb_log_level level_usb, const char *str)
51{
52 int level = LOGL_NOTICE;
53
54 if (level_usb < ARRAY_SIZE(usb2logl))
55 level = usb2logl[level_usb];
56
57 LOGP(DLUSB, level, "%s", str);
58}
59#endif /* LIBUSB_LOG_CB_CONTEXT */
60
61/***********************************************************************
62 * select loop integration
63 ***********************************************************************/
64
65static int osmo_usb_fd_cb(struct osmo_fd *ofd, unsigned int what)
66{
67 libusb_context *luctx = ofd->data;
68
69 /* we assume that we're running Linux v2.6.27 with timerfd support here
70 * and hence don't have to perform manual timeout handling. See
71 * "Notes on time-based events" at
72 * http://libusb.sourceforge.net/api-1.0/group__libusb__poll.html */
73 struct timeval zero_tv = { 0, 0 };
74 libusb_handle_events_timeout(luctx, &zero_tv);
75
76 return 0;
77}
78
79/* called by libusb if it wants to add a file-descriptor */
80static void osmo_usb_added_cb(int fd, short events, void *user_data)
81{
82 struct osmo_fd *ofd = talloc_zero(OTC_GLOBAL, struct osmo_fd);
83 libusb_context *luctx = user_data;
84 unsigned int when = 0;
Vadim Yanitskiy775a6b02020-02-09 04:19:04 +070085 int rc;
Harald Welteda432cd2019-12-15 19:13:26 +010086
87 if (events & POLLIN)
88 when |= OSMO_FD_READ;
89 if (events & POLLOUT)
90 when |= OSMO_FD_WRITE;
91
92 osmo_fd_setup(ofd, fd, when, osmo_usb_fd_cb, luctx, 0);
Vadim Yanitskiy775a6b02020-02-09 04:19:04 +070093 rc = osmo_fd_register(ofd);
94 if (rc)
95 LOGP(DLUSB, LOGL_ERROR, "osmo_fd_register() failed with rc=%d\n", rc);
Harald Welteda432cd2019-12-15 19:13:26 +010096}
97
98/* called by libusb if it wants to remove a file-descriptor */
99static void osmo_usb_removed_cb(int fd, void *user_data)
100{
101 struct osmo_fd *ofd = osmo_fd_get_by_fd(fd);
Harald Weltefca15eb2022-01-25 16:23:45 +0100102 if (!ofd)
Harald Welteda432cd2019-12-15 19:13:26 +0100103 return;
104 osmo_fd_unregister(ofd);
105 talloc_free(ofd);
106}
107
Harald Welted462e3f2019-12-15 20:04:51 +0100108/***********************************************************************
109 * utility functions
110 ***********************************************************************/
Harald Welteda432cd2019-12-15 19:13:26 +0100111
Harald Welted462e3f2019-12-15 20:04:51 +0100112/*! obtain the string representation of the USB device path of given device.
113 * \param[out] buf Output string buffer
114 * \param[in] bufsize Size of output string buffer in bytes
115 * \param[in] dev USB device whose bus path we want to obtain
116 * \returns pointer to 'buf' in case of success; NULL in case of error */
117char *osmo_libusb_dev_get_path_buf(char *buf, size_t bufsize, libusb_device *dev)
118{
119#if (defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102) || \
120 (defined(LIBUSBX_API_VERSION) && LIBUSBX_API_VERSION >= 0x01000102)
121 struct osmo_strbuf sb = { .buf = buf, .len = bufsize };
122 uint8_t path[8];
123 int r,j;
124 r = libusb_get_port_numbers(dev, path, sizeof(path));
125 if (r > 0) {
126 OSMO_STRBUF_PRINTF(sb, "%d-%d", libusb_get_bus_number(dev), path[0]);
127 for (j = 1; j < r; j++){
128 OSMO_STRBUF_PRINTF(sb, ".%d", path[j]);
129 }
130 }
131 return buf;
132#else
133# warning "libusb too old - building without USB path support!"
134 return NULL;
135#endif
136}
137
138/*! obtain the string representation of the USB device path of given device.
139 * \param[in] talloc context from which to dynamically allocate output string buffer
140 * \param[in] dev USB device whose bus path we want to obtain
141 * \returns pointer to 'buf' in case of success; NULL in case of error */
142char *osmo_libusb_dev_get_path_c(void *ctx, libusb_device *dev)
143{
144 char *buf = talloc_zero_size(ctx, USB_MAX_PATH_LEN);
145 if (!buf)
146 return NULL;
147 return osmo_libusb_dev_get_path_buf(buf, USB_MAX_PATH_LEN, dev);
148}
149
150static int match_dev_id(const struct libusb_device_descriptor *desc, const struct dev_id *id)
151{
152 if ((desc->idVendor == id->vendor_id) && (desc->idProduct == id->product_id))
153 return 1;
154 return 0;
155}
156
157static int match_dev_ids(const struct libusb_device_descriptor *desc, const struct dev_id *ids)
158{
159 const struct dev_id *id;
160
161 for (id = ids; id->vendor_id || id->product_id; id++) {
162 if (match_dev_id(desc, id))
163 return 1;
164 }
165 return 0;
166}
167
168/*! Find USB devices matching the specified list of USB VendorID/ProductIDs
169 * \param[in] ctx talloc context from which to allocate output data
170 * \param[in] luctx libusb context on which to operate
171 * \param[in] dev_ids zero-terminated array of VendorId/ProductId tuples
172 * \returns array of up to 256 libusb_device pointers; NULL in case of error */
173libusb_device **osmo_libusb_find_matching_usb_devs(void *ctx, struct libusb_context *luctx,
174 const struct dev_id *dev_ids)
175{
176 libusb_device **list;
177 libusb_device **out = talloc_zero_array(ctx, libusb_device *, 256);
178 libusb_device **cur = out;
179 unsigned int i;
180 int rc;
181
182 if (!out)
183 return NULL;
184
185 rc = libusb_get_device_list(luctx, &list);
186 if (rc <= 0) {
187 perror("No USB devices found");
188 talloc_free(out);
189 return NULL;
190 }
191
192 for (i = 0; list[i] != NULL; i++) {
193 struct libusb_device_descriptor dev_desc;
194 libusb_device *dev = list[i];
195
196 rc = libusb_get_device_descriptor(dev, &dev_desc);
197 if (rc < 0) {
198 perror("Couldn't get device descriptor\n");
199 libusb_unref_device(dev);
200 continue;
201 }
202
203 if (match_dev_ids(&dev_desc, dev_ids)) {
204 *cur = dev;
205 cur++;
206 /* overflow check */
207 if (cur >= out + 256)
208 break;
209 } else
210 libusb_unref_device(dev);
211 }
212 if (cur == out) {
213 libusb_free_device_list(list, 1);
214 talloc_free(out);
215 return NULL;
216 }
217
218 libusb_free_device_list(list, 0);
219 return out;
220}
221
Harald Weltec45787b2019-12-24 12:20:07 +0100222/*! Find a USB device of matching VendorID/ProductID at given path.
223 * \param[in] luctx libusb context on which to operate
224 * \param[in] dev_ids zer-oterminated array of VendorId/ProductId tuples
225 * \param[in] path string representation of USB path
226 * \returns libusb_device if there was exactly one match; NULL otherwise */
227libusb_device *osmo_libusb_find_matching_dev_path(struct libusb_context *luctx,
228 const struct dev_id *dev_ids,
229 const char *path)
230{
231 libusb_device **list;
232 libusb_device *match = NULL;
233 unsigned int i;
234 int rc;
235
236 rc = libusb_get_device_list(luctx, &list);
237 if (rc <= 0)
238 return NULL;
239
240 for (i = 0; list[i] != NULL; i++) {
241 struct libusb_device_descriptor dev_desc;
242 libusb_device *dev = list[i];
243 char pathbuf[128];
244
245 rc = libusb_get_device_descriptor(dev, &dev_desc);
246 if (rc < 0) {
247 LOGP(DLUSB, LOGL_ERROR, "couldn't get device descriptor\n");
248 continue;
249 }
250
251 /* check if device doesn't match */
252 if (!match_dev_ids(&dev_desc, dev_ids))
253 continue;
254
255 /* check if path doesn't match */
256 if (path) {
257 osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), dev);
258 if (strcmp(pathbuf, path))
259 continue;
260 }
261
262 if (match) {
263 /* we already have a match, but now found a second -> FAIL */
264 libusb_free_device_list(list, 1);
265 LOGP(DLUSB, LOGL_ERROR, "Found more than one matching USB device\n");
266 return NULL;
267 } else
268 match = dev;
269 }
270
271 if (!match) {
272 /* no match: free the list with automatic unref of all devices */
273 libusb_free_device_list(list, 1);
274 return NULL;
275 }
276
277 /* unref all devices *except* the match we found */
278 for (i = 0; list[i] != NULL; i++) {
279 libusb_device *dev = list[i];
280 if (dev != match)
281 libusb_unref_device(dev);
282 }
283 /* free the list *without* automatic unref of all devices */
284 libusb_free_device_list(list, 0);
285 return match;
286}
287
288/*! Find a USB device of matching VendorID/ProductID and given iSerial string.
289 * \param[in] luctx libusb context on which to operate
290 * \param[in] dev_ids zer-oterminated array of VendorId/ProductId tuples
291 * \param[in] serial string representation of serial number
292 * \returns libusb_device if there was exactly one match; NULL otherwise */
293libusb_device *osmo_libusb_find_matching_dev_serial(struct libusb_context *luctx,
294 const struct dev_id *dev_ids,
295 const char *serial)
296{
297 libusb_device **list;
298 libusb_device *match = NULL;
299 unsigned int i;
300 int rc;
301
302 rc = libusb_get_device_list(luctx, &list);
303 if (rc <= 0)
304 return NULL;
305
306 for (i = 0; list[i] != NULL; i++) {
307 struct libusb_device_descriptor dev_desc;
308 libusb_device *dev = list[i];
309
310 rc = libusb_get_device_descriptor(dev, &dev_desc);
311 if (rc < 0) {
312 LOGP(DLUSB, LOGL_ERROR, "couldn't get device descriptor\n");
313 continue;
314 }
315
316 /* check if device doesn't match */
317 if (!match_dev_ids(&dev_desc, dev_ids))
318 continue;
319
320 /* check if serial number string doesn't match */
321 if (serial) {
322 char strbuf[256];
323 libusb_device_handle *devh;
324 rc = libusb_open(dev, &devh);
325 if (rc < 0) {
326 LOGP(DLUSB, LOGL_ERROR, "Cannot open USB Device: %s\n",
327 libusb_strerror(rc));
328 /* there's no point in continuing here, as we don't know if there
329 * are multiple matches if we cannot read the iSerial string of all
330 * devices with matching vid/pid */
331 libusb_free_device_list(list, 1);
332 return NULL;
333 }
334 rc = libusb_get_string_descriptor_ascii(devh, dev_desc.iSerialNumber,
335 (uint8_t *) strbuf, sizeof(strbuf));
Harald Weltee51cbe42020-01-18 21:49:31 +0100336 if (rc < 0) {
337 LOGP(DLUSB, LOGL_ERROR, "Cannot read USB Descriptor: %s\n",
338 libusb_strerror(rc));
339 libusb_close(devh);
340 continue;
341 }
Harald Weltec45787b2019-12-24 12:20:07 +0100342 libusb_close(devh);
343 if (strcmp(strbuf, serial))
344 continue;
345 }
346
347 if (match) {
348 /* we already have a match, but now found a second -> FAIL */
349 libusb_free_device_list(list, 1);
350 LOGP(DLUSB, LOGL_ERROR, "Found more than one matching USB device\n");
351 return NULL;
352 } else
353 match = dev;
354 }
355
356 if (!match) {
357 /* no match: free the list with automatic unref of all devices */
358 libusb_free_device_list(list, 1);
359 return NULL;
360 }
361
362 /* unref all devices *except* the match we found */
363 for (i = 0; list[i] != NULL; i++) {
364 libusb_device *dev = list[i];
365 if (dev != match)
366 libusb_unref_device(dev);
367 }
368 /* free the list *without* automatic unref of all devices */
369 libusb_free_device_list(list, 0);
370 return match;
371}
372
373
Harald Welted462e3f2019-12-15 20:04:51 +0100374/*! find a matching interface among all interfaces of the given USB device.
375 * \param[in] dev USB device in which we shall search
376 * \param[in] class USB Interface Class to look for
377 * \param[in] sub_class USB Interface Subclass to look for
378 * \param[in] protocol USB Interface Protocol to look for
379 * \param[out] out User-allocated array for storing matches
380 * \param[in] out_len Length of out array
381 * \returns number of matching interfaces; negative in case of error */
382int osmo_libusb_dev_find_matching_interfaces(libusb_device *dev, int class, int sub_class,
383 int protocol, struct usb_interface_match *out,
384 unsigned int out_len)
385{
386 struct libusb_device_descriptor dev_desc;
387 int rc, i, out_idx = 0;
388 uint8_t addr;
389 char pathbuf[USB_MAX_PATH_LEN];
390 char *path;
391
392 rc = libusb_get_device_descriptor(dev, &dev_desc);
393 if (rc < 0) {
394 perror("Couldn't get device descriptor\n");
395 return -EIO;
396 }
397
398 addr = libusb_get_device_address(dev);
399 path = osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), dev);
400
401 /* iterate over all configurations */
402 for (i = 0; i < dev_desc.bNumConfigurations; i++) {
403 struct libusb_config_descriptor *conf_desc;
404 int j;
405
406 rc = libusb_get_config_descriptor(dev, i, &conf_desc);
407 if (rc < 0) {
408 fprintf(stderr, "Couldn't get config descriptor %u\n", i);
409 continue;
410 }
411 /* iterate over all interfaces */
412 for (j = 0; j < conf_desc->bNumInterfaces; j++) {
413 const struct libusb_interface *intf = &conf_desc->interface[j];
414 int k;
415 /* iterate over all alternate settings */
416 for (k = 0; k < intf->num_altsetting; k++) {
417 const struct libusb_interface_descriptor *if_desc;
418 if_desc = &intf->altsetting[k];
419 if (class >= 0 && if_desc->bInterfaceClass != class)
420 continue;
421 if (sub_class >= 0 && if_desc->bInterfaceSubClass != sub_class)
422 continue;
423 if (protocol >= 0 && if_desc->bInterfaceProtocol != protocol)
424 continue;
425 /* MATCH! */
426 out[out_idx].usb_dev = dev;
427 out[out_idx].vendor = dev_desc.idVendor;
428 out[out_idx].product = dev_desc.idProduct;
429 out[out_idx].addr = addr;
Harald Welte424eac82019-12-24 12:07:47 +0100430 OSMO_STRLCPY_ARRAY(out[out_idx].path, path);
Harald Welted462e3f2019-12-15 20:04:51 +0100431 out[out_idx].path[sizeof(out[out_idx].path)-1] = '\0';
432 out[out_idx].configuration = conf_desc->bConfigurationValue;
433 out[out_idx].interface = if_desc->bInterfaceNumber;
434 out[out_idx].altsetting = if_desc->bAlternateSetting;
435 out[out_idx].class = if_desc->bInterfaceClass;
436 out[out_idx].sub_class = if_desc->bInterfaceSubClass;
437 out[out_idx].protocol = if_desc->bInterfaceProtocol;
438 out[out_idx].string_idx = if_desc->iInterface;
439 out_idx++;
440 if (out_idx >= out_len)
441 return out_idx;
442 }
443 }
444 }
445 return out_idx;
446}
447
448/*! find matching interfaces among a list devices of specified VendorId/ProductID tuples.
449 * \param[in] luctx libusb context on which to operate
450 * \param[in] dev_ids zero-terminated array of VendorId/ProductId tuples
451 * \param[in] class USB Interface Class to look for
452 * \param[in] sub_class USB Interface Subclass to look for
453 * \param[in] protocol USB Interface Protocol to look for
454 * \param[out] out User-allocated array for storing matches
455 * \param[in] out_len Length of out array
456 * \returns number of matching interfaces; negative in case of error */
457int osmo_libusb_find_matching_interfaces(libusb_context *luctx, const struct dev_id *dev_ids,
458 int class, int sub_class, int protocol,
459 struct usb_interface_match *out, unsigned int out_len)
460{
461 struct usb_interface_match *out_cur = out;
462 unsigned int out_len_remain = out_len;
463 libusb_device **list;
464 libusb_device **dev;
465
466 list = osmo_libusb_find_matching_usb_devs(NULL, luctx, dev_ids);
467 if (!list)
468 return 0;
469
470 for (dev = list; *dev; dev++) {
471 int rc;
472
473#if 0
474 struct libusb_device_descriptor dev_desc;
475 uint8_t ports[8];
476 uint8_t addr;
477 rc = libusb_get_device_descriptor(*dev, &dev_desc);
478 if (rc < 0) {
479 perror("Cannot get device descriptor");
480 continue;
481 }
482
483 addr = libusb_get_device_address(*dev);
484
485 rc = libusb_get_port_numbers(*dev, ports, sizeof(ports));
486 if (rc < 0) {
487 perror("Cannot get device path");
488 continue;
489 }
490
491 printf("Found USB Device %04x:%04x at address %d\n",
492 dev_desc.idVendor, dev_desc.idProduct, addr);
493#endif
494
495 rc = osmo_libusb_dev_find_matching_interfaces(*dev, class, sub_class,
496 protocol, out_cur, out_len_remain);
497 if (rc < 0)
498 continue;
499 out_cur += rc;
500 out_len_remain -= rc;
501
502 }
503
504 /* unref / free list */
505 for (dev = list; *dev; dev++)
506 libusb_unref_device(*dev);
507 talloc_free(list);
508
509 return out_len - out_len_remain;
510}
511
512/*! open matching USB device and claim interface
513 * \param[in] ctx talloc context to use for related allocations
514 * \param[in] luctx libusb context on which to operate
515 * \param[in] ifm interface match describing interface to claim
516 * \returns libusb device chandle on success; NULL on error */
517libusb_device_handle *osmo_libusb_open_claim_interface(void *ctx, libusb_context *luctx,
518 const struct usb_interface_match *ifm)
519{
520 int rc, config;
521 struct dev_id dev_ids[] = { { ifm->vendor, ifm->product }, { 0, 0 } };
522 libusb_device **list;
523 libusb_device **dev;
524 libusb_device_handle *usb_devh = NULL;
525
526 list = osmo_libusb_find_matching_usb_devs(ctx, luctx, dev_ids);
527 if (!list) {
528 perror("No USB device with matching VID/PID");
529 return NULL;
530 }
531
532 for (dev = list; *dev; dev++) {
533 int addr;
534 char pathbuf[USB_MAX_PATH_LEN];
535 char *path;
536
537 addr = libusb_get_device_address(*dev);
538 path = osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), *dev);
539 if ((ifm->addr && addr == ifm->addr) ||
Harald Welteef872cb2022-03-03 17:24:41 +0100540 (strlen(ifm->path) && !strcmp(path, ifm->path)) ||
541 (!ifm->addr && !strlen(ifm->path) && !list[1] /* only one device */)) {
Harald Welted462e3f2019-12-15 20:04:51 +0100542 rc = libusb_open(*dev, &usb_devh);
543 if (rc < 0) {
544 fprintf(stderr, "Cannot open device: %s\n", libusb_error_name(rc));
545 usb_devh = NULL;
546 break;
547 }
548 rc = libusb_get_configuration(usb_devh, &config);
549 if (rc < 0) {
550 fprintf(stderr, "Cannot get current configuration: %s\n", libusb_error_name(rc));
551 libusb_close(usb_devh);
552 usb_devh = NULL;
553 break;
554 }
555 if (config != ifm->configuration) {
556 rc = libusb_set_configuration(usb_devh, ifm->configuration);
557 if (rc < 0) {
558 fprintf(stderr, "Cannot set configuration: %s\n", libusb_error_name(rc));
559 libusb_close(usb_devh);
560 usb_devh = NULL;
561 break;
562 }
563 }
564 rc = libusb_claim_interface(usb_devh, ifm->interface);
565 if (rc < 0) {
566 fprintf(stderr, "Cannot claim interface: %s\n", libusb_error_name(rc));
567 libusb_close(usb_devh);
568 usb_devh = NULL;
569 break;
570 }
571 rc = libusb_set_interface_alt_setting(usb_devh, ifm->interface, ifm->altsetting);
572 if (rc < 0) {
573 fprintf(stderr, "Cannot set interface altsetting: %s\n", libusb_error_name(rc));
574 libusb_release_interface(usb_devh, ifm->interface);
575 libusb_close(usb_devh);
576 usb_devh = NULL;
577 break;
578 }
579 }
580 }
581
582 /* unref / free list */
583 for (dev = list; *dev; dev++)
584 libusb_unref_device(*dev);
585 talloc_free(list);
586
587 return usb_devh;
588}
589
Harald Welte3edd70c2020-01-12 23:45:42 +0100590void osmo_libusb_match_init(struct osmo_usb_matchspec *cfg, int if_class, int if_subclass, int if_proto)
591{
592 cfg->dev.vendor_id = -1;
593 cfg->dev.product_id = -1;
594 cfg->dev.path = NULL;
595
596 cfg->config_id = -1;
597
598 cfg->intf.class = if_class;
599 cfg->intf.subclass = if_subclass;
600 cfg->intf.proto = if_proto;
601
602 cfg->intf.num = cfg->intf.altsetting = -1;
603}
604
605
606/*! high-level all-in-one function for USB device, config + interface matching + opening.
607 * This function offers the highest level of API among all libosmousb helper functions. It
608 * is intended as a one-stop shop for everything related to grabbing an interface.
609 *
610 * 1) looks for a device matching either the VID/PID from 'cfg' or 'default_dev_ids',
611 * if more than one is found, the user is expected to fill in cfg->dev.path to disambiguate.
612 * 2) find any interfaces on the device that match the specification in 'cfg'. The match
613 * could be done based on any of (class, subclass, proto, interface number). If there
614 * are multiple matches, the caller must disambiguate by specifying the interface number.
615 * 3) open the USB device; set the configuration (if needed); claim the interface and set
616 * the altsetting
617 *
618 * \param[in] cfg user-supplied match configuration (from command line or config file)
619 * \param[in] default_dev_ids Default list of supported VendorId/ProductIds
620 * \returns libusb_device_handle on success, NULL on error
621 */
622libusb_device_handle *osmo_libusb_find_open_claim(const struct osmo_usb_matchspec *cfg,
623 const struct dev_id *default_dev_ids)
624{
625 struct usb_interface_match if_matches[16];
626 struct usb_interface_match *ifm = NULL;
627 libusb_device_handle *usb_devh = NULL;
628 struct dev_id user_dev_ids[2] = {
629 { cfg->dev.vendor_id, cfg->dev.product_id },
630 { 0, 0 }
631 };
632 const struct dev_id *dev_ids = default_dev_ids;
633 libusb_device *dev;
634 int rc, i;
635
636 /* Stage 1: Find a device matching either the user-specified VID/PID or
637 * the list of IDs in default_dev_ids plus optionally the user-specified path */
638 if (cfg->dev.vendor_id != -1 || cfg->dev.product_id != -1)
639 dev_ids = user_dev_ids;
640 dev = osmo_libusb_find_matching_dev_path(NULL, dev_ids, cfg->dev.path);
641 if (!dev)
642 goto close_exit;
643
644 /* Stage 2: Find any interfaces matching the class/subclass/proto as specified */
645 rc = osmo_libusb_dev_find_matching_interfaces(dev, cfg->intf.class, cfg->intf.subclass,
646 cfg->intf.proto, if_matches, sizeof(if_matches));
647 if (rc < 1) {
648 LOGP(DLUSB, LOGL_NOTICE, "can't find matching USB interface at device\n");
649 goto close_exit;
650 } else if (rc == 1) {
651 ifm = if_matches;
652 } else if (rc > 1) {
653 if (cfg->intf.num == -1) {
654 LOGP(DLUSB, LOGL_ERROR, "Found %d matching USB interfaces, you "
655 "have to specify the interface number\n", rc);
656 goto close_exit;
657 }
658 for (i = 0; i < rc; i++) {
659 if (if_matches[i].interface == cfg->intf.num) {
660 ifm = &if_matches[i];
661 break;
662 }
663 /* FIXME: match altsetting */
664 }
665 }
666 if (!ifm) {
667 LOGP(DLUSB, LOGL_NOTICE, "Couldn't find matching interface\n");
668 goto close_exit;
669 }
670
671 /* Stage 3: Open device; set config (if required); claim interface; set altsetting */
672 usb_devh = osmo_libusb_open_claim_interface(NULL, NULL, ifm);
673 if (!usb_devh) {
674 LOGP(DLUSB, LOGL_ERROR, "can't open USB device (permissions issue?)\n");
675 goto close_exit;
676 }
677 return usb_devh;
678close_exit:
679 /* release if_matches */
680 if (usb_devh)
681 libusb_close(usb_devh);
682
683 return NULL;
684}
685
Harald Welted462e3f2019-12-15 20:04:51 +0100686/*! obtain the endpoint addresses for a given USB interface.
687 * \param[in] devh USB device handle on which to operate
688 * \param[in] if_num USB Interface number on which to operate
689 * \param[out] out user-provided storage for OUT endpoint number
690 * \param[out] in user-provided storage for IN endpoint number
691 * \param[out] irq user-provided storage for IRQ endpoint number
692 * \returns 0 in case of success; negative in case of error */
693int osmo_libusb_get_ep_addrs(libusb_device_handle *devh, unsigned int if_num,
694 uint8_t *out, uint8_t *in, uint8_t *irq)
695{
696 libusb_device *dev = libusb_get_device(devh);
697 struct libusb_config_descriptor *cdesc;
698 const struct libusb_interface_descriptor *idesc;
699 const struct libusb_interface *iface;
700 int rc, l;
701
702 rc = libusb_get_active_config_descriptor(dev, &cdesc);
703 if (rc < 0)
704 return rc;
705
706 iface = &cdesc->interface[if_num];
707 /* FIXME: we assume there's no altsetting */
708 idesc = &iface->altsetting[0];
709
710 for (l = 0; l < idesc->bNumEndpoints; l++) {
711 const struct libusb_endpoint_descriptor *edesc = &idesc->endpoint[l];
712 switch (edesc->bmAttributes & 3) {
713 case LIBUSB_TRANSFER_TYPE_BULK:
714 if (edesc->bEndpointAddress & 0x80) {
715 if (in)
716 *in = edesc->bEndpointAddress;
717 } else {
718 if (out)
719 *out = edesc->bEndpointAddress;
720 }
721 break;
722 case LIBUSB_TRANSFER_TYPE_INTERRUPT:
723 if (irq)
724 *irq = edesc->bEndpointAddress;
725 break;
726 default:
727 break;
728 }
729 }
730 return 0;
731}
Harald Welteda432cd2019-12-15 19:13:26 +0100732/***********************************************************************
733 * initialization
734 ***********************************************************************/
735
736int osmo_libusb_init(libusb_context **pluctx)
737{
738 libusb_context *luctx = NULL;
Harald Weltea366a892022-01-31 18:37:36 +0100739 const struct libusb_pollfd **pfds;
740
Harald Welteda432cd2019-12-15 19:13:26 +0100741 int rc;
742
743 rc = libusb_init(pluctx);
Harald Weltee452dd02022-01-31 18:39:29 +0100744 if (rc != 0) {
745 LOGP(DLUSB, LOGL_ERROR, "Error initializing libusb: %s\n", libusb_strerror(rc));
Harald Welteda432cd2019-12-15 19:13:26 +0100746 return rc;
Harald Weltee452dd02022-01-31 18:39:29 +0100747 }
Harald Welteda432cd2019-12-15 19:13:26 +0100748
749 if (pluctx)
750 luctx = *pluctx;
751
752#ifdef LIBUSB_LOG_CB_CONTEXT /* introduced in 1.0.23 */
753 libusb_set_log_cb(luctx, &libosmo_usb_log_cb, LIBUSB_LOG_CB_CONTEXT);
754#endif
755
756 libusb_set_pollfd_notifiers(luctx, osmo_usb_added_cb, osmo_usb_removed_cb, luctx);
757
Harald Weltea366a892022-01-31 18:37:36 +0100758 /* get the initial file descriptors which were created even before during libusb_init() */
759 pfds = libusb_get_pollfds(luctx);
760 if (pfds) {
761 const struct libusb_pollfd **pfds2 = pfds;
762 const struct libusb_pollfd *pfd;
763 /* synthesize 'add' call-backs. not sure why libusb doesn't do that by itself? */
764 for (pfd = *pfds2; pfd; pfd = *++pfds2)
765 osmo_usb_added_cb(pfd->fd, pfd->events, luctx);
766 libusb_free_pollfds(pfds);
767 }
768
Harald Welteda432cd2019-12-15 19:13:26 +0100769 return 0;
770}
771
772void osmo_libusb_exit(libusb_context *luctx)
773{
774 /* we just assume libusb is cleaning up all the osmo_Fd's we've allocated */
775 libusb_exit(luctx);
776}