fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / usb / serial / option.c
index fd8a50e..0fed43a 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Option Card (PCMCIA to) USB to Serial Driver
+  USB Driver for GSM modems
 
   Copyright (C) 2005  Matthias Urlichs <smurf@smurf.noris.de>
 
@@ -9,35 +9,29 @@
 
   Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org>
 
-  History:
-
-  2005-05-19  v0.1   Initial version, based on incomplete docs
-                     and analysis of misbehavior with the standard driver
-  2005-05-20  v0.2   Extended the input buffer to avoid losing
-                     random 64-byte chunks of data
-  2005-05-21  v0.3   implemented chars_in_buffer()
-                     turned on low_latency
-                     simplified the code somewhat
-  2005-05-24  v0.4   option_write() sometimes deadlocked under heavy load
-                     removed some dead code
-                     added sponsor notice
-                     coding style clean-up
-  2005-06-20  v0.4.1 add missing braces :-/
-                     killed end-of-line whitespace
-  2005-07-15  v0.4.2 rename WLAN product to FUSION, add FUSION2
-  2005-09-10  v0.4.3 added HUAWEI E600 card and Audiovox AirCard
-  2005-09-20  v0.4.4 increased recv buffer size: the card sometimes
-                     wants to send >2000 bytes.
+  History: see the git log.
 
   Work sponsored by: Sigos GmbH, Germany <info@sigos.de>
 
+  This driver exists because the "normal" serial driver doesn't work too well
+  with GSM modems. Issues:
+  - data loss -- one single Receive URB is not nearly enough
+  - nonstandard flow (Option devices) control
+  - controlling the baud rate doesn't make sense
+
+  This driver is named "option" because the most common device it's
+  used for is a PC-Card (with an internal OHCI-USB interface, behind
+  which the GSM interface sits), made by Option Inc.
+
+  Some of the "one port" devices actually exhibit multiple USB instances
+  on the USB bus. This is not a bug, these ports are used for different
+  device features.
 */
 
-#define DRIVER_VERSION "v0.4"
+#define DRIVER_VERSION "v0.7.1"
 #define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
-#define DRIVER_DESC "Option Card (PC-Card to) USB to Serial Driver"
+#define DRIVER_DESC "USB Driver for GSM modems"
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/jiffies.h>
 #include <linux/errno.h>
@@ -45,7 +39,7 @@
 #include <linux/tty_flip.h>
 #include <linux/module.h>
 #include <linux/usb.h>
-#include "usb-serial.h"
+#include <linux/usb/serial.h>
 
 /* Function prototypes */
 static int  option_open(struct usb_serial_port *port, struct file *filp);
@@ -56,7 +50,7 @@ static void option_rx_throttle(struct usb_serial_port *port);
 static void option_rx_unthrottle(struct usb_serial_port *port);
 static int  option_write_room(struct usb_serial_port *port);
 
-static void option_instat_callback(struct urb *urb, struct pt_regs *regs);
+static void option_instat_callback(struct urb *urb);
 
 static int option_write(struct usb_serial_port *port,
                        const unsigned char *buf, int count);
@@ -65,7 +59,7 @@ static int  option_chars_in_buffer(struct usb_serial_port *port);
 static int  option_ioctl(struct usb_serial_port *port, struct file *file,
                        unsigned int cmd, unsigned long arg);
 static void option_set_termios(struct usb_serial_port *port,
-                               struct termios *old);
+                               struct ktermios *old);
 static void option_break_ctl(struct usb_serial_port *port, int break_state);
 static int  option_tiocmget(struct usb_serial_port *port, struct file *file);
 static int  option_tiocmset(struct usb_serial_port *port, struct file *file,
@@ -73,22 +67,51 @@ static int  option_tiocmset(struct usb_serial_port *port, struct file *file,
 static int  option_send_setup(struct usb_serial_port *port);
 
 /* Vendor and product IDs */
-#define OPTION_VENDOR_ID                       0x0AF0
-#define HUAWEI_VENDOR_ID                       0x12D1
-#define AUDIOVOX_VENDOR_ID                     0x0F3D
-
-#define OPTION_PRODUCT_OLD             0x5000
-#define OPTION_PRODUCT_FUSION  0x6000
-#define OPTION_PRODUCT_FUSION2 0x6300
-#define HUAWEI_PRODUCT_E600     0x1001
-#define AUDIOVOX_PRODUCT_AIRCARD 0x0112
+#define OPTION_VENDOR_ID                0x0AF0
+#define HUAWEI_VENDOR_ID                0x12D1
+#define AUDIOVOX_VENDOR_ID              0x0F3D
+#define NOVATELWIRELESS_VENDOR_ID       0x1410
+#define ANYDATA_VENDOR_ID               0x16d5
+
+#define OPTION_PRODUCT_OLD              0x5000
+#define OPTION_PRODUCT_FUSION           0x6000
+#define OPTION_PRODUCT_FUSION2          0x6300
+#define OPTION_PRODUCT_COBRA            0x6500
+#define OPTION_PRODUCT_COBRA2           0x6600
+#define OPTION_PRODUCT_GTMAX36          0x6701
+#define HUAWEI_PRODUCT_E600             0x1001
+#define HUAWEI_PRODUCT_E220             0x1003
+#define AUDIOVOX_PRODUCT_AIRCARD        0x0112
+#define NOVATELWIRELESS_PRODUCT_U740    0x1400
+#define ANYDATA_PRODUCT_ID              0x6501
 
 static struct usb_device_id option_ids[] = {
        { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
        { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
        { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTMAX36) },
+       { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
+       { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) },
+       { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
+       { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
+       { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
+       { } /* Terminating entry */
+};
+
+static struct usb_device_id option_ids1[] = {
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTMAX36) },
        { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
+       { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) },
        { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
+       { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
+       { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
        { } /* Terminating entry */
 };
 
@@ -102,20 +125,21 @@ static struct usb_driver option_driver = {
        .no_dynamic_id =        1,
 };
 
-/* The card has three separate interfaces, wich the serial driver
+/* The card has three separate interfaces, which the serial driver
  * recognizes separately, thus num_port=1.
  */
-static struct usb_serial_driver option_3port_device = {
+
+static struct usb_serial_driver option_1port_device = {
        .driver = {
                .owner =        THIS_MODULE,
-               .name =         "option",
+               .name =         "option1",
        },
-       .description       = "Option 3G data card",
-       .id_table          = option_ids,
+       .description       = "GSM modem (1-port)",
+       .id_table          = option_ids1,
        .num_interrupt_in  = NUM_DONT_CARE,
        .num_bulk_in       = NUM_DONT_CARE,
        .num_bulk_out      = NUM_DONT_CARE,
-       .num_ports         = 1, /* 3, but the card reports its ports separately */
+       .num_ports         = 1,
        .open              = option_open,
        .close             = option_close,
        .write             = option_write,
@@ -169,9 +193,9 @@ struct option_port_private {
 static int __init option_init(void)
 {
        int retval;
-       retval = usb_serial_register(&option_3port_device);
+       retval = usb_serial_register(&option_1port_device);
        if (retval)
-               goto failed_3port_device_register;
+               goto failed_1port_device_register;
        retval = usb_register(&option_driver);
        if (retval)
                goto failed_driver_register;
@@ -181,15 +205,15 @@ static int __init option_init(void)
        return 0;
 
 failed_driver_register:
-       usb_serial_deregister (&option_3port_device);
-failed_3port_device_register:
+       usb_serial_deregister (&option_1port_device);
+failed_1port_device_register:
        return retval;
 }
 
 static void __exit option_exit(void)
 {
        usb_deregister (&option_driver);
-       usb_serial_deregister (&option_3port_device);
+       usb_serial_deregister (&option_1port_device);
 }
 
 module_init(option_init);
@@ -212,7 +236,7 @@ static void option_break_ctl(struct usb_serial_port *port, int break_state)
 }
 
 static void option_set_termios(struct usb_serial_port *port,
-                       struct termios *old_termios)
+                       struct ktermios *old_termios)
 {
        dbg("%s", __FUNCTION__);
 
@@ -319,7 +343,7 @@ static int option_write(struct usb_serial_port *port,
        return count;
 }
 
-static void option_indat_callback(struct urb *urb, struct pt_regs *regs)
+static void option_indat_callback(struct urb *urb)
 {
        int err;
        int endpoint;
@@ -356,7 +380,7 @@ static void option_indat_callback(struct urb *urb, struct pt_regs *regs)
        return;
 }
 
-static void option_outdat_callback(struct urb *urb, struct pt_regs *regs)
+static void option_outdat_callback(struct urb *urb)
 {
        struct usb_serial_port *port;
 
@@ -364,11 +388,10 @@ static void option_outdat_callback(struct urb *urb, struct pt_regs *regs)
 
        port = (struct usb_serial_port *) urb->context;
 
-       if (port->open_count)
-               schedule_work(&port->work);
+       usb_serial_port_softint(port);
 }
 
-static void option_instat_callback(struct urb *urb, struct pt_regs *regs)
+static void option_instat_callback(struct urb *urb)
 {
        int err;
        struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
@@ -548,7 +571,7 @@ static void option_close(struct usb_serial_port *port, struct file *filp)
 /* Helper functions used by option_setup_urbs */
 static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint,
                int dir, void *ctx, char *buf, int len,
-               void (*callback)(struct urb *, struct pt_regs *regs))
+               void (*callback)(struct urb *))
 {
        struct urb *urb;
 
@@ -572,27 +595,29 @@ static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint,
 /* Setup urbs */
 static void option_setup_urbs(struct usb_serial *serial)
 {
-       int j;
+       int i,j;
        struct usb_serial_port *port;
        struct option_port_private *portdata;
 
        dbg("%s", __FUNCTION__);
 
-       port = serial->port[0];
-       portdata = usb_get_serial_port_data(port);
+       for (i = 0; i < serial->num_ports; i++) {
+               port = serial->port[i];
+               portdata = usb_get_serial_port_data(port);
 
        /* Do indat endpoints first */
-       for (j = 0; j < N_IN_URB; ++j) {
-               portdata->in_urbs[j] = option_setup_urb (serial,
-                  port->bulk_in_endpointAddress, USB_DIR_IN, port,
-                  portdata->in_buffer[j], IN_BUFLEN, option_indat_callback);
-       }
+               for (j = 0; j < N_IN_URB; ++j) {
+                       portdata->in_urbs[j] = option_setup_urb (serial,
+                       port->bulk_in_endpointAddress, USB_DIR_IN, port,
+                       portdata->in_buffer[j], IN_BUFLEN, option_indat_callback);
+               }
 
-       /* outdat endpoints */
-       for (j = 0; j < N_OUT_URB; ++j) {
-               portdata->out_urbs[j] = option_setup_urb (serial,
-                  port->bulk_out_endpointAddress, USB_DIR_OUT, port,
-                  portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback);
+               /* outdat endpoints */
+               for (j = 0; j < N_OUT_URB; ++j) {
+                       portdata->out_urbs[j] = option_setup_urb (serial,
+                       port->bulk_out_endpointAddress, USB_DIR_OUT, port,
+                       portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback);
+               }
        }
 }
 
@@ -603,6 +628,9 @@ static int option_send_setup(struct usb_serial_port *port)
 
        dbg("%s", __FUNCTION__);
 
+       if (port->number != 0)
+               return 0;
+
        portdata = usb_get_serial_port_data(port);
 
        if (port->tty) {
@@ -631,13 +659,12 @@ static int option_startup(struct usb_serial *serial)
        /* Now setup per port private data */
        for (i = 0; i < serial->num_ports; i++) {
                port = serial->port[i];
-               portdata = kmalloc(sizeof(*portdata), GFP_KERNEL);
+               portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
                if (!portdata) {
                        dbg("%s: kmalloc for option_port_private (%d) failed!.",
                                        __FUNCTION__, i);
                        return (1);
                }
-               memset(portdata, 0, sizeof(struct option_port_private));
 
                usb_set_serial_port_data(port, portdata);