| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2005-2007 Takahiro Hirofuchi |
| */ |
| |
| #include <libudev.h> |
| #include "usbip_common.h" |
| #include "names.h" |
| |
| #undef PROGNAME |
| #define PROGNAME "libusbip" |
| |
| int usbip_use_syslog; |
| int usbip_use_stderr; |
| int usbip_use_debug; |
| |
| extern struct udev *udev_context; |
| |
| struct speed_string { |
| int num; |
| char *speed; |
| char *desc; |
| }; |
| |
| static const struct speed_string speed_strings[] = { |
| { USB_SPEED_UNKNOWN, "unknown", "Unknown Speed"}, |
| { USB_SPEED_LOW, "1.5", "Low Speed(1.5Mbps)" }, |
| { USB_SPEED_FULL, "12", "Full Speed(12Mbps)" }, |
| { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" }, |
| { USB_SPEED_WIRELESS, "53.3-480", "Wireless"}, |
| { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" }, |
| { 0, NULL, NULL } |
| }; |
| |
| struct portst_string { |
| int num; |
| char *desc; |
| }; |
| |
| static struct portst_string portst_strings[] = { |
| { SDEV_ST_AVAILABLE, "Device Available" }, |
| { SDEV_ST_USED, "Device in Use" }, |
| { SDEV_ST_ERROR, "Device Error"}, |
| { VDEV_ST_NULL, "Port Available"}, |
| { VDEV_ST_NOTASSIGNED, "Port Initializing"}, |
| { VDEV_ST_USED, "Port in Use"}, |
| { VDEV_ST_ERROR, "Port Error"}, |
| { 0, NULL} |
| }; |
| |
| const char *usbip_status_string(int32_t status) |
| { |
| for (int i = 0; portst_strings[i].desc != NULL; i++) |
| if (portst_strings[i].num == status) |
| return portst_strings[i].desc; |
| |
| return "Unknown Status"; |
| } |
| |
| const char *usbip_speed_string(int num) |
| { |
| for (int i = 0; speed_strings[i].speed != NULL; i++) |
| if (speed_strings[i].num == num) |
| return speed_strings[i].desc; |
| |
| return "Unknown Speed"; |
| } |
| |
| struct op_common_status_string { |
| int num; |
| char *desc; |
| }; |
| |
| static struct op_common_status_string op_common_status_strings[] = { |
| { ST_OK, "Request Completed Successfully" }, |
| { ST_NA, "Request Failed" }, |
| { ST_DEV_BUSY, "Device busy (exported)" }, |
| { ST_DEV_ERR, "Device in error state" }, |
| { ST_NODEV, "Device not found" }, |
| { ST_ERROR, "Unexpected response" }, |
| { 0, NULL} |
| }; |
| |
| const char *usbip_op_common_status_string(int status) |
| { |
| for (int i = 0; op_common_status_strings[i].desc != NULL; i++) |
| if (op_common_status_strings[i].num == status) |
| return op_common_status_strings[i].desc; |
| |
| return "Unknown Op Common Status"; |
| } |
| |
| #define DBG_UDEV_INTEGER(name)\ |
| dbg("%-20s = %x", to_string(name), (int) udev->name) |
| |
| #define DBG_UINF_INTEGER(name)\ |
| dbg("%-20s = %x", to_string(name), (int) uinf->name) |
| |
| void dump_usb_interface(struct usbip_usb_interface *uinf) |
| { |
| char buff[100]; |
| |
| usbip_names_get_class(buff, sizeof(buff), |
| uinf->bInterfaceClass, |
| uinf->bInterfaceSubClass, |
| uinf->bInterfaceProtocol); |
| dbg("%-20s = %s", "Interface(C/SC/P)", buff); |
| } |
| |
| void dump_usb_device(struct usbip_usb_device *udev) |
| { |
| char buff[100]; |
| |
| dbg("%-20s = %s", "path", udev->path); |
| dbg("%-20s = %s", "busid", udev->busid); |
| |
| usbip_names_get_class(buff, sizeof(buff), |
| udev->bDeviceClass, |
| udev->bDeviceSubClass, |
| udev->bDeviceProtocol); |
| dbg("%-20s = %s", "Device(C/SC/P)", buff); |
| |
| DBG_UDEV_INTEGER(bcdDevice); |
| |
| usbip_names_get_product(buff, sizeof(buff), |
| udev->idVendor, |
| udev->idProduct); |
| dbg("%-20s = %s", "Vendor/Product", buff); |
| |
| DBG_UDEV_INTEGER(bNumConfigurations); |
| DBG_UDEV_INTEGER(bNumInterfaces); |
| |
| dbg("%-20s = %s", "speed", |
| usbip_speed_string(udev->speed)); |
| |
| DBG_UDEV_INTEGER(busnum); |
| DBG_UDEV_INTEGER(devnum); |
| } |
| |
| |
| int read_attr_value(struct udev_device *dev, const char *name, |
| const char *format) |
| { |
| const char *attr; |
| int num = 0; |
| int ret; |
| |
| attr = udev_device_get_sysattr_value(dev, name); |
| if (!attr) { |
| err("udev_device_get_sysattr_value failed"); |
| goto err; |
| } |
| |
| /* The client chooses the device configuration |
| * when attaching it so right after being bound |
| * to usbip-host on the server the device will |
| * have no configuration. |
| * Therefore, attributes such as bConfigurationValue |
| * and bNumInterfaces will not exist and sscanf will |
| * fail. Check for these cases and don't treat them |
| * as errors. |
| */ |
| |
| ret = sscanf(attr, format, &num); |
| if (ret < 1) { |
| if (strcmp(name, "bConfigurationValue") && |
| strcmp(name, "bNumInterfaces")) { |
| err("sscanf failed for attribute %s", name); |
| goto err; |
| } |
| } |
| |
| err: |
| |
| return num; |
| } |
| |
| |
| int read_attr_speed(struct udev_device *dev) |
| { |
| const char *speed; |
| |
| speed = udev_device_get_sysattr_value(dev, "speed"); |
| if (!speed) { |
| err("udev_device_get_sysattr_value failed"); |
| goto err; |
| } |
| |
| for (int i = 0; speed_strings[i].speed != NULL; i++) { |
| if (!strcmp(speed, speed_strings[i].speed)) |
| return speed_strings[i].num; |
| } |
| |
| err: |
| |
| return USB_SPEED_UNKNOWN; |
| } |
| |
| #define READ_ATTR(object, type, dev, name, format) \ |
| do { \ |
| (object)->name = (type) read_attr_value(dev, to_string(name), \ |
| format); \ |
| } while (0) |
| |
| |
| int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev) |
| { |
| uint32_t busnum, devnum; |
| const char *path, *name; |
| |
| READ_ATTR(udev, uint8_t, sdev, bDeviceClass, "%02x\n"); |
| READ_ATTR(udev, uint8_t, sdev, bDeviceSubClass, "%02x\n"); |
| READ_ATTR(udev, uint8_t, sdev, bDeviceProtocol, "%02x\n"); |
| |
| READ_ATTR(udev, uint16_t, sdev, idVendor, "%04x\n"); |
| READ_ATTR(udev, uint16_t, sdev, idProduct, "%04x\n"); |
| READ_ATTR(udev, uint16_t, sdev, bcdDevice, "%04x\n"); |
| |
| READ_ATTR(udev, uint8_t, sdev, bConfigurationValue, "%02x\n"); |
| READ_ATTR(udev, uint8_t, sdev, bNumConfigurations, "%02x\n"); |
| READ_ATTR(udev, uint8_t, sdev, bNumInterfaces, "%02x\n"); |
| |
| READ_ATTR(udev, uint8_t, sdev, devnum, "%d\n"); |
| udev->speed = read_attr_speed(sdev); |
| |
| path = udev_device_get_syspath(sdev); |
| name = udev_device_get_sysname(sdev); |
| |
| strncpy(udev->path, path, SYSFS_PATH_MAX); |
| strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE); |
| |
| sscanf(name, "%u-%u", &busnum, &devnum); |
| udev->busnum = busnum; |
| |
| return 0; |
| } |
| |
| int read_usb_interface(struct usbip_usb_device *udev, int i, |
| struct usbip_usb_interface *uinf) |
| { |
| char busid[SYSFS_BUS_ID_SIZE]; |
| int size; |
| struct udev_device *sif; |
| |
| size = snprintf(busid, sizeof(busid), "%s:%d.%d", |
| udev->busid, udev->bConfigurationValue, i); |
| if (size < 0 || (unsigned int)size >= sizeof(busid)) { |
| err("busid length %i >= %lu or < 0", size, |
| (long unsigned)sizeof(busid)); |
| return -1; |
| } |
| |
| sif = udev_device_new_from_subsystem_sysname(udev_context, "usb", busid); |
| if (!sif) { |
| err("udev_device_new_from_subsystem_sysname %s failed", busid); |
| return -1; |
| } |
| |
| READ_ATTR(uinf, uint8_t, sif, bInterfaceClass, "%02x\n"); |
| READ_ATTR(uinf, uint8_t, sif, bInterfaceSubClass, "%02x\n"); |
| READ_ATTR(uinf, uint8_t, sif, bInterfaceProtocol, "%02x\n"); |
| |
| return 0; |
| } |
| |
| int usbip_names_init(char *f) |
| { |
| return names_init(f); |
| } |
| |
| void usbip_names_free(void) |
| { |
| names_free(); |
| } |
| |
| void usbip_names_get_product(char *buff, size_t size, uint16_t vendor, |
| uint16_t product) |
| { |
| const char *prod, *vend; |
| |
| prod = names_product(vendor, product); |
| if (!prod) |
| prod = "unknown product"; |
| |
| |
| vend = names_vendor(vendor); |
| if (!vend) |
| vend = "unknown vendor"; |
| |
| snprintf(buff, size, "%s : %s (%04x:%04x)", vend, prod, vendor, product); |
| } |
| |
| void usbip_names_get_class(char *buff, size_t size, uint8_t class, |
| uint8_t subclass, uint8_t protocol) |
| { |
| const char *c, *s, *p; |
| |
| if (class == 0 && subclass == 0 && protocol == 0) { |
| snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)", class, subclass, protocol); |
| return; |
| } |
| |
| p = names_protocol(class, subclass, protocol); |
| if (!p) |
| p = "unknown protocol"; |
| |
| s = names_subclass(class, subclass); |
| if (!s) |
| s = "unknown subclass"; |
| |
| c = names_class(class); |
| if (!c) |
| c = "unknown class"; |
| |
| snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol); |
| } |