X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=drivers%2Fusb%2Fserial%2Foption.c;h=0fed43a96871d0967f4b2d4ee7a13695ea46b2e0;hb=refs%2Fheads%2Fvserver;hp=fd8a50e081d15f2ff758146785a2490f1ce85b56;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index fd8a50e08..0fed43a96 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1,5 +1,5 @@ /* - Option Card (PCMCIA to) USB to Serial Driver + USB Driver for GSM modems Copyright (C) 2005 Matthias Urlichs @@ -9,35 +9,29 @@ Portions copied from the Keyspan driver by Hugh Blemings - 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 + 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 " -#define DRIVER_DESC "Option Card (PC-Card to) USB to Serial Driver" +#define DRIVER_DESC "USB Driver for GSM modems" -#include #include #include #include @@ -45,7 +39,7 @@ #include #include #include -#include "usb-serial.h" +#include /* 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);