162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2005-2007 Takahiro Hirofuchi 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <libudev.h> 762306a36Sopenharmony_ci#include "usbip_common.h" 862306a36Sopenharmony_ci#include "names.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#undef PROGNAME 1162306a36Sopenharmony_ci#define PROGNAME "libusbip" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciint usbip_use_syslog; 1462306a36Sopenharmony_ciint usbip_use_stderr; 1562306a36Sopenharmony_ciint usbip_use_debug; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ciextern struct udev *udev_context; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct speed_string { 2062306a36Sopenharmony_ci int num; 2162306a36Sopenharmony_ci char *speed; 2262306a36Sopenharmony_ci char *desc; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic const struct speed_string speed_strings[] = { 2662306a36Sopenharmony_ci { USB_SPEED_UNKNOWN, "unknown", "Unknown Speed"}, 2762306a36Sopenharmony_ci { USB_SPEED_LOW, "1.5", "Low Speed(1.5Mbps)" }, 2862306a36Sopenharmony_ci { USB_SPEED_FULL, "12", "Full Speed(12Mbps)" }, 2962306a36Sopenharmony_ci { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" }, 3062306a36Sopenharmony_ci { USB_SPEED_WIRELESS, "53.3-480", "Wireless"}, 3162306a36Sopenharmony_ci { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" }, 3262306a36Sopenharmony_ci { 0, NULL, NULL } 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct portst_string { 3662306a36Sopenharmony_ci int num; 3762306a36Sopenharmony_ci char *desc; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic struct portst_string portst_strings[] = { 4162306a36Sopenharmony_ci { SDEV_ST_AVAILABLE, "Device Available" }, 4262306a36Sopenharmony_ci { SDEV_ST_USED, "Device in Use" }, 4362306a36Sopenharmony_ci { SDEV_ST_ERROR, "Device Error"}, 4462306a36Sopenharmony_ci { VDEV_ST_NULL, "Port Available"}, 4562306a36Sopenharmony_ci { VDEV_ST_NOTASSIGNED, "Port Initializing"}, 4662306a36Sopenharmony_ci { VDEV_ST_USED, "Port in Use"}, 4762306a36Sopenharmony_ci { VDEV_ST_ERROR, "Port Error"}, 4862306a36Sopenharmony_ci { 0, NULL} 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciconst char *usbip_status_string(int32_t status) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci for (int i = 0; portst_strings[i].desc != NULL; i++) 5462306a36Sopenharmony_ci if (portst_strings[i].num == status) 5562306a36Sopenharmony_ci return portst_strings[i].desc; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return "Unknown Status"; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciconst char *usbip_speed_string(int num) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci for (int i = 0; speed_strings[i].speed != NULL; i++) 6362306a36Sopenharmony_ci if (speed_strings[i].num == num) 6462306a36Sopenharmony_ci return speed_strings[i].desc; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return "Unknown Speed"; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistruct op_common_status_string { 7062306a36Sopenharmony_ci int num; 7162306a36Sopenharmony_ci char *desc; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic struct op_common_status_string op_common_status_strings[] = { 7562306a36Sopenharmony_ci { ST_OK, "Request Completed Successfully" }, 7662306a36Sopenharmony_ci { ST_NA, "Request Failed" }, 7762306a36Sopenharmony_ci { ST_DEV_BUSY, "Device busy (exported)" }, 7862306a36Sopenharmony_ci { ST_DEV_ERR, "Device in error state" }, 7962306a36Sopenharmony_ci { ST_NODEV, "Device not found" }, 8062306a36Sopenharmony_ci { ST_ERROR, "Unexpected response" }, 8162306a36Sopenharmony_ci { 0, NULL} 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciconst char *usbip_op_common_status_string(int status) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci for (int i = 0; op_common_status_strings[i].desc != NULL; i++) 8762306a36Sopenharmony_ci if (op_common_status_strings[i].num == status) 8862306a36Sopenharmony_ci return op_common_status_strings[i].desc; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return "Unknown Op Common Status"; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define DBG_UDEV_INTEGER(name)\ 9462306a36Sopenharmony_ci dbg("%-20s = %x", to_string(name), (int) udev->name) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define DBG_UINF_INTEGER(name)\ 9762306a36Sopenharmony_ci dbg("%-20s = %x", to_string(name), (int) uinf->name) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_civoid dump_usb_interface(struct usbip_usb_interface *uinf) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci char buff[100]; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci usbip_names_get_class(buff, sizeof(buff), 10462306a36Sopenharmony_ci uinf->bInterfaceClass, 10562306a36Sopenharmony_ci uinf->bInterfaceSubClass, 10662306a36Sopenharmony_ci uinf->bInterfaceProtocol); 10762306a36Sopenharmony_ci dbg("%-20s = %s", "Interface(C/SC/P)", buff); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_civoid dump_usb_device(struct usbip_usb_device *udev) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci char buff[100]; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci dbg("%-20s = %s", "path", udev->path); 11562306a36Sopenharmony_ci dbg("%-20s = %s", "busid", udev->busid); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci usbip_names_get_class(buff, sizeof(buff), 11862306a36Sopenharmony_ci udev->bDeviceClass, 11962306a36Sopenharmony_ci udev->bDeviceSubClass, 12062306a36Sopenharmony_ci udev->bDeviceProtocol); 12162306a36Sopenharmony_ci dbg("%-20s = %s", "Device(C/SC/P)", buff); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci DBG_UDEV_INTEGER(bcdDevice); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci usbip_names_get_product(buff, sizeof(buff), 12662306a36Sopenharmony_ci udev->idVendor, 12762306a36Sopenharmony_ci udev->idProduct); 12862306a36Sopenharmony_ci dbg("%-20s = %s", "Vendor/Product", buff); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci DBG_UDEV_INTEGER(bNumConfigurations); 13162306a36Sopenharmony_ci DBG_UDEV_INTEGER(bNumInterfaces); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci dbg("%-20s = %s", "speed", 13462306a36Sopenharmony_ci usbip_speed_string(udev->speed)); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci DBG_UDEV_INTEGER(busnum); 13762306a36Sopenharmony_ci DBG_UDEV_INTEGER(devnum); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ciint read_attr_value(struct udev_device *dev, const char *name, 14262306a36Sopenharmony_ci const char *format) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci const char *attr; 14562306a36Sopenharmony_ci int num = 0; 14662306a36Sopenharmony_ci int ret; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci attr = udev_device_get_sysattr_value(dev, name); 14962306a36Sopenharmony_ci if (!attr) { 15062306a36Sopenharmony_ci err("udev_device_get_sysattr_value failed"); 15162306a36Sopenharmony_ci goto err; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* The client chooses the device configuration 15562306a36Sopenharmony_ci * when attaching it so right after being bound 15662306a36Sopenharmony_ci * to usbip-host on the server the device will 15762306a36Sopenharmony_ci * have no configuration. 15862306a36Sopenharmony_ci * Therefore, attributes such as bConfigurationValue 15962306a36Sopenharmony_ci * and bNumInterfaces will not exist and sscanf will 16062306a36Sopenharmony_ci * fail. Check for these cases and don't treat them 16162306a36Sopenharmony_ci * as errors. 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci ret = sscanf(attr, format, &num); 16562306a36Sopenharmony_ci if (ret < 1) { 16662306a36Sopenharmony_ci if (strcmp(name, "bConfigurationValue") && 16762306a36Sopenharmony_ci strcmp(name, "bNumInterfaces")) { 16862306a36Sopenharmony_ci err("sscanf failed for attribute %s", name); 16962306a36Sopenharmony_ci goto err; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cierr: 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return num; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciint read_attr_speed(struct udev_device *dev) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci const char *speed; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci speed = udev_device_get_sysattr_value(dev, "speed"); 18462306a36Sopenharmony_ci if (!speed) { 18562306a36Sopenharmony_ci err("udev_device_get_sysattr_value failed"); 18662306a36Sopenharmony_ci goto err; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci for (int i = 0; speed_strings[i].speed != NULL; i++) { 19062306a36Sopenharmony_ci if (!strcmp(speed, speed_strings[i].speed)) 19162306a36Sopenharmony_ci return speed_strings[i].num; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cierr: 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return USB_SPEED_UNKNOWN; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci#define READ_ATTR(object, type, dev, name, format) \ 20062306a36Sopenharmony_ci do { \ 20162306a36Sopenharmony_ci (object)->name = (type) read_attr_value(dev, to_string(name), \ 20262306a36Sopenharmony_ci format); \ 20362306a36Sopenharmony_ci } while (0) 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciint read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci uint32_t busnum, devnum; 20962306a36Sopenharmony_ci const char *path, *name; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, bDeviceClass, "%02x\n"); 21262306a36Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, bDeviceSubClass, "%02x\n"); 21362306a36Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, bDeviceProtocol, "%02x\n"); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci READ_ATTR(udev, uint16_t, sdev, idVendor, "%04x\n"); 21662306a36Sopenharmony_ci READ_ATTR(udev, uint16_t, sdev, idProduct, "%04x\n"); 21762306a36Sopenharmony_ci READ_ATTR(udev, uint16_t, sdev, bcdDevice, "%04x\n"); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, bConfigurationValue, "%02x\n"); 22062306a36Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, bNumConfigurations, "%02x\n"); 22162306a36Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, bNumInterfaces, "%02x\n"); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, devnum, "%d\n"); 22462306a36Sopenharmony_ci udev->speed = read_attr_speed(sdev); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci path = udev_device_get_syspath(sdev); 22762306a36Sopenharmony_ci name = udev_device_get_sysname(sdev); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci strncpy(udev->path, path, SYSFS_PATH_MAX - 1); 23062306a36Sopenharmony_ci udev->path[SYSFS_PATH_MAX - 1] = '\0'; 23162306a36Sopenharmony_ci strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE - 1); 23262306a36Sopenharmony_ci udev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0'; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci sscanf(name, "%u-%u", &busnum, &devnum); 23562306a36Sopenharmony_ci udev->busnum = busnum; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ciint read_usb_interface(struct usbip_usb_device *udev, int i, 24162306a36Sopenharmony_ci struct usbip_usb_interface *uinf) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci char busid[SYSFS_BUS_ID_SIZE]; 24462306a36Sopenharmony_ci int size; 24562306a36Sopenharmony_ci struct udev_device *sif; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci size = snprintf(busid, sizeof(busid), "%s:%d.%d", 24862306a36Sopenharmony_ci udev->busid, udev->bConfigurationValue, i); 24962306a36Sopenharmony_ci if (size < 0 || (unsigned int)size >= sizeof(busid)) { 25062306a36Sopenharmony_ci err("busid length %i >= %lu or < 0", size, 25162306a36Sopenharmony_ci (long unsigned)sizeof(busid)); 25262306a36Sopenharmony_ci return -1; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci sif = udev_device_new_from_subsystem_sysname(udev_context, "usb", busid); 25662306a36Sopenharmony_ci if (!sif) { 25762306a36Sopenharmony_ci err("udev_device_new_from_subsystem_sysname %s failed", busid); 25862306a36Sopenharmony_ci return -1; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci READ_ATTR(uinf, uint8_t, sif, bInterfaceClass, "%02x\n"); 26262306a36Sopenharmony_ci READ_ATTR(uinf, uint8_t, sif, bInterfaceSubClass, "%02x\n"); 26362306a36Sopenharmony_ci READ_ATTR(uinf, uint8_t, sif, bInterfaceProtocol, "%02x\n"); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ciint usbip_names_init(char *f) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci return names_init(f); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_civoid usbip_names_free(void) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci names_free(); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_civoid usbip_names_get_product(char *buff, size_t size, uint16_t vendor, 27962306a36Sopenharmony_ci uint16_t product) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci const char *prod, *vend; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci prod = names_product(vendor, product); 28462306a36Sopenharmony_ci if (!prod) 28562306a36Sopenharmony_ci prod = "unknown product"; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci vend = names_vendor(vendor); 28962306a36Sopenharmony_ci if (!vend) 29062306a36Sopenharmony_ci vend = "unknown vendor"; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci snprintf(buff, size, "%s : %s (%04x:%04x)", vend, prod, vendor, product); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_civoid usbip_names_get_class(char *buff, size_t size, uint8_t class, 29662306a36Sopenharmony_ci uint8_t subclass, uint8_t protocol) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci const char *c, *s, *p; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (class == 0 && subclass == 0 && protocol == 0) { 30162306a36Sopenharmony_ci snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)", class, subclass, protocol); 30262306a36Sopenharmony_ci return; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci p = names_protocol(class, subclass, protocol); 30662306a36Sopenharmony_ci if (!p) 30762306a36Sopenharmony_ci p = "unknown protocol"; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci s = names_subclass(class, subclass); 31062306a36Sopenharmony_ci if (!s) 31162306a36Sopenharmony_ci s = "unknown subclass"; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci c = names_class(class); 31462306a36Sopenharmony_ci if (!c) 31562306a36Sopenharmony_ci c = "unknown class"; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol); 31862306a36Sopenharmony_ci} 319