blob: 0ae4098718c360601976de27738cad3264b5a0c0 [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
Matthias Urlichs7c1c2f72006-07-20 04:56:00 +020019 - nonstandard flow (Option devices) control
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020020 - 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
David Howells7d12e782006-10-05 14:55:46 +010053static void option_instat_callback(struct urb *urb);
Matthias Urlichs58cfe912005-05-23 17:00:48 -070054
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,
Alan Cox606d0992006-12-08 02:38:45 -080062 struct ktermios *old);
Andrew Morton7bb75ae2005-07-27 01:08:30 -070063static 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
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020073#define NOVATELWIRELESS_VENDOR_ID 0x1410
Matthias Urlichs31fcbb72006-07-12 08:35:29 +020074#define ANYDATA_VENDOR_ID 0x16d5
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
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020083#define NOVATELWIRELESS_PRODUCT_U740 0x1400
Matthias Urlichs31fcbb72006-07-12 08:35:29 +020084#define ANYDATA_PRODUCT_ID 0x6501
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(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
Matthias Urlichs31fcbb72006-07-12 08:35:29 +020095 { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020096 { } /* 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) },
Matthias Urlichs31fcbb72006-07-12 08:35:29 +0200108 { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200109 { } /* Terminating entry */
110};
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700111
112MODULE_DEVICE_TABLE(usb, option_ids);
113
114static struct usb_driver option_driver = {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700115 .name = "option",
116 .probe = usb_serial_probe,
117 .disconnect = usb_serial_disconnect,
118 .id_table = option_ids,
Greg Kroah-Hartmanba9dc652005-11-16 13:41:28 -0800119 .no_dynamic_id = 1,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700120};
121
Uwe Zeisbergerc30fe7f2006-03-24 18:23:14 +0100122/* The card has three separate interfaces, which the serial driver
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700123 * recognizes separately, thus num_port=1.
124 */
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200125
126static struct usb_serial_driver option_1port_device = {
127 .driver = {
128 .owner = THIS_MODULE,
Matthias Urlichs02b2ac52006-08-02 16:41:41 -0700129 .name = "option1",
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200130 },
131 .description = "GSM modem (1-port)",
132 .id_table = option_ids1,
133 .num_interrupt_in = NUM_DONT_CARE,
134 .num_bulk_in = NUM_DONT_CARE,
135 .num_bulk_out = NUM_DONT_CARE,
136 .num_ports = 1,
Matthias Urlichsba460e42005-07-14 00:33:47 -0700137 .open = option_open,
138 .close = option_close,
139 .write = option_write,
140 .write_room = option_write_room,
141 .chars_in_buffer = option_chars_in_buffer,
142 .throttle = option_rx_throttle,
143 .unthrottle = option_rx_unthrottle,
144 .ioctl = option_ioctl,
145 .set_termios = option_set_termios,
146 .break_ctl = option_break_ctl,
147 .tiocmget = option_tiocmget,
148 .tiocmset = option_tiocmset,
149 .attach = option_startup,
150 .shutdown = option_shutdown,
151 .read_int_callback = option_instat_callback,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700152};
153
Matthias Urlichsba460e42005-07-14 00:33:47 -0700154#ifdef CONFIG_USB_DEBUG
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700155static int debug;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700156#else
157#define debug 0
158#endif
159
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700160/* per port private data */
161
Matthias Urlichsba460e42005-07-14 00:33:47 -0700162#define N_IN_URB 4
163#define N_OUT_URB 1
Matthias Urlichsb27c73d2005-09-22 00:49:33 -0700164#define IN_BUFLEN 4096
Matthias Urlichsba460e42005-07-14 00:33:47 -0700165#define OUT_BUFLEN 128
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700166
167struct option_port_private {
168 /* Input endpoints and buffer for this port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700169 struct urb *in_urbs[N_IN_URB];
170 char in_buffer[N_IN_URB][IN_BUFLEN];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700171 /* Output endpoints and buffer for this port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700172 struct urb *out_urbs[N_OUT_URB];
173 char out_buffer[N_OUT_URB][OUT_BUFLEN];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700174
175 /* Settings for the port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700176 int rts_state; /* Handshaking pins (outputs) */
177 int dtr_state;
178 int cts_state; /* Handshaking pins (inputs) */
179 int dsr_state;
180 int dcd_state;
181 int ri_state;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700182
Matthias Urlichsba460e42005-07-14 00:33:47 -0700183 unsigned long tx_start_time[N_OUT_URB];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700184};
185
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700186/* Functions used by new usb-serial code. */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700187static int __init option_init(void)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700188{
189 int retval;
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200190 retval = usb_serial_register(&option_1port_device);
191 if (retval)
192 goto failed_1port_device_register;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700193 retval = usb_register(&option_driver);
194 if (retval)
195 goto failed_driver_register;
196
197 info(DRIVER_DESC ": " DRIVER_VERSION);
198
199 return 0;
200
201failed_driver_register:
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200202 usb_serial_deregister (&option_1port_device);
203failed_1port_device_register:
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700204 return retval;
205}
206
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700207static void __exit option_exit(void)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700208{
209 usb_deregister (&option_driver);
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200210 usb_serial_deregister (&option_1port_device);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700211}
212
213module_init(option_init);
214module_exit(option_exit);
215
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700216static void option_rx_throttle(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700217{
218 dbg("%s", __FUNCTION__);
219}
220
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700221static void option_rx_unthrottle(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700222{
223 dbg("%s", __FUNCTION__);
224}
225
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700226static void option_break_ctl(struct usb_serial_port *port, int break_state)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700227{
228 /* Unfortunately, I don't know how to send a break */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700229 dbg("%s", __FUNCTION__);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700230}
231
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700232static void option_set_termios(struct usb_serial_port *port,
Alan Cox606d0992006-12-08 02:38:45 -0800233 struct ktermios *old_termios)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700234{
235 dbg("%s", __FUNCTION__);
236
237 option_send_setup(port);
238}
239
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700240static int option_tiocmget(struct usb_serial_port *port, struct file *file)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700241{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700242 unsigned int value;
243 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700244
245 portdata = usb_get_serial_port_data(port);
246
247 value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
248 ((portdata->dtr_state) ? TIOCM_DTR : 0) |
249 ((portdata->cts_state) ? TIOCM_CTS : 0) |
250 ((portdata->dsr_state) ? TIOCM_DSR : 0) |
251 ((portdata->dcd_state) ? TIOCM_CAR : 0) |
252 ((portdata->ri_state) ? TIOCM_RNG : 0);
253
254 return value;
255}
256
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700257static int option_tiocmset(struct usb_serial_port *port, struct file *file,
258 unsigned int set, unsigned int clear)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700259{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700260 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700261
262 portdata = usb_get_serial_port_data(port);
263
264 if (set & TIOCM_RTS)
265 portdata->rts_state = 1;
266 if (set & TIOCM_DTR)
267 portdata->dtr_state = 1;
268
269 if (clear & TIOCM_RTS)
270 portdata->rts_state = 0;
271 if (clear & TIOCM_DTR)
272 portdata->dtr_state = 0;
273 return option_send_setup(port);
274}
275
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700276static int option_ioctl(struct usb_serial_port *port, struct file *file,
277 unsigned int cmd, unsigned long arg)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700278{
279 return -ENOIOCTLCMD;
280}
281
282/* Write */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700283static int option_write(struct usb_serial_port *port,
284 const unsigned char *buf, int count)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700285{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700286 struct option_port_private *portdata;
287 int i;
288 int left, todo;
289 struct urb *this_urb = NULL; /* spurious */
290 int err;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700291
292 portdata = usb_get_serial_port_data(port);
293
294 dbg("%s: write (%d chars)", __FUNCTION__, count);
295
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700296 i = 0;
297 left = count;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700298 for (i=0; left > 0 && i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700299 todo = left;
300 if (todo > OUT_BUFLEN)
301 todo = OUT_BUFLEN;
302
Matthias Urlichsba460e42005-07-14 00:33:47 -0700303 this_urb = portdata->out_urbs[i];
304 if (this_urb->status == -EINPROGRESS) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700305 if (time_before(jiffies,
306 portdata->tx_start_time[i] + 10 * HZ))
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700307 continue;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700308 usb_unlink_urb(this_urb);
Matthias Urlichsba460e42005-07-14 00:33:47 -0700309 continue;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700310 }
Matthias Urlichsba460e42005-07-14 00:33:47 -0700311 if (this_urb->status != 0)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700312 dbg("usb_write %p failed (err=%d)",
313 this_urb, this_urb->status);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700314
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700315 dbg("%s: endpoint %d buf %d", __FUNCTION__,
316 usb_pipeendpoint(this_urb->pipe), i);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700317
Matthias Urlichsba460e42005-07-14 00:33:47 -0700318 /* send the data */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700319 memcpy (this_urb->transfer_buffer, buf, todo);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700320 this_urb->transfer_buffer_length = todo;
321
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700322 this_urb->dev = port->serial->dev;
323 err = usb_submit_urb(this_urb, GFP_ATOMIC);
324 if (err) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700325 dbg("usb_submit_urb %p (write bulk) failed "
326 "(%d, has %d)", this_urb,
327 err, this_urb->status);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700328 continue;
329 }
330 portdata->tx_start_time[i] = jiffies;
331 buf += todo;
332 left -= todo;
333 }
334
335 count -= left;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700336 dbg("%s: wrote (did %d)", __FUNCTION__, count);
337 return count;
338}
339
David Howells7d12e782006-10-05 14:55:46 +0100340static void option_indat_callback(struct urb *urb)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700341{
Alan Cox33f0f882006-01-09 20:54:13 -0800342 int err;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700343 int endpoint;
344 struct usb_serial_port *port;
345 struct tty_struct *tty;
346 unsigned char *data = urb->transfer_buffer;
347
348 dbg("%s: %p", __FUNCTION__, urb);
349
350 endpoint = usb_pipeendpoint(urb->pipe);
351 port = (struct usb_serial_port *) urb->context;
352
353 if (urb->status) {
354 dbg("%s: nonzero status: %d on endpoint %02x.",
355 __FUNCTION__, urb->status, endpoint);
356 } else {
357 tty = port->tty;
358 if (urb->actual_length) {
Alan Cox33f0f882006-01-09 20:54:13 -0800359 tty_buffer_request_room(tty, urb->actual_length);
360 tty_insert_flip_string(tty, data, urb->actual_length);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700361 tty_flip_buffer_push(tty);
362 } else {
363 dbg("%s: empty read urb received", __FUNCTION__);
364 }
365
366 /* Resubmit urb so we continue receiving */
367 if (port->open_count && urb->status != -ESHUTDOWN) {
368 err = usb_submit_urb(urb, GFP_ATOMIC);
369 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700370 printk(KERN_ERR "%s: resubmit read urb failed. "
371 "(%d)", __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700372 }
373 }
374 return;
375}
376
David Howells7d12e782006-10-05 14:55:46 +0100377static void option_outdat_callback(struct urb *urb)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700378{
379 struct usb_serial_port *port;
380
381 dbg("%s", __FUNCTION__);
382
383 port = (struct usb_serial_port *) urb->context;
384
Pete Zaitcevcf2c7482006-05-22 21:58:49 -0700385 usb_serial_port_softint(port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700386}
387
David Howells7d12e782006-10-05 14:55:46 +0100388static void option_instat_callback(struct urb *urb)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700389{
390 int err;
391 struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
392 struct option_port_private *portdata = usb_get_serial_port_data(port);
393 struct usb_serial *serial = port->serial;
394
395 dbg("%s", __FUNCTION__);
396 dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata);
397
398 if (urb->status == 0) {
399 struct usb_ctrlrequest *req_pkt =
400 (struct usb_ctrlrequest *)urb->transfer_buffer;
401
402 if (!req_pkt) {
403 dbg("%s: NULL req_pkt\n", __FUNCTION__);
404 return;
405 }
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700406 if ((req_pkt->bRequestType == 0xA1) &&
407 (req_pkt->bRequest == 0x20)) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700408 int old_dcd_state;
409 unsigned char signals = *((unsigned char *)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700410 urb->transfer_buffer +
411 sizeof(struct usb_ctrlrequest));
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700412
413 dbg("%s: signal x%x", __FUNCTION__, signals);
414
415 old_dcd_state = portdata->dcd_state;
416 portdata->cts_state = 1;
417 portdata->dcd_state = ((signals & 0x01) ? 1 : 0);
418 portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
419 portdata->ri_state = ((signals & 0x08) ? 1 : 0);
420
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700421 if (port->tty && !C_CLOCAL(port->tty) &&
422 old_dcd_state && !portdata->dcd_state)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700423 tty_hangup(port->tty);
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700424 } else {
425 dbg("%s: type %x req %x", __FUNCTION__,
426 req_pkt->bRequestType,req_pkt->bRequest);
427 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700428 } else
429 dbg("%s: error %d", __FUNCTION__, urb->status);
430
431 /* Resubmit urb so we continue receiving IRQ data */
432 if (urb->status != -ESHUTDOWN) {
433 urb->dev = serial->dev;
434 err = usb_submit_urb(urb, GFP_ATOMIC);
435 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700436 dbg("%s: resubmit intr urb failed. (%d)",
437 __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700438 }
439}
440
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700441static int option_write_room(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700442{
443 struct option_port_private *portdata;
444 int i;
445 int data_len = 0;
446 struct urb *this_urb;
447
448 portdata = usb_get_serial_port_data(port);
449
Matthias Urlichsba460e42005-07-14 00:33:47 -0700450 for (i=0; i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700451 this_urb = portdata->out_urbs[i];
452 if (this_urb && this_urb->status != -EINPROGRESS)
453 data_len += OUT_BUFLEN;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700454 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700455
456 dbg("%s: %d", __FUNCTION__, data_len);
457 return data_len;
458}
459
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700460static int option_chars_in_buffer(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700461{
462 struct option_port_private *portdata;
463 int i;
464 int data_len = 0;
465 struct urb *this_urb;
466
467 portdata = usb_get_serial_port_data(port);
468
Matthias Urlichsba460e42005-07-14 00:33:47 -0700469 for (i=0; i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700470 this_urb = portdata->out_urbs[i];
471 if (this_urb && this_urb->status == -EINPROGRESS)
472 data_len += this_urb->transfer_buffer_length;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700473 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700474 dbg("%s: %d", __FUNCTION__, data_len);
475 return data_len;
476}
477
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700478static int option_open(struct usb_serial_port *port, struct file *filp)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700479{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700480 struct option_port_private *portdata;
481 struct usb_serial *serial = port->serial;
482 int i, err;
483 struct urb *urb;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700484
485 portdata = usb_get_serial_port_data(port);
486
487 dbg("%s", __FUNCTION__);
488
489 /* Set some sane defaults */
490 portdata->rts_state = 1;
491 portdata->dtr_state = 1;
492
493 /* Reset low level data toggle and start reading from endpoints */
494 for (i = 0; i < N_IN_URB; i++) {
495 urb = portdata->in_urbs[i];
496 if (! urb)
497 continue;
498 if (urb->dev != serial->dev) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700499 dbg("%s: dev %p != %p", __FUNCTION__,
500 urb->dev, serial->dev);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700501 continue;
502 }
503
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700504 /*
505 * make sure endpoint data toggle is synchronized with the
506 * device
507 */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700508 usb_clear_halt(urb->dev, urb->pipe);
509
510 err = usb_submit_urb(urb, GFP_KERNEL);
511 if (err) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700512 dbg("%s: submit urb %d failed (%d) %d",
513 __FUNCTION__, i, err,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700514 urb->transfer_buffer_length);
515 }
516 }
517
518 /* Reset low level data toggle on out endpoints */
519 for (i = 0; i < N_OUT_URB; i++) {
520 urb = portdata->out_urbs[i];
521 if (! urb)
522 continue;
523 urb->dev = serial->dev;
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700524 /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
525 usb_pipeout(urb->pipe), 0); */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700526 }
527
528 port->tty->low_latency = 1;
529
530 option_send_setup(port);
531
532 return (0);
533}
534
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700535static inline void stop_urb(struct urb *urb)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700536{
Greg Kroah-Hartman242cf672005-07-29 16:11:07 -0400537 if (urb && urb->status == -EINPROGRESS)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700538 usb_kill_urb(urb);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700539}
540
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700541static void option_close(struct usb_serial_port *port, struct file *filp)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700542{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700543 int i;
544 struct usb_serial *serial = port->serial;
545 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700546
547 dbg("%s", __FUNCTION__);
548 portdata = usb_get_serial_port_data(port);
549
550 portdata->rts_state = 0;
551 portdata->dtr_state = 0;
552
553 if (serial->dev) {
554 option_send_setup(port);
555
556 /* Stop reading/writing urbs */
557 for (i = 0; i < N_IN_URB; i++)
558 stop_urb(portdata->in_urbs[i]);
559 for (i = 0; i < N_OUT_URB; i++)
560 stop_urb(portdata->out_urbs[i]);
561 }
562 port->tty = NULL;
563}
564
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700565/* Helper functions used by option_setup_urbs */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700566static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint,
567 int dir, void *ctx, char *buf, int len,
David Howells7d12e782006-10-05 14:55:46 +0100568 void (*callback)(struct urb *))
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700569{
570 struct urb *urb;
571
572 if (endpoint == -1)
573 return NULL; /* endpoint not needed */
574
575 urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
576 if (urb == NULL) {
577 dbg("%s: alloc for endpoint %d failed.", __FUNCTION__, endpoint);
578 return NULL;
579 }
580
581 /* Fill URB using supplied data. */
582 usb_fill_bulk_urb(urb, serial->dev,
583 usb_sndbulkpipe(serial->dev, endpoint) | dir,
584 buf, len, callback, ctx);
585
586 return urb;
587}
588
589/* Setup urbs */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700590static void option_setup_urbs(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700591{
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200592 int i,j;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700593 struct usb_serial_port *port;
594 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700595
596 dbg("%s", __FUNCTION__);
597
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200598 for (i = 0; i < serial->num_ports; i++) {
599 port = serial->port[i];
600 portdata = usb_get_serial_port_data(port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700601
602 /* Do indat endpoints first */
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200603 for (j = 0; j < N_IN_URB; ++j) {
604 portdata->in_urbs[j] = option_setup_urb (serial,
605 port->bulk_in_endpointAddress, USB_DIR_IN, port,
606 portdata->in_buffer[j], IN_BUFLEN, option_indat_callback);
607 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700608
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200609 /* outdat endpoints */
610 for (j = 0; j < N_OUT_URB; ++j) {
611 portdata->out_urbs[j] = option_setup_urb (serial,
612 port->bulk_out_endpointAddress, USB_DIR_OUT, port,
613 portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback);
614 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700615 }
616}
617
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700618static int option_send_setup(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700619{
620 struct usb_serial *serial = port->serial;
621 struct option_port_private *portdata;
622
623 dbg("%s", __FUNCTION__);
624
625 portdata = usb_get_serial_port_data(port);
626
627 if (port->tty) {
628 int val = 0;
629 if (portdata->dtr_state)
630 val |= 0x01;
631 if (portdata->rts_state)
632 val |= 0x02;
633
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700634 return usb_control_msg(serial->dev,
635 usb_rcvctrlpipe(serial->dev, 0),
636 0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700637 }
638
639 return 0;
640}
641
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700642static int option_startup(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700643{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700644 int i, err;
645 struct usb_serial_port *port;
646 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700647
648 dbg("%s", __FUNCTION__);
649
650 /* Now setup per port private data */
651 for (i = 0; i < serial->num_ports; i++) {
652 port = serial->port[i];
Eric Sesterhenn80b6ca42006-02-27 21:29:43 +0100653 portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700654 if (!portdata) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700655 dbg("%s: kmalloc for option_port_private (%d) failed!.",
656 __FUNCTION__, i);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700657 return (1);
658 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700659
660 usb_set_serial_port_data(port, portdata);
661
662 if (! port->interrupt_in_urb)
663 continue;
664 err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
665 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700666 dbg("%s: submit irq_in urb failed %d",
667 __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700668 }
669
670 option_setup_urbs(serial);
671
672 return (0);
673}
674
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700675static void option_shutdown(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700676{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700677 int i, j;
678 struct usb_serial_port *port;
679 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700680
681 dbg("%s", __FUNCTION__);
682
683 /* Stop reading/writing urbs */
684 for (i = 0; i < serial->num_ports; ++i) {
685 port = serial->port[i];
686 portdata = usb_get_serial_port_data(port);
687 for (j = 0; j < N_IN_URB; j++)
688 stop_urb(portdata->in_urbs[j]);
689 for (j = 0; j < N_OUT_URB; j++)
690 stop_urb(portdata->out_urbs[j]);
691 }
692
693 /* Now free them */
694 for (i = 0; i < serial->num_ports; ++i) {
695 port = serial->port[i];
696 portdata = usb_get_serial_port_data(port);
697
698 for (j = 0; j < N_IN_URB; j++) {
699 if (portdata->in_urbs[j]) {
700 usb_free_urb(portdata->in_urbs[j]);
701 portdata->in_urbs[j] = NULL;
702 }
703 }
704 for (j = 0; j < N_OUT_URB; j++) {
705 if (portdata->out_urbs[j]) {
706 usb_free_urb(portdata->out_urbs[j]);
707 portdata->out_urbs[j] = NULL;
708 }
709 }
710 }
711
712 /* Now free per port private data */
713 for (i = 0; i < serial->num_ports; i++) {
714 port = serial->port[i];
715 kfree(usb_get_serial_port_data(port));
716 }
717}
718
719MODULE_AUTHOR(DRIVER_AUTHOR);
720MODULE_DESCRIPTION(DRIVER_DESC);
721MODULE_VERSION(DRIVER_VERSION);
722MODULE_LICENSE("GPL");
723
Matthias Urlichsba460e42005-07-14 00:33:47 -0700724#ifdef CONFIG_USB_DEBUG
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700725module_param(debug, bool, S_IRUGO | S_IWUSR);
726MODULE_PARM_DESC(debug, "Debug messages");
Matthias Urlichsba460e42005-07-14 00:33:47 -0700727#endif
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700728