blob: 1ded83b66af0979c0f869db5e3c29c82e664c6ff [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;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700260 int retval;
261 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700263 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 case 0:
265 /* success */
266 break;
267 case -ECONNRESET:
268 case -ENOENT:
269 case -ESHUTDOWN:
270 /* this urb is terminated, clean up */
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700271 dbg("%s - urb shutting down with status: %d", __FUNCTION__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 return;
273 default:
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700274 dbg("%s - nonzero urb status received: %d", __FUNCTION__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 goto exit;
276 }
277
278 if (!ACM_READY(acm))
279 goto exit;
280
281 data = (unsigned char *)(dr + 1);
282 switch (dr->bNotificationType) {
283
284 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
285
286 dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
287 break;
288
289 case USB_CDC_NOTIFY_SERIAL_STATE:
290
291 newctrl = le16_to_cpu(get_unaligned((__le16 *) data));
292
293 if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
294 dbg("calling hangup");
295 tty_hangup(acm->tty);
296 }
297
298 acm->ctrlin = newctrl;
299
300 dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
301 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
302 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
303 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
304 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
305
306 break;
307
308 default:
309 dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
310 dr->bNotificationType, dr->wIndex,
311 dr->wLength, data[0], data[1]);
312 break;
313 }
314exit:
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700315 retval = usb_submit_urb (urb, GFP_ATOMIC);
316 if (retval)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 err ("%s - usb_submit_urb failed with result %d",
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700318 __FUNCTION__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319}
320
321/* data interface returns incoming bytes, or we got unthrottled */
David Howells7d12e782006-10-05 14:55:46 +0100322static void acm_read_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323{
David Kubicek61a87ad2005-11-01 18:51:34 +0100324 struct acm_rb *buf;
325 struct acm_ru *rcv = urb->context;
326 struct acm *acm = rcv->instance;
Oliver Neukum86478942006-05-13 22:50:47 +0200327 int status = urb->status;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700328
329 dbg("Entering acm_read_bulk with status %d", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
331 if (!ACM_READY(acm))
332 return;
333
Oliver Neukum86478942006-05-13 22:50:47 +0200334 if (status)
Joe Perches898eb712007-10-18 03:06:30 -0700335 dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
David Kubicek61a87ad2005-11-01 18:51:34 +0100337 buf = rcv->buffer;
338 buf->size = urb->actual_length;
339
Oliver Neukum86478942006-05-13 22:50:47 +0200340 if (likely(status == 0)) {
341 spin_lock(&acm->read_lock);
342 list_add_tail(&rcv->list, &acm->spare_read_urbs);
343 list_add_tail(&buf->list, &acm->filled_read_bufs);
344 spin_unlock(&acm->read_lock);
345 } else {
346 /* we drop the buffer due to an error */
347 spin_lock(&acm->read_lock);
348 list_add_tail(&rcv->list, &acm->spare_read_urbs);
349 list_add(&buf->list, &acm->spare_read_bufs);
350 spin_unlock(&acm->read_lock);
351 /* nevertheless the tasklet must be kicked unconditionally
352 so the queue cannot dry up */
353 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100354 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355}
356
357static void acm_rx_tasklet(unsigned long _acm)
358{
359 struct acm *acm = (void *)_acm;
David Kubicek61a87ad2005-11-01 18:51:34 +0100360 struct acm_rb *buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 struct tty_struct *tty = acm->tty;
David Kubicek61a87ad2005-11-01 18:51:34 +0100362 struct acm_ru *rcv;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200363 unsigned long flags;
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100364 unsigned char throttled;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 dbg("Entering acm_rx_tasklet");
366
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100367 if (!ACM_READY(acm))
368 return;
369
Oliver Neukum834dbca2007-03-06 10:47:04 +0100370 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100371 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100372 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100373 if (throttled)
David Kubicek61a87ad2005-11-01 18:51:34 +0100374 return;
375
376next_buffer:
Jarek Poplawski762f0072006-10-06 07:23:11 +0200377 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100378 if (list_empty(&acm->filled_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200379 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100380 goto urbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100382 buf = list_entry(acm->filled_read_bufs.next,
383 struct acm_rb, list);
384 list_del(&buf->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200385 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100386
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200387 dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
David Kubicek61a87ad2005-11-01 18:51:34 +0100388
Alan Cox33f0f882006-01-09 20:54:13 -0800389 tty_buffer_request_room(tty, buf->size);
Oliver Neukum834dbca2007-03-06 10:47:04 +0100390 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100391 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100392 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100393 if (!throttled)
Alan Cox33f0f882006-01-09 20:54:13 -0800394 tty_insert_flip_string(tty, buf->base, buf->size);
David Kubicek61a87ad2005-11-01 18:51:34 +0100395 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100397 if (throttled) {
398 dbg("Throttling noticed");
Jarek Poplawski762f0072006-10-06 07:23:11 +0200399 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100400 list_add(&buf->list, &acm->filled_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200401 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 return;
403 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
Jarek Poplawski762f0072006-10-06 07:23:11 +0200405 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100406 list_add(&buf->list, &acm->spare_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200407 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100408 goto next_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409
David Kubicek61a87ad2005-11-01 18:51:34 +0100410urbs:
411 while (!list_empty(&acm->spare_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200412 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100413 if (list_empty(&acm->spare_read_urbs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200414 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100415 return;
416 }
417 rcv = list_entry(acm->spare_read_urbs.next,
418 struct acm_ru, list);
419 list_del(&rcv->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200420 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100421
422 buf = list_entry(acm->spare_read_bufs.next,
423 struct acm_rb, list);
424 list_del(&buf->list);
425
426 rcv->buffer = buf;
427
428 usb_fill_bulk_urb(rcv->urb, acm->dev,
429 acm->rx_endpoint,
430 buf->base,
431 acm->readsize,
432 acm_read_bulk, rcv);
433 rcv->urb->transfer_dma = buf->dma;
434 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
435
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200436 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 +0100437
438 /* This shouldn't kill the driver as unsuccessful URBs are returned to the
439 free-urbs-pool and resubmited ASAP */
440 if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
441 list_add(&buf->list, &acm->spare_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200442 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100443 list_add(&rcv->list, &acm->spare_read_urbs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200444 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100445 return;
446 }
447 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448}
449
450/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100451static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452{
453 struct acm *acm = (struct acm *)urb->context;
Oliver Neukum884b6002005-04-21 21:28:02 +0200454
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200455 dbg("Entering acm_write_bulk with status %d", urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456
Oliver Neukum884b6002005-04-21 21:28:02 +0200457 acm_write_done(acm);
458 acm_write_start(acm);
459 if (ACM_READY(acm))
460 schedule_work(&acm->work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461}
462
David Howellsc4028952006-11-22 14:57:56 +0000463static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464{
David Howellsc4028952006-11-22 14:57:56 +0000465 struct acm *acm = container_of(work, struct acm, work);
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200466 dbg("Entering acm_softint.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467
468 if (!ACM_READY(acm))
469 return;
470 tty_wakeup(acm->tty);
471}
472
473/*
474 * TTY handlers
475 */
476
477static int acm_tty_open(struct tty_struct *tty, struct file *filp)
478{
479 struct acm *acm;
480 int rv = -EINVAL;
David Kubicek61a87ad2005-11-01 18:51:34 +0100481 int i;
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200482 dbg("Entering acm_tty_open.");
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100483
484 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
486 acm = acm_table[tty->index];
487 if (!acm || !acm->dev)
488 goto err_out;
489 else
490 rv = 0;
491
David Engraf28d1dfa2008-03-20 10:53:52 +0100492 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 tty->driver_data = acm;
494 acm->tty = tty;
495
David Kubicek61a87ad2005-11-01 18:51:34 +0100496 /* force low_latency on so that our tty_push actually forces the data through,
497 otherwise it is scheduled, and with high data rates data can get lost. */
498 tty->low_latency = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499
Oliver Neukum94409cc2008-02-11 15:22:29 +0100500 if (usb_autopm_get_interface(acm->control) < 0)
501 goto early_bail;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200502
503 mutex_lock(&acm->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 if (acm->used++) {
Oliver Neukum1365baf2007-10-12 17:24:28 +0200505 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 goto done;
507 }
508
Oliver Neukum1365baf2007-10-12 17:24:28 +0200509
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 acm->ctrlurb->dev = acm->dev;
511 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
512 dbg("usb_submit_urb(ctrl irq) failed");
513 goto bail_out;
514 }
515
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100516 if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
517 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 goto full_bailout;
519
David Kubicek61a87ad2005-11-01 18:51:34 +0100520 INIT_LIST_HEAD(&acm->spare_read_urbs);
521 INIT_LIST_HEAD(&acm->spare_read_bufs);
522 INIT_LIST_HEAD(&acm->filled_read_bufs);
Oliver Neukum86478942006-05-13 22:50:47 +0200523 for (i = 0; i < acm->rx_buflimit; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100524 list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
525 }
Oliver Neukum86478942006-05-13 22:50:47 +0200526 for (i = 0; i < acm->rx_buflimit; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100527 list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
528 }
529
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100530 acm->throttle = 0;
531
David Kubicek61a87ad2005-11-01 18:51:34 +0100532 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
534done:
535err_out:
Oliver Neukum1365baf2007-10-12 17:24:28 +0200536 mutex_unlock(&acm->mutex);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100537 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 return rv;
539
540full_bailout:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 usb_kill_urb(acm->ctrlurb);
542bail_out:
Oliver Neukum1365baf2007-10-12 17:24:28 +0200543 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 acm->used--;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200545 mutex_unlock(&acm->mutex);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100546early_bail:
547 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 return -EIO;
549}
550
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700551static void acm_tty_unregister(struct acm *acm)
552{
Oliver Neukum86478942006-05-13 22:50:47 +0200553 int i,nr;
David Kubicek61a87ad2005-11-01 18:51:34 +0100554
Oliver Neukum86478942006-05-13 22:50:47 +0200555 nr = acm->rx_buflimit;
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700556 tty_unregister_device(acm_tty_driver, acm->minor);
557 usb_put_intf(acm->control);
558 acm_table[acm->minor] = NULL;
559 usb_free_urb(acm->ctrlurb);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700560 usb_free_urb(acm->writeurb);
Oliver Neukum86478942006-05-13 22:50:47 +0200561 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100562 usb_free_urb(acm->ru[i].urb);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100563 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700564 kfree(acm);
565}
566
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567static void acm_tty_close(struct tty_struct *tty, struct file *filp)
568{
569 struct acm *acm = tty->driver_data;
Oliver Neukum86478942006-05-13 22:50:47 +0200570 int i,nr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
572 if (!acm || !acm->used)
573 return;
574
Oliver Neukum86478942006-05-13 22:50:47 +0200575 nr = acm->rx_buflimit;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100576 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 if (!--acm->used) {
578 if (acm->dev) {
579 acm_set_control(acm, acm->ctrlout = 0);
580 usb_kill_urb(acm->ctrlurb);
581 usb_kill_urb(acm->writeurb);
Oliver Neukum86478942006-05-13 22:50:47 +0200582 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100583 usb_kill_urb(acm->ru[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +0200584 usb_autopm_put_interface(acm->control);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700585 } else
586 acm_tty_unregister(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 }
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100588 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589}
590
591static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
592{
593 struct acm *acm = tty->driver_data;
594 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200595 unsigned long flags;
596 int wbn;
597 struct acm_wb *wb;
598
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200599 dbg("Entering acm_tty_write to write %d bytes,", count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
601 if (!ACM_READY(acm))
602 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 if (!count)
604 return 0;
605
Oliver Neukum884b6002005-04-21 21:28:02 +0200606 spin_lock_irqsave(&acm->write_lock, flags);
607 if ((wbn = acm_wb_alloc(acm)) < 0) {
608 spin_unlock_irqrestore(&acm->write_lock, flags);
609 acm_write_start(acm);
610 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200612 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
Oliver Neukum884b6002005-04-21 21:28:02 +0200614 count = (count > acm->writesize) ? acm->writesize : count;
615 dbg("Get %d bytes...", count);
616 memcpy(wb->buf, buf, count);
617 wb->len = count;
618 spin_unlock_irqrestore(&acm->write_lock, flags);
619
620 if ((stat = acm_write_start(acm)) < 0)
621 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 return count;
623}
624
625static int acm_tty_write_room(struct tty_struct *tty)
626{
627 struct acm *acm = tty->driver_data;
628 if (!ACM_READY(acm))
629 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200630 /*
631 * Do not let the line discipline to know that we have a reserve,
632 * or it might get too enthusiastic.
633 */
634 return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635}
636
637static int acm_tty_chars_in_buffer(struct tty_struct *tty)
638{
639 struct acm *acm = tty->driver_data;
640 if (!ACM_READY(acm))
641 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200642 /*
643 * This is inaccurate (overcounts), but it works.
644 */
Oliver Neukum86478942006-05-13 22:50:47 +0200645 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646}
647
648static void acm_tty_throttle(struct tty_struct *tty)
649{
650 struct acm *acm = tty->driver_data;
651 if (!ACM_READY(acm))
652 return;
653 spin_lock_bh(&acm->throttle_lock);
654 acm->throttle = 1;
655 spin_unlock_bh(&acm->throttle_lock);
656}
657
658static void acm_tty_unthrottle(struct tty_struct *tty)
659{
660 struct acm *acm = tty->driver_data;
661 if (!ACM_READY(acm))
662 return;
663 spin_lock_bh(&acm->throttle_lock);
664 acm->throttle = 0;
665 spin_unlock_bh(&acm->throttle_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100666 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667}
668
669static void acm_tty_break_ctl(struct tty_struct *tty, int state)
670{
671 struct acm *acm = tty->driver_data;
672 if (!ACM_READY(acm))
673 return;
674 if (acm_send_break(acm, state ? 0xffff : 0))
675 dbg("send break failed");
676}
677
678static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
679{
680 struct acm *acm = tty->driver_data;
681
682 if (!ACM_READY(acm))
683 return -EINVAL;
684
685 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
686 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
687 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
688 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
689 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
690 TIOCM_CTS;
691}
692
693static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
694 unsigned int set, unsigned int clear)
695{
696 struct acm *acm = tty->driver_data;
697 unsigned int newctrl;
698
699 if (!ACM_READY(acm))
700 return -EINVAL;
701
702 newctrl = acm->ctrlout;
703 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
704 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
705
706 newctrl = (newctrl & ~clear) | set;
707
708 if (acm->ctrlout == newctrl)
709 return 0;
710 return acm_set_control(acm, acm->ctrlout = newctrl);
711}
712
713static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
714{
715 struct acm *acm = tty->driver_data;
716
717 if (!ACM_READY(acm))
718 return -EINVAL;
719
720 return -ENOIOCTLCMD;
721}
722
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100723static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 0, 50, 75, 110, 134, 150, 200, 300, 600,
725 1200, 1800, 2400, 4800, 9600, 19200, 38400,
726 57600, 115200, 230400, 460800, 500000, 576000,
727 921600, 1000000, 1152000, 1500000, 2000000,
728 2500000, 3000000, 3500000, 4000000
729};
730
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100731static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 5, 6, 7, 8
733};
734
Alan Cox606d0992006-12-08 02:38:45 -0800735static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736{
737 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800738 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 struct usb_cdc_line_coding newline;
740 int newctrl = acm->ctrlout;
741
742 if (!ACM_READY(acm))
743 return;
744
745 newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
746 (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
747 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
748 newline.bParityType = termios->c_cflag & PARENB ?
749 (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
750 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
751
752 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
753
754 if (!newline.dwDTERate) {
755 newline.dwDTERate = acm->line.dwDTERate;
756 newctrl &= ~ACM_CTRL_DTR;
757 } else newctrl |= ACM_CTRL_DTR;
758
759 if (newctrl != acm->ctrlout)
760 acm_set_control(acm, acm->ctrlout = newctrl);
761
762 if (memcmp(&acm->line, &newline, sizeof newline)) {
763 memcpy(&acm->line, &newline, sizeof newline);
764 dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
765 newline.bCharFormat, newline.bParityType,
766 newline.bDataBits);
767 acm_set_line(acm, &acm->line);
768 }
769}
770
771/*
772 * USB probe and disconnect routines.
773 */
774
Oliver Neukum884b6002005-04-21 21:28:02 +0200775/* Little helper: write buffers free */
776static void acm_write_buffers_free(struct acm *acm)
777{
778 int i;
779 struct acm_wb *wb;
780
Oliver Neukum86478942006-05-13 22:50:47 +0200781 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200782 usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah);
783 }
784}
785
786/* Little helper: write buffers allocate */
787static int acm_write_buffers_alloc(struct acm *acm)
788{
789 int i;
790 struct acm_wb *wb;
791
Oliver Neukum86478942006-05-13 22:50:47 +0200792 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200793 wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
794 &wb->dmah);
795 if (!wb->buf) {
796 while (i != 0) {
797 --i;
798 --wb;
799 usb_buffer_free(acm->dev, acm->writesize,
800 wb->buf, wb->dmah);
801 }
802 return -ENOMEM;
803 }
804 }
805 return 0;
806}
807
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808static int acm_probe (struct usb_interface *intf,
809 const struct usb_device_id *id)
810{
811 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100812 struct usb_cdc_country_functional_desc *cfd = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 char *buffer = intf->altsetting->extra;
814 int buflen = intf->altsetting->extralen;
815 struct usb_interface *control_interface;
816 struct usb_interface *data_interface;
817 struct usb_endpoint_descriptor *epctrl;
818 struct usb_endpoint_descriptor *epread;
819 struct usb_endpoint_descriptor *epwrite;
820 struct usb_device *usb_dev = interface_to_usbdev(intf);
821 struct acm *acm;
822 int minor;
823 int ctrlsize,readsize;
824 u8 *buf;
825 u8 ac_management_function = 0;
826 u8 call_management_function = 0;
827 int call_interface_num = -1;
828 int data_interface_num;
829 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200830 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100831 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
Oliver Neukum86478942006-05-13 22:50:47 +0200833 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200835 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
836
837 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 if (quirks == NO_UNION_NORMAL) {
839 data_interface = usb_ifnum_to_if(usb_dev, 1);
840 control_interface = usb_ifnum_to_if(usb_dev, 0);
841 goto skip_normal_probe;
842 }
843
844 /* normal probing*/
845 if (!buffer) {
Joe Perches898eb712007-10-18 03:06:30 -0700846 err("Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 return -EINVAL;
848 }
849
850 if (!buflen) {
851 if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
Joe Perches898eb712007-10-18 03:06:30 -0700852 dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 buflen = intf->cur_altsetting->endpoint->extralen;
854 buffer = intf->cur_altsetting->endpoint->extra;
855 } else {
856 err("Zero length descriptor references\n");
857 return -EINVAL;
858 }
859 }
860
861 while (buflen > 0) {
862 if (buffer [1] != USB_DT_CS_INTERFACE) {
863 err("skipping garbage\n");
864 goto next_desc;
865 }
866
867 switch (buffer [2]) {
868 case USB_CDC_UNION_TYPE: /* we've found it */
869 if (union_header) {
870 err("More than one union descriptor, skipping ...");
871 goto next_desc;
872 }
873 union_header = (struct usb_cdc_union_desc *)
874 buffer;
875 break;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100876 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
877 cfd = (struct usb_cdc_country_functional_desc *)buffer;
878 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 case USB_CDC_HEADER_TYPE: /* maybe check version */
880 break; /* for now we ignore it */
881 case USB_CDC_ACM_TYPE:
882 ac_management_function = buffer[3];
883 break;
884 case USB_CDC_CALL_MANAGEMENT_TYPE:
885 call_management_function = buffer[3];
886 call_interface_num = buffer[4];
887 if ((call_management_function & 3) != 3)
888 err("This device cannot do calls on its own. It is no modem.");
889 break;
890
891 default:
892 err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]);
893 break;
894 }
895next_desc:
896 buflen -= buffer[0];
897 buffer += buffer[0];
898 }
899
900 if (!union_header) {
901 if (call_interface_num > 0) {
Joe Perches898eb712007-10-18 03:06:30 -0700902 dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
904 control_interface = intf;
905 } else {
Joe Perches898eb712007-10-18 03:06:30 -0700906 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 return -ENODEV;
908 }
909 } else {
910 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
911 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
912 if (!control_interface || !data_interface) {
Joe Perches898eb712007-10-18 03:06:30 -0700913 dev_dbg(&intf->dev,"no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 return -ENODEV;
915 }
916 }
917
918 if (data_interface_num != call_interface_num)
Joe Perchesdc0d5c12007-12-17 11:40:18 -0800919 dev_dbg(&intf->dev,"Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
921skip_normal_probe:
922
923 /*workaround for switched interfaces */
924 if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
925 if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
926 struct usb_interface *t;
Joe Perches898eb712007-10-18 03:06:30 -0700927 dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
929 t = control_interface;
930 control_interface = data_interface;
931 data_interface = t;
932 } else {
933 return -EINVAL;
934 }
935 }
Alan Stern74da5d62007-08-02 13:29:10 -0400936
937 /* Accept probe requests only for the control interface */
938 if (intf != control_interface)
939 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940
941 if (usb_interface_claimed(data_interface)) { /* valid in this context */
Joe Perches898eb712007-10-18 03:06:30 -0700942 dev_dbg(&intf->dev,"The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 return -EBUSY;
944 }
945
946
947 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
948 return -EINVAL;
949
950 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
951 epread = &data_interface->cur_altsetting->endpoint[0].desc;
952 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
953
954
955 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -0300956 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 /* descriptors are swapped */
958 struct usb_endpoint_descriptor *t;
Joe Perches898eb712007-10-18 03:06:30 -0700959 dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960
961 t = epread;
962 epread = epwrite;
963 epwrite = t;
964 }
965 dbg("interfaces are valid");
966 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
967
968 if (minor == ACM_TTY_MINORS) {
969 err("no more free acm devices");
970 return -ENODEV;
971 }
972
Oliver Neukum46f116e2005-10-24 22:42:35 +0200973 if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
Joe Perches898eb712007-10-18 03:06:30 -0700974 dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 goto alloc_fail;
976 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
978 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Oliver Neukum86478942006-05-13 22:50:47 +0200979 readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize);
981 acm->control = control_interface;
982 acm->data = data_interface;
983 acm->minor = minor;
984 acm->dev = usb_dev;
985 acm->ctrl_caps = ac_management_function;
986 acm->ctrlsize = ctrlsize;
987 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +0200988 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100989 acm->urb_task.func = acm_rx_tasklet;
990 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +0000991 INIT_WORK(&acm->work, acm_softint);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +0200993 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100994 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +0200995 mutex_init(&acm->mutex);
Oliver Neukum884b6002005-04-21 21:28:02 +0200996 acm->write_ready = 1;
David Kubicek61a87ad2005-11-01 18:51:34 +0100997 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998
999 buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
1000 if (!buf) {
Joe Perches898eb712007-10-18 03:06:30 -07001001 dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 goto alloc_fail2;
1003 }
1004 acm->ctrl_buffer = buf;
1005
Oliver Neukum884b6002005-04-21 21:28:02 +02001006 if (acm_write_buffers_alloc(acm) < 0) {
Joe Perches898eb712007-10-18 03:06:30 -07001007 dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 goto alloc_fail4;
1009 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010
1011 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1012 if (!acm->ctrlurb) {
Joe Perches898eb712007-10-18 03:06:30 -07001013 dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 goto alloc_fail5;
1015 }
Oliver Neukum86478942006-05-13 22:50:47 +02001016 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001017 struct acm_ru *rcv = &(acm->ru[i]);
1018
1019 if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
Joe Perches898eb712007-10-18 03:06:30 -07001020 dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001021 goto alloc_fail7;
1022 }
1023
1024 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1025 rcv->instance = acm;
1026 }
Oliver Neukum86478942006-05-13 22:50:47 +02001027 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001028 struct acm_rb *buf = &(acm->rb[i]);
1029
David Kubicek61a87ad2005-11-01 18:51:34 +01001030 if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
Joe Perches898eb712007-10-18 03:06:30 -07001031 dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001032 goto alloc_fail7;
1033 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 }
1035 acm->writeurb = usb_alloc_urb(0, GFP_KERNEL);
1036 if (!acm->writeurb) {
Joe Perches898eb712007-10-18 03:06:30 -07001037 dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 goto alloc_fail7;
1039 }
1040
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001041 usb_set_intfdata (intf, acm);
1042
1043 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1044 if (i < 0)
1045 goto alloc_fail8;
1046
1047 if (cfd) { /* export the country data */
1048 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1049 if (!acm->country_codes)
1050 goto skip_countries;
1051 acm->country_code_size = cfd->bLength - 4;
1052 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
1053 acm->country_rel_date = cfd->iCountryCodeRelDate;
1054
1055 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1056 if (i < 0) {
1057 kfree(acm->country_codes);
1058 goto skip_countries;
1059 }
1060
1061 i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
1062 if (i < 0) {
1063 kfree(acm->country_codes);
1064 goto skip_countries;
1065 }
1066 }
1067
1068skip_countries:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1070 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
1071 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1072 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1073
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
Oliver Neukum884b6002005-04-21 21:28:02 +02001075 NULL, acm->writesize, acm_write_bulk, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077
1078 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1079
1080 acm_set_control(acm, acm->ctrlout);
1081
1082 acm->line.dwDTERate = cpu_to_le32(9600);
1083 acm->line.bDataBits = 8;
1084 acm_set_line(acm, &acm->line);
1085
1086 usb_driver_claim_interface(&acm_driver, data_interface, acm);
1087
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001088 usb_get_intf(control_interface);
1089 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090
1091 acm_table[minor] = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001093 return 0;
1094alloc_fail8:
1095 usb_free_urb(acm->writeurb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096alloc_fail7:
Oliver Neukum86478942006-05-13 22:50:47 +02001097 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001098 usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum86478942006-05-13 22:50:47 +02001099 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001100 usb_free_urb(acm->ru[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 usb_free_urb(acm->ctrlurb);
1102alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001103 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104alloc_fail4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
1106alloc_fail2:
1107 kfree(acm);
1108alloc_fail:
1109 return -ENOMEM;
1110}
1111
Oliver Neukum1365baf2007-10-12 17:24:28 +02001112static void stop_data_traffic(struct acm *acm)
1113{
1114 int i;
1115
1116 tasklet_disable(&acm->urb_task);
1117
1118 usb_kill_urb(acm->ctrlurb);
1119 usb_kill_urb(acm->writeurb);
1120 for (i = 0; i < acm->rx_buflimit; i++)
1121 usb_kill_urb(acm->ru[i].urb);
1122
1123 INIT_LIST_HEAD(&acm->filled_read_bufs);
1124 INIT_LIST_HEAD(&acm->spare_read_bufs);
1125
1126 tasklet_enable(&acm->urb_task);
1127
1128 cancel_work_sync(&acm->work);
1129}
1130
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131static void acm_disconnect(struct usb_interface *intf)
1132{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001133 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 struct usb_device *usb_dev = interface_to_usbdev(intf);
David Kubicek61a87ad2005-11-01 18:51:34 +01001135 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136
1137 if (!acm || !acm->dev) {
1138 dbg("disconnect on nonexisting interface");
1139 return;
1140 }
1141
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001142 mutex_lock(&open_mutex);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001143 if (!usb_get_intfdata(intf)) {
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001144 mutex_unlock(&open_mutex);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001145 return;
1146 }
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001147 if (acm->country_codes){
Alan Stern74da5d62007-08-02 13:29:10 -04001148 device_remove_file(&acm->control->dev,
1149 &dev_attr_wCountryCodes);
1150 device_remove_file(&acm->control->dev,
1151 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001152 }
Alan Stern74da5d62007-08-02 13:29:10 -04001153 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 acm->dev = NULL;
Oliver Neukum86067eea2006-01-08 12:39:13 +01001155 usb_set_intfdata(acm->control, NULL);
1156 usb_set_intfdata(acm->data, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157
Oliver Neukum1365baf2007-10-12 17:24:28 +02001158 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159
Oliver Neukum884b6002005-04-21 21:28:02 +02001160 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Oliver Neukum86478942006-05-13 22:50:47 +02001162 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001163 usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164
Oliver Neukum86067eea2006-01-08 12:39:13 +01001165 usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166
1167 if (!acm->used) {
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001168 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001169 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 return;
1171 }
1172
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001173 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174
1175 if (acm->tty)
1176 tty_hangup(acm->tty);
1177}
1178
Oliver Neukum1365baf2007-10-12 17:24:28 +02001179static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1180{
1181 struct acm *acm = usb_get_intfdata(intf);
1182
1183 if (acm->susp_count++)
1184 return 0;
1185 /*
1186 we treat opened interfaces differently,
1187 we must guard against open
1188 */
1189 mutex_lock(&acm->mutex);
1190
1191 if (acm->used)
1192 stop_data_traffic(acm);
1193
1194 mutex_unlock(&acm->mutex);
1195 return 0;
1196}
1197
1198static int acm_resume(struct usb_interface *intf)
1199{
1200 struct acm *acm = usb_get_intfdata(intf);
1201 int rv = 0;
1202
1203 if (--acm->susp_count)
1204 return 0;
1205
1206 mutex_lock(&acm->mutex);
1207 if (acm->used) {
1208 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
1209 if (rv < 0)
1210 goto err_out;
1211
1212 tasklet_schedule(&acm->urb_task);
1213 }
1214
1215err_out:
1216 mutex_unlock(&acm->mutex);
1217 return rv;
1218}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219/*
1220 * USB driver structure.
1221 */
1222
1223static struct usb_device_id acm_ids[] = {
1224 /* quirky and broken devices */
1225 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1226 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1227 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001228 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1229 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1230 },
Masahito Omote8753e652005-07-29 12:17:25 -07001231 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1232 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1233 },
Chris Malley91a9c922006-10-03 10:08:28 +01001234 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1235 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1236 },
Oliver Neukum86478942006-05-13 22:50:47 +02001237 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1238 .driver_info = SINGLE_RX_URB, /* firmware bug */
1239 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001240 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1241 .driver_info = SINGLE_RX_URB, /* firmware bug */
1242 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001243 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1244 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1245 },
1246
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 /* control interfaces with various AT-command sets */
1248 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1249 USB_CDC_ACM_PROTO_AT_V25TER) },
1250 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1251 USB_CDC_ACM_PROTO_AT_PCCA101) },
1252 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1253 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1254 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1255 USB_CDC_ACM_PROTO_AT_GSM) },
1256 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1257 USB_CDC_ACM_PROTO_AT_3G ) },
1258 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1259 USB_CDC_ACM_PROTO_AT_CDMA) },
1260
1261 /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
1262 { }
1263};
1264
1265MODULE_DEVICE_TABLE (usb, acm_ids);
1266
1267static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 .name = "cdc_acm",
1269 .probe = acm_probe,
1270 .disconnect = acm_disconnect,
Oliver Neukum1365baf2007-10-12 17:24:28 +02001271 .suspend = acm_suspend,
1272 .resume = acm_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 .id_table = acm_ids,
Oliver Neukum1365baf2007-10-12 17:24:28 +02001274 .supports_autosuspend = 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275};
1276
1277/*
1278 * TTY driver structures.
1279 */
1280
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001281static const struct tty_operations acm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 .open = acm_tty_open,
1283 .close = acm_tty_close,
1284 .write = acm_tty_write,
1285 .write_room = acm_tty_write_room,
1286 .ioctl = acm_tty_ioctl,
1287 .throttle = acm_tty_throttle,
1288 .unthrottle = acm_tty_unthrottle,
1289 .chars_in_buffer = acm_tty_chars_in_buffer,
1290 .break_ctl = acm_tty_break_ctl,
1291 .set_termios = acm_tty_set_termios,
1292 .tiocmget = acm_tty_tiocmget,
1293 .tiocmset = acm_tty_tiocmset,
1294};
1295
1296/*
1297 * Init / exit.
1298 */
1299
1300static int __init acm_init(void)
1301{
1302 int retval;
1303 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1304 if (!acm_tty_driver)
1305 return -ENOMEM;
1306 acm_tty_driver->owner = THIS_MODULE,
1307 acm_tty_driver->driver_name = "acm",
1308 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 acm_tty_driver->major = ACM_TTY_MAJOR,
1310 acm_tty_driver->minor_start = 0,
1311 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1312 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001313 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 acm_tty_driver->init_termios = tty_std_termios;
1315 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
1316 tty_set_operations(acm_tty_driver, &acm_ops);
1317
1318 retval = tty_register_driver(acm_tty_driver);
1319 if (retval) {
1320 put_tty_driver(acm_tty_driver);
1321 return retval;
1322 }
1323
1324 retval = usb_register(&acm_driver);
1325 if (retval) {
1326 tty_unregister_driver(acm_tty_driver);
1327 put_tty_driver(acm_tty_driver);
1328 return retval;
1329 }
1330
1331 info(DRIVER_VERSION ":" DRIVER_DESC);
1332
1333 return 0;
1334}
1335
1336static void __exit acm_exit(void)
1337{
1338 usb_deregister(&acm_driver);
1339 tty_unregister_driver(acm_tty_driver);
1340 put_tty_driver(acm_tty_driver);
1341}
1342
1343module_init(acm_init);
1344module_exit(acm_exit);
1345
1346MODULE_AUTHOR( DRIVER_AUTHOR );
1347MODULE_DESCRIPTION( DRIVER_DESC );
1348MODULE_LICENSE("GPL");
1349