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