aboutsummaryrefslogtreecommitdiffstats
path: root/usb-serial-idr.patch
diff options
Diffstat (limited to 'usb-serial-idr.patch')
-rw-r--r--usb-serial-idr.patch210
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;