blob: cd51520c7e72e10286017998bf00081c8d8571cc [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * cdc-acm.c
3 *
4 * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de>
5 * Copyright (c) 1999 Pavel Machek <pavel@suse.cz>
6 * Copyright (c) 1999 Johannes Erdfelt <johannes@erdfelt.com>
7 * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
8 * Copyright (c) 2004 Oliver Neukum <oliver@neukum.name>
David Kubicek61a87ad2005-11-01 18:51:34 +01009 * Copyright (c) 2005 David Kubicek <dave@awk.cz>
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 *
11 * USB Abstract Control Model driver for USB modems and ISDN adapters
12 *
13 * Sponsored by SuSE
14 *
15 * ChangeLog:
16 * v0.9 - thorough cleaning, URBification, almost a rewrite
17 * v0.10 - some more cleanups
18 * v0.11 - fixed flow control, read error doesn't stop reads
19 * v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced
20 * v0.13 - added termios, added hangup
21 * v0.14 - sized down struct acm
22 * v0.15 - fixed flow control again - characters could be lost
23 * v0.16 - added code for modems with swapped data and control interfaces
24 * v0.17 - added new style probing
25 * v0.18 - fixed new style probing for devices with more configurations
26 * v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
27 * v0.20 - switched to probing on interface (rather than device) class
28 * v0.21 - revert to probing on device for devices with multiple configs
29 * v0.22 - probe only the control interface. if usbcore doesn't choose the
30 * config we want, sysadmin changes bConfigurationValue in sysfs.
31 * v0.23 - use softirq for rx processing, as needed by tty layer
32 * v0.24 - change probe method to evaluate CDC union descriptor
David Kubicek61a87ad2005-11-01 18:51:34 +010033 * v0.25 - downstream tasks paralelized to maximize throughput
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 */
35
36/*
37 * This program is free software; you can redistribute it and/or modify
38 * it under the terms of the GNU General Public License as published by
39 * the Free Software Foundation; either version 2 of the License, or
40 * (at your option) any later version.
41 *
42 * This program is distributed in the hope that it will be useful,
43 * but WITHOUT ANY WARRANTY; without even the implied warranty of
44 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45 * GNU General Public License for more details.
46 *
47 * You should have received a copy of the GNU General Public License
48 * along with this program; if not, write to the Free Software
49 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
50 */
51
52#undef DEBUG
53
54#include <linux/kernel.h>
55#include <linux/errno.h>
56#include <linux/init.h>
57#include <linux/slab.h>
58#include <linux/tty.h>
59#include <linux/tty_driver.h>
60#include <linux/tty_flip.h>
61#include <linux/module.h>
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010062#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#include <asm/uaccess.h>
64#include <linux/usb.h>
David Brownella8c28f22006-06-13 09:57:47 -070065#include <linux/usb/cdc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070066#include <asm/byteorder.h>
67#include <asm/unaligned.h>
David Kubicek61a87ad2005-11-01 18:51:34 +010068#include <linux/list.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
70#include "cdc-acm.h"
71
72/*
73 * Version Information
74 */
David Kubicek61a87ad2005-11-01 18:51:34 +010075#define DRIVER_VERSION "v0.25"
76#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
Linus Torvalds1da177e2005-04-16 15:20:36 -070077#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
78
79static struct usb_driver acm_driver;
80static struct tty_driver *acm_tty_driver;
81static struct acm *acm_table[ACM_TTY_MINORS];
82
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010083static DEFINE_MUTEX(open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
85#define ACM_READY(acm) (acm && acm->dev && acm->used)
86
87/*
88 * Functions for ACM control messages.
89 */
90
91static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len)
92{
93 int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
94 request, USB_RT_ACM, value,
95 acm->control->altsetting[0].desc.bInterfaceNumber,
96 buf, len, 5000);
97 dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
98 return retval < 0 ? retval : 0;
99}
100
101/* devices aren't required to support these requests.
102 * the cdc acm descriptor tells whether they do...
103 */
104#define acm_set_control(acm, control) \
105 acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
106#define acm_set_line(acm, line) \
107 acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
108#define acm_send_break(acm, ms) \
109 acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
110
111/*
Oliver Neukum884b6002005-04-21 21:28:02 +0200112 * Write buffer management.
113 * All of these assume proper locks taken by the caller.
114 */
115
116static int acm_wb_alloc(struct acm *acm)
117{
118 int i, wbn;
119 struct acm_wb *wb;
120
121 wbn = acm->write_current;
122 i = 0;
123 for (;;) {
124 wb = &acm->wb[wbn];
125 if (!wb->use) {
126 wb->use = 1;
127 return wbn;
128 }
Oliver Neukum86478942006-05-13 22:50:47 +0200129 wbn = (wbn + 1) % ACM_NW;
130 if (++i >= ACM_NW)
Oliver Neukum884b6002005-04-21 21:28:02 +0200131 return -1;
132 }
133}
134
135static void acm_wb_free(struct acm *acm, int wbn)
136{
137 acm->wb[wbn].use = 0;
138}
139
140static int acm_wb_is_avail(struct acm *acm)
141{
142 int i, n;
143
Oliver Neukum86478942006-05-13 22:50:47 +0200144 n = ACM_NW;
145 for (i = 0; i < ACM_NW; i++) {
146 n -= acm->wb[i].use;
Oliver Neukum884b6002005-04-21 21:28:02 +0200147 }
148 return n;
149}
150
151static inline int acm_wb_is_used(struct acm *acm, int wbn)
152{
153 return acm->wb[wbn].use;
154}
155
156/*
157 * Finish write.
158 */
159static void acm_write_done(struct acm *acm)
160{
161 unsigned long flags;
162 int wbn;
163
164 spin_lock_irqsave(&acm->write_lock, flags);
165 acm->write_ready = 1;
166 wbn = acm->write_current;
167 acm_wb_free(acm, wbn);
Oliver Neukum86478942006-05-13 22:50:47 +0200168 acm->write_current = (wbn + 1) % ACM_NW;
Oliver Neukum884b6002005-04-21 21:28:02 +0200169 spin_unlock_irqrestore(&acm->write_lock, flags);
170}
171
172/*
173 * Poke write.
174 */
175static int acm_write_start(struct acm *acm)
176{
177 unsigned long flags;
178 int wbn;
179 struct acm_wb *wb;
180 int rc;
181
182 spin_lock_irqsave(&acm->write_lock, flags);
183 if (!acm->dev) {
184 spin_unlock_irqrestore(&acm->write_lock, flags);
185 return -ENODEV;
186 }
187
188 if (!acm->write_ready) {
189 spin_unlock_irqrestore(&acm->write_lock, flags);
190 return 0; /* A white lie */
191 }
192
193 wbn = acm->write_current;
194 if (!acm_wb_is_used(acm, wbn)) {
195 spin_unlock_irqrestore(&acm->write_lock, flags);
196 return 0;
197 }
198 wb = &acm->wb[wbn];
199
200 acm->write_ready = 0;
201 spin_unlock_irqrestore(&acm->write_lock, flags);
202
203 acm->writeurb->transfer_buffer = wb->buf;
204 acm->writeurb->transfer_dma = wb->dmah;
205 acm->writeurb->transfer_buffer_length = wb->len;
206 acm->writeurb->dev = acm->dev;
207
208 if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) {
209 dbg("usb_submit_urb(write bulk) failed: %d", rc);
210 acm_write_done(acm);
211 }
212 return rc;
213}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100214/*
215 * attributes exported through sysfs
216 */
217static ssize_t show_caps
218(struct device *dev, struct device_attribute *attr, char *buf)
219{
220 struct usb_interface *intf = to_usb_interface(dev);
221 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200222
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100223 return sprintf(buf, "%d", acm->ctrl_caps);
224}
225static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
226
227static ssize_t show_country_codes
228(struct device *dev, struct device_attribute *attr, char *buf)
229{
230 struct usb_interface *intf = to_usb_interface(dev);
231 struct acm *acm = usb_get_intfdata(intf);
232
233 memcpy(buf, acm->country_codes, acm->country_code_size);
234 return acm->country_code_size;
235}
236
237static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
238
239static ssize_t show_country_rel_date
240(struct device *dev, struct device_attribute *attr, char *buf)
241{
242 struct usb_interface *intf = to_usb_interface(dev);
243 struct acm *acm = usb_get_intfdata(intf);
244
245 return sprintf(buf, "%d", acm->country_rel_date);
246}
247
248static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200249/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 * Interrupt handlers for various ACM device responses
251 */
252
253/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100254static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255{
256 struct acm *acm = urb->context;
257 struct usb_cdc_notification *dr = urb->transfer_buffer;
258 unsigned char *data;
259 int newctrl;
260 int status;
261
262 switch (urb->status) {
263 case 0:
264 /* success */
265 break;
266 case -ECONNRESET:
267 case -ENOENT:
268 case -ESHUTDOWN:
269 /* this urb is terminated, clean up */
270 dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
271 return;
272 default:
273 dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
274 goto exit;
275 }
276
277 if (!ACM_READY(acm))
278 goto exit;
279
280 data = (unsigned char *)(dr + 1);
281 switch (dr->bNotificationType) {
282
283 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
284
285 dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
286 break;
287
288 case USB_CDC_NOTIFY_SERIAL_STATE:
289
290 newctrl = le16_to_cpu(get_unaligned((__le16 *) data));
291
292 if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
293 dbg("calling hangup");
294 tty_hangup(acm->tty);
295 }
296
297 acm->ctrlin = newctrl;
298
299 dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
300 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
301 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
302 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
303 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
304
305 break;
306
307 default:
308 dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
309 dr->bNotificationType, dr->wIndex,
310 dr->wLength, data[0], data[1]);
311 break;
312 }
313exit:
314 status = usb_submit_urb (urb, GFP_ATOMIC);
315 if (status)
316 err ("%s - usb_submit_urb failed with result %d",
317 __FUNCTION__, status);
318}
319
320/* data interface returns incoming bytes, or we got unthrottled */
David Howells7d12e782006-10-05 14:55:46 +0100321static void acm_read_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322{
David Kubicek61a87ad2005-11-01 18:51:34 +0100323 struct acm_rb *buf;
324 struct acm_ru *rcv = urb->context;
325 struct acm *acm = rcv->instance;
Oliver Neukum86478942006-05-13 22:50:47 +0200326 int status = urb->status;
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200327 dbg("Entering acm_read_bulk with status %d", urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328
329 if (!ACM_READY(acm))
330 return;
331
Oliver Neukum86478942006-05-13 22:50:47 +0200332 if (status)
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200333 dev_dbg(&acm->data->dev, "bulk rx status %d", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334
David Kubicek61a87ad2005-11-01 18:51:34 +0100335 buf = rcv->buffer;
336 buf->size = urb->actual_length;
337
Oliver Neukum86478942006-05-13 22:50:47 +0200338 if (likely(status == 0)) {
339 spin_lock(&acm->read_lock);
340 list_add_tail(&rcv->list, &acm->spare_read_urbs);
341 list_add_tail(&buf->list, &acm->filled_read_bufs);
342 spin_unlock(&acm->read_lock);
343 } else {
344 /* we drop the buffer due to an error */
345 spin_lock(&acm->read_lock);
346 list_add_tail(&rcv->list, &acm->spare_read_urbs);
347 list_add(&buf->list, &acm->spare_read_bufs);
348 spin_unlock(&acm->read_lock);
349 /* nevertheless the tasklet must be kicked unconditionally
350 so the queue cannot dry up */
351 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100352 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353}
354
355static void acm_rx_tasklet(unsigned long _acm)
356{
357 struct acm *acm = (void *)_acm;
David Kubicek61a87ad2005-11-01 18:51:34 +0100358 struct acm_rb *buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 struct tty_struct *tty = acm->tty;
David Kubicek61a87ad2005-11-01 18:51:34 +0100360 struct acm_ru *rcv;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200361 unsigned long flags;
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100362 unsigned char throttled;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 dbg("Entering acm_rx_tasklet");
364
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100365 if (!ACM_READY(acm))
366 return;
367
Oliver Neukum834dbca2007-03-06 10:47:04 +0100368 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100369 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100370 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100371 if (throttled)
David Kubicek61a87ad2005-11-01 18:51:34 +0100372 return;
373
374next_buffer:
Jarek Poplawski762f0072006-10-06 07:23:11 +0200375 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100376 if (list_empty(&acm->filled_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200377 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100378 goto urbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100380 buf = list_entry(acm->filled_read_bufs.next,
381 struct acm_rb, list);
382 list_del(&buf->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200383 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100384
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200385 dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
David Kubicek61a87ad2005-11-01 18:51:34 +0100386
Alan Cox33f0f882006-01-09 20:54:13 -0800387 tty_buffer_request_room(tty, buf->size);
Oliver Neukum834dbca2007-03-06 10:47:04 +0100388 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100389 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100390 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100391 if (!throttled)
Alan Cox33f0f882006-01-09 20:54:13 -0800392 tty_insert_flip_string(tty, buf->base, buf->size);
David Kubicek61a87ad2005-11-01 18:51:34 +0100393 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100395 if (throttled) {
396 dbg("Throttling noticed");
Jarek Poplawski762f0072006-10-06 07:23:11 +0200397 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100398 list_add(&buf->list, &acm->filled_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200399 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 return;
401 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402
Jarek Poplawski762f0072006-10-06 07:23:11 +0200403 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100404 list_add(&buf->list, &acm->spare_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200405 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100406 goto next_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
David Kubicek61a87ad2005-11-01 18:51:34 +0100408urbs:
409 while (!list_empty(&acm->spare_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200410 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100411 if (list_empty(&acm->spare_read_urbs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200412 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100413 return;
414 }
415 rcv = list_entry(acm->spare_read_urbs.next,
416 struct acm_ru, list);
417 list_del(&rcv->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200418 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100419
420 buf = list_entry(acm->spare_read_bufs.next,
421 struct acm_rb, list);
422 list_del(&buf->list);
423
424 rcv->buffer = buf;
425
426 usb_fill_bulk_urb(rcv->urb, acm->dev,
427 acm->rx_endpoint,
428 buf->base,
429 acm->readsize,
430 acm_read_bulk, rcv);
431 rcv->urb->transfer_dma = buf->dma;
432 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
433
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200434 dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
David Kubicek61a87ad2005-11-01 18:51:34 +0100435
436 /* This shouldn't kill the driver as unsuccessful URBs are returned to the
437 free-urbs-pool and resubmited ASAP */
438 if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
439 list_add(&buf->list, &acm->spare_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200440 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100441 list_add(&rcv->list, &acm->spare_read_urbs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200442 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100443 return;
444 }
445 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446}
447
448/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100449static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450{
451 struct acm *acm = (struct acm *)urb->context;
Oliver Neukum884b6002005-04-21 21:28:02 +0200452
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200453 dbg("Entering acm_write_bulk with status %d", urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
Oliver Neukum884b6002005-04-21 21:28:02 +0200455 acm_write_done(acm);
456 acm_write_start(acm);
457 if (ACM_READY(acm))
458 schedule_work(&acm->work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459}
460
David Howellsc4028952006-11-22 14:57:56 +0000461static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462{
David Howellsc4028952006-11-22 14:57:56 +0000463 struct acm *acm = container_of(work, struct acm, work);
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200464 dbg("Entering acm_softint.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465
466 if (!ACM_READY(acm))
467 return;
468 tty_wakeup(acm->tty);
469}
470
471/*
472 * TTY handlers
473 */
474
475static int acm_tty_open(struct tty_struct *tty, struct file *filp)
476{
477 struct acm *acm;
478 int rv = -EINVAL;
David Kubicek61a87ad2005-11-01 18:51:34 +0100479 int i;
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200480 dbg("Entering acm_tty_open.");
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100481
482 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
484 acm = acm_table[tty->index];
485 if (!acm || !acm->dev)
486 goto err_out;
487 else
488 rv = 0;
489
490 tty->driver_data = acm;
491 acm->tty = tty;
492
David Kubicek61a87ad2005-11-01 18:51:34 +0100493 /* force low_latency on so that our tty_push actually forces the data through,
494 otherwise it is scheduled, and with high data rates data can get lost. */
495 tty->low_latency = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
497 if (acm->used++) {
498 goto done;
499 }
500
501 acm->ctrlurb->dev = acm->dev;
502 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
503 dbg("usb_submit_urb(ctrl irq) failed");
504 goto bail_out;
505 }
506
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100507 if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
508 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 goto full_bailout;
510
David Kubicek61a87ad2005-11-01 18:51:34 +0100511 INIT_LIST_HEAD(&acm->spare_read_urbs);
512 INIT_LIST_HEAD(&acm->spare_read_bufs);
513 INIT_LIST_HEAD(&acm->filled_read_bufs);
Oliver Neukum86478942006-05-13 22:50:47 +0200514 for (i = 0; i < acm->rx_buflimit; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100515 list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
516 }
Oliver Neukum86478942006-05-13 22:50:47 +0200517 for (i = 0; i < acm->rx_buflimit; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100518 list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
519 }
520
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100521 acm->throttle = 0;
522
David Kubicek61a87ad2005-11-01 18:51:34 +0100523 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524
525done:
526err_out:
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100527 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 return rv;
529
530full_bailout:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 usb_kill_urb(acm->ctrlurb);
532bail_out:
533 acm->used--;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100534 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 return -EIO;
536}
537
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700538static void acm_tty_unregister(struct acm *acm)
539{
Oliver Neukum86478942006-05-13 22:50:47 +0200540 int i,nr;
David Kubicek61a87ad2005-11-01 18:51:34 +0100541
Oliver Neukum86478942006-05-13 22:50:47 +0200542 nr = acm->rx_buflimit;
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700543 tty_unregister_device(acm_tty_driver, acm->minor);
544 usb_put_intf(acm->control);
545 acm_table[acm->minor] = NULL;
546 usb_free_urb(acm->ctrlurb);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700547 usb_free_urb(acm->writeurb);
Oliver Neukum86478942006-05-13 22:50:47 +0200548 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100549 usb_free_urb(acm->ru[i].urb);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100550 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700551 kfree(acm);
552}
553
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554static void acm_tty_close(struct tty_struct *tty, struct file *filp)
555{
556 struct acm *acm = tty->driver_data;
Oliver Neukum86478942006-05-13 22:50:47 +0200557 int i,nr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
559 if (!acm || !acm->used)
560 return;
561
Oliver Neukum86478942006-05-13 22:50:47 +0200562 nr = acm->rx_buflimit;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100563 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 if (!--acm->used) {
565 if (acm->dev) {
566 acm_set_control(acm, acm->ctrlout = 0);
567 usb_kill_urb(acm->ctrlurb);
568 usb_kill_urb(acm->writeurb);
Oliver Neukum86478942006-05-13 22:50:47 +0200569 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100570 usb_kill_urb(acm->ru[i].urb);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700571 } else
572 acm_tty_unregister(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 }
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100574 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575}
576
577static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
578{
579 struct acm *acm = tty->driver_data;
580 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200581 unsigned long flags;
582 int wbn;
583 struct acm_wb *wb;
584
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200585 dbg("Entering acm_tty_write to write %d bytes,", count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
587 if (!ACM_READY(acm))
588 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 if (!count)
590 return 0;
591
Oliver Neukum884b6002005-04-21 21:28:02 +0200592 spin_lock_irqsave(&acm->write_lock, flags);
593 if ((wbn = acm_wb_alloc(acm)) < 0) {
594 spin_unlock_irqrestore(&acm->write_lock, flags);
595 acm_write_start(acm);
596 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200598 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599
Oliver Neukum884b6002005-04-21 21:28:02 +0200600 count = (count > acm->writesize) ? acm->writesize : count;
601 dbg("Get %d bytes...", count);
602 memcpy(wb->buf, buf, count);
603 wb->len = count;
604 spin_unlock_irqrestore(&acm->write_lock, flags);
605
606 if ((stat = acm_write_start(acm)) < 0)
607 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 return count;
609}
610
611static int acm_tty_write_room(struct tty_struct *tty)
612{
613 struct acm *acm = tty->driver_data;
614 if (!ACM_READY(acm))
615 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200616 /*
617 * Do not let the line discipline to know that we have a reserve,
618 * or it might get too enthusiastic.
619 */
620 return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621}
622
623static int acm_tty_chars_in_buffer(struct tty_struct *tty)
624{
625 struct acm *acm = tty->driver_data;
626 if (!ACM_READY(acm))
627 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200628 /*
629 * This is inaccurate (overcounts), but it works.
630 */
Oliver Neukum86478942006-05-13 22:50:47 +0200631 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632}
633
634static void acm_tty_throttle(struct tty_struct *tty)
635{
636 struct acm *acm = tty->driver_data;
637 if (!ACM_READY(acm))
638 return;
639 spin_lock_bh(&acm->throttle_lock);
640 acm->throttle = 1;
641 spin_unlock_bh(&acm->throttle_lock);
642}
643
644static void acm_tty_unthrottle(struct tty_struct *tty)
645{
646 struct acm *acm = tty->driver_data;
647 if (!ACM_READY(acm))
648 return;
649 spin_lock_bh(&acm->throttle_lock);
650 acm->throttle = 0;
651 spin_unlock_bh(&acm->throttle_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100652 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653}
654
655static void acm_tty_break_ctl(struct tty_struct *tty, int state)
656{
657 struct acm *acm = tty->driver_data;
658 if (!ACM_READY(acm))
659 return;
660 if (acm_send_break(acm, state ? 0xffff : 0))
661 dbg("send break failed");
662}
663
664static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
665{
666 struct acm *acm = tty->driver_data;
667
668 if (!ACM_READY(acm))
669 return -EINVAL;
670
671 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
672 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
673 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
674 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
675 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
676 TIOCM_CTS;
677}
678
679static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
680 unsigned int set, unsigned int clear)
681{
682 struct acm *acm = tty->driver_data;
683 unsigned int newctrl;
684
685 if (!ACM_READY(acm))
686 return -EINVAL;
687
688 newctrl = acm->ctrlout;
689 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
690 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
691
692 newctrl = (newctrl & ~clear) | set;
693
694 if (acm->ctrlout == newctrl)
695 return 0;
696 return acm_set_control(acm, acm->ctrlout = newctrl);
697}
698
699static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
700{
701 struct acm *acm = tty->driver_data;
702
703 if (!ACM_READY(acm))
704 return -EINVAL;
705
706 return -ENOIOCTLCMD;
707}
708
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100709static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 0, 50, 75, 110, 134, 150, 200, 300, 600,
711 1200, 1800, 2400, 4800, 9600, 19200, 38400,
712 57600, 115200, 230400, 460800, 500000, 576000,
713 921600, 1000000, 1152000, 1500000, 2000000,
714 2500000, 3000000, 3500000, 4000000
715};
716
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100717static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 5, 6, 7, 8
719};
720
Alan Cox606d0992006-12-08 02:38:45 -0800721static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722{
723 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800724 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 struct usb_cdc_line_coding newline;
726 int newctrl = acm->ctrlout;
727
728 if (!ACM_READY(acm))
729 return;
730
731 newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
732 (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
733 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
734 newline.bParityType = termios->c_cflag & PARENB ?
735 (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
736 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
737
738 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
739
740 if (!newline.dwDTERate) {
741 newline.dwDTERate = acm->line.dwDTERate;
742 newctrl &= ~ACM_CTRL_DTR;
743 } else newctrl |= ACM_CTRL_DTR;
744
745 if (newctrl != acm->ctrlout)
746 acm_set_control(acm, acm->ctrlout = newctrl);
747
748 if (memcmp(&acm->line, &newline, sizeof newline)) {
749 memcpy(&acm->line, &newline, sizeof newline);
750 dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
751 newline.bCharFormat, newline.bParityType,
752 newline.bDataBits);
753 acm_set_line(acm, &acm->line);
754 }
755}
756
757/*
758 * USB probe and disconnect routines.
759 */
760
Oliver Neukum884b6002005-04-21 21:28:02 +0200761/* Little helper: write buffers free */
762static void acm_write_buffers_free(struct acm *acm)
763{
764 int i;
765 struct acm_wb *wb;
766
Oliver Neukum86478942006-05-13 22:50:47 +0200767 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200768 usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah);
769 }
770}
771
772/* Little helper: write buffers allocate */
773static int acm_write_buffers_alloc(struct acm *acm)
774{
775 int i;
776 struct acm_wb *wb;
777
Oliver Neukum86478942006-05-13 22:50:47 +0200778 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200779 wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
780 &wb->dmah);
781 if (!wb->buf) {
782 while (i != 0) {
783 --i;
784 --wb;
785 usb_buffer_free(acm->dev, acm->writesize,
786 wb->buf, wb->dmah);
787 }
788 return -ENOMEM;
789 }
790 }
791 return 0;
792}
793
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794static int acm_probe (struct usb_interface *intf,
795 const struct usb_device_id *id)
796{
797 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100798 struct usb_cdc_country_functional_desc *cfd = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 char *buffer = intf->altsetting->extra;
800 int buflen = intf->altsetting->extralen;
801 struct usb_interface *control_interface;
802 struct usb_interface *data_interface;
803 struct usb_endpoint_descriptor *epctrl;
804 struct usb_endpoint_descriptor *epread;
805 struct usb_endpoint_descriptor *epwrite;
806 struct usb_device *usb_dev = interface_to_usbdev(intf);
807 struct acm *acm;
808 int minor;
809 int ctrlsize,readsize;
810 u8 *buf;
811 u8 ac_management_function = 0;
812 u8 call_management_function = 0;
813 int call_interface_num = -1;
814 int data_interface_num;
815 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200816 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100817 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
Oliver Neukum86478942006-05-13 22:50:47 +0200819 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200821 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
822
823 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 if (quirks == NO_UNION_NORMAL) {
825 data_interface = usb_ifnum_to_if(usb_dev, 1);
826 control_interface = usb_ifnum_to_if(usb_dev, 0);
827 goto skip_normal_probe;
828 }
829
830 /* normal probing*/
831 if (!buffer) {
832 err("Wierd descriptor references\n");
833 return -EINVAL;
834 }
835
836 if (!buflen) {
837 if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200838 dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 buflen = intf->cur_altsetting->endpoint->extralen;
840 buffer = intf->cur_altsetting->endpoint->extra;
841 } else {
842 err("Zero length descriptor references\n");
843 return -EINVAL;
844 }
845 }
846
847 while (buflen > 0) {
848 if (buffer [1] != USB_DT_CS_INTERFACE) {
849 err("skipping garbage\n");
850 goto next_desc;
851 }
852
853 switch (buffer [2]) {
854 case USB_CDC_UNION_TYPE: /* we've found it */
855 if (union_header) {
856 err("More than one union descriptor, skipping ...");
857 goto next_desc;
858 }
859 union_header = (struct usb_cdc_union_desc *)
860 buffer;
861 break;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100862 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
863 cfd = (struct usb_cdc_country_functional_desc *)buffer;
864 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 case USB_CDC_HEADER_TYPE: /* maybe check version */
866 break; /* for now we ignore it */
867 case USB_CDC_ACM_TYPE:
868 ac_management_function = buffer[3];
869 break;
870 case USB_CDC_CALL_MANAGEMENT_TYPE:
871 call_management_function = buffer[3];
872 call_interface_num = buffer[4];
873 if ((call_management_function & 3) != 3)
874 err("This device cannot do calls on its own. It is no modem.");
875 break;
876
877 default:
878 err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]);
879 break;
880 }
881next_desc:
882 buflen -= buffer[0];
883 buffer += buffer[0];
884 }
885
886 if (!union_header) {
887 if (call_interface_num > 0) {
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200888 dev_dbg(&intf->dev,"No union descriptor, using call management descriptor");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
890 control_interface = intf;
891 } else {
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200892 dev_dbg(&intf->dev,"No union descriptor, giving up");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 return -ENODEV;
894 }
895 } else {
896 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
897 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
898 if (!control_interface || !data_interface) {
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200899 dev_dbg(&intf->dev,"no interfaces");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 return -ENODEV;
901 }
902 }
903
904 if (data_interface_num != call_interface_num)
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200905 dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
907skip_normal_probe:
908
909 /*workaround for switched interfaces */
910 if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
911 if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
912 struct usb_interface *t;
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200913 dev_dbg(&intf->dev,"Your device has switched interfaces.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
915 t = control_interface;
916 control_interface = data_interface;
917 data_interface = t;
918 } else {
919 return -EINVAL;
920 }
921 }
922
923 if (usb_interface_claimed(data_interface)) { /* valid in this context */
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200924 dev_dbg(&intf->dev,"The data interface isn't available");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 return -EBUSY;
926 }
927
928
929 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
930 return -EINVAL;
931
932 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
933 epread = &data_interface->cur_altsetting->endpoint[0].desc;
934 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
935
936
937 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -0300938 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 /* descriptors are swapped */
940 struct usb_endpoint_descriptor *t;
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200941 dev_dbg(&intf->dev,"The data interface has switched endpoints");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942
943 t = epread;
944 epread = epwrite;
945 epwrite = t;
946 }
947 dbg("interfaces are valid");
948 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
949
950 if (minor == ACM_TTY_MINORS) {
951 err("no more free acm devices");
952 return -ENODEV;
953 }
954
Oliver Neukum46f116e2005-10-24 22:42:35 +0200955 if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200956 dev_dbg(&intf->dev, "out of memory (acm kzalloc)");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 goto alloc_fail;
958 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959
960 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Oliver Neukum86478942006-05-13 22:50:47 +0200961 readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize);
963 acm->control = control_interface;
964 acm->data = data_interface;
965 acm->minor = minor;
966 acm->dev = usb_dev;
967 acm->ctrl_caps = ac_management_function;
968 acm->ctrlsize = ctrlsize;
969 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +0200970 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100971 acm->urb_task.func = acm_rx_tasklet;
972 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +0000973 INIT_WORK(&acm->work, acm_softint);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +0200975 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100976 spin_lock_init(&acm->read_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +0200977 acm->write_ready = 1;
David Kubicek61a87ad2005-11-01 18:51:34 +0100978 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979
980 buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
981 if (!buf) {
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200982 dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 goto alloc_fail2;
984 }
985 acm->ctrl_buffer = buf;
986
Oliver Neukum884b6002005-04-21 21:28:02 +0200987 if (acm_write_buffers_alloc(acm) < 0) {
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200988 dev_dbg(&intf->dev, "out of memory (write buffer alloc)");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 goto alloc_fail4;
990 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991
992 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
993 if (!acm->ctrlurb) {
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200994 dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 goto alloc_fail5;
996 }
Oliver Neukum86478942006-05-13 22:50:47 +0200997 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100998 struct acm_ru *rcv = &(acm->ru[i]);
999
1000 if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001001 dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)");
David Kubicek61a87ad2005-11-01 18:51:34 +01001002 goto alloc_fail7;
1003 }
1004
1005 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1006 rcv->instance = acm;
1007 }
Oliver Neukum86478942006-05-13 22:50:47 +02001008 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001009 struct acm_rb *buf = &(acm->rb[i]);
1010
David Kubicek61a87ad2005-11-01 18:51:34 +01001011 if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001012 dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)");
David Kubicek61a87ad2005-11-01 18:51:34 +01001013 goto alloc_fail7;
1014 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 }
1016 acm->writeurb = usb_alloc_urb(0, GFP_KERNEL);
1017 if (!acm->writeurb) {
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001018 dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 goto alloc_fail7;
1020 }
1021
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001022 usb_set_intfdata (intf, acm);
1023
1024 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1025 if (i < 0)
1026 goto alloc_fail8;
1027
1028 if (cfd) { /* export the country data */
1029 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1030 if (!acm->country_codes)
1031 goto skip_countries;
1032 acm->country_code_size = cfd->bLength - 4;
1033 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
1034 acm->country_rel_date = cfd->iCountryCodeRelDate;
1035
1036 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1037 if (i < 0) {
1038 kfree(acm->country_codes);
1039 goto skip_countries;
1040 }
1041
1042 i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
1043 if (i < 0) {
1044 kfree(acm->country_codes);
1045 goto skip_countries;
1046 }
1047 }
1048
1049skip_countries:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1051 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
1052 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1053 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1054
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
Oliver Neukum884b6002005-04-21 21:28:02 +02001056 NULL, acm->writesize, acm_write_bulk, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
1059 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1060
1061 acm_set_control(acm, acm->ctrlout);
1062
1063 acm->line.dwDTERate = cpu_to_le32(9600);
1064 acm->line.bDataBits = 8;
1065 acm_set_line(acm, &acm->line);
1066
1067 usb_driver_claim_interface(&acm_driver, data_interface, acm);
1068
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001069 usb_get_intf(control_interface);
1070 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
1072 acm_table[minor] = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001074 return 0;
1075alloc_fail8:
1076 usb_free_urb(acm->writeurb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077alloc_fail7:
Oliver Neukum86478942006-05-13 22:50:47 +02001078 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001079 usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum86478942006-05-13 22:50:47 +02001080 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001081 usb_free_urb(acm->ru[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 usb_free_urb(acm->ctrlurb);
1083alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001084 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085alloc_fail4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
1087alloc_fail2:
1088 kfree(acm);
1089alloc_fail:
1090 return -ENOMEM;
1091}
1092
1093static void acm_disconnect(struct usb_interface *intf)
1094{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001095 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 struct usb_device *usb_dev = interface_to_usbdev(intf);
David Kubicek61a87ad2005-11-01 18:51:34 +01001097 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098
1099 if (!acm || !acm->dev) {
1100 dbg("disconnect on nonexisting interface");
1101 return;
1102 }
1103
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001104 mutex_lock(&open_mutex);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001105 if (!usb_get_intfdata(intf)) {
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001106 mutex_unlock(&open_mutex);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001107 return;
1108 }
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001109 if (acm->country_codes){
1110 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
1111 device_remove_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
1112 }
1113 device_remove_file(&intf->dev, &dev_attr_bmCapabilities);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 acm->dev = NULL;
Oliver Neukum86067eea2006-01-08 12:39:13 +01001115 usb_set_intfdata(acm->control, NULL);
1116 usb_set_intfdata(acm->data, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117
David Kubicek61a87ad2005-11-01 18:51:34 +01001118 tasklet_disable(&acm->urb_task);
1119
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 usb_kill_urb(acm->ctrlurb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 usb_kill_urb(acm->writeurb);
Oliver Neukum86478942006-05-13 22:50:47 +02001122 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001123 usb_kill_urb(acm->ru[i].urb);
1124
1125 INIT_LIST_HEAD(&acm->filled_read_bufs);
1126 INIT_LIST_HEAD(&acm->spare_read_bufs);
1127
1128 tasklet_enable(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129
1130 flush_scheduled_work(); /* wait for acm_softint */
1131
Oliver Neukum884b6002005-04-21 21:28:02 +02001132 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Oliver Neukum86478942006-05-13 22:50:47 +02001134 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001135 usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136
Oliver Neukum86067eea2006-01-08 12:39:13 +01001137 usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138
1139 if (!acm->used) {
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001140 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001141 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 return;
1143 }
1144
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001145 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146
1147 if (acm->tty)
1148 tty_hangup(acm->tty);
1149}
1150
1151/*
1152 * USB driver structure.
1153 */
1154
1155static struct usb_device_id acm_ids[] = {
1156 /* quirky and broken devices */
1157 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1158 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1159 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001160 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1161 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1162 },
Masahito Omote8753e652005-07-29 12:17:25 -07001163 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1164 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1165 },
Chris Malley91a9c922006-10-03 10:08:28 +01001166 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1167 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1168 },
Oliver Neukum86478942006-05-13 22:50:47 +02001169 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1170 .driver_info = SINGLE_RX_URB, /* firmware bug */
1171 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001172 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1173 .driver_info = SINGLE_RX_URB, /* firmware bug */
1174 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001175 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1176 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1177 },
1178
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 /* control interfaces with various AT-command sets */
1180 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1181 USB_CDC_ACM_PROTO_AT_V25TER) },
1182 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1183 USB_CDC_ACM_PROTO_AT_PCCA101) },
1184 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1185 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1186 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1187 USB_CDC_ACM_PROTO_AT_GSM) },
1188 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1189 USB_CDC_ACM_PROTO_AT_3G ) },
1190 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1191 USB_CDC_ACM_PROTO_AT_CDMA) },
1192
1193 /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
1194 { }
1195};
1196
1197MODULE_DEVICE_TABLE (usb, acm_ids);
1198
1199static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 .name = "cdc_acm",
1201 .probe = acm_probe,
1202 .disconnect = acm_disconnect,
1203 .id_table = acm_ids,
1204};
1205
1206/*
1207 * TTY driver structures.
1208 */
1209
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001210static const struct tty_operations acm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 .open = acm_tty_open,
1212 .close = acm_tty_close,
1213 .write = acm_tty_write,
1214 .write_room = acm_tty_write_room,
1215 .ioctl = acm_tty_ioctl,
1216 .throttle = acm_tty_throttle,
1217 .unthrottle = acm_tty_unthrottle,
1218 .chars_in_buffer = acm_tty_chars_in_buffer,
1219 .break_ctl = acm_tty_break_ctl,
1220 .set_termios = acm_tty_set_termios,
1221 .tiocmget = acm_tty_tiocmget,
1222 .tiocmset = acm_tty_tiocmset,
1223};
1224
1225/*
1226 * Init / exit.
1227 */
1228
1229static int __init acm_init(void)
1230{
1231 int retval;
1232 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1233 if (!acm_tty_driver)
1234 return -ENOMEM;
1235 acm_tty_driver->owner = THIS_MODULE,
1236 acm_tty_driver->driver_name = "acm",
1237 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 acm_tty_driver->major = ACM_TTY_MAJOR,
1239 acm_tty_driver->minor_start = 0,
1240 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1241 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001242 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 acm_tty_driver->init_termios = tty_std_termios;
1244 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
1245 tty_set_operations(acm_tty_driver, &acm_ops);
1246
1247 retval = tty_register_driver(acm_tty_driver);
1248 if (retval) {
1249 put_tty_driver(acm_tty_driver);
1250 return retval;
1251 }
1252
1253 retval = usb_register(&acm_driver);
1254 if (retval) {
1255 tty_unregister_driver(acm_tty_driver);
1256 put_tty_driver(acm_tty_driver);
1257 return retval;
1258 }
1259
1260 info(DRIVER_VERSION ":" DRIVER_DESC);
1261
1262 return 0;
1263}
1264
1265static void __exit acm_exit(void)
1266{
1267 usb_deregister(&acm_driver);
1268 tty_unregister_driver(acm_tty_driver);
1269 put_tty_driver(acm_tty_driver);
1270}
1271
1272module_init(acm_init);
1273module_exit(acm_exit);
1274
1275MODULE_AUTHOR( DRIVER_AUTHOR );
1276MODULE_DESCRIPTION( DRIVER_DESC );
1277MODULE_LICENSE("GPL");
1278