blob: f0530c1d7b7a274ca7a2c69bc8a33df199a9f6c1 [file] [log] [blame]
Matthias Urlichs58cfe912005-05-23 17:00:48 -07001/*
Matthias Urlichs14f76cc2006-06-02 11:48:56 +02002 USB Driver for GSM modems
Matthias Urlichs58cfe912005-05-23 17:00:48 -07003
4 Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de>
5
6 This driver is free software; you can redistribute it and/or modify
7 it under the terms of Version 2 of the GNU General Public License as
8 published by the Free Software Foundation.
9
10 Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org>
11
12 History:
13
14 2005-05-19 v0.1 Initial version, based on incomplete docs
Matthias Urlichsba460e42005-07-14 00:33:47 -070015 and analysis of misbehavior with the standard driver
Matthias Urlichs58cfe912005-05-23 17:00:48 -070016 2005-05-20 v0.2 Extended the input buffer to avoid losing
17 random 64-byte chunks of data
18 2005-05-21 v0.3 implemented chars_in_buffer()
19 turned on low_latency
20 simplified the code somewhat
Matthias Urlichsba460e42005-07-14 00:33:47 -070021 2005-05-24 v0.4 option_write() sometimes deadlocked under heavy load
22 removed some dead code
23 added sponsor notice
24 coding style clean-up
25 2005-06-20 v0.4.1 add missing braces :-/
26 killed end-of-line whitespace
27 2005-07-15 v0.4.2 rename WLAN product to FUSION, add FUSION2
Matthias Urlichsb6137382005-09-22 00:48:40 -070028 2005-09-10 v0.4.3 added HUAWEI E600 card and Audiovox AirCard
Matthias Urlichsb27c73d2005-09-22 00:49:33 -070029 2005-09-20 v0.4.4 increased recv buffer size: the card sometimes
30 wants to send >2000 bytes.
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020031 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
Matthias Urlichse37de9e2006-07-06 13:12:53 +020038 2006-06-01 v0.7.1 add COBRA2
Matthias Urlichsba460e42005-07-14 00:33:47 -070039
40 Work sponsored by: Sigos GmbH, Germany <info@sigos.de>
41
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020042 This driver exists because the "normal" serial driver doesn't work too well
43 with GSM modems. Issues:
44 - data loss -- one single Receive URB is not nearly enough
45 - nonstandard flow (Option devices) and multiplex (Sierra) control
46 - controlling the baud rate doesn't make sense
47
48 This driver is named "option" because the most common device it's
49 used for is a PC-Card (with an internal OHCI-USB interface, behind
50 which the GSM interface sits), made by Option Inc.
51
52 Some of the "one port" devices actually exhibit multiple USB instances
53 on the USB bus. This is not a bug, these ports are used for different
54 device features.
Matthias Urlichs58cfe912005-05-23 17:00:48 -070055*/
Matthias Urlichsba460e42005-07-14 00:33:47 -070056
Matthias Urlichse37de9e2006-07-06 13:12:53 +020057#define DRIVER_VERSION "v0.7.1"
Matthias Urlichs58cfe912005-05-23 17:00:48 -070058#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020059#define DRIVER_DESC "USB Driver for GSM modems"
Matthias Urlichs58cfe912005-05-23 17:00:48 -070060
Matthias Urlichs58cfe912005-05-23 17:00:48 -070061#include <linux/kernel.h>
62#include <linux/jiffies.h>
63#include <linux/errno.h>
64#include <linux/tty.h>
65#include <linux/tty_flip.h>
66#include <linux/module.h>
67#include <linux/usb.h>
Greg Kroah-Hartmana9698882006-07-11 21:22:58 -070068#include <linux/usb/serial.h>
Matthias Urlichs58cfe912005-05-23 17:00:48 -070069
70/* Function prototypes */
Andrew Morton7bb75ae2005-07-27 01:08:30 -070071static int option_open(struct usb_serial_port *port, struct file *filp);
72static void option_close(struct usb_serial_port *port, struct file *filp);
73static int option_startup(struct usb_serial *serial);
74static void option_shutdown(struct usb_serial *serial);
75static void option_rx_throttle(struct usb_serial_port *port);
76static void option_rx_unthrottle(struct usb_serial_port *port);
77static int option_write_room(struct usb_serial_port *port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -070078
79static void option_instat_callback(struct urb *urb, struct pt_regs *regs);
80
Andrew Morton7bb75ae2005-07-27 01:08:30 -070081static int option_write(struct usb_serial_port *port,
82 const unsigned char *buf, int count);
Matthias Urlichs58cfe912005-05-23 17:00:48 -070083
Andrew Morton7bb75ae2005-07-27 01:08:30 -070084static int option_chars_in_buffer(struct usb_serial_port *port);
85static int option_ioctl(struct usb_serial_port *port, struct file *file,
86 unsigned int cmd, unsigned long arg);
87static void option_set_termios(struct usb_serial_port *port,
88 struct termios *old);
89static void option_break_ctl(struct usb_serial_port *port, int break_state);
90static int option_tiocmget(struct usb_serial_port *port, struct file *file);
91static int option_tiocmset(struct usb_serial_port *port, struct file *file,
92 unsigned int set, unsigned int clear);
93static int option_send_setup(struct usb_serial_port *port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -070094
95/* Vendor and product IDs */
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020096#define OPTION_VENDOR_ID 0x0AF0
97#define HUAWEI_VENDOR_ID 0x12D1
98#define AUDIOVOX_VENDOR_ID 0x0F3D
99#define SIERRAWIRELESS_VENDOR_ID 0x1199
100#define NOVATELWIRELESS_VENDOR_ID 0x1410
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700101
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200102#define OPTION_PRODUCT_OLD 0x5000
103#define OPTION_PRODUCT_FUSION 0x6000
104#define OPTION_PRODUCT_FUSION2 0x6300
105#define OPTION_PRODUCT_COBRA 0x6500
Matthias Urlichse37de9e2006-07-06 13:12:53 +0200106#define OPTION_PRODUCT_COBRA2 0x6600
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200107#define HUAWEI_PRODUCT_E600 0x1001
108#define AUDIOVOX_PRODUCT_AIRCARD 0x0112
109#define SIERRAWIRELESS_PRODUCT_MC8755 0x6802
110#define NOVATELWIRELESS_PRODUCT_U740 0x1400
Matthias Urlichsba460e42005-07-14 00:33:47 -0700111
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700112static struct usb_device_id option_ids[] = {
113 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
Matthias Urlichsba460e42005-07-14 00:33:47 -0700114 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
115 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200116 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
Matthias Urlichse37de9e2006-07-06 13:12:53 +0200117 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
Matthias Urlichsb6137382005-09-22 00:48:40 -0700118 { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
119 { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200120 { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) },
121 { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
122 { } /* Terminating entry */
123};
124
125static struct usb_device_id option_ids1[] = {
126 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
127 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
128 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
129 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
Matthias Urlichse37de9e2006-07-06 13:12:53 +0200130 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200131 { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
132 { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
133 { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
134 { } /* Terminating entry */
135};
136static struct usb_device_id option_ids3[] = {
137 { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) },
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700138 { } /* Terminating entry */
139};
140
141MODULE_DEVICE_TABLE(usb, option_ids);
142
143static struct usb_driver option_driver = {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700144 .name = "option",
145 .probe = usb_serial_probe,
146 .disconnect = usb_serial_disconnect,
147 .id_table = option_ids,
Greg Kroah-Hartmanba9dc652005-11-16 13:41:28 -0800148 .no_dynamic_id = 1,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700149};
150
Uwe Zeisbergerc30fe7f2006-03-24 18:23:14 +0100151/* The card has three separate interfaces, which the serial driver
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700152 * recognizes separately, thus num_port=1.
153 */
Greg Kroah-Hartmanea653702005-06-20 21:15:16 -0700154static struct usb_serial_driver option_3port_device = {
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700155 .driver = {
156 .owner = THIS_MODULE,
Greg Kroah-Hartman269bda12005-06-20 21:15:16 -0700157 .name = "option",
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700158 },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200159 .description = "GSM modem (3-port)",
160 .id_table = option_ids3,
Matthias Urlichsba460e42005-07-14 00:33:47 -0700161 .num_interrupt_in = NUM_DONT_CARE,
162 .num_bulk_in = NUM_DONT_CARE,
163 .num_bulk_out = NUM_DONT_CARE,
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200164 .num_ports = 3,
165 .open = option_open,
166 .close = option_close,
167 .write = option_write,
168 .write_room = option_write_room,
169 .chars_in_buffer = option_chars_in_buffer,
170 .throttle = option_rx_throttle,
171 .unthrottle = option_rx_unthrottle,
172 .set_termios = option_set_termios,
173 .break_ctl = option_break_ctl,
174 .tiocmget = option_tiocmget,
175 .tiocmset = option_tiocmset,
176 .attach = option_startup,
177 .shutdown = option_shutdown,
178 .read_int_callback = option_instat_callback,
179};
180
181static struct usb_serial_driver option_1port_device = {
182 .driver = {
183 .owner = THIS_MODULE,
184 .name = "option",
185 },
186 .description = "GSM modem (1-port)",
187 .id_table = option_ids1,
188 .num_interrupt_in = NUM_DONT_CARE,
189 .num_bulk_in = NUM_DONT_CARE,
190 .num_bulk_out = NUM_DONT_CARE,
191 .num_ports = 1,
Matthias Urlichsba460e42005-07-14 00:33:47 -0700192 .open = option_open,
193 .close = option_close,
194 .write = option_write,
195 .write_room = option_write_room,
196 .chars_in_buffer = option_chars_in_buffer,
197 .throttle = option_rx_throttle,
198 .unthrottle = option_rx_unthrottle,
199 .ioctl = option_ioctl,
200 .set_termios = option_set_termios,
201 .break_ctl = option_break_ctl,
202 .tiocmget = option_tiocmget,
203 .tiocmset = option_tiocmset,
204 .attach = option_startup,
205 .shutdown = option_shutdown,
206 .read_int_callback = option_instat_callback,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700207};
208
Matthias Urlichsba460e42005-07-14 00:33:47 -0700209#ifdef CONFIG_USB_DEBUG
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700210static int debug;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700211#else
212#define debug 0
213#endif
214
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700215/* per port private data */
216
Matthias Urlichsba460e42005-07-14 00:33:47 -0700217#define N_IN_URB 4
218#define N_OUT_URB 1
Matthias Urlichsb27c73d2005-09-22 00:49:33 -0700219#define IN_BUFLEN 4096
Matthias Urlichsba460e42005-07-14 00:33:47 -0700220#define OUT_BUFLEN 128
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700221
222struct option_port_private {
223 /* Input endpoints and buffer for this port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700224 struct urb *in_urbs[N_IN_URB];
225 char in_buffer[N_IN_URB][IN_BUFLEN];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700226 /* Output endpoints and buffer for this port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700227 struct urb *out_urbs[N_OUT_URB];
228 char out_buffer[N_OUT_URB][OUT_BUFLEN];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700229
230 /* Settings for the port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700231 int rts_state; /* Handshaking pins (outputs) */
232 int dtr_state;
233 int cts_state; /* Handshaking pins (inputs) */
234 int dsr_state;
235 int dcd_state;
236 int ri_state;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700237
Matthias Urlichsba460e42005-07-14 00:33:47 -0700238 unsigned long tx_start_time[N_OUT_URB];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700239};
240
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700241/* Functions used by new usb-serial code. */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700242static int __init option_init(void)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700243{
244 int retval;
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200245 retval = usb_serial_register(&option_1port_device);
246 if (retval)
247 goto failed_1port_device_register;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700248 retval = usb_serial_register(&option_3port_device);
249 if (retval)
250 goto failed_3port_device_register;
251 retval = usb_register(&option_driver);
252 if (retval)
253 goto failed_driver_register;
254
255 info(DRIVER_DESC ": " DRIVER_VERSION);
256
257 return 0;
258
259failed_driver_register:
260 usb_serial_deregister (&option_3port_device);
261failed_3port_device_register:
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200262 usb_serial_deregister (&option_1port_device);
263failed_1port_device_register:
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700264 return retval;
265}
266
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700267static void __exit option_exit(void)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700268{
269 usb_deregister (&option_driver);
270 usb_serial_deregister (&option_3port_device);
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200271 usb_serial_deregister (&option_1port_device);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700272}
273
274module_init(option_init);
275module_exit(option_exit);
276
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700277static void option_rx_throttle(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700278{
279 dbg("%s", __FUNCTION__);
280}
281
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700282static void option_rx_unthrottle(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700283{
284 dbg("%s", __FUNCTION__);
285}
286
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700287static void option_break_ctl(struct usb_serial_port *port, int break_state)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700288{
289 /* Unfortunately, I don't know how to send a break */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700290 dbg("%s", __FUNCTION__);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700291}
292
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700293static void option_set_termios(struct usb_serial_port *port,
294 struct termios *old_termios)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700295{
296 dbg("%s", __FUNCTION__);
297
298 option_send_setup(port);
299}
300
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700301static int option_tiocmget(struct usb_serial_port *port, struct file *file)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700302{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700303 unsigned int value;
304 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700305
306 portdata = usb_get_serial_port_data(port);
307
308 value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
309 ((portdata->dtr_state) ? TIOCM_DTR : 0) |
310 ((portdata->cts_state) ? TIOCM_CTS : 0) |
311 ((portdata->dsr_state) ? TIOCM_DSR : 0) |
312 ((portdata->dcd_state) ? TIOCM_CAR : 0) |
313 ((portdata->ri_state) ? TIOCM_RNG : 0);
314
315 return value;
316}
317
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700318static int option_tiocmset(struct usb_serial_port *port, struct file *file,
319 unsigned int set, unsigned int clear)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700320{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700321 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700322
323 portdata = usb_get_serial_port_data(port);
324
325 if (set & TIOCM_RTS)
326 portdata->rts_state = 1;
327 if (set & TIOCM_DTR)
328 portdata->dtr_state = 1;
329
330 if (clear & TIOCM_RTS)
331 portdata->rts_state = 0;
332 if (clear & TIOCM_DTR)
333 portdata->dtr_state = 0;
334 return option_send_setup(port);
335}
336
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700337static int option_ioctl(struct usb_serial_port *port, struct file *file,
338 unsigned int cmd, unsigned long arg)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700339{
340 return -ENOIOCTLCMD;
341}
342
343/* Write */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700344static int option_write(struct usb_serial_port *port,
345 const unsigned char *buf, int count)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700346{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700347 struct option_port_private *portdata;
348 int i;
349 int left, todo;
350 struct urb *this_urb = NULL; /* spurious */
351 int err;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700352
353 portdata = usb_get_serial_port_data(port);
354
355 dbg("%s: write (%d chars)", __FUNCTION__, count);
356
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700357 i = 0;
358 left = count;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700359 for (i=0; left > 0 && i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700360 todo = left;
361 if (todo > OUT_BUFLEN)
362 todo = OUT_BUFLEN;
363
Matthias Urlichsba460e42005-07-14 00:33:47 -0700364 this_urb = portdata->out_urbs[i];
365 if (this_urb->status == -EINPROGRESS) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700366 if (time_before(jiffies,
367 portdata->tx_start_time[i] + 10 * HZ))
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700368 continue;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700369 usb_unlink_urb(this_urb);
Matthias Urlichsba460e42005-07-14 00:33:47 -0700370 continue;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700371 }
Matthias Urlichsba460e42005-07-14 00:33:47 -0700372 if (this_urb->status != 0)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700373 dbg("usb_write %p failed (err=%d)",
374 this_urb, this_urb->status);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700375
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700376 dbg("%s: endpoint %d buf %d", __FUNCTION__,
377 usb_pipeendpoint(this_urb->pipe), i);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700378
Matthias Urlichsba460e42005-07-14 00:33:47 -0700379 /* send the data */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700380 memcpy (this_urb->transfer_buffer, buf, todo);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700381 this_urb->transfer_buffer_length = todo;
382
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700383 this_urb->dev = port->serial->dev;
384 err = usb_submit_urb(this_urb, GFP_ATOMIC);
385 if (err) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700386 dbg("usb_submit_urb %p (write bulk) failed "
387 "(%d, has %d)", this_urb,
388 err, this_urb->status);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700389 continue;
390 }
391 portdata->tx_start_time[i] = jiffies;
392 buf += todo;
393 left -= todo;
394 }
395
396 count -= left;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700397 dbg("%s: wrote (did %d)", __FUNCTION__, count);
398 return count;
399}
400
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700401static void option_indat_callback(struct urb *urb, struct pt_regs *regs)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700402{
Alan Cox33f0f882006-01-09 20:54:13 -0800403 int err;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700404 int endpoint;
405 struct usb_serial_port *port;
406 struct tty_struct *tty;
407 unsigned char *data = urb->transfer_buffer;
408
409 dbg("%s: %p", __FUNCTION__, urb);
410
411 endpoint = usb_pipeendpoint(urb->pipe);
412 port = (struct usb_serial_port *) urb->context;
413
414 if (urb->status) {
415 dbg("%s: nonzero status: %d on endpoint %02x.",
416 __FUNCTION__, urb->status, endpoint);
417 } else {
418 tty = port->tty;
419 if (urb->actual_length) {
Alan Cox33f0f882006-01-09 20:54:13 -0800420 tty_buffer_request_room(tty, urb->actual_length);
421 tty_insert_flip_string(tty, data, urb->actual_length);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700422 tty_flip_buffer_push(tty);
423 } else {
424 dbg("%s: empty read urb received", __FUNCTION__);
425 }
426
427 /* Resubmit urb so we continue receiving */
428 if (port->open_count && urb->status != -ESHUTDOWN) {
429 err = usb_submit_urb(urb, GFP_ATOMIC);
430 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700431 printk(KERN_ERR "%s: resubmit read urb failed. "
432 "(%d)", __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700433 }
434 }
435 return;
436}
437
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700438static void option_outdat_callback(struct urb *urb, struct pt_regs *regs)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700439{
440 struct usb_serial_port *port;
441
442 dbg("%s", __FUNCTION__);
443
444 port = (struct usb_serial_port *) urb->context;
445
Pete Zaitcevcf2c7482006-05-22 21:58:49 -0700446 usb_serial_port_softint(port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700447}
448
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700449static void option_instat_callback(struct urb *urb, struct pt_regs *regs)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700450{
451 int err;
452 struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
453 struct option_port_private *portdata = usb_get_serial_port_data(port);
454 struct usb_serial *serial = port->serial;
455
456 dbg("%s", __FUNCTION__);
457 dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata);
458
459 if (urb->status == 0) {
460 struct usb_ctrlrequest *req_pkt =
461 (struct usb_ctrlrequest *)urb->transfer_buffer;
462
463 if (!req_pkt) {
464 dbg("%s: NULL req_pkt\n", __FUNCTION__);
465 return;
466 }
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700467 if ((req_pkt->bRequestType == 0xA1) &&
468 (req_pkt->bRequest == 0x20)) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700469 int old_dcd_state;
470 unsigned char signals = *((unsigned char *)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700471 urb->transfer_buffer +
472 sizeof(struct usb_ctrlrequest));
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700473
474 dbg("%s: signal x%x", __FUNCTION__, signals);
475
476 old_dcd_state = portdata->dcd_state;
477 portdata->cts_state = 1;
478 portdata->dcd_state = ((signals & 0x01) ? 1 : 0);
479 portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
480 portdata->ri_state = ((signals & 0x08) ? 1 : 0);
481
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700482 if (port->tty && !C_CLOCAL(port->tty) &&
483 old_dcd_state && !portdata->dcd_state)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700484 tty_hangup(port->tty);
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700485 } else {
486 dbg("%s: type %x req %x", __FUNCTION__,
487 req_pkt->bRequestType,req_pkt->bRequest);
488 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700489 } else
490 dbg("%s: error %d", __FUNCTION__, urb->status);
491
492 /* Resubmit urb so we continue receiving IRQ data */
493 if (urb->status != -ESHUTDOWN) {
494 urb->dev = serial->dev;
495 err = usb_submit_urb(urb, GFP_ATOMIC);
496 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700497 dbg("%s: resubmit intr urb failed. (%d)",
498 __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700499 }
500}
501
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700502static int option_write_room(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700503{
504 struct option_port_private *portdata;
505 int i;
506 int data_len = 0;
507 struct urb *this_urb;
508
509 portdata = usb_get_serial_port_data(port);
510
Matthias Urlichsba460e42005-07-14 00:33:47 -0700511 for (i=0; i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700512 this_urb = portdata->out_urbs[i];
513 if (this_urb && this_urb->status != -EINPROGRESS)
514 data_len += OUT_BUFLEN;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700515 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700516
517 dbg("%s: %d", __FUNCTION__, data_len);
518 return data_len;
519}
520
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700521static int option_chars_in_buffer(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700522{
523 struct option_port_private *portdata;
524 int i;
525 int data_len = 0;
526 struct urb *this_urb;
527
528 portdata = usb_get_serial_port_data(port);
529
Matthias Urlichsba460e42005-07-14 00:33:47 -0700530 for (i=0; i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700531 this_urb = portdata->out_urbs[i];
532 if (this_urb && this_urb->status == -EINPROGRESS)
533 data_len += this_urb->transfer_buffer_length;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700534 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700535 dbg("%s: %d", __FUNCTION__, data_len);
536 return data_len;
537}
538
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700539static int option_open(struct usb_serial_port *port, struct file *filp)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700540{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700541 struct option_port_private *portdata;
542 struct usb_serial *serial = port->serial;
543 int i, err;
544 struct urb *urb;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700545
546 portdata = usb_get_serial_port_data(port);
547
548 dbg("%s", __FUNCTION__);
549
550 /* Set some sane defaults */
551 portdata->rts_state = 1;
552 portdata->dtr_state = 1;
553
554 /* Reset low level data toggle and start reading from endpoints */
555 for (i = 0; i < N_IN_URB; i++) {
556 urb = portdata->in_urbs[i];
557 if (! urb)
558 continue;
559 if (urb->dev != serial->dev) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700560 dbg("%s: dev %p != %p", __FUNCTION__,
561 urb->dev, serial->dev);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700562 continue;
563 }
564
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700565 /*
566 * make sure endpoint data toggle is synchronized with the
567 * device
568 */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700569 usb_clear_halt(urb->dev, urb->pipe);
570
571 err = usb_submit_urb(urb, GFP_KERNEL);
572 if (err) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700573 dbg("%s: submit urb %d failed (%d) %d",
574 __FUNCTION__, i, err,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700575 urb->transfer_buffer_length);
576 }
577 }
578
579 /* Reset low level data toggle on out endpoints */
580 for (i = 0; i < N_OUT_URB; i++) {
581 urb = portdata->out_urbs[i];
582 if (! urb)
583 continue;
584 urb->dev = serial->dev;
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700585 /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
586 usb_pipeout(urb->pipe), 0); */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700587 }
588
589 port->tty->low_latency = 1;
590
591 option_send_setup(port);
592
593 return (0);
594}
595
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700596static inline void stop_urb(struct urb *urb)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700597{
Greg Kroah-Hartman242cf672005-07-29 16:11:07 -0400598 if (urb && urb->status == -EINPROGRESS)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700599 usb_kill_urb(urb);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700600}
601
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700602static void option_close(struct usb_serial_port *port, struct file *filp)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700603{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700604 int i;
605 struct usb_serial *serial = port->serial;
606 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700607
608 dbg("%s", __FUNCTION__);
609 portdata = usb_get_serial_port_data(port);
610
611 portdata->rts_state = 0;
612 portdata->dtr_state = 0;
613
614 if (serial->dev) {
615 option_send_setup(port);
616
617 /* Stop reading/writing urbs */
618 for (i = 0; i < N_IN_URB; i++)
619 stop_urb(portdata->in_urbs[i]);
620 for (i = 0; i < N_OUT_URB; i++)
621 stop_urb(portdata->out_urbs[i]);
622 }
623 port->tty = NULL;
624}
625
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700626/* Helper functions used by option_setup_urbs */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700627static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint,
628 int dir, void *ctx, char *buf, int len,
629 void (*callback)(struct urb *, struct pt_regs *regs))
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700630{
631 struct urb *urb;
632
633 if (endpoint == -1)
634 return NULL; /* endpoint not needed */
635
636 urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
637 if (urb == NULL) {
638 dbg("%s: alloc for endpoint %d failed.", __FUNCTION__, endpoint);
639 return NULL;
640 }
641
642 /* Fill URB using supplied data. */
643 usb_fill_bulk_urb(urb, serial->dev,
644 usb_sndbulkpipe(serial->dev, endpoint) | dir,
645 buf, len, callback, ctx);
646
647 return urb;
648}
649
650/* Setup urbs */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700651static void option_setup_urbs(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700652{
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200653 int i,j;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700654 struct usb_serial_port *port;
655 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700656
657 dbg("%s", __FUNCTION__);
658
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200659
660 for (i = 0; i < serial->num_ports; i++) {
661 port = serial->port[i];
662 portdata = usb_get_serial_port_data(port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700663
664 /* Do indat endpoints first */
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200665 for (j = 0; j < N_IN_URB; ++j) {
666 portdata->in_urbs[j] = option_setup_urb (serial,
667 port->bulk_in_endpointAddress, USB_DIR_IN, port,
668 portdata->in_buffer[j], IN_BUFLEN, option_indat_callback);
669 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700670
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200671 /* outdat endpoints */
672 for (j = 0; j < N_OUT_URB; ++j) {
673 portdata->out_urbs[j] = option_setup_urb (serial,
674 port->bulk_out_endpointAddress, USB_DIR_OUT, port,
675 portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback);
676 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700677 }
678}
679
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700680static int option_send_setup(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700681{
682 struct usb_serial *serial = port->serial;
683 struct option_port_private *portdata;
684
685 dbg("%s", __FUNCTION__);
686
687 portdata = usb_get_serial_port_data(port);
688
689 if (port->tty) {
690 int val = 0;
691 if (portdata->dtr_state)
692 val |= 0x01;
693 if (portdata->rts_state)
694 val |= 0x02;
695
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700696 return usb_control_msg(serial->dev,
697 usb_rcvctrlpipe(serial->dev, 0),
698 0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700699 }
700
701 return 0;
702}
703
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700704static int option_startup(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700705{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700706 int i, err;
707 struct usb_serial_port *port;
708 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700709
710 dbg("%s", __FUNCTION__);
711
712 /* Now setup per port private data */
713 for (i = 0; i < serial->num_ports; i++) {
714 port = serial->port[i];
Eric Sesterhenn80b6ca42006-02-27 21:29:43 +0100715 portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700716 if (!portdata) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700717 dbg("%s: kmalloc for option_port_private (%d) failed!.",
718 __FUNCTION__, i);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700719 return (1);
720 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700721
722 usb_set_serial_port_data(port, portdata);
723
724 if (! port->interrupt_in_urb)
725 continue;
726 err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
727 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700728 dbg("%s: submit irq_in urb failed %d",
729 __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700730 }
731
732 option_setup_urbs(serial);
733
734 return (0);
735}
736
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700737static void option_shutdown(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700738{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700739 int i, j;
740 struct usb_serial_port *port;
741 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700742
743 dbg("%s", __FUNCTION__);
744
745 /* Stop reading/writing urbs */
746 for (i = 0; i < serial->num_ports; ++i) {
747 port = serial->port[i];
748 portdata = usb_get_serial_port_data(port);
749 for (j = 0; j < N_IN_URB; j++)
750 stop_urb(portdata->in_urbs[j]);
751 for (j = 0; j < N_OUT_URB; j++)
752 stop_urb(portdata->out_urbs[j]);
753 }
754
755 /* Now free them */
756 for (i = 0; i < serial->num_ports; ++i) {
757 port = serial->port[i];
758 portdata = usb_get_serial_port_data(port);
759
760 for (j = 0; j < N_IN_URB; j++) {
761 if (portdata->in_urbs[j]) {
762 usb_free_urb(portdata->in_urbs[j]);
763 portdata->in_urbs[j] = NULL;
764 }
765 }
766 for (j = 0; j < N_OUT_URB; j++) {
767 if (portdata->out_urbs[j]) {
768 usb_free_urb(portdata->out_urbs[j]);
769 portdata->out_urbs[j] = NULL;
770 }
771 }
772 }
773
774 /* Now free per port private data */
775 for (i = 0; i < serial->num_ports; i++) {
776 port = serial->port[i];
777 kfree(usb_get_serial_port_data(port));
778 }
779}
780
781MODULE_AUTHOR(DRIVER_AUTHOR);
782MODULE_DESCRIPTION(DRIVER_DESC);
783MODULE_VERSION(DRIVER_VERSION);
784MODULE_LICENSE("GPL");
785
Matthias Urlichsba460e42005-07-14 00:33:47 -0700786#ifdef CONFIG_USB_DEBUG
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700787module_param(debug, bool, S_IRUGO | S_IWUSR);
788MODULE_PARM_DESC(debug, "Debug messages");
Matthias Urlichsba460e42005-07-14 00:33:47 -0700789#endif
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700790