diff options
Diffstat (limited to 'drivers/usb/serial/option.c')
| -rw-r--r-- | drivers/usb/serial/option.c | 139 |
1 files changed, 108 insertions, 31 deletions
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 5cf2b80add7a..b0861b61bba7 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | Option Card (PCMCIA to) USB to Serial Driver | 2 | USB Driver for GSM modems |
| 3 | 3 | ||
| 4 | Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de> | 4 | Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de> |
| 5 | 5 | ||
| @@ -28,15 +28,34 @@ | |||
| 28 | 2005-09-10 v0.4.3 added HUAWEI E600 card and Audiovox AirCard | 28 | 2005-09-10 v0.4.3 added HUAWEI E600 card and Audiovox AirCard |
| 29 | 2005-09-20 v0.4.4 increased recv buffer size: the card sometimes | 29 | 2005-09-20 v0.4.4 increased recv buffer size: the card sometimes |
| 30 | wants to send >2000 bytes. | 30 | wants to send >2000 bytes. |
| 31 | 2006-04-10 v0.4.2 fixed two array overrun errors :-/ | 31 | 2006-04-10 v0.5 fixed two array overrun errors :-/ |
| 32 | 2006-04-21 v0.5.1 added support for Sierra Wireless MC8755 | ||
| 33 | 2006-05-15 v0.6 re-enable multi-port support | ||
| 34 | 2006-06-01 v0.6.1 add COBRA | ||
| 35 | 2006-06-01 v0.6.2 add backwards-compatibility stuff | ||
| 36 | 2006-06-01 v0.6.3 add Novatel Wireless | ||
| 37 | 2006-06-01 v0.7 Option => GSM | ||
| 32 | 38 | ||
| 33 | Work sponsored by: Sigos GmbH, Germany <info@sigos.de> | 39 | Work sponsored by: Sigos GmbH, Germany <info@sigos.de> |
| 34 | 40 | ||
| 41 | This driver exists because the "normal" serial driver doesn't work too well | ||
| 42 | with GSM modems. Issues: | ||
| 43 | - data loss -- one single Receive URB is not nearly enough | ||
| 44 | - nonstandard flow (Option devices) and multiplex (Sierra) control | ||
| 45 | - controlling the baud rate doesn't make sense | ||
| 46 | |||
| 47 | This driver is named "option" because the most common device it's | ||
| 48 | used for is a PC-Card (with an internal OHCI-USB interface, behind | ||
| 49 | which the GSM interface sits), made by Option Inc. | ||
| 50 | |||
| 51 | Some of the "one port" devices actually exhibit multiple USB instances | ||
| 52 | on the USB bus. This is not a bug, these ports are used for different | ||
| 53 | device features. | ||
| 35 | */ | 54 | */ |
| 36 | 55 | ||
| 37 | #define DRIVER_VERSION "v0.4" | 56 | #define DRIVER_VERSION "v0.7.0" |
| 38 | #define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>" | 57 | #define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>" |
| 39 | #define DRIVER_DESC "Option Card (PC-Card to) USB to Serial Driver" | 58 | #define DRIVER_DESC "USB Driver for GSM modems" |
| 40 | 59 | ||
| 41 | #include <linux/config.h> | 60 | #include <linux/config.h> |
| 42 | #include <linux/kernel.h> | 61 | #include <linux/kernel.h> |
| @@ -74,22 +93,45 @@ static int option_tiocmset(struct usb_serial_port *port, struct file *file, | |||
| 74 | static int option_send_setup(struct usb_serial_port *port); | 93 | static int option_send_setup(struct usb_serial_port *port); |
| 75 | 94 | ||
| 76 | /* Vendor and product IDs */ | 95 | /* Vendor and product IDs */ |
| 77 | #define OPTION_VENDOR_ID 0x0AF0 | 96 | #define OPTION_VENDOR_ID 0x0AF0 |
| 78 | #define HUAWEI_VENDOR_ID 0x12D1 | 97 | #define HUAWEI_VENDOR_ID 0x12D1 |
| 79 | #define AUDIOVOX_VENDOR_ID 0x0F3D | 98 | #define AUDIOVOX_VENDOR_ID 0x0F3D |
| 80 | 99 | #define SIERRAWIRELESS_VENDOR_ID 0x1199 | |
| 81 | #define OPTION_PRODUCT_OLD 0x5000 | 100 | #define NOVATELWIRELESS_VENDOR_ID 0x1410 |
| 82 | #define OPTION_PRODUCT_FUSION 0x6000 | 101 | |
| 83 | #define OPTION_PRODUCT_FUSION2 0x6300 | 102 | #define OPTION_PRODUCT_OLD 0x5000 |
| 84 | #define HUAWEI_PRODUCT_E600 0x1001 | 103 | #define OPTION_PRODUCT_FUSION 0x6000 |
| 85 | #define AUDIOVOX_PRODUCT_AIRCARD 0x0112 | 104 | #define OPTION_PRODUCT_FUSION2 0x6300 |
| 105 | #define OPTION_PRODUCT_COBRA 0x6500 | ||
| 106 | #define HUAWEI_PRODUCT_E600 0x1001 | ||
| 107 | #define AUDIOVOX_PRODUCT_AIRCARD 0x0112 | ||
| 108 | #define SIERRAWIRELESS_PRODUCT_MC8755 0x6802 | ||
| 109 | #define NOVATELWIRELESS_PRODUCT_U740 0x1400 | ||
| 86 | 110 | ||
| 87 | static struct usb_device_id option_ids[] = { | 111 | static struct usb_device_id option_ids[] = { |
| 88 | { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) }, | 112 | { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) }, |
| 89 | { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) }, | 113 | { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) }, |
| 90 | { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) }, | 114 | { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) }, |
| 115 | { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) }, | ||
| 91 | { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, | 116 | { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, |
| 92 | { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, | 117 | { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, |
| 118 | { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) }, | ||
| 119 | { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) }, | ||
| 120 | { } /* Terminating entry */ | ||
| 121 | }; | ||
| 122 | |||
| 123 | static struct usb_device_id option_ids1[] = { | ||
| 124 | { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) }, | ||
| 125 | { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) }, | ||
| 126 | { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) }, | ||
| 127 | { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) }, | ||
| 128 | { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, | ||
| 129 | { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, | ||
| 130 | { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) }, | ||
| 131 | { } /* Terminating entry */ | ||
| 132 | }; | ||
| 133 | static struct usb_device_id option_ids3[] = { | ||
| 134 | { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) }, | ||
| 93 | { } /* Terminating entry */ | 135 | { } /* Terminating entry */ |
| 94 | }; | 136 | }; |
| 95 | 137 | ||
| @@ -111,12 +153,39 @@ static struct usb_serial_driver option_3port_device = { | |||
| 111 | .owner = THIS_MODULE, | 153 | .owner = THIS_MODULE, |
| 112 | .name = "option", | 154 | .name = "option", |
| 113 | }, | 155 | }, |
| 114 | .description = "Option 3G data card", | 156 | .description = "GSM modem (3-port)", |
| 115 | .id_table = option_ids, | 157 | .id_table = option_ids3, |
| 116 | .num_interrupt_in = NUM_DONT_CARE, | 158 | .num_interrupt_in = NUM_DONT_CARE, |
| 117 | .num_bulk_in = NUM_DONT_CARE, | 159 | .num_bulk_in = NUM_DONT_CARE, |
| 118 | .num_bulk_out = NUM_DONT_CARE, | 160 | .num_bulk_out = NUM_DONT_CARE, |
| 119 | .num_ports = 1, /* 3, but the card reports its ports separately */ | 161 | .num_ports = 3, |
| 162 | .open = option_open, | ||
| 163 | .close = option_close, | ||
| 164 | .write = option_write, | ||
| 165 | .write_room = option_write_room, | ||
| 166 | .chars_in_buffer = option_chars_in_buffer, | ||
| 167 | .throttle = option_rx_throttle, | ||
| 168 | .unthrottle = option_rx_unthrottle, | ||
| 169 | .set_termios = option_set_termios, | ||
| 170 | .break_ctl = option_break_ctl, | ||
| 171 | .tiocmget = option_tiocmget, | ||
| 172 | .tiocmset = option_tiocmset, | ||
| 173 | .attach = option_startup, | ||
| 174 | .shutdown = option_shutdown, | ||
| 175 | .read_int_callback = option_instat_callback, | ||
| 176 | }; | ||
| 177 | |||
| 178 | static struct usb_serial_driver option_1port_device = { | ||
| 179 | .driver = { | ||
| 180 | .owner = THIS_MODULE, | ||
| 181 | .name = "option", | ||
| 182 | }, | ||
| 183 | .description = "GSM modem (1-port)", | ||
| 184 | .id_table = option_ids1, | ||
| 185 | .num_interrupt_in = NUM_DONT_CARE, | ||
| 186 | .num_bulk_in = NUM_DONT_CARE, | ||
| 187 | .num_bulk_out = NUM_DONT_CARE, | ||
| 188 | .num_ports = 1, | ||
| 120 | .open = option_open, | 189 | .open = option_open, |
| 121 | .close = option_close, | 190 | .close = option_close, |
| 122 | .write = option_write, | 191 | .write = option_write, |
| @@ -170,6 +239,9 @@ struct option_port_private { | |||
| 170 | static int __init option_init(void) | 239 | static int __init option_init(void) |
| 171 | { | 240 | { |
| 172 | int retval; | 241 | int retval; |
| 242 | retval = usb_serial_register(&option_1port_device); | ||
| 243 | if (retval) | ||
| 244 | goto failed_1port_device_register; | ||
| 173 | retval = usb_serial_register(&option_3port_device); | 245 | retval = usb_serial_register(&option_3port_device); |
| 174 | if (retval) | 246 | if (retval) |
| 175 | goto failed_3port_device_register; | 247 | goto failed_3port_device_register; |
| @@ -184,6 +256,8 @@ static int __init option_init(void) | |||
| 184 | failed_driver_register: | 256 | failed_driver_register: |
| 185 | usb_serial_deregister (&option_3port_device); | 257 | usb_serial_deregister (&option_3port_device); |
| 186 | failed_3port_device_register: | 258 | failed_3port_device_register: |
| 259 | usb_serial_deregister (&option_1port_device); | ||
| 260 | failed_1port_device_register: | ||
| 187 | return retval; | 261 | return retval; |
| 188 | } | 262 | } |
| 189 | 263 | ||
| @@ -191,6 +265,7 @@ static void __exit option_exit(void) | |||
| 191 | { | 265 | { |
| 192 | usb_deregister (&option_driver); | 266 | usb_deregister (&option_driver); |
| 193 | usb_serial_deregister (&option_3port_device); | 267 | usb_serial_deregister (&option_3port_device); |
| 268 | usb_serial_deregister (&option_1port_device); | ||
| 194 | } | 269 | } |
| 195 | 270 | ||
| 196 | module_init(option_init); | 271 | module_init(option_init); |
| @@ -365,8 +440,7 @@ static void option_outdat_callback(struct urb *urb, struct pt_regs *regs) | |||
| 365 | 440 | ||
| 366 | port = (struct usb_serial_port *) urb->context; | 441 | port = (struct usb_serial_port *) urb->context; |
| 367 | 442 | ||
| 368 | if (port->open_count) | 443 | usb_serial_port_softint(port); |
| 369 | schedule_work(&port->work); | ||
| 370 | } | 444 | } |
| 371 | 445 | ||
| 372 | static void option_instat_callback(struct urb *urb, struct pt_regs *regs) | 446 | static void option_instat_callback(struct urb *urb, struct pt_regs *regs) |
| @@ -573,27 +647,30 @@ static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint, | |||
| 573 | /* Setup urbs */ | 647 | /* Setup urbs */ |
| 574 | static void option_setup_urbs(struct usb_serial *serial) | 648 | static void option_setup_urbs(struct usb_serial *serial) |
| 575 | { | 649 | { |
| 576 | int j; | 650 | int i,j; |
| 577 | struct usb_serial_port *port; | 651 | struct usb_serial_port *port; |
| 578 | struct option_port_private *portdata; | 652 | struct option_port_private *portdata; |
| 579 | 653 | ||
| 580 | dbg("%s", __FUNCTION__); | 654 | dbg("%s", __FUNCTION__); |
| 581 | 655 | ||
| 582 | port = serial->port[0]; | 656 | |
| 583 | portdata = usb_get_serial_port_data(port); | 657 | for (i = 0; i < serial->num_ports; i++) { |
| 658 | port = serial->port[i]; | ||
| 659 | portdata = usb_get_serial_port_data(port); | ||
| 584 | 660 | ||
| 585 | /* Do indat endpoints first */ | 661 | /* Do indat endpoints first */ |
| 586 | for (j = 0; j < N_IN_URB; ++j) { | 662 | for (j = 0; j < N_IN_URB; ++j) { |
| 587 | portdata->in_urbs[j] = option_setup_urb (serial, | 663 | portdata->in_urbs[j] = option_setup_urb (serial, |
| 588 | port->bulk_in_endpointAddress, USB_DIR_IN, port, | 664 | port->bulk_in_endpointAddress, USB_DIR_IN, port, |
| 589 | portdata->in_buffer[j], IN_BUFLEN, option_indat_callback); | 665 | portdata->in_buffer[j], IN_BUFLEN, option_indat_callback); |
| 590 | } | 666 | } |
| 591 | 667 | ||
| 592 | /* outdat endpoints */ | 668 | /* outdat endpoints */ |
| 593 | for (j = 0; j < N_OUT_URB; ++j) { | 669 | for (j = 0; j < N_OUT_URB; ++j) { |
| 594 | portdata->out_urbs[j] = option_setup_urb (serial, | 670 | portdata->out_urbs[j] = option_setup_urb (serial, |
| 595 | port->bulk_out_endpointAddress, USB_DIR_OUT, port, | 671 | port->bulk_out_endpointAddress, USB_DIR_OUT, port, |
| 596 | portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback); | 672 | portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback); |
| 673 | } | ||
| 597 | } | 674 | } |
| 598 | } | 675 | } |
| 599 | 676 | ||
