blob: 7a13a7e3db97603804d4890ead276bd1cfd3d313 [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;
89
90 if (events & POLLIN)
91 when |= OSMO_FD_READ;
92 if (events & POLLOUT)
93 when |= OSMO_FD_WRITE;
94
95 osmo_fd_setup(ofd, fd, when, osmo_usb_fd_cb, luctx, 0);
96 osmo_fd_register(ofd);
97}
98
99/* called by libusb if it wants to remove a file-descriptor */
100static void osmo_usb_removed_cb(int fd, void *user_data)
101{
102 struct osmo_fd *ofd = osmo_fd_get_by_fd(fd);
103 if (!fd)
104 return;
105 osmo_fd_unregister(ofd);
106 talloc_free(ofd);
107}
108
Harald Welted462e3f2019-12-15 20:04:51 +0100109/***********************************************************************
110 * utility functions
111 ***********************************************************************/
Harald Welteda432cd2019-12-15 19:13:26 +0100112
Harald Welted462e3f2019-12-15 20:04:51 +0100113/*! obtain the string representation of the USB device path of given device.
114 * \param[out] buf Output string buffer
115 * \param[in] bufsize Size of output string buffer in bytes
116 * \param[in] dev USB device whose bus path we want to obtain
117 * \returns pointer to 'buf' in case of success; NULL in case of error */
118char *osmo_libusb_dev_get_path_buf(char *buf, size_t bufsize, libusb_device *dev)
119{
120#if (defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102) || \
121 (defined(LIBUSBX_API_VERSION) && LIBUSBX_API_VERSION >= 0x01000102)
122 struct osmo_strbuf sb = { .buf = buf, .len = bufsize };
123 uint8_t path[8];
124 int r,j;
125 r = libusb_get_port_numbers(dev, path, sizeof(path));
126 if (r > 0) {
127 OSMO_STRBUF_PRINTF(sb, "%d-%d", libusb_get_bus_number(dev), path[0]);
128 for (j = 1; j < r; j++){
129 OSMO_STRBUF_PRINTF(sb, ".%d", path[j]);
130 }
131 }
132 return buf;
133#else
134# warning "libusb too old - building without USB path support!"
135 return NULL;
136#endif
137}
138
139/*! obtain the string representation of the USB device path of given device.
140 * \param[in] talloc context from which to dynamically allocate output string buffer
141 * \param[in] dev USB device whose bus path we want to obtain
142 * \returns pointer to 'buf' in case of success; NULL in case of error */
143char *osmo_libusb_dev_get_path_c(void *ctx, libusb_device *dev)
144{
145 char *buf = talloc_zero_size(ctx, USB_MAX_PATH_LEN);
146 if (!buf)
147 return NULL;
148 return osmo_libusb_dev_get_path_buf(buf, USB_MAX_PATH_LEN, dev);
149}
150
151static int match_dev_id(const struct libusb_device_descriptor *desc, const struct dev_id *id)
152{
153 if ((desc->idVendor == id->vendor_id) && (desc->idProduct == id->product_id))
154 return 1;
155 return 0;
156}
157
158static int match_dev_ids(const struct libusb_device_descriptor *desc, const struct dev_id *ids)
159{
160 const struct dev_id *id;
161
162 for (id = ids; id->vendor_id || id->product_id; id++) {
163 if (match_dev_id(desc, id))
164 return 1;
165 }
166 return 0;
167}
168
169/*! Find USB devices matching the specified list of USB VendorID/ProductIDs
170 * \param[in] ctx talloc context from which to allocate output data
171 * \param[in] luctx libusb context on which to operate
172 * \param[in] dev_ids zero-terminated array of VendorId/ProductId tuples
173 * \returns array of up to 256 libusb_device pointers; NULL in case of error */
174libusb_device **osmo_libusb_find_matching_usb_devs(void *ctx, struct libusb_context *luctx,
175 const struct dev_id *dev_ids)
176{
177 libusb_device **list;
178 libusb_device **out = talloc_zero_array(ctx, libusb_device *, 256);
179 libusb_device **cur = out;
180 unsigned int i;
181 int rc;
182
183 if (!out)
184 return NULL;
185
186 rc = libusb_get_device_list(luctx, &list);
187 if (rc <= 0) {
188 perror("No USB devices found");
189 talloc_free(out);
190 return NULL;
191 }
192
193 for (i = 0; list[i] != NULL; i++) {
194 struct libusb_device_descriptor dev_desc;
195 libusb_device *dev = list[i];
196
197 rc = libusb_get_device_descriptor(dev, &dev_desc);
198 if (rc < 0) {
199 perror("Couldn't get device descriptor\n");
200 libusb_unref_device(dev);
201 continue;
202 }
203
204 if (match_dev_ids(&dev_desc, dev_ids)) {
205 *cur = dev;
206 cur++;
207 /* overflow check */
208 if (cur >= out + 256)
209 break;
210 } else
211 libusb_unref_device(dev);
212 }
213 if (cur == out) {
214 libusb_free_device_list(list, 1);
215 talloc_free(out);
216 return NULL;
217 }
218
219 libusb_free_device_list(list, 0);
220 return out;
221}
222
Harald Weltec45787b2019-12-24 12:20:07 +0100223/*! Find a USB device of matching VendorID/ProductID at given path.
224 * \param[in] luctx libusb context on which to operate
225 * \param[in] dev_ids zer-oterminated array of VendorId/ProductId tuples
226 * \param[in] path string representation of USB path
227 * \returns libusb_device if there was exactly one match; NULL otherwise */
228libusb_device *osmo_libusb_find_matching_dev_path(struct libusb_context *luctx,
229 const struct dev_id *dev_ids,
230 const char *path)
231{
232 libusb_device **list;
233 libusb_device *match = NULL;
234 unsigned int i;
235 int rc;
236
237 rc = libusb_get_device_list(luctx, &list);
238 if (rc <= 0)
239 return NULL;
240
241 for (i = 0; list[i] != NULL; i++) {
242 struct libusb_device_descriptor dev_desc;
243 libusb_device *dev = list[i];
244 char pathbuf[128];
245
246 rc = libusb_get_device_descriptor(dev, &dev_desc);
247 if (rc < 0) {
248 LOGP(DLUSB, LOGL_ERROR, "couldn't get device descriptor\n");
249 continue;
250 }
251
252 /* check if device doesn't match */
253 if (!match_dev_ids(&dev_desc, dev_ids))
254 continue;
255
256 /* check if path doesn't match */
257 if (path) {
258 osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), dev);
259 if (strcmp(pathbuf, path))
260 continue;
261 }
262
263 if (match) {
264 /* we already have a match, but now found a second -> FAIL */
265 libusb_free_device_list(list, 1);
266 LOGP(DLUSB, LOGL_ERROR, "Found more than one matching USB device\n");
267 return NULL;
268 } else
269 match = dev;
270 }
271
272 if (!match) {
273 /* no match: free the list with automatic unref of all devices */
274 libusb_free_device_list(list, 1);
275 return NULL;
276 }
277
278 /* unref all devices *except* the match we found */
279 for (i = 0; list[i] != NULL; i++) {
280 libusb_device *dev = list[i];
281 if (dev != match)
282 libusb_unref_device(dev);
283 }
284 /* free the list *without* automatic unref of all devices */
285 libusb_free_device_list(list, 0);
286 return match;
287}
288
289/*! Find a USB device of matching VendorID/ProductID and given iSerial string.
290 * \param[in] luctx libusb context on which to operate
291 * \param[in] dev_ids zer-oterminated array of VendorId/ProductId tuples
292 * \param[in] serial string representation of serial number
293 * \returns libusb_device if there was exactly one match; NULL otherwise */
294libusb_device *osmo_libusb_find_matching_dev_serial(struct libusb_context *luctx,
295 const struct dev_id *dev_ids,
296 const char *serial)
297{
298 libusb_device **list;
299 libusb_device *match = NULL;
300 unsigned int i;
301 int rc;
302
303 rc = libusb_get_device_list(luctx, &list);
304 if (rc <= 0)
305 return NULL;
306
307 for (i = 0; list[i] != NULL; i++) {
308 struct libusb_device_descriptor dev_desc;
309 libusb_device *dev = list[i];
310
311 rc = libusb_get_device_descriptor(dev, &dev_desc);
312 if (rc < 0) {
313 LOGP(DLUSB, LOGL_ERROR, "couldn't get device descriptor\n");
314 continue;
315 }
316
317 /* check if device doesn't match */
318 if (!match_dev_ids(&dev_desc, dev_ids))
319 continue;
320
321 /* check if serial number string doesn't match */
322 if (serial) {
323 char strbuf[256];
324 libusb_device_handle *devh;
325 rc = libusb_open(dev, &devh);
326 if (rc < 0) {
327 LOGP(DLUSB, LOGL_ERROR, "Cannot open USB Device: %s\n",
328 libusb_strerror(rc));
329 /* there's no point in continuing here, as we don't know if there
330 * are multiple matches if we cannot read the iSerial string of all
331 * devices with matching vid/pid */
332 libusb_free_device_list(list, 1);
333 return NULL;
334 }
335 rc = libusb_get_string_descriptor_ascii(devh, dev_desc.iSerialNumber,
336 (uint8_t *) strbuf, sizeof(strbuf));
Harald Weltee51cbe42020-01-18 21:49:31 +0100337 if (rc < 0) {
338 LOGP(DLUSB, LOGL_ERROR, "Cannot read USB Descriptor: %s\n",
339 libusb_strerror(rc));
340 libusb_close(devh);
341 continue;
342 }
Harald Weltec45787b2019-12-24 12:20:07 +0100343 libusb_close(devh);
344 if (strcmp(strbuf, serial))
345 continue;
346 }
347
348 if (match) {
349 /* we already have a match, but now found a second -> FAIL */
350 libusb_free_device_list(list, 1);
351 LOGP(DLUSB, LOGL_ERROR, "Found more than one matching USB device\n");
352 return NULL;
353 } else
354 match = dev;
355 }
356
357 if (!match) {
358 /* no match: free the list with automatic unref of all devices */
359 libusb_free_device_list(list, 1);
360 return NULL;
361 }
362
363 /* unref all devices *except* the match we found */
364 for (i = 0; list[i] != NULL; i++) {
365 libusb_device *dev = list[i];
366 if (dev != match)
367 libusb_unref_device(dev);
368 }
369 /* free the list *without* automatic unref of all devices */
370 libusb_free_device_list(list, 0);
371 return match;
372}
373
374
Harald Welted462e3f2019-12-15 20:04:51 +0100375/*! find a matching interface among all interfaces of the given USB device.
376 * \param[in] dev USB device in which we shall search
377 * \param[in] class USB Interface Class to look for
378 * \param[in] sub_class USB Interface Subclass to look for
379 * \param[in] protocol USB Interface Protocol to look for
380 * \param[out] out User-allocated array for storing matches
381 * \param[in] out_len Length of out array
382 * \returns number of matching interfaces; negative in case of error */
383int osmo_libusb_dev_find_matching_interfaces(libusb_device *dev, int class, int sub_class,
384 int protocol, struct usb_interface_match *out,
385 unsigned int out_len)
386{
387 struct libusb_device_descriptor dev_desc;
388 int rc, i, out_idx = 0;
389 uint8_t addr;
390 char pathbuf[USB_MAX_PATH_LEN];
391 char *path;
392
393 rc = libusb_get_device_descriptor(dev, &dev_desc);
394 if (rc < 0) {
395 perror("Couldn't get device descriptor\n");
396 return -EIO;
397 }
398
399 addr = libusb_get_device_address(dev);
400 path = osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), dev);
401
402 /* iterate over all configurations */
403 for (i = 0; i < dev_desc.bNumConfigurations; i++) {
404 struct libusb_config_descriptor *conf_desc;
405 int j;
406
407 rc = libusb_get_config_descriptor(dev, i, &conf_desc);
408 if (rc < 0) {
409 fprintf(stderr, "Couldn't get config descriptor %u\n", i);
410 continue;
411 }
412 /* iterate over all interfaces */
413 for (j = 0; j < conf_desc->bNumInterfaces; j++) {
414 const struct libusb_interface *intf = &conf_desc->interface[j];
415 int k;
416 /* iterate over all alternate settings */
417 for (k = 0; k < intf->num_altsetting; k++) {
418 const struct libusb_interface_descriptor *if_desc;
419 if_desc = &intf->altsetting[k];
420 if (class >= 0 && if_desc->bInterfaceClass != class)
421 continue;
422 if (sub_class >= 0 && if_desc->bInterfaceSubClass != sub_class)
423 continue;
424 if (protocol >= 0 && if_desc->bInterfaceProtocol != protocol)
425 continue;
426 /* MATCH! */
427 out[out_idx].usb_dev = dev;
428 out[out_idx].vendor = dev_desc.idVendor;
429 out[out_idx].product = dev_desc.idProduct;
430 out[out_idx].addr = addr;
Harald Welte424eac82019-12-24 12:07:47 +0100431 OSMO_STRLCPY_ARRAY(out[out_idx].path, path);
Harald Welted462e3f2019-12-15 20:04:51 +0100432 out[out_idx].path[sizeof(out[out_idx].path)-1] = '\0';
433 out[out_idx].configuration = conf_desc->bConfigurationValue;
434 out[out_idx].interface = if_desc->bInterfaceNumber;
435 out[out_idx].altsetting = if_desc->bAlternateSetting;
436 out[out_idx].class = if_desc->bInterfaceClass;
437 out[out_idx].sub_class = if_desc->bInterfaceSubClass;
438 out[out_idx].protocol = if_desc->bInterfaceProtocol;
439 out[out_idx].string_idx = if_desc->iInterface;
440 out_idx++;
441 if (out_idx >= out_len)
442 return out_idx;
443 }
444 }
445 }
446 return out_idx;
447}
448
449/*! find matching interfaces among a list devices of specified VendorId/ProductID tuples.
450 * \param[in] luctx libusb context on which to operate
451 * \param[in] dev_ids zero-terminated array of VendorId/ProductId tuples
452 * \param[in] class USB Interface Class to look for
453 * \param[in] sub_class USB Interface Subclass to look for
454 * \param[in] protocol USB Interface Protocol to look for
455 * \param[out] out User-allocated array for storing matches
456 * \param[in] out_len Length of out array
457 * \returns number of matching interfaces; negative in case of error */
458int osmo_libusb_find_matching_interfaces(libusb_context *luctx, const struct dev_id *dev_ids,
459 int class, int sub_class, int protocol,
460 struct usb_interface_match *out, unsigned int out_len)
461{
462 struct usb_interface_match *out_cur = out;
463 unsigned int out_len_remain = out_len;
464 libusb_device **list;
465 libusb_device **dev;
466
467 list = osmo_libusb_find_matching_usb_devs(NULL, luctx, dev_ids);
468 if (!list)
469 return 0;
470
471 for (dev = list; *dev; dev++) {
472 int rc;
473
474#if 0
475 struct libusb_device_descriptor dev_desc;
476 uint8_t ports[8];
477 uint8_t addr;
478 rc = libusb_get_device_descriptor(*dev, &dev_desc);
479 if (rc < 0) {
480 perror("Cannot get device descriptor");
481 continue;
482 }
483
484 addr = libusb_get_device_address(*dev);
485
486 rc = libusb_get_port_numbers(*dev, ports, sizeof(ports));
487 if (rc < 0) {
488 perror("Cannot get device path");
489 continue;
490 }
491
492 printf("Found USB Device %04x:%04x at address %d\n",
493 dev_desc.idVendor, dev_desc.idProduct, addr);
494#endif
495
496 rc = osmo_libusb_dev_find_matching_interfaces(*dev, class, sub_class,
497 protocol, out_cur, out_len_remain);
498 if (rc < 0)
499 continue;
500 out_cur += rc;
501 out_len_remain -= rc;
502
503 }
504
505 /* unref / free list */
506 for (dev = list; *dev; dev++)
507 libusb_unref_device(*dev);
508 talloc_free(list);
509
510 return out_len - out_len_remain;
511}
512
513/*! open matching USB device and claim interface
514 * \param[in] ctx talloc context to use for related allocations
515 * \param[in] luctx libusb context on which to operate
516 * \param[in] ifm interface match describing interface to claim
517 * \returns libusb device chandle on success; NULL on error */
518libusb_device_handle *osmo_libusb_open_claim_interface(void *ctx, libusb_context *luctx,
519 const struct usb_interface_match *ifm)
520{
521 int rc, config;
522 struct dev_id dev_ids[] = { { ifm->vendor, ifm->product }, { 0, 0 } };
523 libusb_device **list;
524 libusb_device **dev;
525 libusb_device_handle *usb_devh = NULL;
526
527 list = osmo_libusb_find_matching_usb_devs(ctx, luctx, dev_ids);
528 if (!list) {
529 perror("No USB device with matching VID/PID");
530 return NULL;
531 }
532
533 for (dev = list; *dev; dev++) {
534 int addr;
535 char pathbuf[USB_MAX_PATH_LEN];
536 char *path;
537
538 addr = libusb_get_device_address(*dev);
539 path = osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), *dev);
540 if ((ifm->addr && addr == ifm->addr) ||
541 (strlen(ifm->path) && !strcmp(path, ifm->path))) {
542 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
590/*! obtain the endpoint addresses for a given USB interface.
591 * \param[in] devh USB device handle on which to operate
592 * \param[in] if_num USB Interface number on which to operate
593 * \param[out] out user-provided storage for OUT endpoint number
594 * \param[out] in user-provided storage for IN endpoint number
595 * \param[out] irq user-provided storage for IRQ endpoint number
596 * \returns 0 in case of success; negative in case of error */
597int osmo_libusb_get_ep_addrs(libusb_device_handle *devh, unsigned int if_num,
598 uint8_t *out, uint8_t *in, uint8_t *irq)
599{
600 libusb_device *dev = libusb_get_device(devh);
601 struct libusb_config_descriptor *cdesc;
602 const struct libusb_interface_descriptor *idesc;
603 const struct libusb_interface *iface;
604 int rc, l;
605
606 rc = libusb_get_active_config_descriptor(dev, &cdesc);
607 if (rc < 0)
608 return rc;
609
610 iface = &cdesc->interface[if_num];
611 /* FIXME: we assume there's no altsetting */
612 idesc = &iface->altsetting[0];
613
614 for (l = 0; l < idesc->bNumEndpoints; l++) {
615 const struct libusb_endpoint_descriptor *edesc = &idesc->endpoint[l];
616 switch (edesc->bmAttributes & 3) {
617 case LIBUSB_TRANSFER_TYPE_BULK:
618 if (edesc->bEndpointAddress & 0x80) {
619 if (in)
620 *in = edesc->bEndpointAddress;
621 } else {
622 if (out)
623 *out = edesc->bEndpointAddress;
624 }
625 break;
626 case LIBUSB_TRANSFER_TYPE_INTERRUPT:
627 if (irq)
628 *irq = edesc->bEndpointAddress;
629 break;
630 default:
631 break;
632 }
633 }
634 return 0;
635}
Harald Welteda432cd2019-12-15 19:13:26 +0100636/***********************************************************************
637 * initialization
638 ***********************************************************************/
639
640int osmo_libusb_init(libusb_context **pluctx)
641{
642 libusb_context *luctx = NULL;
643 int rc;
644
645 rc = libusb_init(pluctx);
646 if (rc != 0)
647 return rc;
648
649 if (pluctx)
650 luctx = *pluctx;
651
652#ifdef LIBUSB_LOG_CB_CONTEXT /* introduced in 1.0.23 */
653 libusb_set_log_cb(luctx, &libosmo_usb_log_cb, LIBUSB_LOG_CB_CONTEXT);
654#endif
655
656 libusb_set_pollfd_notifiers(luctx, osmo_usb_added_cb, osmo_usb_removed_cb, luctx);
657
658 return 0;
659}
660
661void osmo_libusb_exit(libusb_context *luctx)
662{
663 /* we just assume libusb is cleaning up all the osmo_Fd's we've allocated */
664 libusb_exit(luctx);
665}