[PATCH] tools: usb: usbip: adding support for older kernel versions

From: David Valleau
Date: Wed Mar 06 2019 - 16:47:38 EST


The current usbip tool relies on the behavior of the vhci-hcd driver in
order to work correctly. In instances where a newer version of the tool
is used with an older version of the kernel, there are incompatibilities
that can sometimes result in failure.

This patch adds some fallback capabilities so that the tool will work
with the vhci-hcd driver from kernel version 3.18 onward.

Signed-off-by: David Valleau <valleau@xxxxxxxxxxxx>
---

tools/usb/usbip/libsrc/vhci_driver.c | 246 ++++++++++++++++++++++++---
tools/usb/usbip/libsrc/vhci_driver.h | 7 +
2 files changed, 228 insertions(+), 25 deletions(-)

diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c
index ed8c9d360c0f..4e02cf6832a6 100644
--- a/tools/usb/usbip/libsrc/vhci_driver.c
+++ b/tools/usb/usbip/libsrc/vhci_driver.c
@@ -37,7 +37,136 @@ imported_device_init(struct usbip_imported_device *idev, char *busid)
return NULL;
}

-static int parse_status(const char *value)
+static void assign_idev(struct usbip_imported_device *idev, uint8_t port,
+ uint32_t status, uint32_t devid)
+{
+ idev->port = port;
+ idev->status = status;
+ idev->devid = devid;
+ idev->busnum = (devid >> 16);
+ idev->devnum = (devid & 0x0000ffff);
+}
+
+static int init_idev(struct usbip_imported_device *idev, const char *lbusid)
+{
+ if (idev->status != VDEV_ST_NULL &&
+ idev->status != VDEV_ST_NOTASSIGNED) {
+ idev = imported_device_init(idev, lbusid);
+ if (!idev) {
+ dbg("imported_device_init failed");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int parse_status_3_18(const char *value)
+{
+ int ret = 0;
+ char *c;
+
+ /* skip a header line */
+ c = strchr(value, '\n');
+ if (!c)
+ return -1;
+ c++;
+
+ while (*c != '\0') {
+ int port, status, speed, devid;
+ unsigned long socket;
+ char lbusid[SYSFS_BUS_ID_SIZE];
+ struct usbip_imported_device *idev;
+
+ ret = sscanf(c, "%d %d %d %x %lx %31s\n",
+ &port, &status, &speed,
+ &devid, &socket, lbusid);
+
+ if (ret < 5) {
+ dbg("sscanf failed: %d", ret);
+ return -1;
+ }
+
+ dbg("port %d status %d speed %d devid %x",
+ port, status, speed, devid);
+ dbg("socket %lx lbusid %s", socket, lbusid);
+
+ /* if a device is connected, look at it */
+ idev = &vhci_driver->idev[port];
+ memset(idev, 0, sizeof(*idev));
+
+ /* assume that the hub is high-speed */
+ idev->hub = HUB_SPEED_HIGH;
+ assign_idev(idev, port, status, devid);
+
+ if (init_idev(idev, lbusid))
+ return -1;
+
+ /* go to the next line */
+ c = strchr(c, '\n');
+ if (!c)
+ break;
+ c++;
+ }
+
+ dbg("exit");
+
+ return 0;
+}
+
+static int parse_status_4_4(const char *value)
+{
+ int ret = 0;
+ char *c;
+
+ /* skip a header line */
+ c = strchr(value, '\n');
+ if (!c)
+ return -1;
+ c++;
+
+ while (*c != '\0') {
+ int port, status, speed, devid;
+ int sockfd;
+ char lbusid[SYSFS_BUS_ID_SIZE];
+ struct usbip_imported_device *idev;
+
+ ret = sscanf(c, "%d %d %d %x %u %31s\n",
+ &port, &status, &speed,
+ &devid, &sockfd, lbusid);
+
+ if (ret < 5) {
+ dbg("sscanf failed: %d", ret);
+ return -1;
+ }
+
+ dbg("port %d status %d speed %d devid %x",
+ port, status, speed, devid);
+ dbg("sockfd %u lbusid %s", sockfd, lbusid);
+
+ /* if a device is connected, look at it */
+ idev = &vhci_driver->idev[port];
+ memset(idev, 0, sizeof(*idev));
+
+ /* assume that the hub is high-speed */
+ idev->hub = HUB_SPEED_HIGH;
+ assign_idev(idev, port, status, devid);
+
+ if (init_idev(idev, lbusid))
+ return -1;
+
+ /* go to the next line */
+ c = strchr(c, '\n');
+ if (!c)
+ break;
+ c++;
+ }
+
+ dbg("exit");
+
+ return 0;
+}
+
+static int parse_status_4_14(const char *value)
{
int ret = 0;
char *c;
@@ -61,7 +190,7 @@ static int parse_status(const char *value)

if (ret < 5) {
dbg("sscanf failed: %d", ret);
- BUG();
+ return -1;
}

dbg("hub %s port %d status %d speed %d devid %x",
@@ -77,22 +206,10 @@ static int parse_status(const char *value)
else /* strncmp("ss", hub, 2) == 0 */
idev->hub = HUB_SPEED_SUPER;

- idev->port = port;
- idev->status = status;
-
- idev->devid = devid;
+ assign_idev(idev, port, status, devid);

- idev->busnum = (devid >> 16);
- idev->devnum = (devid & 0x0000ffff);
-
- if (idev->status != VDEV_ST_NULL
- && idev->status != VDEV_ST_NOTASSIGNED) {
- idev = imported_device_init(idev, lbusid);
- if (!idev) {
- dbg("imported_device_init failed");
- return -1;
- }
- }
+ if (init_idev(idev, lbusid))
+ return -1;

/* go to the next line */
c = strchr(c, '\n');
@@ -106,6 +223,41 @@ static int parse_status(const char *value)
return 0;
}

+/*
+ * Parses the sysfs status contained within value to populate the idev fields
+ * within vhci_driver.
+ *
+ * Uses the format of the header line to determine which parsing function to
+ * call.
+ */
+static int parse_status(const char *value)
+{
+ char *c;
+ char header[STATUS_HEADER_MAX] = { 0 };
+ size_t size;
+ int status = -1;
+
+ c = strchr(value, '\n');
+ if (!c)
+ return -1;
+
+ size = c - value;
+ if (size > STATUS_HEADER_MAX)
+ return -1;
+ strncpy(header, value, size);
+
+ if (strncmp(header, V3_18_STATUS_HEADER, size) == 0)
+ return parse_status_3_18(value);
+ if (strncmp(header, V4_4_STATUS_HEADER, size) == 0)
+ return parse_status_4_4(value);
+ if (strncmp(header, V4_14_STATUS_HEADER, size) == 0)
+ return parse_status_4_14(value);
+
+ dbg("unknown header format in status");
+
+ return -1;
+}
+
#define MAX_STATUS_NAME 16

static int refresh_imported_device_list(void)
@@ -135,14 +287,43 @@ static int refresh_imported_device_list(void)
return 0;
}

+static int fallback_get_nports(struct udev_device *hc_device)
+{
+ char *c;
+ int nports = 0;
+ const char *attr_status;
+
+ attr_status = udev_device_get_sysattr_value(hc_device, "status");
+ if (!attr_status) {
+ err("udev_device_get_sysattr_value failed");
+ return -1;
+ }
+
+ /* skip a header line */
+ c = strchr(attr_status, '\n');
+ if (!c)
+ return 0;
+ c++;
+
+ while (*c != '\0') {
+ nports++;
+ /* go to the next line */
+ c = strchr(c, '\n');
+ if (!c)
+ return nports;
+ c++;
+ }
+
+ return nports;
+}
+
static int get_nports(struct udev_device *hc_device)
{
const char *attr_nports;

attr_nports = udev_device_get_sysattr_value(hc_device, "nports");
if (!attr_nports) {
- err("udev_device_get_sysattr_value nports failed");
- return -1;
+ return fallback_get_nports(hc_device);
}

return (int)strtoul(attr_nports, NULL, 10);
@@ -150,7 +331,8 @@ static int get_nports(struct udev_device *hc_device)

static int vhci_hcd_filter(const struct dirent *dirent)
{
- return strcmp(dirent->d_name, "vhci_hcd") >= 0;
+ return strncmp(dirent->d_name, USBIP_VHCI_DRV_NAME,
+ strlen(USBIP_VHCI_DRV_NAME)) == 0;
}

static int get_ncontrollers(void)
@@ -240,6 +422,24 @@ static int read_record(int rhport, char *host, unsigned long host_len,

/* ---------------------------------------------------------------------- */

+/*
+ * Attempt to open the vhci udev device. If the first lookup fails fall back to
+ * looking up the old name in order to support older kernel versions.
+ */
+static struct udev_device *open_hc_device()
+{
+ struct udev_device *device;
+
+ device = udev_device_new_from_subsystem_sysname(
+ udev_context, USBIP_VHCI_BUS_TYPE, USBIP_VHCI_DEVICE_NAME);
+ if (!device) {
+ device = udev_device_new_from_subsystem_sysname(
+ udev_context, USBIP_VHCI_BUS_TYPE,
+ USBIP_VHCI_DEVICE_NAME_OLD);
+ }
+ return device;
+}
+
int usbip_vhci_driver_open(void)
{
int nports;
@@ -252,10 +452,7 @@ int usbip_vhci_driver_open(void)
}

/* will be freed in usbip_driver_close() */
- hc_device =
- udev_device_new_from_subsystem_sysname(udev_context,
- USBIP_VHCI_BUS_TYPE,
- USBIP_VHCI_DEVICE_NAME);
+ hc_device = open_hc_device();
if (!hc_device) {
err("udev_device_new_from_subsystem_sysname failed");
goto err;
@@ -335,7 +532,6 @@ int usbip_vhci_refresh_device_list(void)
int usbip_vhci_get_free_port(uint32_t speed)
{
for (int i = 0; i < vhci_driver->nports; i++) {
-
switch (speed) {
case USB_SPEED_SUPER:
if (vhci_driver->idev[i].hub != HUB_SPEED_SUPER)
diff --git a/tools/usb/usbip/libsrc/vhci_driver.h b/tools/usb/usbip/libsrc/vhci_driver.h
index 6c9aca216705..a18f4d243c7a 100644
--- a/tools/usb/usbip/libsrc/vhci_driver.h
+++ b/tools/usb/usbip/libsrc/vhci_driver.h
@@ -13,6 +13,13 @@

#define USBIP_VHCI_BUS_TYPE "platform"
#define USBIP_VHCI_DEVICE_NAME "vhci_hcd.0"
+#define USBIP_VHCI_DEVICE_NAME_OLD "vhci_hcd"
+
+#define V3_18_STATUS_HEADER "prt sta spd bus dev socket local_busid"
+#define V4_4_STATUS_HEADER "prt sta spd dev sockfd local_busid"
+#define V4_14_STATUS_HEADER "hub port sta spd dev sockfd local_busid"
+
+#define STATUS_HEADER_MAX 64

enum hub_speed {
HUB_SPEED_HIGH = 0,
--
2.21.0.352.gf09ad66450-goog