[PATCH 460/524] Staging: usbip: fix multiple interfaces

Greg Kroah-Hartman gregkh at suse.de
Thu Aug 5 22:24:03 UTC 2010


From: Endre Kollar <taxy443 at gmail.com>

The stub_probe function instantiates an stub_dev object  for all
interfaces. Wich causes a problem.  The stub_dev object belongs to their
own interfaces. This patch creates the sdev object at the first
stub_probe call, the other calls associate the interfaces to this.

Signed-off-by: Endre Kollar <taxy443 at gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh at suse.de>
---
 drivers/staging/usbip/stub.h      |   17 ++++++-
 drivers/staging/usbip/stub_dev.c  |  101 +++++++++++++++++++++++++++++++++----
 drivers/staging/usbip/stub_main.c |   65 +++++++++++++++++++-----
 3 files changed, 159 insertions(+), 24 deletions(-)

diff --git a/drivers/staging/usbip/stub.h b/drivers/staging/usbip/stub.h
index 022d064..30dbfb6 100644
--- a/drivers/staging/usbip/stub.h
+++ b/drivers/staging/usbip/stub.h
@@ -25,6 +25,11 @@
 #include <linux/module.h>
 #include <linux/net.h>
 
+#define STUB_BUSID_OTHER 0
+#define STUB_BUSID_REMOV 1
+#define STUB_BUSID_ADDED 2
+#define STUB_BUSID_ALLOC 3
+
 struct stub_device {
 	struct usb_interface *interface;
 	struct list_head list;
@@ -72,6 +77,14 @@ struct stub_unlink {
 	__u32 status;
 };
 
+#define BUSID_SIZE 20
+struct bus_id_priv {
+	char name[BUSID_SIZE];
+	char status;
+	int interf_count;
+	struct stub_device *sdev;
+	char shutdown_busid;
+};
 
 extern struct kmem_cache *stub_priv_cache;
 
@@ -91,5 +104,7 @@ void stub_rx_loop(struct usbip_task *);
 void stub_enqueue_ret_unlink(struct stub_device *, __u32, __u32);
 
 /* stub_main.c */
-int match_busid(const char *busid);
+struct bus_id_priv *get_busid_priv(const char *busid);
+int del_match_busid(char *busid);
+
 void stub_device_cleanup_urbs(struct stub_device *sdev);
diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
index 1f40851..b6b753a 100644
--- a/drivers/staging/usbip/stub_dev.c
+++ b/drivers/staging/usbip/stub_dev.c
@@ -393,11 +393,14 @@ static int stub_probe(struct usb_interface *interface,
 	struct stub_device *sdev = NULL;
 	const char *udev_busid = dev_name(interface->dev.parent);
 	int err = 0;
+	struct bus_id_priv *busid_priv;
 
 	dev_dbg(&interface->dev, "Enter\n");
 
 	/* check we should claim or not by busid_table */
-	if (match_busid(udev_busid)) {
+	busid_priv = get_busid_priv(udev_busid);
+	if (!busid_priv  || (busid_priv->status == STUB_BUSID_REMOV) ||
+			     (busid_priv->status == STUB_BUSID_OTHER)) {
 		dev_info(&interface->dev,
 			 "this device %s is not in match_busid table. skip!\n",
 			 udev_busid);
@@ -422,30 +425,80 @@ static int stub_probe(struct usb_interface *interface,
 		return -ENODEV;
 	}
 
+
+	if (busid_priv->status == STUB_BUSID_ALLOC) {
+		busid_priv->interf_count++;
+		sdev = busid_priv->sdev;
+		if (!sdev)
+			return -ENODEV;
+
+		dev_info(&interface->dev,
+		 "USB/IP Stub: register a new interface "
+		 "(bus %u dev %u ifn %u)\n", udev->bus->busnum, udev->devnum,
+		 interface->cur_altsetting->desc.bInterfaceNumber);
+
+		/* set private data to usb_interface */
+		usb_set_intfdata(interface, sdev);
+
+		err = stub_add_files(&interface->dev);
+		if (err) {
+			dev_err(&interface->dev, "create sysfs files for %s\n",
+				udev_busid);
+			usb_set_intfdata(interface, NULL);
+			busid_priv->interf_count--;
+
+			return err;
+		}
+
+		return 0;
+	}
+
 	/* ok. this is my device. */
 	sdev = stub_device_alloc(interface);
 	if (!sdev)
 		return -ENOMEM;
 
-	dev_info(&interface->dev, "USB/IP Stub: register a new interface "
+	dev_info(&interface->dev, "USB/IP Stub: register a new device "
 		 "(bus %u dev %u ifn %u)\n", udev->bus->busnum, udev->devnum,
 		 interface->cur_altsetting->desc.bInterfaceNumber);
 
+	busid_priv->interf_count = 0;
+	busid_priv->shutdown_busid = 0;
+
 	/* set private data to usb_interface */
 	usb_set_intfdata(interface, sdev);
+	busid_priv->interf_count++;
+
+	busid_priv->sdev = sdev;
 
 	err = stub_add_files(&interface->dev);
 	if (err) {
 		dev_err(&interface->dev, "create sysfs files for %s\n",
 			udev_busid);
-		usb_set_intfdata(interface, 0);
+		usb_set_intfdata(interface, NULL);
+		busid_priv->interf_count = 0;
+
+		busid_priv->sdev = NULL;
 		stub_device_free(sdev);
 		return err;
 	}
+	busid_priv->status = STUB_BUSID_ALLOC;
 
 	return 0;
 }
 
+static void shutdown_busid(struct bus_id_priv *busid_priv)
+{
+	if (busid_priv->sdev && !busid_priv->shutdown_busid) {
+		busid_priv->shutdown_busid = 1;
+		usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED);
+
+		/* 2. wait for the stop of the event handler */
+		usbip_stop_eh(&busid_priv->sdev->ud);
+	}
+
+}
+
 
 /*
  * called in usb_disconnect() or usb_deregister()
@@ -453,10 +506,21 @@ static int stub_probe(struct usb_interface *interface,
  */
 static void stub_disconnect(struct usb_interface *interface)
 {
-	struct stub_device *sdev = usb_get_intfdata(interface);
+	struct stub_device *sdev;
+	const char *udev_busid = dev_name(interface->dev.parent);
+	struct bus_id_priv *busid_priv;
+
+	busid_priv = get_busid_priv(udev_busid);
 
 	usbip_udbg("Enter\n");
 
+	if (!busid_priv) {
+		BUG();
+		return;
+	}
+
+	sdev = usb_get_intfdata(interface);
+
 	/* get stub_device */
 	if (!sdev) {
 		err(" could not get device from inteface data");
@@ -466,22 +530,39 @@ static void stub_disconnect(struct usb_interface *interface)
 
 	usb_set_intfdata(interface, NULL);
 
-
 	/*
 	 * NOTE:
 	 * rx/tx threads are invoked for each usb_device.
 	 */
 	stub_remove_files(&interface->dev);
 
-	/* 1. shutdown the current connection */
-	usbip_event_add(&sdev->ud, SDEV_EVENT_REMOVED);
+	/*If usb reset called from event handler*/
+	if (busid_priv->sdev->ud.eh.thread == current) {
+		busid_priv->interf_count--;
+		return;
+	}
+
+	if (busid_priv->interf_count > 1) {
+		busid_priv->interf_count--;
+		shutdown_busid(busid_priv);
+		return;
+	}
+
+	busid_priv->interf_count = 0;
 
-	/* 2. wait for the stop of the event handler */
-	usbip_stop_eh(&sdev->ud);
+
+	/* 1. shutdown the current connection */
+	shutdown_busid(busid_priv);
 
 	/* 3. free sdev */
+	busid_priv->sdev = NULL;
 	stub_device_free(sdev);
 
-
+	if (busid_priv->status == STUB_BUSID_ALLOC) {
+		busid_priv->status = STUB_BUSID_ADDED;
+	} else {
+		busid_priv->status = STUB_BUSID_OTHER;
+		del_match_busid((char *)udev_busid);
+	}
 	usbip_udbg("bye\n");
 }
diff --git a/drivers/staging/usbip/stub_main.c b/drivers/staging/usbip/stub_main.c
index 6665cef..f3a4096 100644
--- a/drivers/staging/usbip/stub_main.c
+++ b/drivers/staging/usbip/stub_main.c
@@ -41,8 +41,7 @@ struct kmem_cache *stub_priv_cache;
  * remote host.
  */
 #define MAX_BUSID 16
-#define BUSID_SIZE 20
-static char busid_table[MAX_BUSID][BUSID_SIZE];
+static struct bus_id_priv busid_table[MAX_BUSID];
 static spinlock_t busid_table_lock;
 
 
@@ -53,8 +52,8 @@ int match_busid(const char *busid)
 	spin_lock(&busid_table_lock);
 
 	for (i = 0; i < MAX_BUSID; i++)
-		if (busid_table[i][0])
-			if (!strncmp(busid_table[i], busid, BUSID_SIZE)) {
+		if (busid_table[i].name[0])
+			if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
 				/* already registerd */
 				spin_unlock(&busid_table_lock);
 				return 0;
@@ -65,6 +64,25 @@ int match_busid(const char *busid)
 	return 1;
 }
 
+struct bus_id_priv *get_busid_priv(const char *busid)
+{
+	int i;
+
+	spin_lock(&busid_table_lock);
+
+	for (i = 0; i < MAX_BUSID; i++)
+		if (busid_table[i].name[0])
+			if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
+				/* already registerd */
+				spin_unlock(&busid_table_lock);
+				return &(busid_table[i]);
+			}
+
+	spin_unlock(&busid_table_lock);
+
+	return NULL;
+}
+
 static ssize_t show_match_busid(struct device_driver *drv, char *buf)
 {
 	int i;
@@ -73,8 +91,8 @@ static ssize_t show_match_busid(struct device_driver *drv, char *buf)
 	spin_lock(&busid_table_lock);
 
 	for (i = 0; i < MAX_BUSID; i++)
-		if (busid_table[i][0])
-			out += sprintf(out, "%s ", busid_table[i]);
+		if (busid_table[i].name[0])
+			out += sprintf(out, "%s ", busid_table[i].name);
 
 	spin_unlock(&busid_table_lock);
 
@@ -93,8 +111,11 @@ static int add_match_busid(char *busid)
 	spin_lock(&busid_table_lock);
 
 	for (i = 0; i < MAX_BUSID; i++)
-		if (!busid_table[i][0]) {
-			strncpy(busid_table[i], busid, BUSID_SIZE);
+		if (!busid_table[i].name[0]) {
+			strncpy(busid_table[i].name, busid, BUSID_SIZE);
+			if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
+			    (busid_table[i].status != STUB_BUSID_REMOV))
+				busid_table[i].status = STUB_BUSID_ADDED;
 			spin_unlock(&busid_table_lock);
 			return 0;
 		}
@@ -104,16 +125,21 @@ static int add_match_busid(char *busid)
 	return -1;
 }
 
-static int del_match_busid(char *busid)
+int del_match_busid(char *busid)
 {
 	int i;
 
 	spin_lock(&busid_table_lock);
 
 	for (i = 0; i < MAX_BUSID; i++)
-		if (!strncmp(busid_table[i], busid, BUSID_SIZE)) {
+		if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
 			/* found */
-			memset(busid_table[i], 0, BUSID_SIZE);
+			if (busid_table[i].status == STUB_BUSID_OTHER)
+				memset(busid_table[i].name, 0, BUSID_SIZE);
+			if ((busid_table[i].status != STUB_BUSID_OTHER) &&
+			    (busid_table[i].status != STUB_BUSID_ADDED)) {
+				busid_table[i].status = STUB_BUSID_REMOV;
+			}
 			spin_unlock(&busid_table_lock);
 			return 0;
 		}
@@ -122,6 +148,20 @@ static int del_match_busid(char *busid)
 
 	return -1;
 }
+static void init_busid_table(void)
+{
+	int i;
+
+
+	for (i = 0; i < MAX_BUSID; i++) {
+		memset(busid_table[i].name, 0, BUSID_SIZE);
+		busid_table[i].status = STUB_BUSID_OTHER;
+		busid_table[i].interf_count = 0;
+		busid_table[i].sdev = NULL;
+		busid_table[i].shutdown_busid = 0;
+	}
+	spin_lock_init(&busid_table_lock);
+}
 
 static ssize_t store_match_busid(struct device_driver *dev, const char *buf,
 		size_t count)
@@ -261,8 +301,7 @@ static int __init usb_stub_init(void)
 	printk(KERN_INFO KBUILD_MODNAME ":"
 	       DRIVER_DESC ":" DRIVER_VERSION "\n");
 
-	memset(busid_table, 0, sizeof(busid_table));
-	spin_lock_init(&busid_table_lock);
+	init_busid_table();
 
 	ret = driver_create_file(&stub_driver.drvwrap.driver,
 				 &driver_attr_match_busid);
-- 
1.7.1




More information about the devel mailing list