blob: b020f13b95bb5731583e572ff3db934ae3b8e7d3 [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 Urlichs31fcbb72006-07-12 08:35:29 +020075#define ANYDATA_VENDOR_ID 0x16d5
Matthias Urlichs58cfe912005-05-23 17:00:48 -070076
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020077#define OPTION_PRODUCT_OLD 0x5000
78#define OPTION_PRODUCT_FUSION 0x6000
79#define OPTION_PRODUCT_FUSION2 0x6300
80#define OPTION_PRODUCT_COBRA 0x6500
Matthias Urlichse37de9e2006-07-06 13:12:53 +020081#define OPTION_PRODUCT_COBRA2 0x6600
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020082#define HUAWEI_PRODUCT_E600 0x1001
83#define AUDIOVOX_PRODUCT_AIRCARD 0x0112
84#define SIERRAWIRELESS_PRODUCT_MC8755 0x6802
85#define NOVATELWIRELESS_PRODUCT_U740 0x1400
Matthias Urlichs31fcbb72006-07-12 08:35:29 +020086#define ANYDATA_PRODUCT_ID 0x6501
Matthias Urlichsba460e42005-07-14 00:33:47 -070087
Matthias Urlichs58cfe912005-05-23 17:00:48 -070088static struct usb_device_id option_ids[] = {
89 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
Matthias Urlichsba460e42005-07-14 00:33:47 -070090 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
91 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020092 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
Matthias Urlichse37de9e2006-07-06 13:12:53 +020093 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
Matthias Urlichsb6137382005-09-22 00:48:40 -070094 { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
95 { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020096 { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) },
97 { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
Matthias Urlichs31fcbb72006-07-12 08:35:29 +020098 { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020099 { } /* Terminating entry */
100};
101
102static struct usb_device_id option_ids1[] = {
103 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
104 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
105 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
106 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
Matthias Urlichse37de9e2006-07-06 13:12:53 +0200107 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200108 { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
109 { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
110 { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
Matthias Urlichs31fcbb72006-07-12 08:35:29 +0200111 { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200112 { } /* Terminating entry */
113};
114static struct usb_device_id option_ids3[] = {
115 { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) },
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700116 { } /* Terminating entry */
117};
118
119MODULE_DEVICE_TABLE(usb, option_ids);
120
121static struct usb_driver option_driver = {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700122 .name = "option",
123 .probe = usb_serial_probe,
124 .disconnect = usb_serial_disconnect,
125 .id_table = option_ids,
Greg Kroah-Hartmanba9dc652005-11-16 13:41:28 -0800126 .no_dynamic_id = 1,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700127};
128
Uwe Zeisbergerc30fe7f2006-03-24 18:23:14 +0100129/* The card has three separate interfaces, which the serial driver
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700130 * recognizes separately, thus num_port=1.
131 */
Greg Kroah-Hartmanea653702005-06-20 21:15:16 -0700132static struct usb_serial_driver option_3port_device = {
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700133 .driver = {
134 .owner = THIS_MODULE,
Matthias Urlichs02b2ac52006-08-02 16:41:41 -0700135 .name = "option3",
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700136 },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200137 .description = "GSM modem (3-port)",
138 .id_table = option_ids3,
Matthias Urlichsba460e42005-07-14 00:33:47 -0700139 .num_interrupt_in = NUM_DONT_CARE,
140 .num_bulk_in = NUM_DONT_CARE,
141 .num_bulk_out = NUM_DONT_CARE,
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200142 .num_ports = 3,
143 .open = option_open,
144 .close = option_close,
145 .write = option_write,
146 .write_room = option_write_room,
147 .chars_in_buffer = option_chars_in_buffer,
148 .throttle = option_rx_throttle,
149 .unthrottle = option_rx_unthrottle,
Matthias Urlichs02b2ac52006-08-02 16:41:41 -0700150 .ioctl = option_ioctl,
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200151 .set_termios = option_set_termios,
152 .break_ctl = option_break_ctl,
153 .tiocmget = option_tiocmget,
154 .tiocmset = option_tiocmset,
155 .attach = option_startup,
156 .shutdown = option_shutdown,
157 .read_int_callback = option_instat_callback,
158};
159
160static struct usb_serial_driver option_1port_device = {
161 .driver = {
162 .owner = THIS_MODULE,
Matthias Urlichs02b2ac52006-08-02 16:41:41 -0700163 .name = "option1",
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200164 },
165 .description = "GSM modem (1-port)",
166 .id_table = option_ids1,
167 .num_interrupt_in = NUM_DONT_CARE,
168 .num_bulk_in = NUM_DONT_CARE,
169 .num_bulk_out = NUM_DONT_CARE,
170 .num_ports = 1,
Matthias Urlichsba460e42005-07-14 00:33:47 -0700171 .open = option_open,
172 .close = option_close,
173 .write = option_write,
174 .write_room = option_write_room,
175 .chars_in_buffer = option_chars_in_buffer,
176 .throttle = option_rx_throttle,
177 .unthrottle = option_rx_unthrottle,
178 .ioctl = option_ioctl,
179 .set_termios = option_set_termios,
180 .break_ctl = option_break_ctl,
181 .tiocmget = option_tiocmget,
182 .tiocmset = option_tiocmset,
183 .attach = option_startup,
184 .shutdown = option_shutdown,
185 .read_int_callback = option_instat_callback,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700186};
187
Matthias Urlichsba460e42005-07-14 00:33:47 -0700188#ifdef CONFIG_USB_DEBUG
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700189static int debug;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700190#else
191#define debug 0
192#endif
193
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700194/* per port private data */
195
Matthias Urlichsba460e42005-07-14 00:33:47 -0700196#define N_IN_URB 4
197#define N_OUT_URB 1
Matthias Urlichsb27c73d2005-09-22 00:49:33 -0700198#define IN_BUFLEN 4096
Matthias Urlichsba460e42005-07-14 00:33:47 -0700199#define OUT_BUFLEN 128
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700200
201struct option_port_private {
202 /* Input endpoints and buffer for this port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700203 struct urb *in_urbs[N_IN_URB];
204 char in_buffer[N_IN_URB][IN_BUFLEN];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700205 /* Output endpoints and buffer for this port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700206 struct urb *out_urbs[N_OUT_URB];
207 char out_buffer[N_OUT_URB][OUT_BUFLEN];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700208
209 /* Settings for the port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700210 int rts_state; /* Handshaking pins (outputs) */
211 int dtr_state;
212 int cts_state; /* Handshaking pins (inputs) */
213 int dsr_state;
214 int dcd_state;
215 int ri_state;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700216
Matthias Urlichsba460e42005-07-14 00:33:47 -0700217 unsigned long tx_start_time[N_OUT_URB];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700218};
219
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700220/* Functions used by new usb-serial code. */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700221static int __init option_init(void)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700222{
223 int retval;
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200224 retval = usb_serial_register(&option_1port_device);
225 if (retval)
226 goto failed_1port_device_register;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700227 retval = usb_serial_register(&option_3port_device);
228 if (retval)
229 goto failed_3port_device_register;
230 retval = usb_register(&option_driver);
231 if (retval)
232 goto failed_driver_register;
233
234 info(DRIVER_DESC ": " DRIVER_VERSION);
235
236 return 0;
237
238failed_driver_register:
239 usb_serial_deregister (&option_3port_device);
240failed_3port_device_register:
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200241 usb_serial_deregister (&option_1port_device);
242failed_1port_device_register:
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700243 return retval;
244}
245
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700246static void __exit option_exit(void)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700247{
248 usb_deregister (&option_driver);
249 usb_serial_deregister (&option_3port_device);
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200250 usb_serial_deregister (&option_1port_device);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700251}
252
253module_init(option_init);
254module_exit(option_exit);
255
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700256static void option_rx_throttle(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_rx_unthrottle(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700262{
263 dbg("%s", __FUNCTION__);
264}
265
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700266static void option_break_ctl(struct usb_serial_port *port, int break_state)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700267{
268 /* Unfortunately, I don't know how to send a break */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700269 dbg("%s", __FUNCTION__);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700270}
271
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700272static void option_set_termios(struct usb_serial_port *port,
273 struct termios *old_termios)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700274{
275 dbg("%s", __FUNCTION__);
276
277 option_send_setup(port);
278}
279
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700280static int option_tiocmget(struct usb_serial_port *port, struct file *file)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700281{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700282 unsigned int value;
283 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700284
285 portdata = usb_get_serial_port_data(port);
286
287 value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
288 ((portdata->dtr_state) ? TIOCM_DTR : 0) |
289 ((portdata->cts_state) ? TIOCM_CTS : 0) |
290 ((portdata->dsr_state) ? TIOCM_DSR : 0) |
291 ((portdata->dcd_state) ? TIOCM_CAR : 0) |
292 ((portdata->ri_state) ? TIOCM_RNG : 0);
293
294 return value;
295}
296
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700297static int option_tiocmset(struct usb_serial_port *port, struct file *file,
298 unsigned int set, unsigned int clear)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700299{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700300 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700301
302 portdata = usb_get_serial_port_data(port);
303
304 if (set & TIOCM_RTS)
305 portdata->rts_state = 1;
306 if (set & TIOCM_DTR)
307 portdata->dtr_state = 1;
308
309 if (clear & TIOCM_RTS)
310 portdata->rts_state = 0;
311 if (clear & TIOCM_DTR)
312 portdata->dtr_state = 0;
313 return option_send_setup(port);
314}
315
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700316static int option_ioctl(struct usb_serial_port *port, struct file *file,
317 unsigned int cmd, unsigned long arg)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700318{
319 return -ENOIOCTLCMD;
320}
321
322/* Write */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700323static int option_write(struct usb_serial_port *port,
324 const unsigned char *buf, int count)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700325{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700326 struct option_port_private *portdata;
327 int i;
328 int left, todo;
329 struct urb *this_urb = NULL; /* spurious */
330 int err;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700331
332 portdata = usb_get_serial_port_data(port);
333
334 dbg("%s: write (%d chars)", __FUNCTION__, count);
335
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700336 i = 0;
337 left = count;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700338 for (i=0; left > 0 && i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700339 todo = left;
340 if (todo > OUT_BUFLEN)
341 todo = OUT_BUFLEN;
342
Matthias Urlichsba460e42005-07-14 00:33:47 -0700343 this_urb = portdata->out_urbs[i];
344 if (this_urb->status == -EINPROGRESS) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700345 if (time_before(jiffies,
346 portdata->tx_start_time[i] + 10 * HZ))
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700347 continue;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700348 usb_unlink_urb(this_urb);
Matthias Urlichsba460e42005-07-14 00:33:47 -0700349 continue;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700350 }
Matthias Urlichsba460e42005-07-14 00:33:47 -0700351 if (this_urb->status != 0)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700352 dbg("usb_write %p failed (err=%d)",
353 this_urb, this_urb->status);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700354
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700355 dbg("%s: endpoint %d buf %d", __FUNCTION__,
356 usb_pipeendpoint(this_urb->pipe), i);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700357
Matthias Urlichsba460e42005-07-14 00:33:47 -0700358 /* send the data */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700359 memcpy (this_urb->transfer_buffer, buf, todo);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700360 this_urb->transfer_buffer_length = todo;
361
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700362 this_urb->dev = port->serial->dev;
363 err = usb_submit_urb(this_urb, GFP_ATOMIC);
364 if (err) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700365 dbg("usb_submit_urb %p (write bulk) failed "
366 "(%d, has %d)", this_urb,
367 err, this_urb->status);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700368 continue;
369 }
370 portdata->tx_start_time[i] = jiffies;
371 buf += todo;
372 left -= todo;
373 }
374
375 count -= left;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700376 dbg("%s: wrote (did %d)", __FUNCTION__, count);
377 return count;
378}
379
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700380static void option_indat_callback(struct urb *urb, struct pt_regs *regs)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700381{
Alan Cox33f0f882006-01-09 20:54:13 -0800382 int err;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700383 int endpoint;
384 struct usb_serial_port *port;
385 struct tty_struct *tty;
386 unsigned char *data = urb->transfer_buffer;
387
388 dbg("%s: %p", __FUNCTION__, urb);
389
390 endpoint = usb_pipeendpoint(urb->pipe);
391 port = (struct usb_serial_port *) urb->context;
392
393 if (urb->status) {
394 dbg("%s: nonzero status: %d on endpoint %02x.",
395 __FUNCTION__, urb->status, endpoint);
396 } else {
397 tty = port->tty;
398 if (urb->actual_length) {
Alan Cox33f0f882006-01-09 20:54:13 -0800399 tty_buffer_request_room(tty, urb->actual_length);
400 tty_insert_flip_string(tty, data, urb->actual_length);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700401 tty_flip_buffer_push(tty);
402 } else {
403 dbg("%s: empty read urb received", __FUNCTION__);
404 }
405
406 /* Resubmit urb so we continue receiving */
407 if (port->open_count && urb->status != -ESHUTDOWN) {
408 err = usb_submit_urb(urb, GFP_ATOMIC);
409 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700410 printk(KERN_ERR "%s: resubmit read urb failed. "
411 "(%d)", __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700412 }
413 }
414 return;
415}
416
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700417static void option_outdat_callback(struct urb *urb, struct pt_regs *regs)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700418{
419 struct usb_serial_port *port;
420
421 dbg("%s", __FUNCTION__);
422
423 port = (struct usb_serial_port *) urb->context;
424
Pete Zaitcevcf2c7482006-05-22 21:58:49 -0700425 usb_serial_port_softint(port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700426}
427
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700428static void option_instat_callback(struct urb *urb, struct pt_regs *regs)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700429{
430 int err;
431 struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
432 struct option_port_private *portdata = usb_get_serial_port_data(port);
433 struct usb_serial *serial = port->serial;
434
435 dbg("%s", __FUNCTION__);
436 dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata);
437
438 if (urb->status == 0) {
439 struct usb_ctrlrequest *req_pkt =
440 (struct usb_ctrlrequest *)urb->transfer_buffer;
441
442 if (!req_pkt) {
443 dbg("%s: NULL req_pkt\n", __FUNCTION__);
444 return;
445 }
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700446 if ((req_pkt->bRequestType == 0xA1) &&
447 (req_pkt->bRequest == 0x20)) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700448 int old_dcd_state;
449 unsigned char signals = *((unsigned char *)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700450 urb->transfer_buffer +
451 sizeof(struct usb_ctrlrequest));
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700452
453 dbg("%s: signal x%x", __FUNCTION__, signals);
454
455 old_dcd_state = portdata->dcd_state;
456 portdata->cts_state = 1;
457 portdata->dcd_state = ((signals & 0x01) ? 1 : 0);
458 portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
459 portdata->ri_state = ((signals & 0x08) ? 1 : 0);
460
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700461 if (port->tty && !C_CLOCAL(port->tty) &&
462 old_dcd_state && !portdata->dcd_state)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700463 tty_hangup(port->tty);
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700464 } else {
465 dbg("%s: type %x req %x", __FUNCTION__,
466 req_pkt->bRequestType,req_pkt->bRequest);
467 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700468 } else
469 dbg("%s: error %d", __FUNCTION__, urb->status);
470
471 /* Resubmit urb so we continue receiving IRQ data */
472 if (urb->status != -ESHUTDOWN) {
473 urb->dev = serial->dev;
474 err = usb_submit_urb(urb, GFP_ATOMIC);
475 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700476 dbg("%s: resubmit intr urb failed. (%d)",
477 __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700478 }
479}
480
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700481static int option_write_room(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700482{
483 struct option_port_private *portdata;
484 int i;
485 int data_len = 0;
486 struct urb *this_urb;
487
488 portdata = usb_get_serial_port_data(port);
489
Matthias Urlichsba460e42005-07-14 00:33:47 -0700490 for (i=0; i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700491 this_urb = portdata->out_urbs[i];
492 if (this_urb && this_urb->status != -EINPROGRESS)
493 data_len += OUT_BUFLEN;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700494 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700495
496 dbg("%s: %d", __FUNCTION__, data_len);
497 return data_len;
498}
499
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700500static int option_chars_in_buffer(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700501{
502 struct option_port_private *portdata;
503 int i;
504 int data_len = 0;
505 struct urb *this_urb;
506
507 portdata = usb_get_serial_port_data(port);
508
Matthias Urlichsba460e42005-07-14 00:33:47 -0700509 for (i=0; i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700510 this_urb = portdata->out_urbs[i];
511 if (this_urb && this_urb->status == -EINPROGRESS)
512 data_len += this_urb->transfer_buffer_length;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700513 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700514 dbg("%s: %d", __FUNCTION__, data_len);
515 return data_len;
516}
517
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700518static int option_open(struct usb_serial_port *port, struct file *filp)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700519{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700520 struct option_port_private *portdata;
521 struct usb_serial *serial = port->serial;
522 int i, err;
523 struct urb *urb;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700524
525 portdata = usb_get_serial_port_data(port);
526
527 dbg("%s", __FUNCTION__);
528
529 /* Set some sane defaults */
530 portdata->rts_state = 1;
531 portdata->dtr_state = 1;
532
533 /* Reset low level data toggle and start reading from endpoints */
534 for (i = 0; i < N_IN_URB; i++) {
535 urb = portdata->in_urbs[i];
536 if (! urb)
537 continue;
538 if (urb->dev != serial->dev) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700539 dbg("%s: dev %p != %p", __FUNCTION__,
540 urb->dev, serial->dev);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700541 continue;
542 }
543
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700544 /*
545 * make sure endpoint data toggle is synchronized with the
546 * device
547 */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700548 usb_clear_halt(urb->dev, urb->pipe);
549
550 err = usb_submit_urb(urb, GFP_KERNEL);
551 if (err) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700552 dbg("%s: submit urb %d failed (%d) %d",
553 __FUNCTION__, i, err,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700554 urb->transfer_buffer_length);
555 }
556 }
557
558 /* Reset low level data toggle on out endpoints */
559 for (i = 0; i < N_OUT_URB; i++) {
560 urb = portdata->out_urbs[i];
561 if (! urb)
562 continue;
563 urb->dev = serial->dev;
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700564 /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
565 usb_pipeout(urb->pipe), 0); */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700566 }
567
568 port->tty->low_latency = 1;
569
570 option_send_setup(port);
571
572 return (0);
573}
574
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700575static inline void stop_urb(struct urb *urb)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700576{
Greg Kroah-Hartman242cf672005-07-29 16:11:07 -0400577 if (urb && urb->status == -EINPROGRESS)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700578 usb_kill_urb(urb);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700579}
580
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700581static void option_close(struct usb_serial_port *port, struct file *filp)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700582{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700583 int i;
584 struct usb_serial *serial = port->serial;
585 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700586
587 dbg("%s", __FUNCTION__);
588 portdata = usb_get_serial_port_data(port);
589
590 portdata->rts_state = 0;
591 portdata->dtr_state = 0;
592
593 if (serial->dev) {
594 option_send_setup(port);
595
596 /* Stop reading/writing urbs */
597 for (i = 0; i < N_IN_URB; i++)
598 stop_urb(portdata->in_urbs[i]);
599 for (i = 0; i < N_OUT_URB; i++)
600 stop_urb(portdata->out_urbs[i]);
601 }
602 port->tty = NULL;
603}
604
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700605/* Helper functions used by option_setup_urbs */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700606static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint,
607 int dir, void *ctx, char *buf, int len,
608 void (*callback)(struct urb *, struct pt_regs *regs))
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700609{
610 struct urb *urb;
611
612 if (endpoint == -1)
613 return NULL; /* endpoint not needed */
614
615 urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
616 if (urb == NULL) {
617 dbg("%s: alloc for endpoint %d failed.", __FUNCTION__, endpoint);
618 return NULL;
619 }
620
621 /* Fill URB using supplied data. */
622 usb_fill_bulk_urb(urb, serial->dev,
623 usb_sndbulkpipe(serial->dev, endpoint) | dir,
624 buf, len, callback, ctx);
625
626 return urb;
627}
628
629/* Setup urbs */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700630static void option_setup_urbs(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700631{
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200632 int i,j;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700633 struct usb_serial_port *port;
634 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700635
636 dbg("%s", __FUNCTION__);
637
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200638 for (i = 0; i < serial->num_ports; i++) {
639 port = serial->port[i];
640 portdata = usb_get_serial_port_data(port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700641
642 /* Do indat endpoints first */
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200643 for (j = 0; j < N_IN_URB; ++j) {
644 portdata->in_urbs[j] = option_setup_urb (serial,
645 port->bulk_in_endpointAddress, USB_DIR_IN, port,
646 portdata->in_buffer[j], IN_BUFLEN, option_indat_callback);
647 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700648
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200649 /* outdat endpoints */
650 for (j = 0; j < N_OUT_URB; ++j) {
651 portdata->out_urbs[j] = option_setup_urb (serial,
652 port->bulk_out_endpointAddress, USB_DIR_OUT, port,
653 portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback);
654 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700655 }
656}
657
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700658static int option_send_setup(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700659{
660 struct usb_serial *serial = port->serial;
661 struct option_port_private *portdata;
662
663 dbg("%s", __FUNCTION__);
664
665 portdata = usb_get_serial_port_data(port);
666
667 if (port->tty) {
668 int val = 0;
669 if (portdata->dtr_state)
670 val |= 0x01;
671 if (portdata->rts_state)
672 val |= 0x02;
673
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700674 return usb_control_msg(serial->dev,
675 usb_rcvctrlpipe(serial->dev, 0),
676 0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700677 }
678
679 return 0;
680}
681
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700682static int option_startup(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700683{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700684 int i, err;
685 struct usb_serial_port *port;
686 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700687
688 dbg("%s", __FUNCTION__);
689
690 /* Now setup per port private data */
691 for (i = 0; i < serial->num_ports; i++) {
692 port = serial->port[i];
Eric Sesterhenn80b6ca42006-02-27 21:29:43 +0100693 portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700694 if (!portdata) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700695 dbg("%s: kmalloc for option_port_private (%d) failed!.",
696 __FUNCTION__, i);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700697 return (1);
698 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700699
700 usb_set_serial_port_data(port, portdata);
701
702 if (! port->interrupt_in_urb)
703 continue;
704 err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
705 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700706 dbg("%s: submit irq_in urb failed %d",
707 __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700708 }
709
710 option_setup_urbs(serial);
711
712 return (0);
713}
714
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700715static void option_shutdown(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700716{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700717 int i, j;
718 struct usb_serial_port *port;
719 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700720
721 dbg("%s", __FUNCTION__);
722
723 /* Stop reading/writing urbs */
724 for (i = 0; i < serial->num_ports; ++i) {
725 port = serial->port[i];
726 portdata = usb_get_serial_port_data(port);
727 for (j = 0; j < N_IN_URB; j++)
728 stop_urb(portdata->in_urbs[j]);
729 for (j = 0; j < N_OUT_URB; j++)
730 stop_urb(portdata->out_urbs[j]);
731 }
732
733 /* Now free them */
734 for (i = 0; i < serial->num_ports; ++i) {
735 port = serial->port[i];
736 portdata = usb_get_serial_port_data(port);
737
738 for (j = 0; j < N_IN_URB; j++) {
739 if (portdata->in_urbs[j]) {
740 usb_free_urb(portdata->in_urbs[j]);
741 portdata->in_urbs[j] = NULL;
742 }
743 }
744 for (j = 0; j < N_OUT_URB; j++) {
745 if (portdata->out_urbs[j]) {
746 usb_free_urb(portdata->out_urbs[j]);
747 portdata->out_urbs[j] = NULL;
748 }
749 }
750 }
751
752 /* Now free per port private data */
753 for (i = 0; i < serial->num_ports; i++) {
754 port = serial->port[i];
755 kfree(usb_get_serial_port_data(port));
756 }
757}
758
759MODULE_AUTHOR(DRIVER_AUTHOR);
760MODULE_DESCRIPTION(DRIVER_DESC);
761MODULE_VERSION(DRIVER_VERSION);
762MODULE_LICENSE("GPL");
763
Matthias Urlichsba460e42005-07-14 00:33:47 -0700764#ifdef CONFIG_USB_DEBUG
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700765module_param(debug, bool, S_IRUGO | S_IWUSR);
766MODULE_PARM_DESC(debug, "Debug messages");
Matthias Urlichsba460e42005-07-14 00:33:47 -0700767#endif
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700768