diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/serial/Kconfig | 16 | ||||
-rw-r--r-- | drivers/usb/serial/option.c | 136 |
2 files changed, 118 insertions, 34 deletions
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 12ad6f0897e0..8bd44fda5eaf 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig | |||
@@ -491,16 +491,22 @@ config USB_SERIAL_XIRCOM | |||
491 | module will be called keyspan_pda. | 491 | module will be called keyspan_pda. |
492 | 492 | ||
493 | config USB_SERIAL_OPTION | 493 | config USB_SERIAL_OPTION |
494 | tristate "USB Option PCMCIA serial driver" | 494 | tristate "USB driver for GSM modems" |
495 | depends on USB_SERIAL && USB_OHCI_HCD && PCCARD | 495 | depends on USB_SERIAL |
496 | help | 496 | help |
497 | Say Y here if you want to use an Option card. This is a | 497 | Say Y here if you have an "Option" GSM PCMCIA card |
498 | GSM card, controlled by three serial ports which are connected | 498 | (or an OEM version: branded Huawei, Audiovox, or Novatel). |
499 | via an OHCI adapter located on a PC card. | 499 | |
500 | These cards feature a built-in OHCI-USB adapter and an | ||
501 | internally-connected GSM modem. The USB bus is not | ||
502 | accessible externally. | ||
500 | 503 | ||
501 | To compile this driver as a module, choose M here: the | 504 | To compile this driver as a module, choose M here: the |
502 | module will be called option. | 505 | module will be called option. |
503 | 506 | ||
507 | If this driver doesn't recognize your device, | ||
508 | it might be accessible via the FTDI_SIO driver. | ||
509 | |||
504 | config USB_SERIAL_OMNINET | 510 | config USB_SERIAL_OMNINET |
505 | tristate "USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)" | 511 | tristate "USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)" |
506 | depends on USB_SERIAL && EXPERIMENTAL | 512 | depends on USB_SERIAL && EXPERIMENTAL |
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 759d36087184..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); |
@@ -572,27 +647,30 @@ static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint, | |||
572 | /* Setup urbs */ | 647 | /* Setup urbs */ |
573 | static void option_setup_urbs(struct usb_serial *serial) | 648 | static void option_setup_urbs(struct usb_serial *serial) |
574 | { | 649 | { |
575 | int j; | 650 | int i,j; |
576 | struct usb_serial_port *port; | 651 | struct usb_serial_port *port; |
577 | struct option_port_private *portdata; | 652 | struct option_port_private *portdata; |
578 | 653 | ||
579 | dbg("%s", __FUNCTION__); | 654 | dbg("%s", __FUNCTION__); |
580 | 655 | ||
581 | port = serial->port[0]; | 656 | |
582 | 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); | ||
583 | 660 | ||
584 | /* Do indat endpoints first */ | 661 | /* Do indat endpoints first */ |
585 | for (j = 0; j < N_IN_URB; ++j) { | 662 | for (j = 0; j < N_IN_URB; ++j) { |
586 | portdata->in_urbs[j] = option_setup_urb (serial, | 663 | portdata->in_urbs[j] = option_setup_urb (serial, |
587 | port->bulk_in_endpointAddress, USB_DIR_IN, port, | 664 | port->bulk_in_endpointAddress, USB_DIR_IN, port, |
588 | portdata->in_buffer[j], IN_BUFLEN, option_indat_callback); | 665 | portdata->in_buffer[j], IN_BUFLEN, option_indat_callback); |
589 | } | 666 | } |
590 | 667 | ||
591 | /* outdat endpoints */ | 668 | /* outdat endpoints */ |
592 | for (j = 0; j < N_OUT_URB; ++j) { | 669 | for (j = 0; j < N_OUT_URB; ++j) { |
593 | portdata->out_urbs[j] = option_setup_urb (serial, | 670 | portdata->out_urbs[j] = option_setup_urb (serial, |
594 | port->bulk_out_endpointAddress, USB_DIR_OUT, port, | 671 | port->bulk_out_endpointAddress, USB_DIR_OUT, port, |
595 | portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback); | 672 | portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback); |
673 | } | ||
596 | } | 674 | } |
597 | } | 675 | } |
598 | 676 | ||