Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aa5873e9 authored by Endre Kollar's avatar Endre Kollar Committed by Greg Kroah-Hartman
Browse files

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: default avatarEndre Kollar <taxy443@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 125ed824
Loading
Loading
Loading
Loading
+16 −1
Original line number Original line Diff line number Diff line
@@ -25,6 +25,11 @@
#include <linux/module.h>
#include <linux/module.h>
#include <linux/net.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 stub_device {
	struct usb_interface *interface;
	struct usb_interface *interface;
	struct list_head list;
	struct list_head list;
@@ -72,6 +77,14 @@ struct stub_unlink {
	__u32 status;
	__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;
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);
void stub_enqueue_ret_unlink(struct stub_device *, __u32, __u32);


/* stub_main.c */
/* 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);
void stub_device_cleanup_urbs(struct stub_device *sdev);
+91 −10
Original line number Original line Diff line number Diff line
@@ -393,11 +393,14 @@ static int stub_probe(struct usb_interface *interface,
	struct stub_device *sdev = NULL;
	struct stub_device *sdev = NULL;
	const char *udev_busid = dev_name(interface->dev.parent);
	const char *udev_busid = dev_name(interface->dev.parent);
	int err = 0;
	int err = 0;
	struct bus_id_priv *busid_priv;


	dev_dbg(&interface->dev, "Enter\n");
	dev_dbg(&interface->dev, "Enter\n");


	/* check we should claim or not by busid_table */
	/* 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,
		dev_info(&interface->dev,
			 "this device %s is not in match_busid table. skip!\n",
			 "this device %s is not in match_busid table. skip!\n",
			 udev_busid);
			 udev_busid);
@@ -422,30 +425,80 @@ static int stub_probe(struct usb_interface *interface,
		return -ENODEV;
		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. */
	/* ok. this is my device. */
	sdev = stub_device_alloc(interface);
	sdev = stub_device_alloc(interface);
	if (!sdev)
	if (!sdev)
		return -ENOMEM;
		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,
		 "(bus %u dev %u ifn %u)\n", udev->bus->busnum, udev->devnum,
		 interface->cur_altsetting->desc.bInterfaceNumber);
		 interface->cur_altsetting->desc.bInterfaceNumber);


	busid_priv->interf_count = 0;
	busid_priv->shutdown_busid = 0;

	/* set private data to usb_interface */
	/* set private data to usb_interface */
	usb_set_intfdata(interface, sdev);
	usb_set_intfdata(interface, sdev);
	busid_priv->interf_count++;

	busid_priv->sdev = sdev;


	err = stub_add_files(&interface->dev);
	err = stub_add_files(&interface->dev);
	if (err) {
	if (err) {
		dev_err(&interface->dev, "create sysfs files for %s\n",
		dev_err(&interface->dev, "create sysfs files for %s\n",
			udev_busid);
			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);
		stub_device_free(sdev);
		return err;
		return err;
	}
	}
	busid_priv->status = STUB_BUSID_ALLOC;


	return 0;
	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()
 * 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)
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");
	usbip_udbg("Enter\n");


	if (!busid_priv) {
		BUG();
		return;
	}

	sdev = usb_get_intfdata(interface);

	/* get stub_device */
	/* get stub_device */
	if (!sdev) {
	if (!sdev) {
		err(" could not get device from inteface data");
		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);
	usb_set_intfdata(interface, NULL);



	/*
	/*
	 * NOTE:
	 * NOTE:
	 * rx/tx threads are invoked for each usb_device.
	 * rx/tx threads are invoked for each usb_device.
	 */
	 */
	stub_remove_files(&interface->dev);
	stub_remove_files(&interface->dev);


	/* 1. shutdown the current connection */
	/*If usb reset called from event handler*/
	usbip_event_add(&sdev->ud, SDEV_EVENT_REMOVED);
	if (busid_priv->sdev->ud.eh.thread == current) {
		busid_priv->interf_count--;
		return;
	}


	/* 2. wait for the stop of the event handler */
	if (busid_priv->interf_count > 1) {
	usbip_stop_eh(&sdev->ud);
		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 */
	/* 3. free sdev */
	busid_priv->sdev = NULL;
	stub_device_free(sdev);
	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");
	usbip_udbg("bye\n");
}
}
+52 −13
Original line number Original line Diff line number Diff line
@@ -41,8 +41,7 @@ struct kmem_cache *stub_priv_cache;
 * remote host.
 * remote host.
 */
 */
#define MAX_BUSID 16
#define MAX_BUSID 16
#define BUSID_SIZE 20
static struct bus_id_priv busid_table[MAX_BUSID];
static char busid_table[MAX_BUSID][BUSID_SIZE];
static spinlock_t busid_table_lock;
static spinlock_t busid_table_lock;




@@ -53,8 +52,8 @@ int match_busid(const char *busid)
	spin_lock(&busid_table_lock);
	spin_lock(&busid_table_lock);


	for (i = 0; i < MAX_BUSID; i++)
	for (i = 0; i < MAX_BUSID; i++)
		if (busid_table[i][0])
		if (busid_table[i].name[0])
			if (!strncmp(busid_table[i], busid, BUSID_SIZE)) {
			if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
				/* already registerd */
				/* already registerd */
				spin_unlock(&busid_table_lock);
				spin_unlock(&busid_table_lock);
				return 0;
				return 0;
@@ -65,6 +64,25 @@ int match_busid(const char *busid)
	return 1;
	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)
static ssize_t show_match_busid(struct device_driver *drv, char *buf)
{
{
	int i;
	int i;
@@ -73,8 +91,8 @@ static ssize_t show_match_busid(struct device_driver *drv, char *buf)
	spin_lock(&busid_table_lock);
	spin_lock(&busid_table_lock);


	for (i = 0; i < MAX_BUSID; i++)
	for (i = 0; i < MAX_BUSID; i++)
		if (busid_table[i][0])
		if (busid_table[i].name[0])
			out += sprintf(out, "%s ", busid_table[i]);
			out += sprintf(out, "%s ", busid_table[i].name);


	spin_unlock(&busid_table_lock);
	spin_unlock(&busid_table_lock);


@@ -93,8 +111,11 @@ static int add_match_busid(char *busid)
	spin_lock(&busid_table_lock);
	spin_lock(&busid_table_lock);


	for (i = 0; i < MAX_BUSID; i++)
	for (i = 0; i < MAX_BUSID; i++)
		if (!busid_table[i][0]) {
		if (!busid_table[i].name[0]) {
			strncpy(busid_table[i], busid, BUSID_SIZE);
			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);
			spin_unlock(&busid_table_lock);
			return 0;
			return 0;
		}
		}
@@ -104,16 +125,21 @@ static int add_match_busid(char *busid)
	return -1;
	return -1;
}
}


static int del_match_busid(char *busid)
int del_match_busid(char *busid)
{
{
	int i;
	int i;


	spin_lock(&busid_table_lock);
	spin_lock(&busid_table_lock);


	for (i = 0; i < MAX_BUSID; i++)
	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 */
			/* 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);
			spin_unlock(&busid_table_lock);
			return 0;
			return 0;
		}
		}
@@ -122,6 +148,20 @@ static int del_match_busid(char *busid)


	return -1;
	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,
static ssize_t store_match_busid(struct device_driver *dev, const char *buf,
		size_t count)
		size_t count)
@@ -261,8 +301,7 @@ static int __init usb_stub_init(void)
	printk(KERN_INFO KBUILD_MODNAME ":"
	printk(KERN_INFO KBUILD_MODNAME ":"
	       DRIVER_DESC ":" DRIVER_VERSION "\n");
	       DRIVER_DESC ":" DRIVER_VERSION "\n");


	memset(busid_table, 0, sizeof(busid_table));
	init_busid_table();
	spin_lock_init(&busid_table_lock);


	ret = driver_create_file(&stub_driver.drvwrap.driver,
	ret = driver_create_file(&stub_driver.drvwrap.driver,
				 &driver_attr_match_busid);
				 &driver_attr_match_busid);