diff options
Diffstat (limited to '')
-rw-r--r-- | drivers/staging/usbip/stub_dev.c | 101 |
1 files changed, 92 insertions, 9 deletions
diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c index 3f95605427a7..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,28 +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, 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() @@ -451,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"); @@ -464,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"); } |