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