18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2005-2007 Takahiro Hirofuchi 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <libudev.h> 78c2ecf20Sopenharmony_ci#include "usbip_common.h" 88c2ecf20Sopenharmony_ci#include "names.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#undef PROGNAME 118c2ecf20Sopenharmony_ci#define PROGNAME "libusbip" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ciint usbip_use_syslog; 148c2ecf20Sopenharmony_ciint usbip_use_stderr; 158c2ecf20Sopenharmony_ciint usbip_use_debug; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ciextern struct udev *udev_context; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct speed_string { 208c2ecf20Sopenharmony_ci int num; 218c2ecf20Sopenharmony_ci char *speed; 228c2ecf20Sopenharmony_ci char *desc; 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic const struct speed_string speed_strings[] = { 268c2ecf20Sopenharmony_ci { USB_SPEED_UNKNOWN, "unknown", "Unknown Speed"}, 278c2ecf20Sopenharmony_ci { USB_SPEED_LOW, "1.5", "Low Speed(1.5Mbps)" }, 288c2ecf20Sopenharmony_ci { USB_SPEED_FULL, "12", "Full Speed(12Mbps)" }, 298c2ecf20Sopenharmony_ci { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" }, 308c2ecf20Sopenharmony_ci { USB_SPEED_WIRELESS, "53.3-480", "Wireless"}, 318c2ecf20Sopenharmony_ci { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" }, 328c2ecf20Sopenharmony_ci { 0, NULL, NULL } 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct portst_string { 368c2ecf20Sopenharmony_ci int num; 378c2ecf20Sopenharmony_ci char *desc; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic struct portst_string portst_strings[] = { 418c2ecf20Sopenharmony_ci { SDEV_ST_AVAILABLE, "Device Available" }, 428c2ecf20Sopenharmony_ci { SDEV_ST_USED, "Device in Use" }, 438c2ecf20Sopenharmony_ci { SDEV_ST_ERROR, "Device Error"}, 448c2ecf20Sopenharmony_ci { VDEV_ST_NULL, "Port Available"}, 458c2ecf20Sopenharmony_ci { VDEV_ST_NOTASSIGNED, "Port Initializing"}, 468c2ecf20Sopenharmony_ci { VDEV_ST_USED, "Port in Use"}, 478c2ecf20Sopenharmony_ci { VDEV_ST_ERROR, "Port Error"}, 488c2ecf20Sopenharmony_ci { 0, NULL} 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciconst char *usbip_status_string(int32_t status) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci for (int i = 0; portst_strings[i].desc != NULL; i++) 548c2ecf20Sopenharmony_ci if (portst_strings[i].num == status) 558c2ecf20Sopenharmony_ci return portst_strings[i].desc; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return "Unknown Status"; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ciconst char *usbip_speed_string(int num) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci for (int i = 0; speed_strings[i].speed != NULL; i++) 638c2ecf20Sopenharmony_ci if (speed_strings[i].num == num) 648c2ecf20Sopenharmony_ci return speed_strings[i].desc; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return "Unknown Speed"; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct op_common_status_string { 708c2ecf20Sopenharmony_ci int num; 718c2ecf20Sopenharmony_ci char *desc; 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic struct op_common_status_string op_common_status_strings[] = { 758c2ecf20Sopenharmony_ci { ST_OK, "Request Completed Successfully" }, 768c2ecf20Sopenharmony_ci { ST_NA, "Request Failed" }, 778c2ecf20Sopenharmony_ci { ST_DEV_BUSY, "Device busy (exported)" }, 788c2ecf20Sopenharmony_ci { ST_DEV_ERR, "Device in error state" }, 798c2ecf20Sopenharmony_ci { ST_NODEV, "Device not found" }, 808c2ecf20Sopenharmony_ci { ST_ERROR, "Unexpected response" }, 818c2ecf20Sopenharmony_ci { 0, NULL} 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ciconst char *usbip_op_common_status_string(int status) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci for (int i = 0; op_common_status_strings[i].desc != NULL; i++) 878c2ecf20Sopenharmony_ci if (op_common_status_strings[i].num == status) 888c2ecf20Sopenharmony_ci return op_common_status_strings[i].desc; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return "Unknown Op Common Status"; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define DBG_UDEV_INTEGER(name)\ 948c2ecf20Sopenharmony_ci dbg("%-20s = %x", to_string(name), (int) udev->name) 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define DBG_UINF_INTEGER(name)\ 978c2ecf20Sopenharmony_ci dbg("%-20s = %x", to_string(name), (int) uinf->name) 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_civoid dump_usb_interface(struct usbip_usb_interface *uinf) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci char buff[100]; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci usbip_names_get_class(buff, sizeof(buff), 1048c2ecf20Sopenharmony_ci uinf->bInterfaceClass, 1058c2ecf20Sopenharmony_ci uinf->bInterfaceSubClass, 1068c2ecf20Sopenharmony_ci uinf->bInterfaceProtocol); 1078c2ecf20Sopenharmony_ci dbg("%-20s = %s", "Interface(C/SC/P)", buff); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_civoid dump_usb_device(struct usbip_usb_device *udev) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci char buff[100]; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci dbg("%-20s = %s", "path", udev->path); 1158c2ecf20Sopenharmony_ci dbg("%-20s = %s", "busid", udev->busid); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci usbip_names_get_class(buff, sizeof(buff), 1188c2ecf20Sopenharmony_ci udev->bDeviceClass, 1198c2ecf20Sopenharmony_ci udev->bDeviceSubClass, 1208c2ecf20Sopenharmony_ci udev->bDeviceProtocol); 1218c2ecf20Sopenharmony_ci dbg("%-20s = %s", "Device(C/SC/P)", buff); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci DBG_UDEV_INTEGER(bcdDevice); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci usbip_names_get_product(buff, sizeof(buff), 1268c2ecf20Sopenharmony_ci udev->idVendor, 1278c2ecf20Sopenharmony_ci udev->idProduct); 1288c2ecf20Sopenharmony_ci dbg("%-20s = %s", "Vendor/Product", buff); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci DBG_UDEV_INTEGER(bNumConfigurations); 1318c2ecf20Sopenharmony_ci DBG_UDEV_INTEGER(bNumInterfaces); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci dbg("%-20s = %s", "speed", 1348c2ecf20Sopenharmony_ci usbip_speed_string(udev->speed)); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci DBG_UDEV_INTEGER(busnum); 1378c2ecf20Sopenharmony_ci DBG_UDEV_INTEGER(devnum); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ciint read_attr_value(struct udev_device *dev, const char *name, 1428c2ecf20Sopenharmony_ci const char *format) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci const char *attr; 1458c2ecf20Sopenharmony_ci int num = 0; 1468c2ecf20Sopenharmony_ci int ret; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci attr = udev_device_get_sysattr_value(dev, name); 1498c2ecf20Sopenharmony_ci if (!attr) { 1508c2ecf20Sopenharmony_ci err("udev_device_get_sysattr_value failed"); 1518c2ecf20Sopenharmony_ci goto err; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* The client chooses the device configuration 1558c2ecf20Sopenharmony_ci * when attaching it so right after being bound 1568c2ecf20Sopenharmony_ci * to usbip-host on the server the device will 1578c2ecf20Sopenharmony_ci * have no configuration. 1588c2ecf20Sopenharmony_ci * Therefore, attributes such as bConfigurationValue 1598c2ecf20Sopenharmony_ci * and bNumInterfaces will not exist and sscanf will 1608c2ecf20Sopenharmony_ci * fail. Check for these cases and don't treat them 1618c2ecf20Sopenharmony_ci * as errors. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = sscanf(attr, format, &num); 1658c2ecf20Sopenharmony_ci if (ret < 1) { 1668c2ecf20Sopenharmony_ci if (strcmp(name, "bConfigurationValue") && 1678c2ecf20Sopenharmony_ci strcmp(name, "bNumInterfaces")) { 1688c2ecf20Sopenharmony_ci err("sscanf failed for attribute %s", name); 1698c2ecf20Sopenharmony_ci goto err; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cierr: 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return num; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciint read_attr_speed(struct udev_device *dev) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci const char *speed; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci speed = udev_device_get_sysattr_value(dev, "speed"); 1848c2ecf20Sopenharmony_ci if (!speed) { 1858c2ecf20Sopenharmony_ci err("udev_device_get_sysattr_value failed"); 1868c2ecf20Sopenharmony_ci goto err; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci for (int i = 0; speed_strings[i].speed != NULL; i++) { 1908c2ecf20Sopenharmony_ci if (!strcmp(speed, speed_strings[i].speed)) 1918c2ecf20Sopenharmony_ci return speed_strings[i].num; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cierr: 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return USB_SPEED_UNKNOWN; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci#define READ_ATTR(object, type, dev, name, format) \ 2008c2ecf20Sopenharmony_ci do { \ 2018c2ecf20Sopenharmony_ci (object)->name = (type) read_attr_value(dev, to_string(name), \ 2028c2ecf20Sopenharmony_ci format); \ 2038c2ecf20Sopenharmony_ci } while (0) 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciint read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci uint32_t busnum, devnum; 2098c2ecf20Sopenharmony_ci const char *path, *name; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, bDeviceClass, "%02x\n"); 2128c2ecf20Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, bDeviceSubClass, "%02x\n"); 2138c2ecf20Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, bDeviceProtocol, "%02x\n"); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci READ_ATTR(udev, uint16_t, sdev, idVendor, "%04x\n"); 2168c2ecf20Sopenharmony_ci READ_ATTR(udev, uint16_t, sdev, idProduct, "%04x\n"); 2178c2ecf20Sopenharmony_ci READ_ATTR(udev, uint16_t, sdev, bcdDevice, "%04x\n"); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, bConfigurationValue, "%02x\n"); 2208c2ecf20Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, bNumConfigurations, "%02x\n"); 2218c2ecf20Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, bNumInterfaces, "%02x\n"); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci READ_ATTR(udev, uint8_t, sdev, devnum, "%d\n"); 2248c2ecf20Sopenharmony_ci udev->speed = read_attr_speed(sdev); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci path = udev_device_get_syspath(sdev); 2278c2ecf20Sopenharmony_ci name = udev_device_get_sysname(sdev); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci strncpy(udev->path, path, SYSFS_PATH_MAX - 1); 2308c2ecf20Sopenharmony_ci udev->path[SYSFS_PATH_MAX - 1] = '\0'; 2318c2ecf20Sopenharmony_ci strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE - 1); 2328c2ecf20Sopenharmony_ci udev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0'; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci sscanf(name, "%u-%u", &busnum, &devnum); 2358c2ecf20Sopenharmony_ci udev->busnum = busnum; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ciint read_usb_interface(struct usbip_usb_device *udev, int i, 2418c2ecf20Sopenharmony_ci struct usbip_usb_interface *uinf) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci char busid[SYSFS_BUS_ID_SIZE]; 2448c2ecf20Sopenharmony_ci int size; 2458c2ecf20Sopenharmony_ci struct udev_device *sif; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci size = snprintf(busid, sizeof(busid), "%s:%d.%d", 2488c2ecf20Sopenharmony_ci udev->busid, udev->bConfigurationValue, i); 2498c2ecf20Sopenharmony_ci if (size < 0 || (unsigned int)size >= sizeof(busid)) { 2508c2ecf20Sopenharmony_ci err("busid length %i >= %lu or < 0", size, 2518c2ecf20Sopenharmony_ci (long unsigned)sizeof(busid)); 2528c2ecf20Sopenharmony_ci return -1; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci sif = udev_device_new_from_subsystem_sysname(udev_context, "usb", busid); 2568c2ecf20Sopenharmony_ci if (!sif) { 2578c2ecf20Sopenharmony_ci err("udev_device_new_from_subsystem_sysname %s failed", busid); 2588c2ecf20Sopenharmony_ci return -1; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci READ_ATTR(uinf, uint8_t, sif, bInterfaceClass, "%02x\n"); 2628c2ecf20Sopenharmony_ci READ_ATTR(uinf, uint8_t, sif, bInterfaceSubClass, "%02x\n"); 2638c2ecf20Sopenharmony_ci READ_ATTR(uinf, uint8_t, sif, bInterfaceProtocol, "%02x\n"); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ciint usbip_names_init(char *f) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci return names_init(f); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_civoid usbip_names_free(void) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci names_free(); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_civoid usbip_names_get_product(char *buff, size_t size, uint16_t vendor, 2798c2ecf20Sopenharmony_ci uint16_t product) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci const char *prod, *vend; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci prod = names_product(vendor, product); 2848c2ecf20Sopenharmony_ci if (!prod) 2858c2ecf20Sopenharmony_ci prod = "unknown product"; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci vend = names_vendor(vendor); 2898c2ecf20Sopenharmony_ci if (!vend) 2908c2ecf20Sopenharmony_ci vend = "unknown vendor"; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci snprintf(buff, size, "%s : %s (%04x:%04x)", vend, prod, vendor, product); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_civoid usbip_names_get_class(char *buff, size_t size, uint8_t class, 2968c2ecf20Sopenharmony_ci uint8_t subclass, uint8_t protocol) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci const char *c, *s, *p; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (class == 0 && subclass == 0 && protocol == 0) { 3018c2ecf20Sopenharmony_ci snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)", class, subclass, protocol); 3028c2ecf20Sopenharmony_ci return; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci p = names_protocol(class, subclass, protocol); 3068c2ecf20Sopenharmony_ci if (!p) 3078c2ecf20Sopenharmony_ci p = "unknown protocol"; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci s = names_subclass(class, subclass); 3108c2ecf20Sopenharmony_ci if (!s) 3118c2ecf20Sopenharmony_ci s = "unknown subclass"; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci c = names_class(class); 3148c2ecf20Sopenharmony_ci if (!c) 3158c2ecf20Sopenharmony_ci c = "unknown class"; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol); 3188c2ecf20Sopenharmony_ci} 319