summaryrefslogtreecommitdiffstats
path: root/drivers/staging/usbip
diff options
context:
space:
mode:
authorEndre Kollar <taxy443@gmail.com>2010-07-27 12:39:30 +0200
committerGreg Kroah-Hartman <gregkh@suse.de>2010-07-27 11:06:29 -0700
commitaa5873e96271611ae55586f65e49ea1fab90cb88 (patch)
treeb2a0c0b5d06976b262e4403225d6ca3bc8380daf /drivers/staging/usbip
parent125ed824d3d03f5c470cf98aca757fd9423c49ab (diff)
downloadblackbird-obmc-linux-aa5873e96271611ae55586f65e49ea1fab90cb88.tar.gz
blackbird-obmc-linux-aa5873e96271611ae55586f65e49ea1fab90cb88.zip
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 <taxy443@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/usbip')
-rw-r--r--drivers/staging/usbip/stub.h17
-rw-r--r--drivers/staging/usbip/stub_dev.c101
-rw-r--r--drivers/staging/usbip/stub_main.c65
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 <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 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;
+ }
+
+ 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 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);
OpenPOWER on IntegriCloud