blob: 819266b7e2f8e3368ddb6f20201918d44a14a889 [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
Johann Wilhelmab195892006-12-02 07:25:31 +010082#define HUAWEI_PRODUCT_E220 0x1003
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020083#define AUDIOVOX_PRODUCT_AIRCARD 0x0112
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020084#define NOVATELWIRELESS_PRODUCT_U740 0x1400
Matthias Urlichs31fcbb72006-07-12 08:35:29 +020085#define ANYDATA_PRODUCT_ID 0x6501
Matthias Urlichsba460e42005-07-14 00:33:47 -070086
Matthias Urlichs58cfe912005-05-23 17:00:48 -070087static struct usb_device_id option_ids[] = {
88 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
Matthias Urlichsba460e42005-07-14 00:33:47 -070089 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
90 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020091 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
Matthias Urlichse37de9e2006-07-06 13:12:53 +020092 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
Matthias Urlichsb6137382005-09-22 00:48:40 -070093 { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
Johann Wilhelmab195892006-12-02 07:25:31 +010094 { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) },
Matthias Urlichsb6137382005-09-22 00:48:40 -070095 { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020096 { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
Matthias Urlichs31fcbb72006-07-12 08:35:29 +020097 { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +020098 { } /* Terminating entry */
99};
100
101static struct usb_device_id option_ids1[] = {
102 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
103 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
104 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
105 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
Matthias Urlichse37de9e2006-07-06 13:12:53 +0200106 { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200107 { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
Johann Wilhelmab195892006-12-02 07:25:31 +0100108 { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) },
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200109 { 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};
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700114
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 */
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200128
129static struct usb_serial_driver option_1port_device = {
130 .driver = {
131 .owner = THIS_MODULE,
Matthias Urlichs02b2ac52006-08-02 16:41:41 -0700132 .name = "option1",
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200133 },
134 .description = "GSM modem (1-port)",
135 .id_table = option_ids1,
136 .num_interrupt_in = NUM_DONT_CARE,
137 .num_bulk_in = NUM_DONT_CARE,
138 .num_bulk_out = NUM_DONT_CARE,
139 .num_ports = 1,
Matthias Urlichsba460e42005-07-14 00:33:47 -0700140 .open = option_open,
141 .close = option_close,
142 .write = option_write,
143 .write_room = option_write_room,
144 .chars_in_buffer = option_chars_in_buffer,
145 .throttle = option_rx_throttle,
146 .unthrottle = option_rx_unthrottle,
147 .ioctl = option_ioctl,
148 .set_termios = option_set_termios,
149 .break_ctl = option_break_ctl,
150 .tiocmget = option_tiocmget,
151 .tiocmset = option_tiocmset,
152 .attach = option_startup,
153 .shutdown = option_shutdown,
154 .read_int_callback = option_instat_callback,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700155};
156
Matthias Urlichsba460e42005-07-14 00:33:47 -0700157#ifdef CONFIG_USB_DEBUG
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700158static int debug;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700159#else
160#define debug 0
161#endif
162
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700163/* per port private data */
164
Matthias Urlichsba460e42005-07-14 00:33:47 -0700165#define N_IN_URB 4
166#define N_OUT_URB 1
Matthias Urlichsb27c73d2005-09-22 00:49:33 -0700167#define IN_BUFLEN 4096
Matthias Urlichsba460e42005-07-14 00:33:47 -0700168#define OUT_BUFLEN 128
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700169
170struct option_port_private {
171 /* Input endpoints and buffer for this port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700172 struct urb *in_urbs[N_IN_URB];
173 char in_buffer[N_IN_URB][IN_BUFLEN];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700174 /* Output endpoints and buffer for this port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700175 struct urb *out_urbs[N_OUT_URB];
176 char out_buffer[N_OUT_URB][OUT_BUFLEN];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700177
178 /* Settings for the port */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700179 int rts_state; /* Handshaking pins (outputs) */
180 int dtr_state;
181 int cts_state; /* Handshaking pins (inputs) */
182 int dsr_state;
183 int dcd_state;
184 int ri_state;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700185
Matthias Urlichsba460e42005-07-14 00:33:47 -0700186 unsigned long tx_start_time[N_OUT_URB];
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700187};
188
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700189/* Functions used by new usb-serial code. */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700190static int __init option_init(void)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700191{
192 int retval;
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200193 retval = usb_serial_register(&option_1port_device);
194 if (retval)
195 goto failed_1port_device_register;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700196 retval = usb_register(&option_driver);
197 if (retval)
198 goto failed_driver_register;
199
200 info(DRIVER_DESC ": " DRIVER_VERSION);
201
202 return 0;
203
204failed_driver_register:
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200205 usb_serial_deregister (&option_1port_device);
206failed_1port_device_register:
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700207 return retval;
208}
209
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700210static void __exit option_exit(void)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700211{
212 usb_deregister (&option_driver);
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200213 usb_serial_deregister (&option_1port_device);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700214}
215
216module_init(option_init);
217module_exit(option_exit);
218
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700219static void option_rx_throttle(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700220{
221 dbg("%s", __FUNCTION__);
222}
223
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700224static void option_rx_unthrottle(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700225{
226 dbg("%s", __FUNCTION__);
227}
228
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700229static void option_break_ctl(struct usb_serial_port *port, int break_state)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700230{
231 /* Unfortunately, I don't know how to send a break */
Matthias Urlichsba460e42005-07-14 00:33:47 -0700232 dbg("%s", __FUNCTION__);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700233}
234
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700235static void option_set_termios(struct usb_serial_port *port,
Alan Cox606d0992006-12-08 02:38:45 -0800236 struct ktermios *old_termios)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700237{
238 dbg("%s", __FUNCTION__);
239
240 option_send_setup(port);
241}
242
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700243static int option_tiocmget(struct usb_serial_port *port, struct file *file)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700244{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700245 unsigned int value;
246 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700247
248 portdata = usb_get_serial_port_data(port);
249
250 value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
251 ((portdata->dtr_state) ? TIOCM_DTR : 0) |
252 ((portdata->cts_state) ? TIOCM_CTS : 0) |
253 ((portdata->dsr_state) ? TIOCM_DSR : 0) |
254 ((portdata->dcd_state) ? TIOCM_CAR : 0) |
255 ((portdata->ri_state) ? TIOCM_RNG : 0);
256
257 return value;
258}
259
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700260static int option_tiocmset(struct usb_serial_port *port, struct file *file,
261 unsigned int set, unsigned int clear)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700262{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700263 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700264
265 portdata = usb_get_serial_port_data(port);
266
267 if (set & TIOCM_RTS)
268 portdata->rts_state = 1;
269 if (set & TIOCM_DTR)
270 portdata->dtr_state = 1;
271
272 if (clear & TIOCM_RTS)
273 portdata->rts_state = 0;
274 if (clear & TIOCM_DTR)
275 portdata->dtr_state = 0;
276 return option_send_setup(port);
277}
278
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700279static int option_ioctl(struct usb_serial_port *port, struct file *file,
280 unsigned int cmd, unsigned long arg)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700281{
282 return -ENOIOCTLCMD;
283}
284
285/* Write */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700286static int option_write(struct usb_serial_port *port,
287 const unsigned char *buf, int count)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700288{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700289 struct option_port_private *portdata;
290 int i;
291 int left, todo;
292 struct urb *this_urb = NULL; /* spurious */
293 int err;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700294
295 portdata = usb_get_serial_port_data(port);
296
297 dbg("%s: write (%d chars)", __FUNCTION__, count);
298
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700299 i = 0;
300 left = count;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700301 for (i=0; left > 0 && i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700302 todo = left;
303 if (todo > OUT_BUFLEN)
304 todo = OUT_BUFLEN;
305
Matthias Urlichsba460e42005-07-14 00:33:47 -0700306 this_urb = portdata->out_urbs[i];
307 if (this_urb->status == -EINPROGRESS) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700308 if (time_before(jiffies,
309 portdata->tx_start_time[i] + 10 * HZ))
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700310 continue;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700311 usb_unlink_urb(this_urb);
Matthias Urlichsba460e42005-07-14 00:33:47 -0700312 continue;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700313 }
Matthias Urlichsba460e42005-07-14 00:33:47 -0700314 if (this_urb->status != 0)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700315 dbg("usb_write %p failed (err=%d)",
316 this_urb, this_urb->status);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700317
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700318 dbg("%s: endpoint %d buf %d", __FUNCTION__,
319 usb_pipeendpoint(this_urb->pipe), i);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700320
Matthias Urlichsba460e42005-07-14 00:33:47 -0700321 /* send the data */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700322 memcpy (this_urb->transfer_buffer, buf, todo);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700323 this_urb->transfer_buffer_length = todo;
324
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700325 this_urb->dev = port->serial->dev;
326 err = usb_submit_urb(this_urb, GFP_ATOMIC);
327 if (err) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700328 dbg("usb_submit_urb %p (write bulk) failed "
329 "(%d, has %d)", this_urb,
330 err, this_urb->status);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700331 continue;
332 }
333 portdata->tx_start_time[i] = jiffies;
334 buf += todo;
335 left -= todo;
336 }
337
338 count -= left;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700339 dbg("%s: wrote (did %d)", __FUNCTION__, count);
340 return count;
341}
342
David Howells7d12e782006-10-05 14:55:46 +0100343static void option_indat_callback(struct urb *urb)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700344{
Alan Cox33f0f882006-01-09 20:54:13 -0800345 int err;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700346 int endpoint;
347 struct usb_serial_port *port;
348 struct tty_struct *tty;
349 unsigned char *data = urb->transfer_buffer;
350
351 dbg("%s: %p", __FUNCTION__, urb);
352
353 endpoint = usb_pipeendpoint(urb->pipe);
354 port = (struct usb_serial_port *) urb->context;
355
356 if (urb->status) {
357 dbg("%s: nonzero status: %d on endpoint %02x.",
358 __FUNCTION__, urb->status, endpoint);
359 } else {
360 tty = port->tty;
361 if (urb->actual_length) {
Alan Cox33f0f882006-01-09 20:54:13 -0800362 tty_buffer_request_room(tty, urb->actual_length);
363 tty_insert_flip_string(tty, data, urb->actual_length);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700364 tty_flip_buffer_push(tty);
365 } else {
366 dbg("%s: empty read urb received", __FUNCTION__);
367 }
368
369 /* Resubmit urb so we continue receiving */
370 if (port->open_count && urb->status != -ESHUTDOWN) {
371 err = usb_submit_urb(urb, GFP_ATOMIC);
372 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700373 printk(KERN_ERR "%s: resubmit read urb failed. "
374 "(%d)", __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700375 }
376 }
377 return;
378}
379
David Howells7d12e782006-10-05 14:55:46 +0100380static void option_outdat_callback(struct urb *urb)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700381{
382 struct usb_serial_port *port;
383
384 dbg("%s", __FUNCTION__);
385
386 port = (struct usb_serial_port *) urb->context;
387
Pete Zaitcevcf2c7482006-05-22 21:58:49 -0700388 usb_serial_port_softint(port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700389}
390
David Howells7d12e782006-10-05 14:55:46 +0100391static void option_instat_callback(struct urb *urb)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700392{
393 int err;
394 struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
395 struct option_port_private *portdata = usb_get_serial_port_data(port);
396 struct usb_serial *serial = port->serial;
397
398 dbg("%s", __FUNCTION__);
399 dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata);
400
401 if (urb->status == 0) {
402 struct usb_ctrlrequest *req_pkt =
403 (struct usb_ctrlrequest *)urb->transfer_buffer;
404
405 if (!req_pkt) {
406 dbg("%s: NULL req_pkt\n", __FUNCTION__);
407 return;
408 }
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700409 if ((req_pkt->bRequestType == 0xA1) &&
410 (req_pkt->bRequest == 0x20)) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700411 int old_dcd_state;
412 unsigned char signals = *((unsigned char *)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700413 urb->transfer_buffer +
414 sizeof(struct usb_ctrlrequest));
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700415
416 dbg("%s: signal x%x", __FUNCTION__, signals);
417
418 old_dcd_state = portdata->dcd_state;
419 portdata->cts_state = 1;
420 portdata->dcd_state = ((signals & 0x01) ? 1 : 0);
421 portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
422 portdata->ri_state = ((signals & 0x08) ? 1 : 0);
423
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700424 if (port->tty && !C_CLOCAL(port->tty) &&
425 old_dcd_state && !portdata->dcd_state)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700426 tty_hangup(port->tty);
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700427 } else {
428 dbg("%s: type %x req %x", __FUNCTION__,
429 req_pkt->bRequestType,req_pkt->bRequest);
430 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700431 } else
432 dbg("%s: error %d", __FUNCTION__, urb->status);
433
434 /* Resubmit urb so we continue receiving IRQ data */
435 if (urb->status != -ESHUTDOWN) {
436 urb->dev = serial->dev;
437 err = usb_submit_urb(urb, GFP_ATOMIC);
438 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700439 dbg("%s: resubmit intr urb failed. (%d)",
440 __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700441 }
442}
443
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700444static int option_write_room(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700445{
446 struct option_port_private *portdata;
447 int i;
448 int data_len = 0;
449 struct urb *this_urb;
450
451 portdata = usb_get_serial_port_data(port);
452
Matthias Urlichsba460e42005-07-14 00:33:47 -0700453 for (i=0; i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700454 this_urb = portdata->out_urbs[i];
455 if (this_urb && this_urb->status != -EINPROGRESS)
456 data_len += OUT_BUFLEN;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700457 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700458
459 dbg("%s: %d", __FUNCTION__, data_len);
460 return data_len;
461}
462
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700463static int option_chars_in_buffer(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700464{
465 struct option_port_private *portdata;
466 int i;
467 int data_len = 0;
468 struct urb *this_urb;
469
470 portdata = usb_get_serial_port_data(port);
471
Matthias Urlichsba460e42005-07-14 00:33:47 -0700472 for (i=0; i < N_OUT_URB; i++) {
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700473 this_urb = portdata->out_urbs[i];
474 if (this_urb && this_urb->status == -EINPROGRESS)
475 data_len += this_urb->transfer_buffer_length;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700476 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700477 dbg("%s: %d", __FUNCTION__, data_len);
478 return data_len;
479}
480
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700481static int option_open(struct usb_serial_port *port, struct file *filp)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700482{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700483 struct option_port_private *portdata;
484 struct usb_serial *serial = port->serial;
485 int i, err;
486 struct urb *urb;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700487
488 portdata = usb_get_serial_port_data(port);
489
490 dbg("%s", __FUNCTION__);
491
492 /* Set some sane defaults */
493 portdata->rts_state = 1;
494 portdata->dtr_state = 1;
495
496 /* Reset low level data toggle and start reading from endpoints */
497 for (i = 0; i < N_IN_URB; i++) {
498 urb = portdata->in_urbs[i];
499 if (! urb)
500 continue;
501 if (urb->dev != serial->dev) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700502 dbg("%s: dev %p != %p", __FUNCTION__,
503 urb->dev, serial->dev);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700504 continue;
505 }
506
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700507 /*
508 * make sure endpoint data toggle is synchronized with the
509 * device
510 */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700511 usb_clear_halt(urb->dev, urb->pipe);
512
513 err = usb_submit_urb(urb, GFP_KERNEL);
514 if (err) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700515 dbg("%s: submit urb %d failed (%d) %d",
516 __FUNCTION__, i, err,
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700517 urb->transfer_buffer_length);
518 }
519 }
520
521 /* Reset low level data toggle on out endpoints */
522 for (i = 0; i < N_OUT_URB; i++) {
523 urb = portdata->out_urbs[i];
524 if (! urb)
525 continue;
526 urb->dev = serial->dev;
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700527 /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
528 usb_pipeout(urb->pipe), 0); */
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700529 }
530
531 port->tty->low_latency = 1;
532
533 option_send_setup(port);
534
535 return (0);
536}
537
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700538static inline void stop_urb(struct urb *urb)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700539{
Greg Kroah-Hartman242cf672005-07-29 16:11:07 -0400540 if (urb && urb->status == -EINPROGRESS)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700541 usb_kill_urb(urb);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700542}
543
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700544static void option_close(struct usb_serial_port *port, struct file *filp)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700545{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700546 int i;
547 struct usb_serial *serial = port->serial;
548 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700549
550 dbg("%s", __FUNCTION__);
551 portdata = usb_get_serial_port_data(port);
552
553 portdata->rts_state = 0;
554 portdata->dtr_state = 0;
555
556 if (serial->dev) {
557 option_send_setup(port);
558
559 /* Stop reading/writing urbs */
560 for (i = 0; i < N_IN_URB; i++)
561 stop_urb(portdata->in_urbs[i]);
562 for (i = 0; i < N_OUT_URB; i++)
563 stop_urb(portdata->out_urbs[i]);
564 }
565 port->tty = NULL;
566}
567
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700568/* Helper functions used by option_setup_urbs */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700569static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint,
570 int dir, void *ctx, char *buf, int len,
David Howells7d12e782006-10-05 14:55:46 +0100571 void (*callback)(struct urb *))
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700572{
573 struct urb *urb;
574
575 if (endpoint == -1)
576 return NULL; /* endpoint not needed */
577
578 urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
579 if (urb == NULL) {
580 dbg("%s: alloc for endpoint %d failed.", __FUNCTION__, endpoint);
581 return NULL;
582 }
583
584 /* Fill URB using supplied data. */
585 usb_fill_bulk_urb(urb, serial->dev,
586 usb_sndbulkpipe(serial->dev, endpoint) | dir,
587 buf, len, callback, ctx);
588
589 return urb;
590}
591
592/* Setup urbs */
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700593static void option_setup_urbs(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700594{
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200595 int i,j;
Matthias Urlichsba460e42005-07-14 00:33:47 -0700596 struct usb_serial_port *port;
597 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700598
599 dbg("%s", __FUNCTION__);
600
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200601 for (i = 0; i < serial->num_ports; i++) {
602 port = serial->port[i];
603 portdata = usb_get_serial_port_data(port);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700604
605 /* Do indat endpoints first */
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200606 for (j = 0; j < N_IN_URB; ++j) {
607 portdata->in_urbs[j] = option_setup_urb (serial,
608 port->bulk_in_endpointAddress, USB_DIR_IN, port,
609 portdata->in_buffer[j], IN_BUFLEN, option_indat_callback);
610 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700611
Matthias Urlichs14f76cc2006-06-02 11:48:56 +0200612 /* outdat endpoints */
613 for (j = 0; j < N_OUT_URB; ++j) {
614 portdata->out_urbs[j] = option_setup_urb (serial,
615 port->bulk_out_endpointAddress, USB_DIR_OUT, port,
616 portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback);
617 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700618 }
619}
620
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700621static int option_send_setup(struct usb_serial_port *port)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700622{
623 struct usb_serial *serial = port->serial;
624 struct option_port_private *portdata;
625
626 dbg("%s", __FUNCTION__);
627
628 portdata = usb_get_serial_port_data(port);
629
630 if (port->tty) {
631 int val = 0;
632 if (portdata->dtr_state)
633 val |= 0x01;
634 if (portdata->rts_state)
635 val |= 0x02;
636
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700637 return usb_control_msg(serial->dev,
638 usb_rcvctrlpipe(serial->dev, 0),
639 0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700640 }
641
642 return 0;
643}
644
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700645static int option_startup(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700646{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700647 int i, err;
648 struct usb_serial_port *port;
649 struct option_port_private *portdata;
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700650
651 dbg("%s", __FUNCTION__);
652
653 /* Now setup per port private data */
654 for (i = 0; i < serial->num_ports; i++) {
655 port = serial->port[i];
Eric Sesterhenn80b6ca42006-02-27 21:29:43 +0100656 portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700657 if (!portdata) {
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700658 dbg("%s: kmalloc for option_port_private (%d) failed!.",
659 __FUNCTION__, i);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700660 return (1);
661 }
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700662
663 usb_set_serial_port_data(port, portdata);
664
665 if (! port->interrupt_in_urb)
666 continue;
667 err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
668 if (err)
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700669 dbg("%s: submit irq_in urb failed %d",
670 __FUNCTION__, err);
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700671 }
672
673 option_setup_urbs(serial);
674
675 return (0);
676}
677
Andrew Morton7bb75ae2005-07-27 01:08:30 -0700678static void option_shutdown(struct usb_serial *serial)
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700679{
Matthias Urlichsba460e42005-07-14 00:33:47 -0700680 int i, j;
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 /* Stop reading/writing urbs */
687 for (i = 0; i < serial->num_ports; ++i) {
688 port = serial->port[i];
689 portdata = usb_get_serial_port_data(port);
690 for (j = 0; j < N_IN_URB; j++)
691 stop_urb(portdata->in_urbs[j]);
692 for (j = 0; j < N_OUT_URB; j++)
693 stop_urb(portdata->out_urbs[j]);
694 }
695
696 /* Now free them */
697 for (i = 0; i < serial->num_ports; ++i) {
698 port = serial->port[i];
699 portdata = usb_get_serial_port_data(port);
700
701 for (j = 0; j < N_IN_URB; j++) {
702 if (portdata->in_urbs[j]) {
703 usb_free_urb(portdata->in_urbs[j]);
704 portdata->in_urbs[j] = NULL;
705 }
706 }
707 for (j = 0; j < N_OUT_URB; j++) {
708 if (portdata->out_urbs[j]) {
709 usb_free_urb(portdata->out_urbs[j]);
710 portdata->out_urbs[j] = NULL;
711 }
712 }
713 }
714
715 /* Now free per port private data */
716 for (i = 0; i < serial->num_ports; i++) {
717 port = serial->port[i];
718 kfree(usb_get_serial_port_data(port));
719 }
720}
721
722MODULE_AUTHOR(DRIVER_AUTHOR);
723MODULE_DESCRIPTION(DRIVER_DESC);
724MODULE_VERSION(DRIVER_VERSION);
725MODULE_LICENSE("GPL");
726
Matthias Urlichsba460e42005-07-14 00:33:47 -0700727#ifdef CONFIG_USB_DEBUG
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700728module_param(debug, bool, S_IRUGO | S_IWUSR);
729MODULE_PARM_DESC(debug, "Debug messages");
Matthias Urlichsba460e42005-07-14 00:33:47 -0700730#endif
Matthias Urlichs58cfe912005-05-23 17:00:48 -0700731