blob: 5b012b865e369b1d39b4629127a92cf9dfb89212 [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
223/*! find a matching interface among all interfaces of the given USB device.
224 * \param[in] dev USB device in which we shall search
225 * \param[in] class USB Interface Class to look for
226 * \param[in] sub_class USB Interface Subclass to look for
227 * \param[in] protocol USB Interface Protocol to look for
228 * \param[out] out User-allocated array for storing matches
229 * \param[in] out_len Length of out array
230 * \returns number of matching interfaces; negative in case of error */
231int osmo_libusb_dev_find_matching_interfaces(libusb_device *dev, int class, int sub_class,
232 int protocol, struct usb_interface_match *out,
233 unsigned int out_len)
234{
235 struct libusb_device_descriptor dev_desc;
236 int rc, i, out_idx = 0;
237 uint8_t addr;
238 char pathbuf[USB_MAX_PATH_LEN];
239 char *path;
240
241 rc = libusb_get_device_descriptor(dev, &dev_desc);
242 if (rc < 0) {
243 perror("Couldn't get device descriptor\n");
244 return -EIO;
245 }
246
247 addr = libusb_get_device_address(dev);
248 path = osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), dev);
249
250 /* iterate over all configurations */
251 for (i = 0; i < dev_desc.bNumConfigurations; i++) {
252 struct libusb_config_descriptor *conf_desc;
253 int j;
254
255 rc = libusb_get_config_descriptor(dev, i, &conf_desc);
256 if (rc < 0) {
257 fprintf(stderr, "Couldn't get config descriptor %u\n", i);
258 continue;
259 }
260 /* iterate over all interfaces */
261 for (j = 0; j < conf_desc->bNumInterfaces; j++) {
262 const struct libusb_interface *intf = &conf_desc->interface[j];
263 int k;
264 /* iterate over all alternate settings */
265 for (k = 0; k < intf->num_altsetting; k++) {
266 const struct libusb_interface_descriptor *if_desc;
267 if_desc = &intf->altsetting[k];
268 if (class >= 0 && if_desc->bInterfaceClass != class)
269 continue;
270 if (sub_class >= 0 && if_desc->bInterfaceSubClass != sub_class)
271 continue;
272 if (protocol >= 0 && if_desc->bInterfaceProtocol != protocol)
273 continue;
274 /* MATCH! */
275 out[out_idx].usb_dev = dev;
276 out[out_idx].vendor = dev_desc.idVendor;
277 out[out_idx].product = dev_desc.idProduct;
278 out[out_idx].addr = addr;
279 strncpy(out[out_idx].path, path, sizeof(out[out_idx].path)-1);
280 out[out_idx].path[sizeof(out[out_idx].path)-1] = '\0';
281 out[out_idx].configuration = conf_desc->bConfigurationValue;
282 out[out_idx].interface = if_desc->bInterfaceNumber;
283 out[out_idx].altsetting = if_desc->bAlternateSetting;
284 out[out_idx].class = if_desc->bInterfaceClass;
285 out[out_idx].sub_class = if_desc->bInterfaceSubClass;
286 out[out_idx].protocol = if_desc->bInterfaceProtocol;
287 out[out_idx].string_idx = if_desc->iInterface;
288 out_idx++;
289 if (out_idx >= out_len)
290 return out_idx;
291 }
292 }
293 }
294 return out_idx;
295}
296
297/*! find matching interfaces among a list devices of specified VendorId/ProductID tuples.
298 * \param[in] luctx libusb context on which to operate
299 * \param[in] dev_ids zero-terminated array of VendorId/ProductId tuples
300 * \param[in] class USB Interface Class to look for
301 * \param[in] sub_class USB Interface Subclass to look for
302 * \param[in] protocol USB Interface Protocol to look for
303 * \param[out] out User-allocated array for storing matches
304 * \param[in] out_len Length of out array
305 * \returns number of matching interfaces; negative in case of error */
306int osmo_libusb_find_matching_interfaces(libusb_context *luctx, const struct dev_id *dev_ids,
307 int class, int sub_class, int protocol,
308 struct usb_interface_match *out, unsigned int out_len)
309{
310 struct usb_interface_match *out_cur = out;
311 unsigned int out_len_remain = out_len;
312 libusb_device **list;
313 libusb_device **dev;
314
315 list = osmo_libusb_find_matching_usb_devs(NULL, luctx, dev_ids);
316 if (!list)
317 return 0;
318
319 for (dev = list; *dev; dev++) {
320 int rc;
321
322#if 0
323 struct libusb_device_descriptor dev_desc;
324 uint8_t ports[8];
325 uint8_t addr;
326 rc = libusb_get_device_descriptor(*dev, &dev_desc);
327 if (rc < 0) {
328 perror("Cannot get device descriptor");
329 continue;
330 }
331
332 addr = libusb_get_device_address(*dev);
333
334 rc = libusb_get_port_numbers(*dev, ports, sizeof(ports));
335 if (rc < 0) {
336 perror("Cannot get device path");
337 continue;
338 }
339
340 printf("Found USB Device %04x:%04x at address %d\n",
341 dev_desc.idVendor, dev_desc.idProduct, addr);
342#endif
343
344 rc = osmo_libusb_dev_find_matching_interfaces(*dev, class, sub_class,
345 protocol, out_cur, out_len_remain);
346 if (rc < 0)
347 continue;
348 out_cur += rc;
349 out_len_remain -= rc;
350
351 }
352
353 /* unref / free list */
354 for (dev = list; *dev; dev++)
355 libusb_unref_device(*dev);
356 talloc_free(list);
357
358 return out_len - out_len_remain;
359}
360
361/*! open matching USB device and claim interface
362 * \param[in] ctx talloc context to use for related allocations
363 * \param[in] luctx libusb context on which to operate
364 * \param[in] ifm interface match describing interface to claim
365 * \returns libusb device chandle on success; NULL on error */
366libusb_device_handle *osmo_libusb_open_claim_interface(void *ctx, libusb_context *luctx,
367 const struct usb_interface_match *ifm)
368{
369 int rc, config;
370 struct dev_id dev_ids[] = { { ifm->vendor, ifm->product }, { 0, 0 } };
371 libusb_device **list;
372 libusb_device **dev;
373 libusb_device_handle *usb_devh = NULL;
374
375 list = osmo_libusb_find_matching_usb_devs(ctx, luctx, dev_ids);
376 if (!list) {
377 perror("No USB device with matching VID/PID");
378 return NULL;
379 }
380
381 for (dev = list; *dev; dev++) {
382 int addr;
383 char pathbuf[USB_MAX_PATH_LEN];
384 char *path;
385
386 addr = libusb_get_device_address(*dev);
387 path = osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), *dev);
388 if ((ifm->addr && addr == ifm->addr) ||
389 (strlen(ifm->path) && !strcmp(path, ifm->path))) {
390 rc = libusb_open(*dev, &usb_devh);
391 if (rc < 0) {
392 fprintf(stderr, "Cannot open device: %s\n", libusb_error_name(rc));
393 usb_devh = NULL;
394 break;
395 }
396 rc = libusb_get_configuration(usb_devh, &config);
397 if (rc < 0) {
398 fprintf(stderr, "Cannot get current configuration: %s\n", libusb_error_name(rc));
399 libusb_close(usb_devh);
400 usb_devh = NULL;
401 break;
402 }
403 if (config != ifm->configuration) {
404 rc = libusb_set_configuration(usb_devh, ifm->configuration);
405 if (rc < 0) {
406 fprintf(stderr, "Cannot set configuration: %s\n", libusb_error_name(rc));
407 libusb_close(usb_devh);
408 usb_devh = NULL;
409 break;
410 }
411 }
412 rc = libusb_claim_interface(usb_devh, ifm->interface);
413 if (rc < 0) {
414 fprintf(stderr, "Cannot claim interface: %s\n", libusb_error_name(rc));
415 libusb_close(usb_devh);
416 usb_devh = NULL;
417 break;
418 }
419 rc = libusb_set_interface_alt_setting(usb_devh, ifm->interface, ifm->altsetting);
420 if (rc < 0) {
421 fprintf(stderr, "Cannot set interface altsetting: %s\n", libusb_error_name(rc));
422 libusb_release_interface(usb_devh, ifm->interface);
423 libusb_close(usb_devh);
424 usb_devh = NULL;
425 break;
426 }
427 }
428 }
429
430 /* unref / free list */
431 for (dev = list; *dev; dev++)
432 libusb_unref_device(*dev);
433 talloc_free(list);
434
435 return usb_devh;
436}
437
438/*! obtain the endpoint addresses for a given USB interface.
439 * \param[in] devh USB device handle on which to operate
440 * \param[in] if_num USB Interface number on which to operate
441 * \param[out] out user-provided storage for OUT endpoint number
442 * \param[out] in user-provided storage for IN endpoint number
443 * \param[out] irq user-provided storage for IRQ endpoint number
444 * \returns 0 in case of success; negative in case of error */
445int osmo_libusb_get_ep_addrs(libusb_device_handle *devh, unsigned int if_num,
446 uint8_t *out, uint8_t *in, uint8_t *irq)
447{
448 libusb_device *dev = libusb_get_device(devh);
449 struct libusb_config_descriptor *cdesc;
450 const struct libusb_interface_descriptor *idesc;
451 const struct libusb_interface *iface;
452 int rc, l;
453
454 rc = libusb_get_active_config_descriptor(dev, &cdesc);
455 if (rc < 0)
456 return rc;
457
458 iface = &cdesc->interface[if_num];
459 /* FIXME: we assume there's no altsetting */
460 idesc = &iface->altsetting[0];
461
462 for (l = 0; l < idesc->bNumEndpoints; l++) {
463 const struct libusb_endpoint_descriptor *edesc = &idesc->endpoint[l];
464 switch (edesc->bmAttributes & 3) {
465 case LIBUSB_TRANSFER_TYPE_BULK:
466 if (edesc->bEndpointAddress & 0x80) {
467 if (in)
468 *in = edesc->bEndpointAddress;
469 } else {
470 if (out)
471 *out = edesc->bEndpointAddress;
472 }
473 break;
474 case LIBUSB_TRANSFER_TYPE_INTERRUPT:
475 if (irq)
476 *irq = edesc->bEndpointAddress;
477 break;
478 default:
479 break;
480 }
481 }
482 return 0;
483}
Harald Welteda432cd2019-12-15 19:13:26 +0100484/***********************************************************************
485 * initialization
486 ***********************************************************************/
487
488int osmo_libusb_init(libusb_context **pluctx)
489{
490 libusb_context *luctx = NULL;
491 int rc;
492
493 rc = libusb_init(pluctx);
494 if (rc != 0)
495 return rc;
496
497 if (pluctx)
498 luctx = *pluctx;
499
500#ifdef LIBUSB_LOG_CB_CONTEXT /* introduced in 1.0.23 */
501 libusb_set_log_cb(luctx, &libosmo_usb_log_cb, LIBUSB_LOG_CB_CONTEXT);
502#endif
503
504 libusb_set_pollfd_notifiers(luctx, osmo_usb_added_cb, osmo_usb_removed_cb, luctx);
505
506 return 0;
507}
508
509void osmo_libusb_exit(libusb_context *luctx)
510{
511 /* we just assume libusb is cleaning up all the osmo_Fd's we've allocated */
512 libusb_exit(luctx);
513}