ChangeSet 1.1123.18.9, 2003/08/13 10:08:22-07:00, greg@kroah.com

[PATCH] USB: fix usb serial port release problem by untieing it from the usb_serial structure.

This fixes the kobject messages when disconnecting a usb serial device that
people have been complaining about.


 drivers/usb/serial/usb-serial.c |  153 ++++++++++++++++++++++++----------------
 drivers/usb/serial/usb-serial.h |    3 
 2 files changed, 95 insertions(+), 61 deletions(-)


diff -Nru a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
--- a/drivers/usb/serial/usb-serial.c	Fri Aug 15 10:46:59 2003
+++ b/drivers/usb/serial/usb-serial.c	Fri Aug 15 10:46:59 2003
@@ -466,7 +466,7 @@
 
 	/* set up our port structure making the tty driver remember our port object, and us it */
 	portNumber = tty->index - serial->minor;
-	port = &serial->port[portNumber];
+	port = serial->port[portNumber];
 	tty->driver_data = port;
 
 	port->tty = tty;
@@ -863,13 +863,13 @@
 	struct usb_serial_port *port;
 	int i;
 
-	dbg ("%s", __FUNCTION__);
+	dbg ("%s - %s", __FUNCTION__, kobj->name);
 
 	serial = to_usb_serial(kobj);
 
 	/* fail all future close/read/write/ioctl/etc calls */
 	for (i = 0; i < serial->num_ports; ++i) {
-		port = &serial->port[i];
+		port = serial->port[i];
 		if (port->tty != NULL) {
 			port->tty->driver_data = NULL;
 			while (port->open_count > 0) {
@@ -881,41 +881,41 @@
 
 	serial_shutdown (serial);
 
+	/* return the minor range that this device had */
+	return_serial(serial);
+
 	for (i = 0; i < serial->num_ports; ++i)
-		device_unregister(&serial->port[i].dev);
+		serial->port[i]->open_count = 0;
 
+	/* the ports are cleaned up and released in port_release() */
 	for (i = 0; i < serial->num_ports; ++i)
-		serial->port[i].open_count = 0;
+		device_unregister(&serial->port[i]->dev);
 
-	for (i = 0; i < serial->num_bulk_in; ++i) {
-		port = &serial->port[i];
-		if (port->read_urb) {
-			usb_unlink_urb (port->read_urb);
-			usb_free_urb (port->read_urb);
-		}
-		if (port->bulk_in_buffer)
-			kfree (port->bulk_in_buffer);
-	}
-	for (i = 0; i < serial->num_bulk_out; ++i) {
-		port = &serial->port[i];
-		if (port->write_urb) {
-			usb_unlink_urb (port->write_urb);
-			usb_free_urb (port->write_urb);
-		}
-		if (port->bulk_out_buffer)
-			kfree (port->bulk_out_buffer);
-	}
-	for (i = 0; i < serial->num_interrupt_in; ++i) {
-		port = &serial->port[i];
-		if (port->interrupt_in_urb) {
-			usb_unlink_urb (port->interrupt_in_urb);
-			usb_free_urb (port->interrupt_in_urb);
+	/* If this is a "fake" port, we have to clean it up here, as it will
+	 * not get cleaned up in port_release() as it was never registered with
+	 * the driver core */
+	if (serial->num_ports < serial->num_port_pointers) {
+		for (i = serial->num_ports; i < serial->num_port_pointers; ++i) {
+			port = serial->port[i];
+			if (!port)
+				continue;
+			if (port->read_urb) {
+				usb_unlink_urb(port->read_urb);
+				usb_free_urb(port->read_urb);
+			}
+			if (port->write_urb) {
+				usb_unlink_urb(port->write_urb);
+				usb_free_urb(port->write_urb);
+			}
+			if (port->interrupt_in_urb) {
+				usb_unlink_urb(port->interrupt_in_urb);
+				usb_free_urb(port->interrupt_in_urb);
+			}
+			kfree(port->bulk_in_buffer);
+			kfree(port->bulk_out_buffer);
+			kfree(port->interrupt_in_buffer);
 		}
-		if (port->interrupt_in_buffer)
-			kfree (port->interrupt_in_buffer);
 	}
-	/* return the minor range that this device had */
-	return_serial (serial);
 
 	usb_put_dev(serial->dev);
 
@@ -927,6 +927,29 @@
 	.release = destroy_serial,
 };
 
+static void port_release(struct device *dev)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+
+	dbg ("%s - %s", __FUNCTION__, dev->bus_id);
+	if (port->read_urb) {
+		usb_unlink_urb(port->read_urb);
+		usb_free_urb(port->read_urb);
+	}
+	if (port->write_urb) {
+		usb_unlink_urb(port->write_urb);
+		usb_free_urb(port->write_urb);
+	}
+	if (port->interrupt_in_urb) {
+		usb_unlink_urb(port->interrupt_in_urb);
+		usb_free_urb(port->interrupt_in_urb);
+	}
+	kfree(port->bulk_in_buffer);
+	kfree(port->bulk_out_buffer);
+	kfree(port->interrupt_in_buffer);
+	kfree(port);
+}
+
 static struct usb_serial * create_serial (struct usb_device *dev, 
 					  struct usb_interface *interface,
 					  struct usb_serial_device_type *type)
@@ -1124,10 +1147,29 @@
 	serial->num_bulk_out = num_bulk_out;
 	serial->num_interrupt_in = num_interrupt_in;
 
+	/* create our ports, we need as many as the max endpoints */
+	/* we don't use num_ports here cauz some devices have more endpoint pairs than ports */
+	max_endpoints = max(num_bulk_in, num_bulk_out);
+	max_endpoints = max(max_endpoints, num_interrupt_in);
+	max_endpoints = max(max_endpoints, (int)serial->num_ports);
+	serial->num_port_pointers = max_endpoints;
+	dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints);
+	for (i = 0; i < max_endpoints; ++i) {
+		port = kmalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
+		if (!port)
+			goto probe_error;
+		memset(port, 0x00, sizeof(struct usb_serial_port));
+		port->number = i + serial->minor;
+		port->serial = serial;
+		port->magic = USB_SERIAL_PORT_MAGIC;
+		INIT_WORK(&port->work, usb_serial_port_softint, port);
+		serial->port[i] = port;
+	}
+
 	/* set up the endpoint information */
 	for (i = 0; i < num_bulk_in; ++i) {
 		endpoint = bulk_in_endpoint[i];
-		port = &serial->port[i];
+		port = serial->port[i];
 		port->read_urb = usb_alloc_urb (0, GFP_KERNEL);
 		if (!port->read_urb) {
 			dev_err(&interface->dev, "No free urbs available\n");
@@ -1152,7 +1194,7 @@
 
 	for (i = 0; i < num_bulk_out; ++i) {
 		endpoint = bulk_out_endpoint[i];
-		port = &serial->port[i];
+		port = serial->port[i];
 		port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
 		if (!port->write_urb) {
 			dev_err(&interface->dev, "No free urbs available\n");
@@ -1178,7 +1220,7 @@
 
 	for (i = 0; i < num_interrupt_in; ++i) {
 		endpoint = interrupt_in_endpoint[i];
-		port = &serial->port[i];
+		port = serial->port[i];
 		port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
 		if (!port->interrupt_in_urb) {
 			dev_err(&interface->dev, "No free urbs available\n");
@@ -1199,20 +1241,6 @@
 				  endpoint->bInterval);
 	}
 
-	/* initialize some parts of the port structures */
-	/* we don't use num_ports here cauz some devices have more endpoint pairs than ports */
-	max_endpoints = max(num_bulk_in, num_bulk_out);
-	max_endpoints = max(max_endpoints, num_interrupt_in);
-	max_endpoints = max(max_endpoints, (int)serial->num_ports);
-	dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints);
-	for (i = 0; i < max_endpoints; ++i) {
-		port = &serial->port[i];
-		port->number = i + serial->minor;
-		port->serial = serial;
-		port->magic = USB_SERIAL_PORT_MAGIC;
-		INIT_WORK(&port->work, usb_serial_port_softint, port);
-	}
-
 	/* if this device type has an attach function, call it */
 	if (type->attach) {
 		if (!try_module_get(type->owner)) {
@@ -1232,10 +1260,11 @@
 
 	/* register all of the individual ports with the driver core */
 	for (i = 0; i < num_ports; ++i) {
-		port = &serial->port[i];
+		port = serial->port[i];
 		port->dev.parent = &serial->dev->dev;
 		port->dev.driver = NULL;
 		port->dev.bus = &usb_serial_bus_type;
+		port->dev.release = &port_release;
 
 		snprintf (&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number);
 		dbg ("%s - registering %s", __FUNCTION__, port->dev.bus_id);
@@ -1249,34 +1278,38 @@
 	usb_set_intfdata (interface, serial);
 	return 0;
 
-
 probe_error:
 	for (i = 0; i < num_bulk_in; ++i) {
-		port = &serial->port[i];
+		port = serial->port[i];
+		if (!port)
+			continue;
 		if (port->read_urb)
 			usb_free_urb (port->read_urb);
-		if (port->bulk_in_buffer)
-			kfree (port->bulk_in_buffer);
+		kfree(port->bulk_in_buffer);
 	}
 	for (i = 0; i < num_bulk_out; ++i) {
-		port = &serial->port[i];
+		port = serial->port[i];
+		if (!port)
+			continue;
 		if (port->write_urb)
 			usb_free_urb (port->write_urb);
-		if (port->bulk_out_buffer)
-			kfree (port->bulk_out_buffer);
+		kfree(port->bulk_out_buffer);
 	}
 	for (i = 0; i < num_interrupt_in; ++i) {
-		port = &serial->port[i];
+		port = serial->port[i];
+		if (!port)
+			continue;
 		if (port->interrupt_in_urb)
 			usb_free_urb (port->interrupt_in_urb);
-		if (port->interrupt_in_buffer)
-			kfree (port->interrupt_in_buffer);
+		kfree(port->interrupt_in_buffer);
 	}
 
 	/* return the minor range that this device had */
 	return_serial (serial);
 
 	/* free up any memory that we allocated */
+	for (i = 0; i < serial->num_port_pointers; ++i)
+		kfree(serial->port[i]);
 	kfree (serial);
 	return -EIO;
 }
diff -Nru a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h
--- a/drivers/usb/serial/usb-serial.h	Fri Aug 15 10:46:59 2003
+++ b/drivers/usb/serial/usb-serial.h	Fri Aug 15 10:46:59 2003
@@ -156,12 +156,13 @@
 	struct usb_interface *		interface;
 	unsigned char			minor;
 	unsigned char			num_ports;
+	unsigned char			num_port_pointers;
 	char				num_interrupt_in;
 	char				num_bulk_in;
 	char				num_bulk_out;
 	__u16				vendor;
 	__u16				product;
-	struct usb_serial_port		port[MAX_NUM_PORTS];
+	struct usb_serial_port *	port[MAX_NUM_PORTS];
 	struct kobject			kobj;
 	void *				private;
 };
