From aa5873e96271611ae55586f65e49ea1fab90cb88 Mon Sep 17 00:00:00 2001 From: Endre Kollar Date: Tue, 27 Jul 2010 12:39:30 +0200 Subject: [PATCH] Staging: usbip: fix multiple interfaces 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 Signed-off-by: Greg Kroah-Hartman --- 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 022d0649ac5e..30dbfb6d16f2 100644 --- a/drivers/staging/usbip/stub.h +++ b/drivers/staging/usbip/stub.h @@ -25,6 +25,11 @@ #include #include +#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 1f4085116927..b6b753a49346 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; + } - /* 2. wait for the stop of the event handler */ - usbip_stop_eh(&sdev->ud); + if (busid_priv->interf_count > 1) { + busid_priv->interf_count--; + shutdown_busid(busid_priv); + return; + } + + busid_priv->interf_count = 0; + + + /* 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 6665cefe573b..f3a40968aae2 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);