mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
318 lines
7.5 KiB
318 lines
7.5 KiB
// 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 - 1); |
|
udev->path[SYSFS_PATH_MAX - 1] = '\0'; |
|
strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE - 1); |
|
udev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0'; |
|
|
|
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); |
|
}
|
|
|