[PATCH v3 1/2] usbip: vhci number of ports extension

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch extends number of ports limitation in application (vhci) 
side.

To do so, vhci driver supports multiple host controllers. The number of 
controllers can be specified as a module parameter 'num_controllers'. 
The default is 1.

ex) # insmod vhci_hcd.ko num_controllers=4

Also, ports per controller is changed from 8 to USB_MAXCHILDREN (31). 
It can be modified with VHCI_NPORTS flag at module compilation.

So number of ports supported by vhci is 'num_controllers' * 31.

Sysfs structure is changes as following.
BEFORE:
    /sys/devices/platform
        +-- vhci
            +-- status
            +-- attach
            +-- detach
            +-- usbip_debug
AFTER: example for num_controllers=4
    /sys/devices/platform
        +-- vhci
        |   +-- nports
        |   +-- status
        |   +-- status.1
        |   +-- status.2
        |   +-- status.3
        |   +-- attach
        |   +-- detach
        |   +-- usbip_debug
        +-- vhci.1
        +-- vhci.2
        +-- vhci.3
        
vhci[.N] is shown for each host controller kobj. vhch.1, vhci.2, ... 
are shown only when num_controllers is more than 1. Only vhci has user 
space interfaces. 'nports' is newly added to give ports-per-controller 
and number of controlles. Before that, number of ports is acquired by 
counting status lines. Status is divided for each controller to avoid 
page size (4KB) limitation.

Variable wording relating port has been corrected. 'port' represents id 
across multiple controllers. 'rhport (root hub port)' represents id 
within a controller.

Some unimportant info level messages are changed to debug level because 
they are too busy when using many ports.

Following bugs are fixed with this patch

1) The last port is ignored
tools/usb/usbip/libsrc/vhci_device.s: get_nports() ignores the last 
status line because udev_device_get_sysattr_value() drops last new 
line. New version uses nports attribute so it's doesn't have this 
problem. 

2) Status header and content inconsistency
4th and 5th column are
header:          "dev bus"
content(unused): "000 000"
content(used):   "%08x", devid
Only 1st and 2nd column are used by program. In old version, sscanf() 
in parse_status expect no bus column. And bus_id string is shown in the 
last column. Then bus in header is removed and unused content is 
replaced with 8 zeros. The sscanf() expects more than 5 columns and new 
has 6 columns. These no compatibility issue for this.

NOTE: Syslog error messages "systemd-udevd[390]: error opening USB 
device 'descriptors' file" may be shown. They are not caused by this 
patch. It seems to be a systemd problem.

Signed-off-by: Nobuo Iwata <nobuo.iwata@xxxxxxxxxxxxxxx>
---
 drivers/usb/usbip/README             |   3 +
 drivers/usb/usbip/vhci.h             |  42 ++-
 drivers/usb/usbip/vhci_hcd.c         | 268 ++++++++-----
 drivers/usb/usbip/vhci_rx.c          |  21 +-
 drivers/usb/usbip/vhci_sysfs.c       | 294 +++++++++++----
 tools/usb/usbip/libsrc/vhci_driver.c | 541 ++++++++++++++-------------
 tools/usb/usbip/libsrc/vhci_driver.h |  38 +-
 tools/usb/usbip/src/usbip_attach.c   |   8 +-
 tools/usb/usbip/src/usbip_port.c     |  13 +-
 tools/usb/usbip/src/usbipd_app.c     |  49 ++-
 10 files changed, 775 insertions(+), 502 deletions(-)

diff --git a/drivers/usb/usbip/README b/drivers/usb/usbip/README
index 41a2cf2..fce3d7f 100644
--- a/drivers/usb/usbip/README
+++ b/drivers/usb/usbip/README
@@ -1,3 +1,6 @@
+MODULE PARAMS:
+	- num_controllers : number of controllers. Default is 1.
+
 TODO:
 	- more discussion about the protocol
 	- testing
diff --git a/drivers/usb/usbip/vhci.h b/drivers/usb/usbip/vhci.h
index a863a98..894c2da 100644
--- a/drivers/usb/usbip/vhci.h
+++ b/drivers/usb/usbip/vhci.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -72,7 +73,11 @@ struct vhci_unlink {
 };
 
 /* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */
-#define VHCI_NPORTS 8
+#ifndef VHCI_NPORTS
+#define VHCI_NPORTS USB_MAXCHILDREN
+#endif
+
+#define MAX_STATUS_NAME 16
 
 /* for usb_bus.hcpriv */
 struct vhci_hcd {
@@ -93,11 +98,16 @@ struct vhci_hcd {
 	struct vhci_device vdev[VHCI_NPORTS];
 };
 
-extern struct vhci_hcd *the_controller;
-extern const struct attribute_group dev_attr_group;
+extern int num_controllers;
+extern struct platform_device **the_pdevs;
+extern struct attribute_group dev_attr_group;
 
 /* vhci_hcd.c */
-void rh_port_connect(int rhport, enum usb_device_speed speed);
+void rh_port_connect(struct vhci_device *vdev, enum usb_device_speed speed);
+
+/* vhci_sysfs.c */
+int vhci_init_attr_group(void);
+void vhci_finish_attr_group(void);
 
 /* vhci_rx.c */
 struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum);
@@ -106,9 +116,14 @@ int vhci_rx_loop(void *data);
 /* vhci_tx.c */
 int vhci_tx_loop(void *data);
 
-static inline struct vhci_device *port_to_vdev(__u32 port)
+static inline __u32 port_to_rhport(__u32 port)
+{
+	return port % VHCI_NPORTS;
+}
+
+static inline int port_to_pdev_nr(__u32 port)
 {
-	return &the_controller->vdev[port];
+	return port / VHCI_NPORTS;
 }
 
 static inline struct vhci_hcd *hcd_to_vhci(struct usb_hcd *hcd)
@@ -116,14 +131,25 @@ static inline struct vhci_hcd *hcd_to_vhci(struct usb_hcd *hcd)
 	return (struct vhci_hcd *) (hcd->hcd_priv);
 }
 
+static inline struct device *hcd_dev(struct usb_hcd *hcd)
+{
+	return (hcd)->self.controller;
+}
+
+static inline const char *hcd_name(struct usb_hcd *hcd)
+{
+	return (hcd)->self.bus_name;
+}
+
 static inline struct usb_hcd *vhci_to_hcd(struct vhci_hcd *vhci)
 {
 	return container_of((void *) vhci, struct usb_hcd, hcd_priv);
 }
 
-static inline struct device *vhci_dev(struct vhci_hcd *vhci)
+static inline struct vhci_hcd *vdev_to_vhci(struct vhci_device *vdev)
 {
-	return vhci_to_hcd(vhci)->self.controller;
+	return container_of(
+			(void *)(vdev - vdev->rhport), struct vhci_hcd, vdev);
 }
 
 #endif /* __USBIP_VHCI_H */
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index fca5110..bd2fa88 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015-2016 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -56,7 +57,10 @@ static int vhci_get_frame_number(struct usb_hcd *hcd);
 static const char driver_name[] = "vhci_hcd";
 static const char driver_desc[] = "USB/IP Virtual Host Controller";
 
-struct vhci_hcd *the_controller;
+int num_controllers = 1;
+module_param(num_controllers, int, S_IRUGO);
+
+struct platform_device **the_pdevs;
 
 static const char * const bit_desc[] = {
 	"CONNECTION",		/*0*/
@@ -119,47 +123,59 @@ static void dump_port_status_diff(u32 prev_status, u32 new_status)
 	pr_debug("\n");
 }
 
-void rh_port_connect(int rhport, enum usb_device_speed speed)
+void rh_port_connect(struct vhci_device *vdev, enum usb_device_speed speed)
 {
+	struct vhci_hcd	*vhci = vdev_to_vhci(vdev);
+	int		rhport = vdev->rhport;
+	u32		status;
 	unsigned long	flags;
 
 	usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport);
 
-	spin_lock_irqsave(&the_controller->lock, flags);
+	spin_lock_irqsave(&vhci->lock, flags);
+
+	status = vhci->port_status[rhport];
 
-	the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION
-		| (1 << USB_PORT_FEAT_C_CONNECTION);
+	status |= USB_PORT_STAT_CONNECTION | (1 << USB_PORT_FEAT_C_CONNECTION);
 
 	switch (speed) {
 	case USB_SPEED_HIGH:
-		the_controller->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED;
+		status |= USB_PORT_STAT_HIGH_SPEED;
 		break;
 	case USB_SPEED_LOW:
-		the_controller->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED;
+		status |= USB_PORT_STAT_LOW_SPEED;
 		break;
 	default:
 		break;
 	}
 
-	spin_unlock_irqrestore(&the_controller->lock, flags);
+	vhci->port_status[rhport] = status;
+
+	spin_unlock_irqrestore(&vhci->lock, flags);
 
-	usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
+	usb_hcd_poll_rh_status(vhci_to_hcd(vhci));
 }
 
-static void rh_port_disconnect(int rhport)
+static void rh_port_disconnect(struct vhci_device *vdev)
 {
+	struct vhci_hcd	*vhci = vdev_to_vhci(vdev);
+	int		rhport = vdev->rhport;
+	u32		status;
 	unsigned long	flags;
 
 	usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport);
 
-	spin_lock_irqsave(&the_controller->lock, flags);
+	spin_lock_irqsave(&vhci->lock, flags);
+
+	status = vhci->port_status[rhport];
+
+	status &= ~USB_PORT_STAT_CONNECTION;
+	status |= (1 << USB_PORT_FEAT_C_CONNECTION);
 
-	the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION;
-	the_controller->port_status[rhport] |=
-					(1 << USB_PORT_FEAT_C_CONNECTION);
+	vhci->port_status[rhport] = status;
 
-	spin_unlock_irqrestore(&the_controller->lock, flags);
-	usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
+	spin_unlock_irqrestore(&vhci->lock, flags);
+	usb_hcd_poll_rh_status(vhci_to_hcd(vhci));
 }
 
 #define PORT_C_MASK				\
@@ -416,14 +432,27 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
 static struct vhci_device *get_vdev(struct usb_device *udev)
 {
-	int i;
+	struct platform_device *pdev;
+	struct usb_hcd *hcd;
+	struct vhci_hcd *vhci;
+	int pdev_nr, rhport;
 
 	if (!udev)
 		return NULL;
 
-	for (i = 0; i < VHCI_NPORTS; i++)
-		if (the_controller->vdev[i].udev == udev)
-			return port_to_vdev(i);
+	for (pdev_nr = 0; pdev_nr < num_controllers; pdev_nr++) {
+		pdev = *(the_pdevs + pdev_nr);
+		if (pdev == NULL)
+			continue;
+		hcd = platform_get_drvdata(pdev);
+		if (hcd == NULL)
+			continue;
+		vhci = hcd_to_vhci(hcd);
+		for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
+			if (vhci->vdev[rhport].udev == udev)
+				return &vhci->vdev[rhport];
+		}
+	}
 
 	return NULL;
 }
@@ -432,6 +461,7 @@ static void vhci_tx_urb(struct urb *urb)
 {
 	struct vhci_device *vdev = get_vdev(urb->dev);
 	struct vhci_priv *priv;
+	struct vhci_hcd *vhci = vdev_to_vhci(vdev);
 	unsigned long flags;
 
 	if (!vdev) {
@@ -447,7 +477,7 @@ static void vhci_tx_urb(struct urb *urb)
 
 	spin_lock_irqsave(&vdev->priv_lock, flags);
 
-	priv->seqnum = atomic_inc_return(&the_controller->seqnum);
+	priv->seqnum = atomic_inc_return(&vhci->seqnum);
 	if (priv->seqnum == 0xffff)
 		dev_info(&urb->dev->dev, "seqnum max\n");
 
@@ -465,7 +495,9 @@ static void vhci_tx_urb(struct urb *urb)
 static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
 			    gfp_t mem_flags)
 {
+	struct vhci_hcd *vhci = hcd_to_vhci(hcd);
 	struct device *dev = &urb->dev->dev;
+	u8 portnum = urb->dev->portnum;
 	int ret = 0;
 	struct vhci_device *vdev;
 	unsigned long flags;
@@ -473,26 +505,30 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
 	usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n",
 			  hcd, urb, mem_flags);
 
+	if (portnum > VHCI_NPORTS) {
+		pr_err("invalid port number %d\n", portnum);
+		return -ENODEV;
+	}
+	vdev = &vhci->vdev[portnum-1];
+
 	/* patch to usb_sg_init() is in 2.5.60 */
 	BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length);
 
-	spin_lock_irqsave(&the_controller->lock, flags);
+	spin_lock_irqsave(&vhci->lock, flags);
 
 	if (urb->status != -EINPROGRESS) {
 		dev_err(dev, "URB already unlinked!, status %d\n", urb->status);
-		spin_unlock_irqrestore(&the_controller->lock, flags);
+		spin_unlock_irqrestore(&vhci->lock, flags);
 		return urb->status;
 	}
 
-	vdev = port_to_vdev(urb->dev->portnum-1);
-
 	/* refuse enqueue for dead connection */
 	spin_lock(&vdev->ud.lock);
 	if (vdev->ud.status == VDEV_ST_NULL ||
 	    vdev->ud.status == VDEV_ST_ERROR) {
 		dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport);
 		spin_unlock(&vdev->ud.lock);
-		spin_unlock_irqrestore(&the_controller->lock, flags);
+		spin_unlock_irqrestore(&vhci->lock, flags);
 		return -ENODEV;
 	}
 	spin_unlock(&vdev->ud.lock);
@@ -565,17 +601,16 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
 
 out:
 	vhci_tx_urb(urb);
-	spin_unlock_irqrestore(&the_controller->lock, flags);
+	spin_unlock_irqrestore(&vhci->lock, flags);
 
 	return 0;
 
 no_need_xmit:
 	usb_hcd_unlink_urb_from_ep(hcd, urb);
 no_need_unlink:
-	spin_unlock_irqrestore(&the_controller->lock, flags);
+	spin_unlock_irqrestore(&vhci->lock, flags);
 	if (!ret)
-		usb_hcd_giveback_urb(vhci_to_hcd(the_controller),
-				     urb, urb->status);
+		usb_hcd_giveback_urb(hcd, urb, urb->status);
 	return ret;
 }
 
@@ -627,19 +662,20 @@ no_need_unlink:
  */
 static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 {
+	struct vhci_hcd *vhci = hcd_to_vhci(hcd);
 	struct vhci_priv *priv;
 	struct vhci_device *vdev;
 	unsigned long flags;
 
 	pr_info("dequeue a urb %p\n", urb);
 
-	spin_lock_irqsave(&the_controller->lock, flags);
+	spin_lock_irqsave(&vhci->lock, flags);
 
 	priv = urb->hcpriv;
 	if (!priv) {
 		/* URB was never linked! or will be soon given back by
 		 * vhci_rx. */
-		spin_unlock_irqrestore(&the_controller->lock, flags);
+		spin_unlock_irqrestore(&vhci->lock, flags);
 		return -EIDRM;
 	}
 
@@ -648,7 +684,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 
 		ret = usb_hcd_check_unlink_urb(hcd, urb, status);
 		if (ret) {
-			spin_unlock_irqrestore(&the_controller->lock, flags);
+			spin_unlock_irqrestore(&vhci->lock, flags);
 			return ret;
 		}
 	}
@@ -676,10 +712,9 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 
 		usb_hcd_unlink_urb_from_ep(hcd, urb);
 
-		spin_unlock_irqrestore(&the_controller->lock, flags);
-		usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
-				     urb->status);
-		spin_lock_irqsave(&the_controller->lock, flags);
+		spin_unlock_irqrestore(&vhci->lock, flags);
+		usb_hcd_giveback_urb(vhci_to_hcd(vhci), urb, urb->status);
+		spin_lock_irqsave(&vhci->lock, flags);
 
 	} else {
 		/* tcp connection is alive */
@@ -691,12 +726,12 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 		unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC);
 		if (!unlink) {
 			spin_unlock(&vdev->priv_lock);
-			spin_unlock_irqrestore(&the_controller->lock, flags);
+			spin_unlock_irqrestore(&vhci->lock, flags);
 			usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC);
 			return -ENOMEM;
 		}
 
-		unlink->seqnum = atomic_inc_return(&the_controller->seqnum);
+		unlink->seqnum = atomic_inc_return(&vhci->seqnum);
 		if (unlink->seqnum == 0xffff)
 			pr_info("seqnum max\n");
 
@@ -712,7 +747,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 		spin_unlock(&vdev->priv_lock);
 	}
 
-	spin_unlock_irqrestore(&the_controller->lock, flags);
+	spin_unlock_irqrestore(&vhci->lock, flags);
 
 	usbip_dbg_vhci_hc("leave\n");
 	return 0;
@@ -720,10 +755,12 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 
 static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
 {
+	struct vhci_hcd *vhci = vdev_to_vhci(vdev);
+	struct usb_hcd *hcd = vhci_to_hcd(vhci);
 	struct vhci_unlink *unlink, *tmp;
 	unsigned long flags;
 
-	spin_lock_irqsave(&the_controller->lock, flags);
+	spin_lock_irqsave(&vhci->lock, flags);
 	spin_lock(&vdev->priv_lock);
 
 	list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
@@ -752,24 +789,23 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
 
 		urb->status = -ENODEV;
 
-		usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
+		usb_hcd_unlink_urb_from_ep(hcd, urb);
 
 		list_del(&unlink->list);
 
 		spin_unlock(&vdev->priv_lock);
-		spin_unlock_irqrestore(&the_controller->lock, flags);
+		spin_unlock_irqrestore(&vhci->lock, flags);
 
-		usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
-				     urb->status);
+		usb_hcd_giveback_urb(hcd, urb, urb->status);
 
-		spin_lock_irqsave(&the_controller->lock, flags);
+		spin_lock_irqsave(&vhci->lock, flags);
 		spin_lock(&vdev->priv_lock);
 
 		kfree(unlink);
 	}
 
 	spin_unlock(&vdev->priv_lock);
-	spin_unlock_irqrestore(&the_controller->lock, flags);
+	spin_unlock_irqrestore(&vhci->lock, flags);
 }
 
 /*
@@ -827,7 +863,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
 	 * is actually given back by vhci_rx after receiving its return pdu.
 	 *
 	 */
-	rh_port_disconnect(vdev->rhport);
+	rh_port_disconnect(vdev);
 
 	pr_info("disconnect device\n");
 }
@@ -866,7 +902,7 @@ static void vhci_device_unusable(struct usbip_device *ud)
 
 static void vhci_device_init(struct vhci_device *vdev)
 {
-	memset(vdev, 0, sizeof(*vdev));
+	memset(vdev, 0, sizeof(struct vhci_device));
 
 	vdev->ud.side   = USBIP_VHCI;
 	vdev->ud.status = VDEV_ST_NULL;
@@ -887,10 +923,27 @@ static void vhci_device_init(struct vhci_device *vdev)
 	usbip_start_eh(&vdev->ud);
 }
 
+static int hcd_name_to_id(const char *name)
+{
+	char *c;
+	long val;
+	int ret;
+
+	c = strchr(name, '.');
+	if (c == NULL)
+		return 0;
+
+	ret = kstrtol(c+1, 10, &val);
+	if (ret < 0)
+		return ret;
+
+	return val;
+}
+
 static int vhci_start(struct usb_hcd *hcd)
 {
 	struct vhci_hcd *vhci = hcd_to_vhci(hcd);
-	int rhport;
+	int id, rhport;
 	int err = 0;
 
 	usbip_dbg_vhci_hc("enter vhci_start\n");
@@ -910,11 +963,26 @@ static int vhci_start(struct usb_hcd *hcd)
 	hcd->power_budget = 0; /* no limit */
 	hcd->uses_new_polling = 1;
 
+	id = hcd_name_to_id(hcd_name(hcd));
+	if (id < 0) {
+		pr_err("invalid vhci name %s\n", hcd_name(hcd));
+		return -EINVAL;
+	}
+
 	/* vhci_hcd is now ready to be controlled through sysfs */
-	err = sysfs_create_group(&vhci_dev(vhci)->kobj, &dev_attr_group);
-	if (err) {
-		pr_err("create sysfs files\n");
-		return err;
+	if (id == 0) {
+		err = vhci_init_attr_group();
+		if (err) {
+			pr_err("init attr group\n");
+			return err;
+		}
+		err = sysfs_create_group(&hcd_dev(hcd)->kobj, &dev_attr_group);
+		if (err) {
+			pr_err("create sysfs files\n");
+			vhci_finish_attr_group();
+			return err;
+		}
+		pr_info("created sysfs %s\n", hcd_name(hcd));
 	}
 
 	return 0;
@@ -923,12 +991,16 @@ static int vhci_start(struct usb_hcd *hcd)
 static void vhci_stop(struct usb_hcd *hcd)
 {
 	struct vhci_hcd *vhci = hcd_to_vhci(hcd);
-	int rhport = 0;
+	int id, rhport;
 
 	usbip_dbg_vhci_hc("stop VHCI controller\n");
 
 	/* 1. remove the userland interface of vhci_hcd */
-	sysfs_remove_group(&vhci_dev(vhci)->kobj, &dev_attr_group);
+	id = hcd_name_to_id(hcd_name(hcd));
+	if (id == 0) {
+		sysfs_remove_group(&hcd_dev(hcd)->kobj, &dev_attr_group);
+		vhci_finish_attr_group();
+	}
 
 	/* 2. shutdown all the ports of vhci_hcd */
 	for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
@@ -1025,9 +1097,6 @@ static int vhci_hcd_probe(struct platform_device *pdev)
 	}
 	hcd->has_tt = 1;
 
-	/* this is private data for vhci_hcd */
-	the_controller = hcd_to_vhci(hcd);
-
 	/*
 	 * Finish generic HCD structure initialization and register.
 	 * Call the driver's reset() and start() routines.
@@ -1036,7 +1105,6 @@ static int vhci_hcd_probe(struct platform_device *pdev)
 	if (ret != 0) {
 		pr_err("usb_add_hcd failed %d\n", ret);
 		usb_put_hcd(hcd);
-		the_controller = NULL;
 		return ret;
 	}
 
@@ -1059,7 +1127,6 @@ static int vhci_hcd_remove(struct platform_device *pdev)
 	 */
 	usb_remove_hcd(hcd);
 	usb_put_hcd(hcd);
-	the_controller = NULL;
 
 	return 0;
 }
@@ -1070,21 +1137,24 @@ static int vhci_hcd_remove(struct platform_device *pdev)
 static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct usb_hcd *hcd;
-	int rhport = 0;
+	struct vhci_hcd *vhci;
+	int rhport;
 	int connected = 0;
 	int ret = 0;
 	unsigned long flags;
 
 	hcd = platform_get_drvdata(pdev);
+	if (!hcd)
+		return 0;
+	vhci = hcd_to_vhci(hcd);
 
-	spin_lock_irqsave(&the_controller->lock, flags);
+	spin_lock_irqsave(&vhci->lock, flags);
 
 	for (rhport = 0; rhport < VHCI_NPORTS; rhport++)
-		if (the_controller->port_status[rhport] &
-		    USB_PORT_STAT_CONNECTION)
+		if (vhci->port_status[rhport] & USB_PORT_STAT_CONNECTION)
 			connected += 1;
 
-	spin_unlock_irqrestore(&the_controller->lock, flags);
+	spin_unlock_irqrestore(&vhci->lock, flags);
 
 	if (connected > 0) {
 		dev_info(&pdev->dev,
@@ -1106,6 +1176,8 @@ static int vhci_hcd_resume(struct platform_device *pdev)
 	dev_dbg(&pdev->dev, "%s\n", __func__);
 
 	hcd = platform_get_drvdata(pdev);
+	if (!hcd)
+		return 0;
 	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
 	usb_hcd_poll_rh_status(hcd);
 
@@ -1129,52 +1201,78 @@ static struct platform_driver vhci_driver = {
 	},
 };
 
-/*
- * The VHCI 'device' is 'virtual'; not a real plug&play hardware.
- * We need to add this virtual device as a platform device arbitrarily:
- *	1. platform_device_register()
- */
-static void the_pdev_release(struct device *dev)
+static int add_platform_device(int id)
 {
+	struct platform_device *pdev;
+	int dev_nr;
+
+	if (id == 0)
+		dev_nr = -1;
+	else
+		dev_nr = id;
+
+	pdev = platform_device_register_simple(driver_name, dev_nr, NULL, 0);
+	if (pdev == NULL)
+		return -ENODEV;
+
+	*(the_pdevs + id) = pdev;
+	return 0;
 }
 
-static struct platform_device the_pdev = {
-	/* should be the same name as driver_name */
-	.name = driver_name,
-	.id = -1,
-	.dev = {
-		.release = the_pdev_release,
-	},
-};
+static void del_platform_devices(void)
+{
+	struct platform_device *pdev;
+	int i;
+
+	for (i = 0; i < num_controllers; i++) {
+		pdev = *(the_pdevs + i);
+		if (pdev != NULL)
+			platform_device_unregister(pdev);
+		*(the_pdevs + i) = NULL;
+	}
+	sysfs_remove_link(&platform_bus.kobj, driver_name);
+}
 
 static int __init vhci_hcd_init(void)
 {
-	int ret;
+	int i, ret;
 
 	if (usb_disabled())
 		return -ENODEV;
 
+	if (num_controllers < 1)
+		num_controllers = 1;
+
+	the_pdevs = kcalloc(num_controllers, sizeof(void *), GFP_KERNEL);
+	if (the_pdevs == NULL)
+		return -ENOMEM;
+
 	ret = platform_driver_register(&vhci_driver);
 	if (ret)
 		goto err_driver_register;
 
-	ret = platform_device_register(&the_pdev);
-	if (ret)
-		goto err_platform_device_register;
+	for (i = 0; i < num_controllers; i++) {
+		ret = add_platform_device(i);
+		if (ret)
+			goto err_platform_device_register;
+	}
 
 	pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
 	return ret;
 
 err_platform_device_register:
+	del_platform_devices();
 	platform_driver_unregister(&vhci_driver);
 err_driver_register:
+	kfree(the_pdevs);
 	return ret;
 }
 
 static void __exit vhci_hcd_exit(void)
 {
-	platform_device_unregister(&the_pdev);
+	del_platform_devices();
 	platform_driver_unregister(&vhci_driver);
+	kfree(the_pdevs);
 }
 
 module_init(vhci_hcd_init);
diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c
index d656e0e..fc2d319 100644
--- a/drivers/usb/usbip/vhci_rx.c
+++ b/drivers/usb/usbip/vhci_rx.c
@@ -70,6 +70,7 @@ struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum)
 static void vhci_recv_ret_submit(struct vhci_device *vdev,
 				 struct usbip_header *pdu)
 {
+	struct vhci_hcd *vhci = vdev_to_vhci(vdev);
 	struct usbip_device *ud = &vdev->ud;
 	struct urb *urb;
 	unsigned long flags;
@@ -81,7 +82,7 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
 	if (!urb) {
 		pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum);
 		pr_info("max seqnum %d\n",
-			atomic_read(&the_controller->seqnum));
+			atomic_read(&vhci->seqnum));
 		usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
 		return;
 	}
@@ -105,11 +106,11 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
 
 	usbip_dbg_vhci_rx("now giveback urb %p\n", urb);
 
-	spin_lock_irqsave(&the_controller->lock, flags);
-	usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
-	spin_unlock_irqrestore(&the_controller->lock, flags);
+	spin_lock_irqsave(&vhci->lock, flags);
+	usb_hcd_unlink_urb_from_ep(vhci_to_hcd(vhci), urb);
+	spin_unlock_irqrestore(&vhci->lock, flags);
 
-	usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status);
+	usb_hcd_giveback_urb(vhci_to_hcd(vhci), urb, urb->status);
 
 	usbip_dbg_vhci_rx("Leave\n");
 }
@@ -142,6 +143,7 @@ static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev,
 static void vhci_recv_ret_unlink(struct vhci_device *vdev,
 				 struct usbip_header *pdu)
 {
+	struct vhci_hcd *vhci = vdev_to_vhci(vdev);
 	struct vhci_unlink *unlink;
 	struct urb *urb;
 	unsigned long flags;
@@ -174,12 +176,11 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev,
 		urb->status = pdu->u.ret_unlink.status;
 		pr_info("urb->status %d\n", urb->status);
 
-		spin_lock_irqsave(&the_controller->lock, flags);
-		usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
-		spin_unlock_irqrestore(&the_controller->lock, flags);
+		spin_lock_irqsave(&vhci->lock, flags);
+		usb_hcd_unlink_urb_from_ep(vhci_to_hcd(vhci), urb);
+		spin_unlock_irqrestore(&vhci->lock, flags);
 
-		usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
-				     urb->status);
+		usb_hcd_giveback_urb(vhci_to_hcd(vhci), urb, urb->status);
 	}
 
 	kfree(unlink);
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c
index 5b5462e..dc319c7 100644
--- a/drivers/usb/usbip/vhci_sysfs.c
+++ b/drivers/usb/usbip/vhci_sysfs.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015-2016 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,6 +21,8 @@
 #include <linux/kthread.h>
 #include <linux/file.h>
 #include <linux/net.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
 
 #include "usbip_common.h"
 #include "vhci.h"
@@ -27,106 +30,190 @@
 /* TODO: refine locking ?*/
 
 /* Sysfs entry to show port status */
-static ssize_t status_show(struct device *dev, struct device_attribute *attr,
-			   char *out)
+static ssize_t status_show_vhci(int pdev_nr, char *out)
 {
+	struct platform_device *pdev = *(the_pdevs + pdev_nr);
+	struct vhci_hcd *vhci;
 	char *s = out;
 	int i = 0;
 	unsigned long flags;
 
-	BUG_ON(!the_controller || !out);
+	if (!pdev || !out) {
+		usbip_dbg_vhci_sysfs("show status error\n");
+		return 0;
+	}
+
+	vhci = hcd_to_vhci(platform_get_drvdata(pdev));
 
-	spin_lock_irqsave(&the_controller->lock, flags);
+	spin_lock_irqsave(&vhci->lock, flags);
 
 	/*
 	 * output example:
-	 * prt sta spd dev socket           local_busid
-	 * 000 004 000 000         c5a7bb80 1-2.3
-	 * 001 004 000 000         d8cee980 2-3.4
+	 * port sta spd dev      socket           local_busid
+	 * 0000 004 000 00000000         c5a7bb80 1-2.3
+	 * 0001 004 000 00000000         d8cee980 2-3.4
 	 *
 	 * IP address can be retrieved from a socket pointer address by looking
 	 * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a
 	 * port number and its peer IP address.
 	 */
-	out += sprintf(out,
-		       "prt sta spd bus dev socket           local_busid\n");
-
 	for (i = 0; i < VHCI_NPORTS; i++) {
-		struct vhci_device *vdev = port_to_vdev(i);
+		struct vhci_device *vdev = &vhci->vdev[i];
 
 		spin_lock(&vdev->ud.lock);
-		out += sprintf(out, "%03u %03u ", i, vdev->ud.status);
+		out += sprintf(out, "%04u %03u ",
+				    (pdev_nr * VHCI_NPORTS) + i,
+				    vdev->ud.status);
 
 		if (vdev->ud.status == VDEV_ST_USED) {
 			out += sprintf(out, "%03u %08x ",
-				       vdev->speed, vdev->devid);
-			out += sprintf(out, "%16p ", vdev->ud.tcp_socket);
-			out += sprintf(out, "%s", dev_name(&vdev->udev->dev));
+					    vdev->speed, vdev->devid);
+			out += sprintf(out, "%16p %s",
+					    vdev->ud.tcp_socket,
+					    dev_name(&vdev->udev->dev));
 
 		} else {
-			out += sprintf(out, "000 000 000 0000000000000000 0-0");
+			out += sprintf(out, "000 00000000 ");
+			out += sprintf(out, "0000000000000000 0-0");
 		}
 
 		out += sprintf(out, "\n");
 		spin_unlock(&vdev->ud.lock);
 	}
 
-	spin_unlock_irqrestore(&the_controller->lock, flags);
+	spin_unlock_irqrestore(&vhci->lock, flags);
+
+	return out - s;
+}
+
+static ssize_t status_show_not_ready(int pdev_nr, char *out)
+{
+	char *s = out;
+	int i = 0;
+
+	for (i = 0; i < VHCI_NPORTS; i++) {
+		out += sprintf(out, "%04u %03u ",
+				    (pdev_nr * VHCI_NPORTS) + i,
+				    VDEV_ST_NOTASSIGNED);
+		out += sprintf(out, "000 00000000 0000000000000000 0-0");
+		out += sprintf(out, "\n");
+	}
+	return out - s;
+}
+
+static int status_name_to_id(const char *name)
+{
+	char *c;
+	long val;
+	int ret;
+
+	c = strchr(name, '.');
+	if (c == NULL)
+		return 0;
+
+	ret = kstrtol(c+1, 10, &val);
+	if (ret < 0)
+		return ret;
+
+	return val;
+}
+
+static ssize_t status_show(struct device *dev,
+			   struct device_attribute *attr, char *out)
+{
+	char *s = out;
+	int pdev_nr;
+
+	out += sprintf(out,
+		       "port sta spd dev      socket           local_busid\n");
+
+	pdev_nr = status_name_to_id(attr->attr.name);
+	if (pdev_nr < 0)
+		out += status_show_not_ready(pdev_nr, out);
+	else
+		out += status_show_vhci(pdev_nr, out);
 
 	return out - s;
 }
-static DEVICE_ATTR_RO(status);
+
+static ssize_t nports_show(struct device *dev, struct device_attribute *attr,
+			   char *out)
+{
+	char *s = out;
+
+	out += sprintf(out, "%d\n", VHCI_NPORTS * num_controllers);
+	return out - s;
+}
+static DEVICE_ATTR_RO(nports);
 
 /* Sysfs entry to shutdown a virtual connection */
-static int vhci_port_disconnect(__u32 rhport)
+static int vhci_port_disconnect(struct vhci_hcd *vhci, __u32 rhport)
 {
-	struct vhci_device *vdev;
+	struct vhci_device *vdev = &vhci->vdev[rhport];
 	unsigned long flags;
 
 	usbip_dbg_vhci_sysfs("enter\n");
 
 	/* lock */
-	spin_lock_irqsave(&the_controller->lock, flags);
-
-	vdev = port_to_vdev(rhport);
-
+	spin_lock_irqsave(&vhci->lock, flags);
 	spin_lock(&vdev->ud.lock);
+
 	if (vdev->ud.status == VDEV_ST_NULL) {
 		pr_err("not connected %d\n", vdev->ud.status);
 
 		/* unlock */
 		spin_unlock(&vdev->ud.lock);
-		spin_unlock_irqrestore(&the_controller->lock, flags);
+		spin_unlock_irqrestore(&vhci->lock, flags);
 
 		return -EINVAL;
 	}
 
 	/* unlock */
 	spin_unlock(&vdev->ud.lock);
-	spin_unlock_irqrestore(&the_controller->lock, flags);
+	spin_unlock_irqrestore(&vhci->lock, flags);
 
 	usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN);
 
 	return 0;
 }
 
+static int valid_port(__u32 pdev_nr, __u32 rhport)
+{
+	if (pdev_nr >= num_controllers) {
+		pr_err("pdev %u\n", pdev_nr);
+		return 0;
+	}
+	if (rhport >= VHCI_NPORTS) {
+		pr_err("rhport %u\n", rhport);
+		return 0;
+	}
+	return 1;
+}
+
 static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
 			    const char *buf, size_t count)
 {
-	int err;
-	__u32 rhport = 0;
+	__u32 port = 0, pdev_nr = 0, rhport = 0;
+	struct usb_hcd *hcd;
+	int ret;
 
-	if (sscanf(buf, "%u", &rhport) != 1)
+	if (kstrtoint(buf, 10, &port) < 0)
 		return -EINVAL;
 
-	/* check rhport */
-	if (rhport >= VHCI_NPORTS) {
-		dev_err(dev, "invalid port %u\n", rhport);
+	pdev_nr = port_to_pdev_nr(port);
+	rhport = port_to_rhport(port);
+
+	if (!valid_port(pdev_nr, rhport))
 		return -EINVAL;
+
+	hcd = platform_get_drvdata(*(the_pdevs + pdev_nr));
+	if (hcd == NULL) {
+		dev_err(dev, "port is not ready %u\n", port);
+		return -EAGAIN;
 	}
 
-	err = vhci_port_disconnect(rhport);
-	if (err < 0)
+	ret = vhci_port_disconnect(hcd_to_vhci(hcd), rhport);
+	if (ret < 0)
 		return -EINVAL;
 
 	usbip_dbg_vhci_sysfs("Leave\n");
@@ -135,16 +222,12 @@ static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach);
 
-/* Sysfs entry to establish a virtual connection */
-static int valid_args(__u32 rhport, enum usb_device_speed speed)
+static int valid_args(__u32 pdev_nr, __u32 rhport, enum usb_device_speed speed)
 {
-	/* check rhport */
-	if (rhport >= VHCI_NPORTS) {
-		pr_err("port %u\n", rhport);
-		return -EINVAL;
+	if (!valid_port(pdev_nr, rhport)) {
+		return 0;
 	}
 
-	/* check speed */
 	switch (speed) {
 	case USB_SPEED_LOW:
 	case USB_SPEED_FULL:
@@ -154,12 +237,13 @@ static int valid_args(__u32 rhport, enum usb_device_speed speed)
 	default:
 		pr_err("Failed attach request for unsupported USB speed: %s\n",
 			usb_speed_string(speed));
-		return -EINVAL;
+		return 0;
 	}
 
-	return 0;
+	return 1;
 }
 
+/* Sysfs entry to establish a virtual connection */
 /*
  * To start a new USB/IP attachment, a userland program needs to setup a TCP
  * connection and then write its socket descriptor with remote device
@@ -174,10 +258,12 @@ static int valid_args(__u32 rhport, enum usb_device_speed speed)
 static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 			    const char *buf, size_t count)
 {
-	struct vhci_device *vdev;
 	struct socket *socket;
 	int sockfd = 0;
-	__u32 rhport = 0, devid = 0, speed = 0;
+	__u32 port = 0, pdev_nr = 0, rhport = 0, devid = 0, speed = 0;
+	struct usb_hcd *hcd;
+	struct vhci_hcd *vhci;
+	struct vhci_device *vdev;
 	int err;
 	unsigned long flags;
 
@@ -187,16 +273,28 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 	 * @devid: unique device identifier in a remote host
 	 * @speed: usb device speed in a remote host
 	 */
-	if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4)
+	if (sscanf(buf, "%u %u %u %u", &port, &sockfd, &devid, &speed) != 4)
 		return -EINVAL;
+	pdev_nr = port_to_pdev_nr(port);
+	rhport = port_to_rhport(port);
 
-	usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n",
-			     rhport, sockfd, devid, speed);
+	usbip_dbg_vhci_sysfs("port(%u) pdev(%d) rhport(%u)\n",
+				port, pdev_nr, rhport);
+	usbip_dbg_vhci_sysfs("sockfd(%u) devid(%u) speed(%u)\n",
+				sockfd, devid, speed);
 
 	/* check received parameters */
-	if (valid_args(rhport, speed) < 0)
+	if (!valid_args(pdev_nr, rhport, speed))
 		return -EINVAL;
 
+	hcd = platform_get_drvdata(*(the_pdevs + pdev_nr));
+	if (hcd == NULL) {
+		dev_err(dev, "port %d is not ready\n", port);
+		return -EAGAIN;
+	}
+	vhci = hcd_to_vhci(hcd);
+	vdev = &vhci->vdev[rhport];
+
 	/* Extract socket from fd. */
 	socket = sockfd_lookup(sockfd, &err);
 	if (!socket)
@@ -205,14 +303,13 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 	/* now need lock until setting vdev status as used */
 
 	/* begin a lock */
-	spin_lock_irqsave(&the_controller->lock, flags);
-	vdev = port_to_vdev(rhport);
+	spin_lock_irqsave(&vhci->lock, flags);
 	spin_lock(&vdev->ud.lock);
 
 	if (vdev->ud.status != VDEV_ST_NULL) {
 		/* end of the lock */
 		spin_unlock(&vdev->ud.lock);
-		spin_unlock_irqrestore(&the_controller->lock, flags);
+		spin_unlock_irqrestore(&vhci->lock, flags);
 
 		sockfd_put(socket);
 
@@ -220,9 +317,10 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 		return -EINVAL;
 	}
 
-	dev_info(dev,
-		 "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n",
-		 rhport, sockfd, devid, speed, usb_speed_string(speed));
+	dev_info(dev, "pdev(%u) rhport(%u) sockfd(%d)\n",
+		 pdev_nr, rhport, sockfd);
+	dev_info(dev, "devid(%u) speed(%u) speed_str(%s)\n",
+		 devid, speed, usb_speed_string(speed));
 
 	vdev->devid         = devid;
 	vdev->speed         = speed;
@@ -230,26 +328,92 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 	vdev->ud.status     = VDEV_ST_NOTASSIGNED;
 
 	spin_unlock(&vdev->ud.lock);
-	spin_unlock_irqrestore(&the_controller->lock, flags);
+	spin_unlock_irqrestore(&vhci->lock, flags);
 	/* end the lock */
 
 	vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx");
 	vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx");
 
-	rh_port_connect(rhport, speed);
+	rh_port_connect(vdev, speed);
 
 	return count;
 }
 static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach);
 
-static struct attribute *dev_attrs[] = {
-	&dev_attr_status.attr,
-	&dev_attr_detach.attr,
-	&dev_attr_attach.attr,
-	&dev_attr_usbip_debug.attr,
-	NULL,
+#define MAX_STATUS_NAME 16
+
+struct status_attr {
+	struct device_attribute attr;
+	char name[MAX_STATUS_NAME+1];
 };
 
-const struct attribute_group dev_attr_group = {
-	.attrs = dev_attrs,
+static struct status_attr *status_attrs;
+
+static void set_status_attr(int id)
+{
+	struct status_attr *status;
+
+	status = status_attrs + id;
+	if (id == 0)
+		strcpy(status->name, "status");
+	else
+		snprintf(status->name, MAX_STATUS_NAME+1, "status.%d", id);
+	status->attr.attr.name = status->name;
+	status->attr.attr.mode = S_IRUGO;
+	status->attr.show = status_show;
+}
+
+static int init_status_attrs(void)
+{
+	int id;
+
+	status_attrs = kcalloc(num_controllers, sizeof(struct status_attr),
+			       GFP_KERNEL);
+	if (status_attrs == NULL)
+		return -ENOMEM;
+
+	for (id = 0; id < num_controllers; id++)
+		set_status_attr(id);
+
+	return 0;
+}
+
+static void finish_status_attrs(void)
+{
+	kfree(status_attrs);
+}
+
+struct attribute_group dev_attr_group = {
+	.attrs = NULL,
 };
+
+int vhci_init_attr_group(void)
+{
+	struct attribute **attrs;
+	int ret, i;
+
+	attrs = kcalloc((num_controllers + 6), sizeof(struct attribute *),
+			GFP_KERNEL);
+	if (attrs == NULL)
+		return -ENOMEM;
+
+	ret = init_status_attrs();
+	if (ret) {
+		kfree(attrs);
+		return ret;
+	}
+	*attrs = &dev_attr_nports.attr;
+	*(attrs + 1) = &dev_attr_detach.attr;
+	*(attrs + 2) = &dev_attr_attach.attr;
+	*(attrs + 3) = &dev_attr_usbip_debug.attr;
+	for (i = 0; i < num_controllers; i++)
+		*(attrs + i + 4) = &((status_attrs + i)->attr.attr);
+	dev_attr_group.attrs = attrs;
+	return 0;
+}
+
+void vhci_finish_attr_group(void)
+{
+	finish_status_attrs();
+	kfree(dev_attr_group.attrs);
+}
diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c
index 501a61d..1508a48 100644
--- a/tools/usb/usbip/libsrc/vhci_driver.c
+++ b/tools/usb/usbip/libsrc/vhci_driver.c
@@ -15,11 +15,39 @@
 #undef  PROGNAME
 #define PROGNAME "libusbip"
 
-struct usbip_vhci_driver *vhci_driver;
-struct udev *udev_context;
+static struct udev *udev_context;
 
-static struct usbip_imported_device *
-imported_device_init(struct usbip_imported_device *idev, char *busid)
+/* /sys/devices/platform/vhci_hcd.0 */
+static  struct udev_device *vhci_hc_device;
+
+static int vhci_nports;
+
+#define OPEN_MODE_FIRST      0
+#define OPEN_MODE_REOPEN     1
+
+static int open_hc_device(int mode)
+{
+	if (vhci_hc_device && mode == OPEN_MODE_REOPEN)
+		udev_device_unref(vhci_hc_device);
+
+	vhci_hc_device =
+		udev_device_new_from_subsystem_sysname(udev_context,
+						       USBIP_VHCI_BUS_TYPE,
+						       USBIP_VHCI_DRV_NAME);
+	if (!vhci_hc_device) {
+		err("udev_device_new_from_subsystem_sysname failed");
+		return -1;
+	}
+	return 0;
+}
+
+static void close_hc_device(void)
+{
+	udev_device_unref(vhci_hc_device);
+	vhci_hc_device = NULL;
+}
+
+static int imported_device_init(struct usbip_vhci_device *vdev, char *busid)
 {
 	struct udev_device *sudev;
 
@@ -27,323 +55,186 @@ imported_device_init(struct usbip_imported_device *idev, char *busid)
 						       "usb", busid);
 	if (!sudev) {
 		dbg("udev_device_new_from_subsystem_sysname failed: %s", busid);
-		goto err;
+		return -1;
 	}
-	read_usb_device(sudev, &idev->udev);
+	read_usb_device(sudev, &vdev->udev);
 	udev_device_unref(sudev);
 
-	return idev;
-
-err:
-	return NULL;
+	return 0;
 }
 
+struct status_context {
+	int controller;
+	const char *c;
+};
 
+#define MAX_STATUS_NAME 16
 
-static int parse_status(const char *value)
+static int status_open(struct status_context *ctx, int mode)
 {
-	int ret = 0;
-	char *c;
-
-
-	for (int i = 0; i < vhci_driver->nports; i++)
-		memset(&vhci_driver->idev[i], 0, sizeof(vhci_driver->idev[i]));
+	char name[MAX_STATUS_NAME+1];
 
+	if (mode == OPEN_MODE_FIRST)
+		ctx->controller = 0;
+	else
+		(ctx->controller)++;
 
-	/* skip a header line */
-	c = strchr(value, '\n');
-	if (!c)
+	if (open_hc_device(OPEN_MODE_REOPEN))
 		return -1;
-	c++;
-
-	while (*c != '\0') {
-		int port, status, speed, devid;
-		unsigned long socket;
-		char lbusid[SYSFS_BUS_ID_SIZE];
-
-		ret = sscanf(c, "%d %d %d %x %lx %31s\n",
-				&port, &status, &speed,
-				&devid, &socket, lbusid);
-
-		if (ret < 5) {
-			dbg("sscanf failed: %d", ret);
-			BUG();
-		}
-
-		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 */
-		{
-			struct usbip_imported_device *idev = &vhci_driver->idev[port];
-
-			idev->port	= port;
-			idev->status	= status;
-
-			idev->devid	= 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;
-				}
-			}
-		}
-
-
-		/* go to the next line */
-		c = strchr(c, '\n');
-		if (!c)
-			break;
-		c++;
-	}
-
-	dbg("exit");
+	if (ctx->controller == 0)
+		strcpy(name, "status");
+	else
+		snprintf(name, MAX_STATUS_NAME + 1,
+				"status.%d", ctx->controller);
+	ctx->c = udev_device_get_sysattr_value(vhci_hc_device, name);
+	if (ctx->c == NULL)
+		return -1;
 
 	return 0;
 }
 
-static int refresh_imported_device_list(void)
+static int status_next_line(struct status_context *ctx)
 {
-	const char *attr_status;
+	const char *c = ctx->c;
 
-	attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device,
-					       "status");
-	if (!attr_status) {
-		err("udev_device_get_sysattr_value failed");
-		return -1;
+	while ((c = strchr(c, '\n')) == NULL) {
+		if (status_open(ctx, OPEN_MODE_REOPEN))
+			return -1;
+		c = ctx->c;
 	}
-
-	return parse_status(attr_status);
+	ctx->c = c + 1;
+	return 0;
 }
 
-static int get_nports(void)
+static int status_get_device(struct status_context *ctx,
+			     struct usbip_vhci_device *vdev)
 {
-	char *c;
-	int nports = 0;
-	const char *attr_status;
-
-	attr_status = udev_device_get_sysattr_value(vhci_driver->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') {
-		/* go to the next line */
-		c = strchr(c, '\n');
-		if (!c)
-			return nports;
-		c++;
-		nports += 1;
-	}
-
-	return nports;
-}
-
-/*
- * Read the given port's record.
- *
- * To avoid buffer overflow we will read the entire line and
- * validate each part's size. The initial buffer is padded by 4 to
- * accommodate the 2 spaces, 1 newline and an additional character
- * which is needed to properly validate the 3rd part without it being
- * truncated to an acceptable length.
- */
-static int read_record(int rhport, char *host, unsigned long host_len,
-		char *port, unsigned long port_len, char *busid)
-{
-	int part;
-	FILE *file;
-	char path[PATH_MAX+1];
-	char *buffer, *start, *end;
-	char delim[] = {' ', ' ', '\n'};
-	int max_len[] = {(int)host_len, (int)port_len, SYSFS_BUS_ID_SIZE};
-	size_t buffer_len = host_len + port_len + SYSFS_BUS_ID_SIZE + 4;
+	int port, status, speed, devid;
+	unsigned long socket;
+	char lbusid[SYSFS_BUS_ID_SIZE];
+	int ret;
 
-	buffer = malloc(buffer_len);
-	if (!buffer)
+	if (status_next_line(ctx))
 		return -1;
 
-	snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
+	memset(vdev, 0, sizeof(struct usbip_vhci_device));
 
-	file = fopen(path, "r");
-	if (!file) {
-		err("fopen");
-		free(buffer);
+	if (ctx->c == NULL || *(ctx->c) == '\0') {
+		dbg("no more data to scan");
 		return -1;
 	}
 
-	if (fgets(buffer, buffer_len, file) == NULL) {
-		err("fgets");
-		free(buffer);
-		fclose(file);
+	ret = sscanf(ctx->c, "%d %d %d %x %lx %31s\n",
+				&port, &status, &speed,
+				&devid, &socket, lbusid);
+	if (ret < 6) {
+		dbg("sscanf failed: %d", ret);
 		return -1;
 	}
-	fclose(file);
 
-	/* validate the length of each of the 3 parts */
-	start = buffer;
-	for (part = 0; part < 3; part++) {
-		end = strchr(start, delim[part]);
-		if (end == NULL || (end - start) > max_len[part]) {
-			free(buffer);
+	dbg("port %d status %d speed %d devid %x", port, status, speed, devid);
+	dbg("socket %lx lbusid %s", socket, lbusid);
+
+	vdev->port	= port;
+	vdev->status	= status;
+	vdev->devid	= devid;
+	vdev->busnum	= (devid >> 16);
+	vdev->devnum	= (devid & 0x0000ffff);
+
+	if (vdev->status != VDEV_ST_NULL &&
+	    vdev->status != VDEV_ST_NOTASSIGNED) {
+		if (imported_device_init(vdev, lbusid)) {
+			dbg("imported_device_init failed");
 			return -1;
 		}
-		start = end + 1;
 	}
 
-	if (sscanf(buffer, "%s %s %s\n", host, port, busid) != 3) {
-		err("sscanf");
-		free(buffer);
-		return -1;
-	}
-
-	free(buffer);
-
 	return 0;
 }
 
-#define OPEN_HC_MODE_FIRST	0
-#define OPEN_HC_MODE_REOPEN	1
+static void status_close(struct status_context *ctx)
+{
+	ctx->c = NULL;
+}
 
-static int open_hc_device(int mode)
+static int get_nports(void)
 {
-	if (mode == OPEN_HC_MODE_REOPEN)
-		udev_device_unref(vhci_driver->hc_device);
+	const char *attr_nports;
 
-	vhci_driver->hc_device =
-		udev_device_new_from_subsystem_sysname(udev_context,
-						       USBIP_VHCI_BUS_TYPE,
-						       USBIP_VHCI_DRV_NAME);
-	if (!vhci_driver->hc_device) {
-		err("udev_device_new_from_subsystem_sysname failed");
+	attr_nports = udev_device_get_sysattr_value(vhci_hc_device, "nports");
+	if (!attr_nports) {
+		err("udev_device_get_sysattr_value failed");
+		return -1;
+	}
+
+	vhci_nports = strtol(attr_nports, NULL, 10);
+	if (vhci_nports <= 0) {
+		err("invalid nports value");
 		return -1;
 	}
+
 	return 0;
 }
 
-/* ---------------------------------------------------------------------- */
-
 int usbip_vhci_driver_open(void)
 {
 	udev_context = udev_new();
 	if (!udev_context) {
 		err("udev_new failed");
-		return -1;
+		goto err_out;
 	}
 
-	vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver));
-
-	if (open_hc_device(OPEN_HC_MODE_FIRST))
-		goto err_free_driver;
-
-	vhci_driver->nports = get_nports();
-
-	dbg("available ports: %d", vhci_driver->nports);
+	if (open_hc_device(OPEN_MODE_FIRST))
+		goto err_unref_udev;
 
-	if (refresh_imported_device_list())
-		goto err_unref_device;
+	if (get_nports() || vhci_nports <= 0) {
+		err("failed to get nports");
+		goto err_close_hc;
+	}
+	dbg("available ports: %d", vhci_nports);
 
 	return 0;
 
-err_unref_device:
-	udev_device_unref(vhci_driver->hc_device);
-err_free_driver:
-	if (vhci_driver)
-		free(vhci_driver);
-
-	vhci_driver = NULL;
-
+err_close_hc:
+	close_hc_device();
+err_unref_udev:
 	udev_unref(udev_context);
-
+	udev_context = NULL;
+err_out:
 	return -1;
 }
 
-
 void usbip_vhci_driver_close(void)
 {
-	if (!vhci_driver)
-		return;
-
-	udev_device_unref(vhci_driver->hc_device);
-
-	free(vhci_driver);
-
-	vhci_driver = NULL;
+	close_hc_device();
 
-	udev_unref(udev_context);
-}
-
-
-int usbip_vhci_refresh_device_list(void)
-{
-	if (open_hc_device(OPEN_HC_MODE_REOPEN))
-		goto err;
-	if (refresh_imported_device_list())
-		goto err;
-
-	return 0;
-err:
-	dbg("failed to refresh device list");
-	return -1;
+	if (udev_context)
+		udev_unref(udev_context);
 }
 
-
 int usbip_vhci_get_free_port(void)
 {
-	for (int i = 0; i < vhci_driver->nports; i++) {
-		if (vhci_driver->idev[i].status == VDEV_ST_NULL)
-			return i;
-	}
-
-	return -1;
-}
+	struct status_context context;
+	struct usbip_vhci_device vdev;
 
-struct usbip_imported_device *usbip_vhci_get_device(int port)
-{
-	for (int i = 0; i < vhci_driver->nports; i++) {
-		if (vhci_driver->idev[i].port == port)
-			return vhci_driver->idev + i;
-	}
-	return NULL;
-}
-
-struct usbip_imported_device *usbip_vhci_find_device(char *host, char *busid)
-{
-	int ret;
-	char rhost[NI_MAXHOST] = "unknown host";
-	char rserv[NI_MAXSERV] = "unknown port";
-	char rbusid[SYSFS_BUS_ID_SIZE];
+	if (status_open(&context, OPEN_MODE_FIRST))
+		return -1;
 
-	for (int i = 0; i < vhci_driver->nports; i++) {
-		ret = read_record(vhci_driver->idev[i].port, rhost, NI_MAXHOST,
-				rserv, NI_MAXSERV, rbusid);
-		if (!ret &&
-			!strncmp(host, rhost, NI_MAXHOST) &&
-			!strncmp(busid, rbusid, SYSFS_BUS_ID_SIZE)) {
-			return vhci_driver->idev + i;
+	while (!status_get_device(&context, &vdev)) {
+		if (vdev.status == VDEV_ST_NULL) {
+			dbg("found free port %d", vdev.port);
+			status_close(&context);
+			return vdev.port;
 		}
 	}
-	return NULL;
+	status_close(&context);
+	return -1;
 }
 
-int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
-		uint32_t speed) {
+static int usbip_vhci_attach_device2(int port, int sockfd, uint32_t devid,
+		uint32_t speed)
+{
 	char buff[200]; /* what size should be ? */
 	char attach_attr_path[SYSFS_PATH_MAX];
 	char attr_attach[] = "attach";
@@ -354,7 +245,7 @@ int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
 			port, sockfd, devid, speed);
 	dbg("writing: %s", buff);
 
-	path = udev_device_get_syspath(vhci_driver->hc_device);
+	path = udev_device_get_syspath(vhci_hc_device);
 	snprintf(attach_attr_path, sizeof(attach_attr_path), "%s/%s",
 		 path, attr_attach);
 	dbg("attach attribute path: %s", attach_attr_path);
@@ -376,7 +267,7 @@ static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
 }
 
 /* will be removed */
-int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
+int usbip_vhci_attach_device(int port, int sockfd, uint8_t busnum,
 		uint8_t devnum, uint32_t speed)
 {
 	int devid = get_devid(busnum, devnum);
@@ -384,7 +275,7 @@ int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
 	return usbip_vhci_attach_device2(port, sockfd, devid, speed);
 }
 
-int usbip_vhci_detach_device(uint8_t port)
+int usbip_vhci_detach_device(int port)
 {
 	char detach_attr_path[SYSFS_PATH_MAX];
 	char attr_detach[] = "detach";
@@ -395,7 +286,7 @@ int usbip_vhci_detach_device(uint8_t port)
 	snprintf(buff, sizeof(buff), "%u", port);
 	dbg("writing: %s", buff);
 
-	path = udev_device_get_syspath(vhci_driver->hc_device);
+	path = udev_device_get_syspath(vhci_hc_device);
 	snprintf(detach_attr_path, sizeof(detach_attr_path), "%s/%s",
 		 path, attr_detach);
 	dbg("detach attribute path: %s", detach_attr_path);
@@ -411,7 +302,99 @@ int usbip_vhci_detach_device(uint8_t port)
 	return 0;
 }
 
-int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
+/*
+ * Read the given port's record.
+ *
+ * To avoid buffer overflow we will read the entire line and
+ * validate each part's size. The initial buffer is padded by 4 to
+ * accommodate the 2 spaces, 1 newline and an additional character
+ * which is needed to properly validate the 3rd part without it being
+ * truncated to an acceptable length.
+ */
+static int read_record(int port, char *host, unsigned long host_len,
+		char *port_s, unsigned long port_slen, char *busid)
+{
+	int part;
+	FILE *file;
+	char path[PATH_MAX+1];
+	char *buffer, *start, *end;
+	char delim[] = {' ', ' ', '\n'};
+	int max_len[] = {(int)host_len, (int)port_slen, SYSFS_BUS_ID_SIZE};
+	size_t buffer_len = host_len + port_slen + SYSFS_BUS_ID_SIZE + 4;
+
+	buffer = (char *)malloc(buffer_len);
+	if (!buffer)
+		return -1;
+
+	snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", port);
+
+	file = fopen(path, "r");
+	if (!file) {
+		err("fopen %s", path);
+		free(buffer);
+		return -1;
+	}
+
+	if (fgets(buffer, buffer_len, file) == NULL) {
+		err("fgets %s", path);
+		free(buffer);
+		fclose(file);
+		return -1;
+	}
+	fclose(file);
+
+	/* validate the length of each of the 3 parts */
+	start = buffer;
+	for (part = 0; part < 3; part++) {
+		end = strchr(start, delim[part]);
+		if (end == NULL || (end - start) > max_len[part]) {
+			free(buffer);
+			return -1;
+		}
+		start = end + 1;
+	}
+
+	if (sscanf(buffer, "%s %s %s\n", host, port_s, busid) != 3) {
+		err("sscanf");
+		free(buffer);
+		return -1;
+	}
+
+	free(buffer);
+
+	return 0;
+}
+
+int usbip_vhci_find_device(const char *host, const char *busid)
+{
+	int ret;
+	char rhost[NI_MAXHOST] = "unknown host";
+	char rserv[NI_MAXSERV] = "unknown port";
+	char rbusid[SYSFS_BUS_ID_SIZE];
+	struct status_context context;
+	struct usbip_vhci_device vdev;
+
+	if (status_open(&context, OPEN_MODE_FIRST))
+		return -1;
+
+	while (!status_get_device(&context, &vdev)) {
+		if (vdev.status != VDEV_ST_USED)
+			continue;
+
+		ret = read_record(vdev.port, rhost, NI_MAXHOST,
+				  rserv, NI_MAXSERV, rbusid);
+		if (!ret &&
+			!strncmp(host, rhost, NI_MAXHOST) &&
+			!strncmp(busid, rbusid, SYSFS_BUS_ID_SIZE)) {
+			status_close(&context);
+			return vdev.port;
+		}
+	}
+	status_close(&context);
+	return -1;
+}
+
+static int usbip_vhci_imported_device_dump(struct usbip_vhci_device *vdev)
 {
 	char product_name[100];
 	char host[NI_MAXHOST] = "unknown host";
@@ -420,42 +403,68 @@ int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
 	int ret;
 	int read_record_error = 0;
 
-	if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED)
+	if (vdev->status == VDEV_ST_NULL || vdev->status == VDEV_ST_NOTASSIGNED)
 		return 0;
 
-	ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv),
+	ret = read_record(vdev->port, host, sizeof(host), serv, sizeof(serv),
 			  remote_busid);
 	if (ret) {
 		err("read_record");
 		read_record_error = 1;
 	}
 
-	printf("Port %02d: <%s> at %s\n", idev->port,
-	       usbip_status_string(idev->status),
-	       usbip_speed_string(idev->udev.speed));
+	printf("Port %02d: <%s> at %s\n", vdev->port,
+	       usbip_status_string(vdev->status),
+	       usbip_speed_string(vdev->udev.speed));
 
 	usbip_names_get_product(product_name, sizeof(product_name),
-				idev->udev.idVendor, idev->udev.idProduct);
+				vdev->udev.idVendor, vdev->udev.idProduct);
 
 	printf("       %s\n",  product_name);
 
 	if (!read_record_error) {
-		printf("%10s -> usbip://%s:%s/%s\n", idev->udev.busid,
+		printf("%10s -> usbip://%s:%s/%s\n", vdev->udev.busid,
 		       host, serv, remote_busid);
 		printf("%10s -> remote bus/dev %03d/%03d\n", " ",
-		       idev->busnum, idev->devnum);
+		       vdev->busnum, vdev->devnum);
 	} else {
 		printf("%10s -> unknown host, remote port and remote busid\n",
-		       idev->udev.busid);
+		       vdev->udev.busid);
 		printf("%10s -> remote bus/dev %03d/%03d\n", " ",
-		       idev->busnum, idev->devnum);
+		       vdev->busnum, vdev->devnum);
+	}
+
+	return 0;
+}
+
+int usbip_vhci_imported_devices_dump(void)
+{
+	struct status_context context;
+	struct usbip_vhci_device vdev;
+
+	if (status_open(&context, OPEN_MODE_FIRST))
+		goto err_out;
+
+	while (!status_get_device(&context, &vdev)) {
+		if (vdev.status == VDEV_ST_NULL ||
+		    vdev.status == VDEV_ST_NOTASSIGNED)
+			continue;
+
+		if (usbip_vhci_imported_device_dump(&vdev))
+			goto err_close;
 	}
+	status_close(&context);
 
 	return 0;
+err_close:
+	status_close(&context);
+err_out:
+	return -1;
 }
 
 #define MAX_BUFF 100
-int usbip_vhci_create_record(char *host, char *port, char *busid, int rhport)
+int usbip_vhci_create_record(const char *host, const char *port_s,
+			     const char *busid, int port)
 {
 	int fd;
 	char path[PATH_MAX+1];
@@ -477,14 +486,14 @@ int usbip_vhci_create_record(char *host, char *port, char *busid, int rhport)
 			return -1;
 	}
 
-	snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
+	snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", port);
 
 	fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
 	if (fd < 0)
 		return -1;
 
 	snprintf(buff, MAX_BUFF, "%s %s %s\n",
-			host, port, busid);
+			host, port_s, busid);
 
 	ret = write(fd, buff, strlen(buff));
 	if (ret != (ssize_t) strlen(buff)) {
@@ -497,11 +506,11 @@ int usbip_vhci_create_record(char *host, char *port, char *busid, int rhport)
 	return 0;
 }
 
-int usbip_vhci_delete_record(int rhport)
+int usbip_vhci_delete_record(int port)
 {
 	char path[PATH_MAX+1];
 
-	snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
+	snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", port);
 
 	remove(path);
 	rmdir(VHCI_STATE_PATH);
diff --git a/tools/usb/usbip/libsrc/vhci_driver.h b/tools/usb/usbip/libsrc/vhci_driver.h
index 2a9e9c1..3ee6ddf 100644
--- a/tools/usb/usbip/libsrc/vhci_driver.h
+++ b/tools/usb/usbip/libsrc/vhci_driver.h
@@ -12,10 +12,9 @@
 #include "usbip_common.h"
 
 #define USBIP_VHCI_BUS_TYPE "platform"
-#define MAXNPORT 128
 
-struct usbip_imported_device {
-	uint8_t port;
+struct usbip_vhci_device {
+	int port;
 	uint32_t status;
 
 	uint32_t devid;
@@ -27,39 +26,20 @@ struct usbip_imported_device {
 	struct usbip_usb_device udev;
 };
 
-struct usbip_vhci_driver {
-
-	/* /sys/devices/platform/vhci_hcd */
-	struct udev_device *hc_device;
-
-	int nports;
-	struct usbip_imported_device idev[MAXNPORT];
-};
-
-
-extern struct usbip_vhci_driver *vhci_driver;
-
 int usbip_vhci_driver_open(void);
 void usbip_vhci_driver_close(void);
 
-int  usbip_vhci_refresh_device_list(void);
-
-
 int usbip_vhci_get_free_port(void);
-struct usbip_imported_device *usbip_vhci_get_device(int port);
-struct usbip_imported_device *usbip_vhci_find_device(char *host, char *busid);
-int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
-		uint32_t speed);
 
-/* will be removed */
-int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
-		uint8_t devnum, uint32_t speed);
+int usbip_vhci_attach_device(int port, int sockfd, uint8_t busnum,
+			uint8_t devnum, uint32_t speed);
+int usbip_vhci_detach_device(int port);
 
-int usbip_vhci_detach_device(uint8_t port);
+int usbip_vhci_find_device(const char *host, const char *busid);
+int usbip_vhci_imported_devices_dump(void);
 
-int usbip_vhci_create_record(char *host, char *port, char *busid, int rhport);
+int usbip_vhci_create_record(const char *host, const char *port,
+			const char *busid, int rhport);
 int usbip_vhci_delete_record(int rhport);
 
-int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev);
-
 #endif /* __VHCI_DRIVER_H */
diff --git a/tools/usb/usbip/src/usbip_attach.c b/tools/usb/usbip/src/usbip_attach.c
index 0817e92..46ac398 100644
--- a/tools/usb/usbip/src/usbip_attach.c
+++ b/tools/usb/usbip/src/usbip_attach.c
@@ -131,7 +131,7 @@ static int attach_device(char *host, char *busid)
 {
 	int sockfd;
 	int rc;
-	int rhport;
+	int port_nr;
 
 	sockfd = usbip_net_tcp_connect(host, usbip_port_string);
 	if (sockfd < 0) {
@@ -139,8 +139,8 @@ static int attach_device(char *host, char *busid)
 		return -1;
 	}
 
-	rhport = query_import_device(sockfd, busid);
-	if (rhport < 0) {
+	port_nr = query_import_device(sockfd, busid);
+	if (port_nr < 0) {
 		err("query");
 		close(sockfd);
 		return -1;
@@ -148,7 +148,7 @@ static int attach_device(char *host, char *busid)
 
 	close(sockfd);
 
-	rc = usbip_vhci_create_record(host, usbip_port_string, busid, rhport);
+	rc = usbip_vhci_create_record(host, usbip_port_string, busid, port_nr);
 	if (rc < 0) {
 		err("record connection");
 		return -1;
diff --git a/tools/usb/usbip/src/usbip_port.c b/tools/usb/usbip/src/usbip_port.c
index acb49c71..b3959ca 100644
--- a/tools/usb/usbip/src/usbip_port.c
+++ b/tools/usb/usbip/src/usbip_port.c
@@ -24,8 +24,6 @@ void usbip_port_usage(void)
 
 static int list_imported_devices(void)
 {
-	int i;
-	struct usbip_imported_device *idev;
 	int ret;
 
 	if (usbip_names_init(USBIDS_FILE))
@@ -33,18 +31,17 @@ static int list_imported_devices(void)
 
 	ret = usbip_vhci_driver_open();
 	if (ret < 0) {
-		err("open vhci_driver");
+		err("open vhci driver");
 		goto err_names_free;
 	}
 
 	printf("Imported USB devices\n");
 	printf("====================\n");
 
-	for (i = 0; i < vhci_driver->nports; i++) {
-		idev = &vhci_driver->idev[i];
-
-		if (usbip_vhci_imported_device_dump(idev) < 0)
-			goto err_driver_close;
+	ret = usbip_vhci_imported_devices_dump();
+	if (ret < 0) {
+		err("dump vhci devices");
+		goto err_driver_close;
 	}
 
 	usbip_vhci_driver_close();
diff --git a/tools/usb/usbip/src/usbipd_app.c b/tools/usb/usbip/src/usbipd_app.c
index 29e112f..b28fdf8 100644
--- a/tools/usb/usbip/src/usbipd_app.c
+++ b/tools/usb/usbip/src/usbipd_app.c
@@ -78,7 +78,7 @@ static int recv_request_export(int sockfd, char *host, char *port)
 {
 	struct op_export_request req;
 	struct op_export_reply reply;
-	int rhport = 0;
+	int port_nr = 0;
 	int error = 0;
 	int rc;
 
@@ -92,8 +92,8 @@ static int recv_request_export(int sockfd, char *host, char *port)
 	}
 	PACK_OP_EXPORT_REQUEST(0, &req);
 
-	rhport = import_device(sockfd, &req.udev);
-	if (rhport < 0) {
+	port_nr = import_device(sockfd, &req.udev);
+	if (port_nr < 0) {
 		dbg("export request busid %s: failed", req.udev.busid);
 		error = 1;
 	}
@@ -118,41 +118,42 @@ static int recv_request_export(int sockfd, char *host, char *port)
 		return -1;
 	}
 
-	rc = usbip_vhci_create_record(host, port, req.udev.busid, rhport);
-	if (rc < 0) {
-		err("record connection");
-		return -1;
+	if (!error) {
+		rc = usbip_vhci_create_record(host, port, req.udev.busid,
+					      port_nr);
+		if (rc < 0) {
+			err("record connection");
+			return -1;
+		}
 	}
-
-	dbg("export request busid %s: complete", req.udev.busid);
+	dbg("export request busid %s: complete %d", req.udev.busid, error);
 
 	return 0;
 }
 
 static int unimport_device(char *host, struct usbip_usb_device *udev)
 {
-	int rc;
-	struct usbip_imported_device *idev;
+	int port, rc;
 
-	idev = usbip_vhci_find_device(host, udev->busid);
-	if (idev == NULL) {
+	port = usbip_vhci_find_device(host, udev->busid);
+	if (port < 0) {
 		err("no imported port %s %s", host, udev->busid);
 		return -1;
 	}
 
-	rc = usbip_vhci_detach_device(idev->port);
+	rc = usbip_vhci_detach_device(port);
 	if (rc < 0) {
-		err("no imported port %d %s %s", idev->port, host, udev->busid);
+		err("no imported port %d %s %s", port, host, udev->busid);
 		return -1;
 	}
-	return idev->port;
+	return port;
 }
 
 static int recv_request_unexport(int sockfd, char *host)
 {
 	struct op_unexport_request req;
 	struct op_unexport_reply reply;
-	int rhport = 0;
+	int port_nr = 0;
 	int error = 0;
 	int rc;
 
@@ -166,8 +167,8 @@ static int recv_request_unexport(int sockfd, char *host)
 	}
 	PACK_OP_UNEXPORT_REQUEST(0, &req);
 
-	rhport = unimport_device(host, &req.udev);
-	if (rhport < 0)
+	port_nr = unimport_device(host, &req.udev);
+	if (port_nr < 0)
 		error = 1;
 
 	rc = usbip_net_send_op_common(sockfd, OP_REP_UNEXPORT,
@@ -192,7 +193,8 @@ static int recv_request_unexport(int sockfd, char *host)
 		return -1;
 	}
 
-	usbip_vhci_delete_record(rhport);
+	if (!error)
+		usbip_vhci_delete_record(port_nr);
 
 	dbg("unexport request busid %s: complete", req.udev.busid);
 
@@ -210,12 +212,6 @@ int usbip_recv_pdu(int connfd, char *host, char *port)
 		return -1;
 	}
 
-	ret = usbip_vhci_refresh_device_list();
-	if (ret < 0) {
-		dbg("could not refresh device list: %d", ret);
-		return -1;
-	}
-
 	info("received request: %#0x(%d)", code, connfd);
 	switch (code) {
 	case OP_REQ_EXPORT:
@@ -236,4 +232,3 @@ int usbip_recv_pdu(int connfd, char *host, char *port)
 
 	return ret;
 }
-
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux