blob: 2a07945c4232576cfab3d1ab6569222258169426 [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
Matthias Urlichsb3fdab52006-08-02 16:41:41 -070012 History: see the git log.
Matthias Urlichsba460e42005-07-14 00:33:47 -070013
14 Work sponsored by: Sigos GmbH, Germany <info@sigos.de>
15
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020016 This driver exists because the "normal" serial driver doesn't work too well
17 with GSM modems. Issues:
18 - data loss -- one single Receive URB is not nearly enough
19 - nonstandard flow (Option devices) and multiplex (Sierra) control
20 - controlling the baud rate doesn't make sense
21
22 This driver is named "option" because the most common device it's
23 used for is a PC-Card (with an internal OHCI-USB interface, behind
24 which the GSM interface sits), made by Option Inc.
25
26 Some of the "one port" devices actually exhibit multiple USB instances
27 on the USB bus. This is not a bug, these ports are used for different
28 device features.
Matthias Urlichs58cfe912005-05-23 17:00:48 -070029*/
Matthias Urlichsba460e42005-07-14 00:33:47 -070030
Matthias Urlichse37de9e2006-07-06 13:12:53 +020031#define DRIVER_VERSION "v0.7.1"
Matthias Urlichs58cfe912005-05-23 17:00:48 -070032#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020033#define DRIVER_DESC "USB Driver for GSM modems"
Matthias Urlichs58cfe912005-05-23 17:00:48 -070034
Matthias Urlichs58cfe912005-05-23 17:00:48 -070035#include <linux/kernel.h>
36#include <linux/jiffies.h>
37#include <linux/errno.h>
38#include <linux/tty.h>
39#include <linux/tty_flip.h>
40#include <linux/module.h>
41#include <linux/usb.h>
Greg Kroah-Hartmana9698882006-07-11 21:22:58 -070042#include <linux/usb/serial.h>
Matthias Urlichs58cfe912005-05-23 17:00:48 -070043
44/* Function prototypes */
Andrew Morton7bb75ae2005-07-27 01:08:30 -070045static int option_open(struct usb_serial_port *port, struct file *filp);
46static void option_close(struct usb_serial_port *port, struct file *filp);
47static int option_startup(struct usb_serial *serial);
48static void option_shutdown(struct usb_serial *serial);
49static void option_rx_throttle(struct usb_serial_port *port);
50static void option_rx_unthrottle(struct usb_serial_port *port);
51static int option_write_room(struct usb_serial_port *port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -070052
53static void option_instat_callback(struct urb *urb, struct pt_regs *regs);
54
Andrew Morton7bb75ae2005-07-27 01:08:30 -070055static int option_write(struct usb_serial_port *port,
56 const unsigned char *buf, int count);
Matthias Urlichs58cfe912005-05-23 17:00:48 -070057
Andrew Morton7bb75ae2005-07-27 01:08:30 -070058static int option_chars_in_buffer(struct usb_serial_port *port);
59static int option_ioctl(struct usb_serial_port *port, struct file *file,
60 unsigned int cmd, unsigned long arg);
61static void option_set_termios(struct usb_serial_port *port,
62 struct termios *old);
63static void option_break_ctl(struct usb_serial_port *port, int break_state);
64static int option_tiocmget(struct usb_serial_port *port, struct file *file);
65static int option_tiocmset(struct usb_serial_port *port, struct file *file,
66 unsigned int set, unsigned int clear);
67static int option_send_setup(struct usb_serial_port *port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -070068
69/* Vendor and product IDs */
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020070#define OPTION_VENDOR_ID 0x0AF0
71#define HUAWEI_VENDOR_ID 0x12D1
72#define AUDIOVOX_VENDOR_ID 0x0F3D
73#define SIERRAWIRELESS_VENDOR_ID 0x1199
74#define NOVATELWIRELESS_VENDOR_ID 0x1410
Matthias Urlichs58cfe912005-05-23 17:00:48 -070075
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020076#define OPTION_PRODUCT_OLD 0x5000
77#define OPTION_PRODUCT_FUSION 0x6000
78#define OPTION_PRODUCT_FUSION2 0x6300
79#define OPTION_PRODUCT_COBRA 0x6500
Matthias Urlichse37de9e2006-07-06 13:12:53 +020080#define OPTION_PRODUCT_COBRA2 0x6600
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020081#define HUAWEI_PRODUCT_E600 0x1001
82#define AUDIOVOX_PRODUCT_AIRCARD 0x0112
83#define SIERRAWIRELESS_PRODUCT_MC8755 0x6802
84#define NOVATELWIRELESS_PRODUCT_U740 0x1400
Matthias Urlichsba460e42005-07-14 00:33:47 -070085
Matthias Urlichs58cfe912005-05-23 17:00:48 -070086static struct usb_device_id option_ids[] = {
87 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
Matthias Urlichsba460e42005-07-14 00:33:47 -070088 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
89 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020090 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
Matthias Urlichse37de9e2006-07-06 13:12:53 +020091 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
Matthias Urlichsb6137382005-09-22 00:48:40 -070092 { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
93 { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020094 { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) },
95 { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
96 { } /* Terminating entry */
97};
98
99static struct usb_device_id option_ids1[] = {
100 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
101 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
102 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
103 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
Matthias Urlichse37de9e2006-07-06 13:12:53 +0200104 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200105 { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
106 { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
107 { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
108 { } /* Terminating entry */
109};
110static struct usb_device_id option_ids3[] = {
111 { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) },
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700112 { } /* Terminating entry */
113};
114
115MODULE_DEVICE_TABLE(usb, option_ids);
116
117static struct usb_driver option_driver = {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700118 .name = "option",
119 .probe = usb_serial_probe,
120 .disconnect = usb_serial_disconnect,
121 .id_table = option_ids,
Greg Kroah-Hartmanba9dc652005-11-16 13:41:28 -0800122 .no_dynamic_id = 1,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700123};
124
Uwe Zeisbergerc30fe7f2006-03-24 18:23:14 +0100125/* The card has three separate interfaces, which the serial driver
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700126 * recognizes separately, thus num_port=1.
127 */
Greg Kroah-Hartmanea653702005-06-20 21:15:16 -0700128static struct usb_serial_driver option_3port_device = {
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700129 .driver = {
130 .owner = THIS_MODULE,
Greg Kroah-Hartman269bda12005-06-20 21:15:16 -0700131 .name = "option",
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700132 },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200133 .description = "GSM modem (3-port)",
134 .id_table = option_ids3,
Matthias Urlichsba460e42005-07-14 00:33:47 -0700135 .num_interrupt_in = NUM_DONT_CARE,
136 .num_bulk_in = NUM_DONT_CARE,
137 .num_bulk_out = NUM_DONT_CARE,
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200138 .num_ports = 3,
139 .open = option_open,
140 .close = option_close,
141 .write = option_write,
142 .write_room = option_write_room,
143 .chars_in_buffer = option_chars_in_buffer,
144 .throttle = option_rx_throttle,
145 .unthrottle = option_rx_unthrottle,
146 .set_termios = option_set_termios,
147 .break_ctl = option_break_ctl,
148 .tiocmget = option_tiocmget,
149 .tiocmset = option_tiocmset,
150 .attach = option_startup,
151 .shutdown = option_shutdown,
152 .read_int_callback = option_instat_callback,
153};
154
155static struct usb_serial_driver option_1port_device = {
156 .driver = {
157 .owner = THIS_MODULE,
158 .name = "option",
159 },
160 .description = "GSM modem (1-port)",
161 .id_table = option_ids1,
162 .num_interrupt_in = NUM_DONT_CARE,
163 .num_bulk_in = NUM_DONT_CARE,
164 .num_bulk_out = NUM_DONT_CARE,
165 .num_ports = 1,
Matthias Urlichsba460e42005-07-14 00:33:47 -0700166 .open = option_open,
167 .close = option_close,
168 .write = option_write,
169 .write_room = option_write_room,
170 .chars_in_buffer = option_chars_in_buffer,
171 .throttle = option_rx_throttle,
172 .unthrottle = option_rx_unthrottle,
173 .ioctl = option_ioctl,
174 .set_termios = option_set_termios,
175 .break_ctl = option_break_ctl,
176 .tiocmget = option_tiocmget,
177 .tiocmset = option_tiocmset,
178 .attach = option_startup,
179 .shutdown = option_shutdown,
180 .read_int_callback = option_instat_callback,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700181};
182
Matthias Urlichsba460e42005-07-14 00:33:47 -0700183#ifdef CONFIG_USB_DEBUG
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700184static int debug;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700185#else
186#define debug 0
187#endif
188
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700189/* per port private data */
190
Matthias Urlichsba460e42005-07-14 00:33:47 -0700191#define N_IN_URB 4
192#define N_OUT_URB 1
Matthias Urlichsb27c73d2005-09-22 00:49:33 -0700193#define IN_BUFLEN 4096
Matthias Urlichsba460e42005-07-14 00:33:47 -0700194#define OUT_BUFLEN 128
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700195
196struct option_port_private {
197 /* Input endpoints and buffer for this port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700198 struct urb *in_urbs[N_IN_URB];
199 char in_buffer[N_IN_URB][IN_BUFLEN];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700200 /* Output endpoints and buffer for this port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700201 struct urb *out_urbs[N_OUT_URB];
202 char out_buffer[N_OUT_URB][OUT_BUFLEN];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700203
204 /* Settings for the port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700205 int rts_state; /* Handshaking pins (outputs) */
206 int dtr_state;
207 int cts_state; /* Handshaking pins (inputs) */
208 int dsr_state;
209 int dcd_state;
210 int ri_state;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700211
Matthias Urlichsba460e42005-07-14 00:33:47 -0700212 unsigned long tx_start_time[N_OUT_URB];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700213};
214
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700215/* Functions used by new usb-serial code. */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700216static int __init option_init(void)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700217{
218 int retval;
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200219 retval = usb_serial_register(&option_1port_device);
220 if (retval)
221 goto failed_1port_device_register;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700222 retval = usb_serial_register(&option_3port_device);
223 if (retval)
224 goto failed_3port_device_register;
225 retval = usb_register(&option_driver);
226 if (retval)
227 goto failed_driver_register;
228
229 info(DRIVER_DESC ": " DRIVER_VERSION);
230
231 return 0;
232
233failed_driver_register:
234 usb_serial_deregister (&option_3port_device);
235failed_3port_device_register:
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200236 usb_serial_deregister (&option_1port_device);
237failed_1port_device_register:
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700238 return retval;
239}
240
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700241static void __exit option_exit(void)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700242{
243 usb_deregister (&option_driver);
244 usb_serial_deregister (&option_3port_device);
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200245 usb_serial_deregister (&option_1port_device);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700246}
247
248module_init(option_init);
249module_exit(option_exit);
250
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700251static void option_rx_throttle(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700252{
253 dbg("%s", __FUNCTION__);
254}
255
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700256static void option_rx_unthrottle(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700257{
258 dbg("%s", __FUNCTION__);
259}
260
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700261static void option_break_ctl(struct usb_serial_port *port, int break_state)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700262{
263 /* Unfortunately, I don't know how to send a break */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700264 dbg("%s", __FUNCTION__);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700265}
266
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700267static void option_set_termios(struct usb_serial_port *port,
268 struct termios *old_termios)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700269{
270 dbg("%s", __FUNCTION__);
271
272 option_send_setup(port);
273}
274
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700275static int option_tiocmget(struct usb_serial_port *port, struct file *file)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700276{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700277 unsigned int value;
278 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700279
280 portdata = usb_get_serial_port_data(port);
281
282 value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
283 ((portdata->dtr_state) ? TIOCM_DTR : 0) |
284 ((portdata->cts_state) ? TIOCM_CTS : 0) |
285 ((portdata->dsr_state) ? TIOCM_DSR : 0) |
286 ((portdata->dcd_state) ? TIOCM_CAR : 0) |
287 ((portdata->ri_state) ? TIOCM_RNG : 0);
288
289 return value;
290}
291
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700292static int option_tiocmset(struct usb_serial_port *port, struct file *file,
293 unsigned int set, unsigned int clear)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700294{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700295 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700296
297 portdata = usb_get_serial_port_data(port);
298
299 if (set & TIOCM_RTS)
300 portdata->rts_state = 1;
301 if (set & TIOCM_DTR)
302 portdata->dtr_state = 1;
303
304 if (clear & TIOCM_RTS)
305 portdata->rts_state = 0;
306 if (clear & TIOCM_DTR)
307 portdata->dtr_state = 0;
308 return option_send_setup(port);
309}
310
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700311static int option_ioctl(struct usb_serial_port *port, struct file *file,
312 unsigned int cmd, unsigned long arg)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700313{
314 return -ENOIOCTLCMD;
315}
316
317/* Write */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700318static int option_write(struct usb_serial_port *port,
319 const unsigned char *buf, int count)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700320{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700321 struct option_port_private *portdata;
322 int i;
323 int left, todo;
324 struct urb *this_urb = NULL; /* spurious */
325 int err;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700326
327 portdata = usb_get_serial_port_data(port);
328
329 dbg("%s: write (%d chars)", __FUNCTION__, count);
330
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700331 i = 0;
332 left = count;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700333 for (i=0; left > 0 && i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700334 todo = left;
335 if (todo > OUT_BUFLEN)
336 todo = OUT_BUFLEN;
337
Matthias Urlichsba460e42005-07-14 00:33:47 -0700338 this_urb = portdata->out_urbs[i];
339 if (this_urb->status == -EINPROGRESS) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700340 if (time_before(jiffies,
341 portdata->tx_start_time[i] + 10 * HZ))
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700342 continue;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700343 usb_unlink_urb(this_urb);
Matthias Urlichsba460e42005-07-14 00:33:47 -0700344 continue;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700345 }
Matthias Urlichsba460e42005-07-14 00:33:47 -0700346 if (this_urb->status != 0)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700347 dbg("usb_write %p failed (err=%d)",
348 this_urb, this_urb->status);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700349
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700350 dbg("%s: endpoint %d buf %d", __FUNCTION__,
351 usb_pipeendpoint(this_urb->pipe), i);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700352
Matthias Urlichsba460e42005-07-14 00:33:47 -0700353 /* send the data */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700354 memcpy (this_urb->transfer_buffer, buf, todo);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700355 this_urb->transfer_buffer_length = todo;
356
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700357 this_urb->dev = port->serial->dev;
358 err = usb_submit_urb(this_urb, GFP_ATOMIC);
359 if (err) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700360 dbg("usb_submit_urb %p (write bulk) failed "
361 "(%d, has %d)", this_urb,
362 err, this_urb->status);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700363 continue;
364 }
365 portdata->tx_start_time[i] = jiffies;
366 buf += todo;
367 left -= todo;
368 }
369
370 count -= left;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700371 dbg("%s: wrote (did %d)", __FUNCTION__, count);
372 return count;
373}
374
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700375static void option_indat_callback(struct urb *urb, struct pt_regs *regs)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700376{
Alan Cox33f0f882006-01-09 20:54:13 -0800377 int err;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700378 int endpoint;
379 struct usb_serial_port *port;
380 struct tty_struct *tty;
381 unsigned char *data = urb->transfer_buffer;
382
383 dbg("%s: %p", __FUNCTION__, urb);
384
385 endpoint = usb_pipeendpoint(urb->pipe);
386 port = (struct usb_serial_port *) urb->context;
387
388 if (urb->status) {
389 dbg("%s: nonzero status: %d on endpoint %02x.",
390 __FUNCTION__, urb->status, endpoint);
391 } else {
392 tty = port->tty;
393 if (urb->actual_length) {
Alan Cox33f0f882006-01-09 20:54:13 -0800394 tty_buffer_request_room(tty, urb->actual_length);
395 tty_insert_flip_string(tty, data, urb->actual_length);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700396 tty_flip_buffer_push(tty);
397 } else {
398 dbg("%s: empty read urb received", __FUNCTION__);
399 }
400
401 /* Resubmit urb so we continue receiving */
402 if (port->open_count && urb->status != -ESHUTDOWN) {
403 err = usb_submit_urb(urb, GFP_ATOMIC);
404 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700405 printk(KERN_ERR "%s: resubmit read urb failed. "
406 "(%d)", __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700407 }
408 }
409 return;
410}
411
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700412static void option_outdat_callback(struct urb *urb, struct pt_regs *regs)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700413{
414 struct usb_serial_port *port;
415
416 dbg("%s", __FUNCTION__);
417
418 port = (struct usb_serial_port *) urb->context;
419
Pete Zaitcevcf2c7482006-05-22 21:58:49 -0700420 usb_serial_port_softint(port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700421}
422
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700423static void option_instat_callback(struct urb *urb, struct pt_regs *regs)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700424{
425 int err;
426 struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
427 struct option_port_private *portdata = usb_get_serial_port_data(port);
428 struct usb_serial *serial = port->serial;
429
430 dbg("%s", __FUNCTION__);
431 dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata);
432
433 if (urb->status == 0) {
434 struct usb_ctrlrequest *req_pkt =
435 (struct usb_ctrlrequest *)urb->transfer_buffer;
436
437 if (!req_pkt) {
438 dbg("%s: NULL req_pkt\n", __FUNCTION__);
439 return;
440 }
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700441 if ((req_pkt->bRequestType == 0xA1) &&
442 (req_pkt->bRequest == 0x20)) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700443 int old_dcd_state;
444 unsigned char signals = *((unsigned char *)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700445 urb->transfer_buffer +
446 sizeof(struct usb_ctrlrequest));
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700447
448 dbg("%s: signal x%x", __FUNCTION__, signals);
449
450 old_dcd_state = portdata->dcd_state;
451 portdata->cts_state = 1;
452 portdata->dcd_state = ((signals & 0x01) ? 1 : 0);
453 portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
454 portdata->ri_state = ((signals & 0x08) ? 1 : 0);
455
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700456 if (port->tty && !C_CLOCAL(port->tty) &&
457 old_dcd_state && !portdata->dcd_state)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700458 tty_hangup(port->tty);
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700459 } else {
460 dbg("%s: type %x req %x", __FUNCTION__,
461 req_pkt->bRequestType,req_pkt->bRequest);
462 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700463 } else
464 dbg("%s: error %d", __FUNCTION__, urb->status);
465
466 /* Resubmit urb so we continue receiving IRQ data */
467 if (urb->status != -ESHUTDOWN) {
468 urb->dev = serial->dev;
469 err = usb_submit_urb(urb, GFP_ATOMIC);
470 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700471 dbg("%s: resubmit intr urb failed. (%d)",
472 __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700473 }
474}
475
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700476static int option_write_room(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700477{
478 struct option_port_private *portdata;
479 int i;
480 int data_len = 0;
481 struct urb *this_urb;
482
483 portdata = usb_get_serial_port_data(port);
484
Matthias Urlichsba460e42005-07-14 00:33:47 -0700485 for (i=0; i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700486 this_urb = portdata->out_urbs[i];
487 if (this_urb && this_urb->status != -EINPROGRESS)
488 data_len += OUT_BUFLEN;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700489 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700490
491 dbg("%s: %d", __FUNCTION__, data_len);
492 return data_len;
493}
494
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700495static int option_chars_in_buffer(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700496{
497 struct option_port_private *portdata;
498 int i;
499 int data_len = 0;
500 struct urb *this_urb;
501
502 portdata = usb_get_serial_port_data(port);
503
Matthias Urlichsba460e42005-07-14 00:33:47 -0700504 for (i=0; i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700505 this_urb = portdata->out_urbs[i];
506 if (this_urb && this_urb->status == -EINPROGRESS)
507 data_len += this_urb->transfer_buffer_length;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700508 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700509 dbg("%s: %d", __FUNCTION__, data_len);
510 return data_len;
511}
512
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700513static int option_open(struct usb_serial_port *port, struct file *filp)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700514{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700515 struct option_port_private *portdata;
516 struct usb_serial *serial = port->serial;
517 int i, err;
518 struct urb *urb;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700519
520 portdata = usb_get_serial_port_data(port);
521
522 dbg("%s", __FUNCTION__);
523
524 /* Set some sane defaults */
525 portdata->rts_state = 1;
526 portdata->dtr_state = 1;
527
528 /* Reset low level data toggle and start reading from endpoints */
529 for (i = 0; i < N_IN_URB; i++) {
530 urb = portdata->in_urbs[i];
531 if (! urb)
532 continue;
533 if (urb->dev != serial->dev) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700534 dbg("%s: dev %p != %p", __FUNCTION__,
535 urb->dev, serial->dev);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700536 continue;
537 }
538
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700539 /*
540 * make sure endpoint data toggle is synchronized with the
541 * device
542 */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700543 usb_clear_halt(urb->dev, urb->pipe);
544
545 err = usb_submit_urb(urb, GFP_KERNEL);
546 if (err) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700547 dbg("%s: submit urb %d failed (%d) %d",
548 __FUNCTION__, i, err,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700549 urb->transfer_buffer_length);
550 }
551 }
552
553 /* Reset low level data toggle on out endpoints */
554 for (i = 0; i < N_OUT_URB; i++) {
555 urb = portdata->out_urbs[i];
556 if (! urb)
557 continue;
558 urb->dev = serial->dev;
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700559 /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
560 usb_pipeout(urb->pipe), 0); */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700561 }
562
563 port->tty->low_latency = 1;
564
565 option_send_setup(port);
566
567 return (0);
568}
569
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700570static inline void stop_urb(struct urb *urb)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700571{
Greg Kroah-Hartman242cf672005-07-29 16:11:07 -0400572 if (urb && urb->status == -EINPROGRESS)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700573 usb_kill_urb(urb);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700574}
575
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700576static void option_close(struct usb_serial_port *port, struct file *filp)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700577{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700578 int i;
579 struct usb_serial *serial = port->serial;
580 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700581
582 dbg("%s", __FUNCTION__);
583 portdata = usb_get_serial_port_data(port);
584
585 portdata->rts_state = 0;
586 portdata->dtr_state = 0;
587
588 if (serial->dev) {
589 option_send_setup(port);
590
591 /* Stop reading/writing urbs */
592 for (i = 0; i < N_IN_URB; i++)
593 stop_urb(portdata->in_urbs[i]);
594 for (i = 0; i < N_OUT_URB; i++)
595 stop_urb(portdata->out_urbs[i]);
596 }
597 port->tty = NULL;
598}
599
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700600/* Helper functions used by option_setup_urbs */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700601static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint,
602 int dir, void *ctx, char *buf, int len,
603 void (*callback)(struct urb *, struct pt_regs *regs))
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700604{
605 struct urb *urb;
606
607 if (endpoint == -1)
608 return NULL; /* endpoint not needed */
609
610 urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
611 if (urb == NULL) {
612 dbg("%s: alloc for endpoint %d failed.", __FUNCTION__, endpoint);
613 return NULL;
614 }
615
616 /* Fill URB using supplied data. */
617 usb_fill_bulk_urb(urb, serial->dev,
618 usb_sndbulkpipe(serial->dev, endpoint) | dir,
619 buf, len, callback, ctx);
620
621 return urb;
622}
623
624/* Setup urbs */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700625static void option_setup_urbs(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700626{
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200627 int i,j;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700628 struct usb_serial_port *port;
629 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700630
631 dbg("%s", __FUNCTION__);
632
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200633
634 for (i = 0; i < serial->num_ports; i++) {
635 port = serial->port[i];
636 portdata = usb_get_serial_port_data(port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700637
638 /* Do indat endpoints first */
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200639 for (j = 0; j < N_IN_URB; ++j) {
640 portdata->in_urbs[j] = option_setup_urb (serial,
641 port->bulk_in_endpointAddress, USB_DIR_IN, port,
642 portdata->in_buffer[j], IN_BUFLEN, option_indat_callback);
643 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700644
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200645 /* outdat endpoints */
646 for (j = 0; j < N_OUT_URB; ++j) {
647 portdata->out_urbs[j] = option_setup_urb (serial,
648 port->bulk_out_endpointAddress, USB_DIR_OUT, port,
649 portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback);
650 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700651 }
652}
653
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700654static int option_send_setup(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700655{
656 struct usb_serial *serial = port->serial;
657 struct option_port_private *portdata;
658
659 dbg("%s", __FUNCTION__);
660
661 portdata = usb_get_serial_port_data(port);
662
663 if (port->tty) {
664 int val = 0;
665 if (portdata->dtr_state)
666 val |= 0x01;
667 if (portdata->rts_state)
668 val |= 0x02;
669
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700670 return usb_control_msg(serial->dev,
671 usb_rcvctrlpipe(serial->dev, 0),
672 0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700673 }
674
675 return 0;
676}
677
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700678static int option_startup(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700679{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700680 int i, err;
681 struct usb_serial_port *port;
682 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700683
684 dbg("%s", __FUNCTION__);
685
686 /* Now setup per port private data */
687 for (i = 0; i < serial->num_ports; i++) {
688 port = serial->port[i];
Eric Sesterhenn80b6ca42006-02-27 21:29:43 +0100689 portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700690 if (!portdata) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700691 dbg("%s: kmalloc for option_port_private (%d) failed!.",
692 __FUNCTION__, i);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700693 return (1);
694 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700695
696 usb_set_serial_port_data(port, portdata);
697
698 if (! port->interrupt_in_urb)
699 continue;
700 err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
701 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700702 dbg("%s: submit irq_in urb failed %d",
703 __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700704 }
705
706 option_setup_urbs(serial);
707
708 return (0);
709}
710
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700711static void option_shutdown(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700712{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700713 int i, j;
714 struct usb_serial_port *port;
715 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700716
717 dbg("%s", __FUNCTION__);
718
719 /* Stop reading/writing urbs */
720 for (i = 0; i < serial->num_ports; ++i) {
721 port = serial->port[i];
722 portdata = usb_get_serial_port_data(port);
723 for (j = 0; j < N_IN_URB; j++)
724 stop_urb(portdata->in_urbs[j]);
725 for (j = 0; j < N_OUT_URB; j++)
726 stop_urb(portdata->out_urbs[j]);
727 }
728
729 /* Now free them */
730 for (i = 0; i < serial->num_ports; ++i) {
731 port = serial->port[i];
732 portdata = usb_get_serial_port_data(port);
733
734 for (j = 0; j < N_IN_URB; j++) {
735 if (portdata->in_urbs[j]) {
736 usb_free_urb(portdata->in_urbs[j]);
737 portdata->in_urbs[j] = NULL;
738 }
739 }
740 for (j = 0; j < N_OUT_URB; j++) {
741 if (portdata->out_urbs[j]) {
742 usb_free_urb(portdata->out_urbs[j]);
743 portdata->out_urbs[j] = NULL;
744 }
745 }
746 }
747
748 /* Now free per port private data */
749 for (i = 0; i < serial->num_ports; i++) {
750 port = serial->port[i];
751 kfree(usb_get_serial_port_data(port));
752 }
753}
754
755MODULE_AUTHOR(DRIVER_AUTHOR);
756MODULE_DESCRIPTION(DRIVER_DESC);
757MODULE_VERSION(DRIVER_VERSION);
758MODULE_LICENSE("GPL");
759
Matthias Urlichsba460e42005-07-14 00:33:47 -0700760#ifdef CONFIG_USB_DEBUG
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700761module_param(debug, bool, S_IRUGO | S_IWUSR);
762MODULE_PARM_DESC(debug, "Debug messages");
Matthias Urlichsba460e42005-07-14 00:33:47 -0700763#endif
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700764