Staging: usbip: fix multiple interfaces
authorEndre Kollar <taxy443@gmail.com>
Tue, 27 Jul 2010 10:39:30 +0000 (12:39 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 27 Jul 2010 18:06:29 +0000 (11:06 -0700)
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@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/staging/usbip/stub.h
drivers/staging/usbip/stub_dev.c
drivers/staging/usbip/stub_main.c

index 022d0649ac5ee219b67d5b50ad7f95f58432b5d3..30dbfb6d16f20febb872a60ab882490d9c87abfb 100644 (file)
 #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);
index 1f408511692748efd19dce9e7101449d42786e21..b6b753a493469c4c2a7067eb8a3a0542bf9f39c1 100644 (file)
@@ -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");
 }
index 6665cefe573b539a2c4046bc2280594500f8d2a6..f3a40968aae2f60614e45af70b4eee660b4f5d58 100644 (file)
@@ -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);
This page took 0.029368 seconds and 5 git commands to generate.