diff options
Diffstat (limited to 'usbmisc.c')
-rw-r--r-- | usbmisc.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/usbmisc.c b/usbmisc.c new file mode 100644 index 0000000..b12928f --- /dev/null +++ b/usbmisc.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Misc USB routines + * + * Copyright (C) 2003 Aurelien Jarno (aurelien@aurel32.net) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> + +#ifdef HAVE_ICONV +#include <iconv.h> +#endif + +#ifdef HAVE_NL_LANGINFO +#include <langinfo.h> +#endif + +#include "usbmisc.h" + +/* ---------------------------------------------------------------------- */ + +static const char *devbususb = "/dev/bus/usb"; + +/* ---------------------------------------------------------------------- */ + +static int readlink_recursive(const char *path, char *buf, size_t bufsize) +{ + char temp[PATH_MAX + 1]; + char *ptemp; + int ret; + + ret = readlink(path, buf, bufsize-1); + + if (ret > 0) { + buf[ret] = 0; + if (*buf != '/') { + strncpy(temp, path, sizeof(temp) - 1); + ptemp = temp + strlen(temp); + while (*ptemp != '/' && ptemp != temp) + ptemp--; + ptemp++; + strncpy(ptemp, buf, bufsize + temp - ptemp - 1); + } else + strncpy(temp, buf, sizeof(temp) - 1); + return readlink_recursive(temp, buf, bufsize); + } else { + strncpy(buf, path, bufsize); + return strlen(buf); + } +} + +static char *get_absolute_path(const char *path, char *result, + size_t result_size) +{ + const char *ppath; /* pointer on the input string */ + char *presult; /* pointer on the output string */ + + ppath = path; + presult = result; + result[0] = 0; + + if (path == NULL) + return result; + + if (*ppath != '/') { + result = getcwd(result, result_size); + presult += strlen(result); + result_size -= strlen(result); + + *presult++ = '/'; + result_size--; + } + + while (*ppath != 0 && result_size > 1) { + if (*ppath == '/') { + do + ppath++; + while (*ppath == '/'); + *presult++ = '/'; + result_size--; + } else if (*ppath == '.' && *(ppath + 1) == '.' && + *(ppath + 2) == '/' && *(presult - 1) == '/') { + if ((presult - 1) != result) { + /* go one directory upper */ + do { + presult--; + result_size++; + } while (*(presult - 1) != '/'); + } + ppath += 3; + } else if (*ppath == '.' && + *(ppath + 1) == '/' && + *(presult - 1) == '/') { + ppath += 2; + } else { + *presult++ = *ppath++; + result_size--; + } + } + /* Don't forget to mark the end of the string! */ + *presult = 0; + + return result; +} + +libusb_device *get_usb_device(libusb_context *ctx, const char *path) +{ + libusb_device **list; + libusb_device *dev; + ssize_t num_devs, i; + char device_path[PATH_MAX + 1]; + char absolute_path[PATH_MAX + 1]; + + readlink_recursive(path, device_path, sizeof(device_path)); + get_absolute_path(device_path, absolute_path, sizeof(absolute_path)); + + dev = NULL; + num_devs = libusb_get_device_list(ctx, &list); + + for (i = 0; i < num_devs; ++i) { + uint8_t bnum = libusb_get_bus_number(list[i]); + uint8_t dnum = libusb_get_device_address(list[i]); + + snprintf(device_path, sizeof(device_path), "%s/%03u/%03u", + devbususb, bnum, dnum); + if (!strcmp(device_path, absolute_path)) { + dev = list[i]; + break; + } + } + + libusb_free_device_list(list, 0); + return dev; +} + +static char *get_dev_string_ascii(libusb_device_handle *dev, size_t size, + uint8_t id) +{ + char *buf = malloc(size); + int ret = libusb_get_string_descriptor_ascii(dev, id, + (unsigned char *) buf, + size); + + if (ret < 0) { + free(buf); + return strdup("(error)"); + } + + return buf; +} + +#if defined(HAVE_NL_LANGINFO) && defined(HAVE_ICONV) +static uint16_t get_any_langid(libusb_device_handle *dev) +{ + unsigned char buf[4]; + int ret = libusb_get_string_descriptor(dev, 0, 0, buf, sizeof buf); + if (ret != sizeof buf) return 0; + return buf[2] | (buf[3] << 8); +} + +static char *usb_string_to_native(char * str, size_t len) +{ + size_t num_converted; + iconv_t conv; + char *result, *result_end; + size_t in_bytes_left, out_bytes_left; + + conv = iconv_open(nl_langinfo(CODESET), "UTF-16LE"); + + if (conv == (iconv_t) -1) + return NULL; + + in_bytes_left = len * 2; + out_bytes_left = len * MB_CUR_MAX; + result = result_end = malloc(out_bytes_left + 1); + + num_converted = iconv(conv, &str, &in_bytes_left, + &result_end, &out_bytes_left); + + iconv_close(conv); + if (num_converted == (size_t) -1) { + free(result); + return NULL; + } + + *result_end = 0; + return result; +} +#endif + +char *get_dev_string(libusb_device_handle *dev, uint8_t id) +{ +#if defined(HAVE_NL_LANGINFO) && defined(HAVE_ICONV) + int ret; + char *buf, unicode_buf[254]; + uint16_t langid; +#endif + + if (!dev || !id) return strdup(""); + +#if defined(HAVE_NL_LANGINFO) && defined(HAVE_ICONV) + langid = get_any_langid(dev); + if (!langid) return strdup("(error)"); + + /* + * Some devices lie about their string size, so initialize + * the buffer with all 0 to account for that. + */ + memset(unicode_buf, 0x00, sizeof(unicode_buf)); + + ret = libusb_get_string_descriptor(dev, id, langid, + (unsigned char *) unicode_buf, + sizeof unicode_buf); + if (ret < 2) return strdup("(error)"); + + if ((unsigned char)unicode_buf[0] < 2 || unicode_buf[1] != LIBUSB_DT_STRING) + return strdup("(error)"); + + buf = usb_string_to_native(unicode_buf + 2, + ((unsigned char) unicode_buf[0] - 2) / 2); + + if (!buf) return get_dev_string_ascii(dev, 127, id); + + return buf; +#else + return get_dev_string_ascii(dev, 127, id); +#endif +} |