diff options
Diffstat (limited to 'usb-serial-idr.patch')
| -rw-r--r-- | usb-serial-idr.patch | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/usb-serial-idr.patch b/usb-serial-idr.patch new file mode 100644 index 00000000000000..91dbc029c50629 --- /dev/null +++ b/usb-serial-idr.patch @@ -0,0 +1,210 @@ +From foo@baz Tue Jun 4 12:06:11 PDT 2013 +Date: Tue, 04 Jun 2013 12:06:11 -0700 +To: Greg KH <gregkh@linuxfoundation.org> +From: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Subject: [PATCH 2/2] USB: serial: make minor allocation dynamic + +This moves the allocation of minor device numbers from a static array to +be dynamic, using the idr interface. This means that you could +potentially get "gaps" in a minor number range for a single USB serial +device with multiple ports, but all should still work properly. + +Note, we still have the limitation of 255 USB to serial devices in the +system, as that is all we are registering with the TTY layer at this +point in time. + +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +--- + drivers/usb/serial/usb-serial.c | 97 ++++++++++++++++++++-------------------- + include/linux/usb/serial.h | 4 - + 2 files changed, 51 insertions(+), 50 deletions(-) + +--- a/drivers/usb/serial/usb-serial.c ++++ b/drivers/usb/serial/usb-serial.c +@@ -37,6 +37,7 @@ + #include <linux/usb.h> + #include <linux/usb/serial.h> + #include <linux/kfifo.h> ++#include <linux/idr.h> + #include "pl2303.h" + + #define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@linuxfoundation.org>" +@@ -49,7 +50,7 @@ + drivers depend on it. + */ + +-static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; ++static DEFINE_IDR(serial_minors); + static DEFINE_MUTEX(table_lock); + static LIST_HEAD(usb_serial_driver_list); + +@@ -60,61 +61,65 @@ static LIST_HEAD(usb_serial_driver_list) + */ + struct usb_serial *usb_serial_get_by_index(unsigned index) + { +- struct usb_serial *serial; ++ struct usb_serial *serial = NULL; ++ struct usb_serial_port *port; + + mutex_lock(&table_lock); +- serial = serial_table[index]; ++ port = idr_find(&serial_minors, index); ++ if (!port) ++ goto exit; + +- if (serial) { +- mutex_lock(&serial->disc_mutex); +- if (serial->disconnected) { +- mutex_unlock(&serial->disc_mutex); +- serial = NULL; +- } else { +- kref_get(&serial->kref); +- } ++ serial = port->serial; ++ mutex_lock(&serial->disc_mutex); ++ if (serial->disconnected) { ++ mutex_unlock(&serial->disc_mutex); ++ serial = NULL; ++ } else { ++ kref_get(&serial->kref); + } + mutex_unlock(&table_lock); ++exit: + return serial; + } + +-static struct usb_serial *get_free_serial(struct usb_serial *serial, +- int num_ports, unsigned int *minor) ++static int get_free_port(struct usb_serial_port *port) + { +- unsigned int i, j; +- int good_spot; +- +- dev_dbg(&serial->interface->dev, "%s %d\n", __func__, num_ports); ++ int i; + +- *minor = 0; + mutex_lock(&table_lock); +- for (i = 0; i < SERIAL_TTY_MINORS; ++i) { +- if (serial_table[i]) +- continue; ++ i = idr_alloc(&serial_minors, port, 0, 0, GFP_KERNEL); ++ if (i < 0) ++ goto exit; ++ port->minor = i; ++exit: ++ mutex_unlock(&table_lock); ++ return i; ++} + +- good_spot = 1; +- for (j = 1; j <= num_ports-1; ++j) +- if ((i+j >= SERIAL_TTY_MINORS) || (serial_table[i+j])) { +- good_spot = 0; +- i += j; +- break; +- } +- if (good_spot == 0) +- continue; ++static int get_free_serial(struct usb_serial *serial, int num_ports, ++ unsigned int *minor) ++{ ++ unsigned int i; ++ unsigned int j; ++ int x; + +- *minor = i; +- j = 0; +- dev_dbg(&serial->interface->dev, "%s - minor base = %d\n", __func__, *minor); +- for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i, ++j) { +- serial_table[i] = serial; +- serial->port[j]->minor = i; +- serial->port[j]->port_number = i - *minor; +- } +- mutex_unlock(&table_lock); +- return serial; ++ dev_dbg(&serial->interface->dev, "%s %d\n", __func__, num_ports); ++ ++ *minor = 0xffffffff; ++ for (i = 0; i < num_ports; ++i) { ++ x = get_free_port(serial->port[i]); ++ if (x < 0) ++ goto error; ++ if (*minor == 0xffffffff) ++ *minor = x; ++ serial->port[i]->port_number = i; + } +- mutex_unlock(&table_lock); +- return NULL; ++ return 0; ++error: ++ /* unwind the already allocated minors */ ++ for (j = 0; j < i; ++j) ++ idr_remove(&serial_minors, serial->port[j]->port_number); ++ return x; + } + + static void return_serial(struct usb_serial *serial) +@@ -123,7 +128,7 @@ static void return_serial(struct usb_ser + + mutex_lock(&table_lock); + for (i = 0; i < serial->num_ports; ++i) +- serial_table[serial->minor + i] = NULL; ++ idr_remove(&serial_minors, serial->port[i]->port_number); + mutex_unlock(&table_lock); + } + +@@ -1042,7 +1047,7 @@ static int usb_serial_probe(struct usb_i + */ + serial->disconnected = 1; + +- if (get_free_serial(serial, num_ports, &minor) == NULL) { ++ if (get_free_serial(serial, num_ports, &minor)) { + dev_err(ddev, "No more free serial devices\n"); + goto probe_error; + } +@@ -1226,7 +1231,6 @@ static struct usb_driver usb_serial_driv + + static int __init usb_serial_init(void) + { +- int i; + int result; + + usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); +@@ -1234,9 +1238,6 @@ static int __init usb_serial_init(void) + return -ENOMEM; + + /* Initialize our global data */ +- for (i = 0; i < SERIAL_TTY_MINORS; ++i) +- serial_table[i] = NULL; +- + result = bus_register(&usb_serial_bus_type); + if (result) { + pr_err("%s - registering bus driver failed\n", __func__); +--- a/include/linux/usb/serial.h ++++ b/include/linux/usb/serial.h +@@ -21,7 +21,7 @@ + + #define SERIAL_TTY_MAJOR 188 /* Nice legal number now */ + #define SERIAL_TTY_MINORS 254 /* loads of devices :) */ +-#define SERIAL_TTY_NO_MINOR 255 /* No minor was assigned */ ++#define SERIAL_TTY_NO_MINOR 0xffffffff /* No minor was assigned */ + + /* The maximum number of ports one device can grab at once */ + #define MAX_NUM_PORTS 8 +@@ -161,13 +161,13 @@ struct usb_serial { + unsigned char disconnected:1; + unsigned char suspending:1; + unsigned char attached:1; +- unsigned char minor; + unsigned char num_ports; + unsigned char num_port_pointers; + char num_interrupt_in; + char num_interrupt_out; + char num_bulk_in; + char num_bulk_out; ++ u32 minor; + struct usb_serial_port *port[MAX_NUM_PORTS]; + struct kref kref; + struct mutex disc_mutex; |
