blob: 38bfdb0f666058061081e6035512843cb0eaa850 [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
Alan Cox6e47e062009-06-11 12:37:06 +010019 * v0.12 - added TIOCM ioctls, added break handling, made struct acm
20 * kmalloced
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 * v0.13 - added termios, added hangup
22 * v0.14 - sized down struct acm
23 * v0.15 - fixed flow control again - characters could be lost
24 * v0.16 - added code for modems with swapped data and control interfaces
25 * v0.17 - added new style probing
26 * v0.18 - fixed new style probing for devices with more configurations
27 * v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
28 * v0.20 - switched to probing on interface (rather than device) class
29 * v0.21 - revert to probing on device for devices with multiple configs
30 * v0.22 - probe only the control interface. if usbcore doesn't choose the
31 * config we want, sysadmin changes bConfigurationValue in sysfs.
32 * v0.23 - use softirq for rx processing, as needed by tty layer
33 * v0.24 - change probe method to evaluate CDC union descriptor
David Kubicek61a87ad2005-11-01 18:51:34 +010034 * v0.25 - downstream tasks paralelized to maximize throughput
David Engrafe4cf3aa2008-03-20 10:01:34 +010035 * v0.26 - multiple write urbs, writesize increased
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 */
37
38/*
39 * This program is free software; you can redistribute it and/or modify
40 * it under the terms of the GNU General Public License as published by
41 * the Free Software Foundation; either version 2 of the License, or
42 * (at your option) any later version.
43 *
44 * This program is distributed in the hope that it will be useful,
45 * but WITHOUT ANY WARRANTY; without even the implied warranty of
46 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 * GNU General Public License for more details.
48 *
49 * You should have received a copy of the GNU General Public License
50 * along with this program; if not, write to the Free Software
51 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
52 */
53
54#undef DEBUG
David Brownelle5fbab52008-08-06 18:46:10 -070055#undef VERBOSE_DEBUG
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
57#include <linux/kernel.h>
58#include <linux/errno.h>
59#include <linux/init.h>
60#include <linux/slab.h>
61#include <linux/tty.h>
62#include <linux/tty_driver.h>
63#include <linux/tty_flip.h>
64#include <linux/module.h>
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010065#include <linux/mutex.h>
Alan Cox10077d42009-06-11 12:36:09 +010066#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070067#include <linux/usb.h>
David Brownella8c28f22006-06-13 09:57:47 -070068#include <linux/usb/cdc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070069#include <asm/byteorder.h>
70#include <asm/unaligned.h>
David Kubicek61a87ad2005-11-01 18:51:34 +010071#include <linux/list.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070072
73#include "cdc-acm.h"
74
David Brownelle5fbab52008-08-06 18:46:10 -070075
76#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078/*
79 * Version Information
80 */
David Engrafe4cf3aa2008-03-20 10:01:34 +010081#define DRIVER_VERSION "v0.26"
David Kubicek61a87ad2005-11-01 18:51:34 +010082#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
Linus Torvalds1da177e2005-04-16 15:20:36 -070083#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
84
85static struct usb_driver acm_driver;
86static struct tty_driver *acm_tty_driver;
87static struct acm *acm_table[ACM_TTY_MINORS];
88
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010089static DEFINE_MUTEX(open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
Alan Cox10077d42009-06-11 12:36:09 +010091#define ACM_READY(acm) (acm && acm->dev && acm->port.count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
Alan Cox739e0282009-06-11 12:27:50 +010093static const struct tty_port_operations acm_port_ops = {
94};
95
David Brownelle5fbab52008-08-06 18:46:10 -070096#ifdef VERBOSE_DEBUG
97#define verbose 1
98#else
99#define verbose 0
100#endif
101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102/*
103 * Functions for ACM control messages.
104 */
105
Alan Cox6e47e062009-06-11 12:37:06 +0100106static int acm_ctrl_msg(struct acm *acm, int request, int value,
107 void *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108{
109 int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
110 request, USB_RT_ACM, value,
111 acm->control->altsetting[0].desc.bInterfaceNumber,
112 buf, len, 5000);
Alan Cox6e47e062009-06-11 12:37:06 +0100113 dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d",
114 request, value, len, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 return retval < 0 ? retval : 0;
116}
117
118/* devices aren't required to support these requests.
119 * the cdc acm descriptor tells whether they do...
120 */
121#define acm_set_control(acm, control) \
122 acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
123#define acm_set_line(acm, line) \
124 acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
125#define acm_send_break(acm, ms) \
126 acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
127
128/*
Oliver Neukum884b6002005-04-21 21:28:02 +0200129 * Write buffer management.
130 * All of these assume proper locks taken by the caller.
131 */
132
133static int acm_wb_alloc(struct acm *acm)
134{
135 int i, wbn;
136 struct acm_wb *wb;
137
David Engrafe4cf3aa2008-03-20 10:01:34 +0100138 wbn = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200139 i = 0;
140 for (;;) {
141 wb = &acm->wb[wbn];
142 if (!wb->use) {
143 wb->use = 1;
144 return wbn;
145 }
Oliver Neukum86478942006-05-13 22:50:47 +0200146 wbn = (wbn + 1) % ACM_NW;
147 if (++i >= ACM_NW)
Oliver Neukum884b6002005-04-21 21:28:02 +0200148 return -1;
149 }
150}
151
Oliver Neukum884b6002005-04-21 21:28:02 +0200152static int acm_wb_is_avail(struct acm *acm)
153{
154 int i, n;
David Brownelle5fbab52008-08-06 18:46:10 -0700155 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200156
Oliver Neukum86478942006-05-13 22:50:47 +0200157 n = ACM_NW;
David Brownelle5fbab52008-08-06 18:46:10 -0700158 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100159 for (i = 0; i < ACM_NW; i++)
Oliver Neukum86478942006-05-13 22:50:47 +0200160 n -= acm->wb[i].use;
David Brownelle5fbab52008-08-06 18:46:10 -0700161 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200162 return n;
163}
164
Oliver Neukum884b6002005-04-21 21:28:02 +0200165/*
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800166 * Finish write. Caller must hold acm->write_lock
Oliver Neukum884b6002005-04-21 21:28:02 +0200167 */
David Engrafe4cf3aa2008-03-20 10:01:34 +0100168static void acm_write_done(struct acm *acm, struct acm_wb *wb)
Oliver Neukum884b6002005-04-21 21:28:02 +0200169{
David Engrafe4cf3aa2008-03-20 10:01:34 +0100170 wb->use = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200171 acm->transmitting--;
Oliver Neukum884b6002005-04-21 21:28:02 +0200172}
173
174/*
175 * Poke write.
Oliver Neukum11ea8592008-06-20 11:25:57 +0200176 *
177 * the caller is responsible for locking
Oliver Neukum884b6002005-04-21 21:28:02 +0200178 */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200179
180static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
181{
182 int rc;
183
184 acm->transmitting++;
185
186 wb->urb->transfer_buffer = wb->buf;
187 wb->urb->transfer_dma = wb->dmah;
188 wb->urb->transfer_buffer_length = wb->len;
189 wb->urb->dev = acm->dev;
190
Alan Cox6e47e062009-06-11 12:37:06 +0100191 rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
192 if (rc < 0) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200193 dbg("usb_submit_urb(write bulk) failed: %d", rc);
194 acm_write_done(acm, wb);
195 }
196 return rc;
197}
198
David Engrafe4cf3aa2008-03-20 10:01:34 +0100199static int acm_write_start(struct acm *acm, int wbn)
Oliver Neukum884b6002005-04-21 21:28:02 +0200200{
201 unsigned long flags;
David Brownell934da462008-08-06 18:44:12 -0700202 struct acm_wb *wb = &acm->wb[wbn];
Oliver Neukum884b6002005-04-21 21:28:02 +0200203 int rc;
204
205 spin_lock_irqsave(&acm->write_lock, flags);
206 if (!acm->dev) {
David Brownell934da462008-08-06 18:44:12 -0700207 wb->use = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200208 spin_unlock_irqrestore(&acm->write_lock, flags);
209 return -ENODEV;
210 }
211
Oliver Neukum11ea8592008-06-20 11:25:57 +0200212 dbg("%s susp_count: %d", __func__, acm->susp_count);
213 if (acm->susp_count) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200214 acm->delayed_wb = wb;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200215 schedule_work(&acm->waker);
216 spin_unlock_irqrestore(&acm->write_lock, flags);
217 return 0; /* A white lie */
218 }
219 usb_mark_last_busy(acm->dev);
220
Oliver Neukum11ea8592008-06-20 11:25:57 +0200221 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200222 spin_unlock_irqrestore(&acm->write_lock, flags);
223
Oliver Neukum884b6002005-04-21 21:28:02 +0200224 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200225
Oliver Neukum884b6002005-04-21 21:28:02 +0200226}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100227/*
228 * attributes exported through sysfs
229 */
230static ssize_t show_caps
231(struct device *dev, struct device_attribute *attr, char *buf)
232{
233 struct usb_interface *intf = to_usb_interface(dev);
234 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200235
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100236 return sprintf(buf, "%d", acm->ctrl_caps);
237}
238static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
239
240static ssize_t show_country_codes
241(struct device *dev, struct device_attribute *attr, char *buf)
242{
243 struct usb_interface *intf = to_usb_interface(dev);
244 struct acm *acm = usb_get_intfdata(intf);
245
246 memcpy(buf, acm->country_codes, acm->country_code_size);
247 return acm->country_code_size;
248}
249
250static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
251
252static ssize_t show_country_rel_date
253(struct device *dev, struct device_attribute *attr, char *buf)
254{
255 struct usb_interface *intf = to_usb_interface(dev);
256 struct acm *acm = usb_get_intfdata(intf);
257
258 return sprintf(buf, "%d", acm->country_rel_date);
259}
260
261static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200262/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 * Interrupt handlers for various ACM device responses
264 */
265
266/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100267static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
269 struct acm *acm = urb->context;
270 struct usb_cdc_notification *dr = urb->transfer_buffer;
Alan Cox10077d42009-06-11 12:36:09 +0100271 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 unsigned char *data;
273 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700274 int retval;
275 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700277 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 case 0:
279 /* success */
280 break;
281 case -ECONNRESET:
282 case -ENOENT:
283 case -ESHUTDOWN:
284 /* this urb is terminated, clean up */
Harvey Harrison441b62c2008-03-03 16:08:34 -0800285 dbg("%s - urb shutting down with status: %d", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 return;
287 default:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800288 dbg("%s - nonzero urb status received: %d", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 goto exit;
290 }
291
292 if (!ACM_READY(acm))
293 goto exit;
294
295 data = (unsigned char *)(dr + 1);
296 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100297 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
298 dbg("%s network", dr->wValue ?
299 "connected to" : "disconnected from");
300 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
Alan Cox6e47e062009-06-11 12:37:06 +0100302 case USB_CDC_NOTIFY_SERIAL_STATE:
303 tty = tty_port_tty_get(&acm->port);
304 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
Alan Cox6e47e062009-06-11 12:37:06 +0100306 if (tty) {
307 if (!acm->clocal &&
308 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
309 dbg("calling hangup");
310 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 }
Alan Cox6e47e062009-06-11 12:37:06 +0100312 tty_kref_put(tty);
313 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
Alan Cox6e47e062009-06-11 12:37:06 +0100315 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
Alan Cox6e47e062009-06-11 12:37:06 +0100317 dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
318 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
319 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
320 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
321 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
322 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
323 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
324 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 break;
326
Alan Cox6e47e062009-06-11 12:37:06 +0100327 default:
328 dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
329 dr->bNotificationType, dr->wIndex,
330 dr->wLength, data[0], data[1]);
331 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 }
333exit:
Oliver Neukum11ea8592008-06-20 11:25:57 +0200334 usb_mark_last_busy(acm->dev);
Alan Cox6e47e062009-06-11 12:37:06 +0100335 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700336 if (retval)
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700337 dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
338 "result %d", __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339}
340
341/* data interface returns incoming bytes, or we got unthrottled */
David Howells7d12e782006-10-05 14:55:46 +0100342static void acm_read_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343{
David Kubicek61a87ad2005-11-01 18:51:34 +0100344 struct acm_rb *buf;
345 struct acm_ru *rcv = urb->context;
346 struct acm *acm = rcv->instance;
Oliver Neukum86478942006-05-13 22:50:47 +0200347 int status = urb->status;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700348
349 dbg("Entering acm_read_bulk with status %d", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
Oliver Neukum11ea8592008-06-20 11:25:57 +0200351 if (!ACM_READY(acm)) {
352 dev_dbg(&acm->data->dev, "Aborting, acm not ready");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200354 }
355 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
Oliver Neukum86478942006-05-13 22:50:47 +0200357 if (status)
Joe Perches898eb712007-10-18 03:06:30 -0700358 dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
David Kubicek61a87ad2005-11-01 18:51:34 +0100360 buf = rcv->buffer;
361 buf->size = urb->actual_length;
362
Oliver Neukum86478942006-05-13 22:50:47 +0200363 if (likely(status == 0)) {
364 spin_lock(&acm->read_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200365 acm->processing++;
Oliver Neukum86478942006-05-13 22:50:47 +0200366 list_add_tail(&rcv->list, &acm->spare_read_urbs);
367 list_add_tail(&buf->list, &acm->filled_read_bufs);
368 spin_unlock(&acm->read_lock);
369 } else {
370 /* we drop the buffer due to an error */
371 spin_lock(&acm->read_lock);
372 list_add_tail(&rcv->list, &acm->spare_read_urbs);
373 list_add(&buf->list, &acm->spare_read_bufs);
374 spin_unlock(&acm->read_lock);
375 /* nevertheless the tasklet must be kicked unconditionally
376 so the queue cannot dry up */
377 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200378 if (likely(!acm->susp_count))
379 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380}
381
382static void acm_rx_tasklet(unsigned long _acm)
383{
384 struct acm *acm = (void *)_acm;
David Kubicek61a87ad2005-11-01 18:51:34 +0100385 struct acm_rb *buf;
Alan Cox10077d42009-06-11 12:36:09 +0100386 struct tty_struct *tty;
David Kubicek61a87ad2005-11-01 18:51:34 +0100387 struct acm_ru *rcv;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200388 unsigned long flags;
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100389 unsigned char throttled;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200390
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 dbg("Entering acm_rx_tasklet");
392
Alan Cox10077d42009-06-11 12:36:09 +0100393 if (!ACM_READY(acm)) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200394 dbg("acm_rx_tasklet: ACM not ready");
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100395 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200396 }
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100397
Oliver Neukum834dbca2007-03-06 10:47:04 +0100398 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100399 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100400 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Alan Cox10077d42009-06-11 12:36:09 +0100401 if (throttled) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200402 dbg("acm_rx_tasklet: throttled");
David Kubicek61a87ad2005-11-01 18:51:34 +0100403 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200404 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100405
Alan Cox10077d42009-06-11 12:36:09 +0100406 tty = tty_port_tty_get(&acm->port);
407
David Kubicek61a87ad2005-11-01 18:51:34 +0100408next_buffer:
Jarek Poplawski762f0072006-10-06 07:23:11 +0200409 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100410 if (list_empty(&acm->filled_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200411 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100412 goto urbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100414 buf = list_entry(acm->filled_read_bufs.next,
415 struct acm_rb, list);
416 list_del(&buf->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200417 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100418
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200419 dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
David Kubicek61a87ad2005-11-01 18:51:34 +0100420
Alan Cox10077d42009-06-11 12:36:09 +0100421 if (tty) {
422 spin_lock_irqsave(&acm->throttle_lock, flags);
423 throttled = acm->throttle;
424 spin_unlock_irqrestore(&acm->throttle_lock, flags);
425 if (!throttled) {
426 tty_buffer_request_room(tty, buf->size);
427 tty_insert_flip_string(tty, buf->base, buf->size);
428 tty_flip_buffer_push(tty);
429 } else {
430 tty_kref_put(tty);
431 dbg("Throttling noticed");
432 spin_lock_irqsave(&acm->read_lock, flags);
433 list_add(&buf->list, &acm->filled_read_bufs);
434 spin_unlock_irqrestore(&acm->read_lock, flags);
435 return;
436 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
Jarek Poplawski762f0072006-10-06 07:23:11 +0200439 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100440 list_add(&buf->list, &acm->spare_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200441 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100442 goto next_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443
David Kubicek61a87ad2005-11-01 18:51:34 +0100444urbs:
Alan Cox10077d42009-06-11 12:36:09 +0100445 tty_kref_put(tty);
446
David Kubicek61a87ad2005-11-01 18:51:34 +0100447 while (!list_empty(&acm->spare_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200448 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100449 if (list_empty(&acm->spare_read_urbs)) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200450 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200451 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100452 return;
453 }
454 rcv = list_entry(acm->spare_read_urbs.next,
455 struct acm_ru, list);
456 list_del(&rcv->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200457 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100458
459 buf = list_entry(acm->spare_read_bufs.next,
460 struct acm_rb, list);
461 list_del(&buf->list);
462
463 rcv->buffer = buf;
464
465 usb_fill_bulk_urb(rcv->urb, acm->dev,
466 acm->rx_endpoint,
467 buf->base,
468 acm->readsize,
469 acm_read_bulk, rcv);
470 rcv->urb->transfer_dma = buf->dma;
471 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
472
Alan Cox6e47e062009-06-11 12:37:06 +0100473 /* This shouldn't kill the driver as unsuccessful URBs are
474 returned to the free-urbs-pool and resubmited ASAP */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200475 spin_lock_irqsave(&acm->read_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100476 if (acm->susp_count ||
477 usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100478 list_add(&buf->list, &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100479 list_add(&rcv->list, &acm->spare_read_urbs);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200480 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200481 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100482 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200483 } else {
484 spin_unlock_irqrestore(&acm->read_lock, flags);
485 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 +0100486 }
487 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200488 spin_lock_irqsave(&acm->read_lock, flags);
489 acm->processing = 0;
490 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491}
492
493/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100494static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495{
Ming Leicdc97792008-02-24 18:41:47 +0800496 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700497 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800498 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200499
David Brownelle5fbab52008-08-06 18:46:10 -0700500 if (verbose || urb->status
501 || (urb->actual_length != urb->transfer_buffer_length))
502 dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
503 urb->actual_length,
504 urb->transfer_buffer_length,
505 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800507 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100508 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800509 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200510 if (ACM_READY(acm))
511 schedule_work(&acm->work);
David Brownelle5fbab52008-08-06 18:46:10 -0700512 else
513 wake_up_interruptible(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514}
515
David Howellsc4028952006-11-22 14:57:56 +0000516static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517{
David Howellsc4028952006-11-22 14:57:56 +0000518 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100519 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700520
521 dev_vdbg(&acm->data->dev, "tx work\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 if (!ACM_READY(acm))
523 return;
Alan Cox10077d42009-06-11 12:36:09 +0100524 tty = tty_port_tty_get(&acm->port);
525 tty_wakeup(tty);
526 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527}
528
Oliver Neukum11ea8592008-06-20 11:25:57 +0200529static void acm_waker(struct work_struct *waker)
530{
531 struct acm *acm = container_of(waker, struct acm, waker);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200532 int rv;
533
534 rv = usb_autopm_get_interface(acm->control);
535 if (rv < 0) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700536 dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200537 return;
538 }
539 if (acm->delayed_wb) {
540 acm_start_wb(acm, acm->delayed_wb);
541 acm->delayed_wb = NULL;
542 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200543 usb_autopm_put_interface(acm->control);
544}
545
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546/*
547 * TTY handlers
548 */
549
550static int acm_tty_open(struct tty_struct *tty, struct file *filp)
551{
552 struct acm *acm;
553 int rv = -EINVAL;
David Kubicek61a87ad2005-11-01 18:51:34 +0100554 int i;
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200555 dbg("Entering acm_tty_open.");
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100556
557 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
559 acm = acm_table[tty->index];
560 if (!acm || !acm->dev)
561 goto err_out;
562 else
563 rv = 0;
564
David Engraf28d1dfa2008-03-20 10:53:52 +0100565 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
Alan Cox10077d42009-06-11 12:36:09 +0100566
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 tty->driver_data = acm;
Alan Cox10077d42009-06-11 12:36:09 +0100568 tty_port_tty_set(&acm->port, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
Oliver Neukum94409cc2008-02-11 15:22:29 +0100570 if (usb_autopm_get_interface(acm->control) < 0)
571 goto early_bail;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200572 else
573 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200574
575 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100576 if (acm->port.count++) {
Oliver Neukum1365baf2007-10-12 17:24:28 +0200577 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 goto done;
Alan Cox10077d42009-06-11 12:36:09 +0100579 }
Oliver Neukum1365baf2007-10-12 17:24:28 +0200580
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 acm->ctrlurb->dev = acm->dev;
582 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
583 dbg("usb_submit_urb(ctrl irq) failed");
584 goto bail_out;
585 }
586
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100587 if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
588 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 goto full_bailout;
Alan Cox10077d42009-06-11 12:36:09 +0100590
Oliver Neukum11ea8592008-06-20 11:25:57 +0200591 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
David Kubicek61a87ad2005-11-01 18:51:34 +0100593 INIT_LIST_HEAD(&acm->spare_read_urbs);
594 INIT_LIST_HEAD(&acm->spare_read_bufs);
595 INIT_LIST_HEAD(&acm->filled_read_bufs);
Alan Cox6e47e062009-06-11 12:37:06 +0100596
597 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100598 list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
Alan Cox6e47e062009-06-11 12:37:06 +0100599 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100600 list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100601
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100602 acm->throttle = 0;
603
David Kubicek61a87ad2005-11-01 18:51:34 +0100604 tasklet_schedule(&acm->urb_task);
Alan Cox10077d42009-06-11 12:36:09 +0100605 rv = tty_port_block_til_ready(&acm->port, tty, filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606done:
Oliver Neukum1365baf2007-10-12 17:24:28 +0200607 mutex_unlock(&acm->mutex);
Alexey Dobriyan74573ee2008-08-20 16:56:04 -0700608err_out:
Oliver Neukum94409cc2008-02-11 15:22:29 +0100609 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 return rv;
611
612full_bailout:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 usb_kill_urb(acm->ctrlurb);
614bail_out:
Oliver Neukum1365baf2007-10-12 17:24:28 +0200615 usb_autopm_put_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100616 acm->port.count--;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200617 mutex_unlock(&acm->mutex);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100618early_bail:
619 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100620 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 return -EIO;
622}
623
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700624static void acm_tty_unregister(struct acm *acm)
625{
Alan Cox10077d42009-06-11 12:36:09 +0100626 int i, nr;
David Kubicek61a87ad2005-11-01 18:51:34 +0100627
Oliver Neukum86478942006-05-13 22:50:47 +0200628 nr = acm->rx_buflimit;
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700629 tty_unregister_device(acm_tty_driver, acm->minor);
630 usb_put_intf(acm->control);
631 acm_table[acm->minor] = NULL;
632 usb_free_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100633 for (i = 0; i < ACM_NW; i++)
634 usb_free_urb(acm->wb[i].urb);
Oliver Neukum86478942006-05-13 22:50:47 +0200635 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100636 usb_free_urb(acm->ru[i].urb);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100637 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700638 kfree(acm);
639}
640
David Brownelle5fbab52008-08-06 18:46:10 -0700641static int acm_tty_chars_in_buffer(struct tty_struct *tty);
642
Alan Cox10077d42009-06-11 12:36:09 +0100643static void acm_port_down(struct acm *acm, int drain)
644{
645 int i, nr = acm->rx_buflimit;
646 mutex_lock(&open_mutex);
647 if (acm->dev) {
648 usb_autopm_get_interface(acm->control);
649 acm_set_control(acm, acm->ctrlout = 0);
650 /* try letting the last writes drain naturally */
651 if (drain) {
652 wait_event_interruptible_timeout(acm->drain_wait,
653 (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
654 ACM_CLOSE_TIMEOUT * HZ);
655 }
656 usb_kill_urb(acm->ctrlurb);
657 for (i = 0; i < ACM_NW; i++)
658 usb_kill_urb(acm->wb[i].urb);
659 for (i = 0; i < nr; i++)
660 usb_kill_urb(acm->ru[i].urb);
661 acm->control->needs_remote_wakeup = 0;
662 usb_autopm_put_interface(acm->control);
663 }
664 mutex_unlock(&open_mutex);
665}
666
667static void acm_tty_hangup(struct tty_struct *tty)
668{
669 struct acm *acm = tty->driver_data;
670 tty_port_hangup(&acm->port);
671 acm_port_down(acm, 0);
672}
673
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674static void acm_tty_close(struct tty_struct *tty, struct file *filp)
675{
676 struct acm *acm = tty->driver_data;
677
Alan Cox10077d42009-06-11 12:36:09 +0100678 /* Perform the closing process and see if we need to do the hardware
679 shutdown */
680 if (tty_port_close_start(&acm->port, tty, filp) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 return;
Alan Cox10077d42009-06-11 12:36:09 +0100682 acm_port_down(acm, 0);
683 tty_port_close_end(&acm->port, tty);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100684 mutex_lock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100685 tty_port_tty_set(&acm->port, NULL);
686 if (!acm->dev)
687 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100688 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689}
690
Alan Cox6e47e062009-06-11 12:37:06 +0100691static int acm_tty_write(struct tty_struct *tty,
692 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693{
694 struct acm *acm = tty->driver_data;
695 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200696 unsigned long flags;
697 int wbn;
698 struct acm_wb *wb;
699
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200700 dbg("Entering acm_tty_write to write %d bytes,", count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
702 if (!ACM_READY(acm))
703 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 if (!count)
705 return 0;
706
Oliver Neukum884b6002005-04-21 21:28:02 +0200707 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100708 wbn = acm_wb_alloc(acm);
709 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200710 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200711 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200713 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Oliver Neukum884b6002005-04-21 21:28:02 +0200715 count = (count > acm->writesize) ? acm->writesize : count;
716 dbg("Get %d bytes...", count);
717 memcpy(wb->buf, buf, count);
718 wb->len = count;
719 spin_unlock_irqrestore(&acm->write_lock, flags);
720
Alan Cox6e47e062009-06-11 12:37:06 +0100721 stat = acm_write_start(acm, wbn);
722 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200723 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 return count;
725}
726
727static int acm_tty_write_room(struct tty_struct *tty)
728{
729 struct acm *acm = tty->driver_data;
730 if (!ACM_READY(acm))
731 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200732 /*
733 * Do not let the line discipline to know that we have a reserve,
734 * or it might get too enthusiastic.
735 */
David Brownell934da462008-08-06 18:44:12 -0700736 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737}
738
739static int acm_tty_chars_in_buffer(struct tty_struct *tty)
740{
741 struct acm *acm = tty->driver_data;
742 if (!ACM_READY(acm))
743 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200744 /*
745 * This is inaccurate (overcounts), but it works.
746 */
Oliver Neukum86478942006-05-13 22:50:47 +0200747 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748}
749
750static void acm_tty_throttle(struct tty_struct *tty)
751{
752 struct acm *acm = tty->driver_data;
753 if (!ACM_READY(acm))
754 return;
755 spin_lock_bh(&acm->throttle_lock);
756 acm->throttle = 1;
757 spin_unlock_bh(&acm->throttle_lock);
758}
759
760static void acm_tty_unthrottle(struct tty_struct *tty)
761{
762 struct acm *acm = tty->driver_data;
763 if (!ACM_READY(acm))
764 return;
765 spin_lock_bh(&acm->throttle_lock);
766 acm->throttle = 0;
767 spin_unlock_bh(&acm->throttle_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100768 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769}
770
Alan Cox9e989662008-07-22 11:18:03 +0100771static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772{
773 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100774 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 if (!ACM_READY(acm))
Alan Cox9e989662008-07-22 11:18:03 +0100776 return -EINVAL;
777 retval = acm_send_break(acm, state ? 0xffff : 0);
778 if (retval < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 dbg("send break failed");
Alan Cox9e989662008-07-22 11:18:03 +0100780 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781}
782
783static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
784{
785 struct acm *acm = tty->driver_data;
786
787 if (!ACM_READY(acm))
788 return -EINVAL;
789
790 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
791 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
792 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
793 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
794 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
795 TIOCM_CTS;
796}
797
798static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
799 unsigned int set, unsigned int clear)
800{
801 struct acm *acm = tty->driver_data;
802 unsigned int newctrl;
803
804 if (!ACM_READY(acm))
805 return -EINVAL;
806
807 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100808 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
809 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
810 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
811 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
813 newctrl = (newctrl & ~clear) | set;
814
815 if (acm->ctrlout == newctrl)
816 return 0;
817 return acm_set_control(acm, acm->ctrlout = newctrl);
818}
819
Alan Cox6e47e062009-06-11 12:37:06 +0100820static int acm_tty_ioctl(struct tty_struct *tty, struct file *file,
821 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822{
823 struct acm *acm = tty->driver_data;
824
825 if (!ACM_READY(acm))
826 return -EINVAL;
827
828 return -ENOIOCTLCMD;
829}
830
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100831static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 0, 50, 75, 110, 134, 150, 200, 300, 600,
833 1200, 1800, 2400, 4800, 9600, 19200, 38400,
834 57600, 115200, 230400, 460800, 500000, 576000,
835 921600, 1000000, 1152000, 1500000, 2000000,
836 2500000, 3000000, 3500000, 4000000
837};
838
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100839static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 5, 6, 7, 8
841};
842
Alan Cox6e47e062009-06-11 12:37:06 +0100843static void acm_tty_set_termios(struct tty_struct *tty,
844 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845{
846 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800847 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 struct usb_cdc_line_coding newline;
849 int newctrl = acm->ctrlout;
850
851 if (!ACM_READY(acm))
852 return;
853
Alan Cox6e47e062009-06-11 12:37:06 +0100854 /* FIXME: Needs to support the tty_baud interface */
855 /* FIXME: Broken on sparc */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
857 (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
858 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
859 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100860 (termios->c_cflag & PARODD ? 1 : 2) +
861 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
Alan Cox6e47e062009-06-11 12:37:06 +0100863 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
865
866 if (!newline.dwDTERate) {
867 newline.dwDTERate = acm->line.dwDTERate;
868 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +0100869 } else
870 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871
872 if (newctrl != acm->ctrlout)
873 acm_set_control(acm, acm->ctrlout = newctrl);
874
875 if (memcmp(&acm->line, &newline, sizeof newline)) {
876 memcpy(&acm->line, &newline, sizeof newline);
877 dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
878 newline.bCharFormat, newline.bParityType,
879 newline.bDataBits);
880 acm_set_line(acm, &acm->line);
881 }
882}
883
884/*
885 * USB probe and disconnect routines.
886 */
887
Oliver Neukum830f4022008-06-25 14:17:16 +0200888/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +0200889static void acm_write_buffers_free(struct acm *acm)
890{
891 int i;
892 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +0200893 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200894
Alan Cox6e47e062009-06-11 12:37:06 +0100895 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Oliver Neukuma496c642008-10-21 10:39:04 +0200896 usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +0200897}
898
Oliver Neukum830f4022008-06-25 14:17:16 +0200899static void acm_read_buffers_free(struct acm *acm)
900{
901 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
902 int i, n = acm->rx_buflimit;
903
904 for (i = 0; i < n; i++)
Alan Cox6e47e062009-06-11 12:37:06 +0100905 usb_buffer_free(usb_dev, acm->readsize,
906 acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +0200907}
908
Oliver Neukum884b6002005-04-21 21:28:02 +0200909/* Little helper: write buffers allocate */
910static int acm_write_buffers_alloc(struct acm *acm)
911{
912 int i;
913 struct acm_wb *wb;
914
Oliver Neukum86478942006-05-13 22:50:47 +0200915 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200916 wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
917 &wb->dmah);
918 if (!wb->buf) {
919 while (i != 0) {
920 --i;
921 --wb;
922 usb_buffer_free(acm->dev, acm->writesize,
923 wb->buf, wb->dmah);
924 }
925 return -ENOMEM;
926 }
927 }
928 return 0;
929}
930
Alan Cox10077d42009-06-11 12:36:09 +0100931static int acm_probe(struct usb_interface *intf,
932 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933{
934 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100935 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -0700936 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 int buflen = intf->altsetting->extralen;
938 struct usb_interface *control_interface;
939 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200940 struct usb_endpoint_descriptor *epctrl = NULL;
941 struct usb_endpoint_descriptor *epread = NULL;
942 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 struct usb_device *usb_dev = interface_to_usbdev(intf);
944 struct acm *acm;
945 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +0100946 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 u8 *buf;
948 u8 ac_management_function = 0;
949 u8 call_management_function = 0;
950 int call_interface_num = -1;
951 int data_interface_num;
952 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200953 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100954 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200955 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956
Oliver Neukum86478942006-05-13 22:50:47 +0200957 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200959 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
960
961 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 if (quirks == NO_UNION_NORMAL) {
963 data_interface = usb_ifnum_to_if(usb_dev, 1);
964 control_interface = usb_ifnum_to_if(usb_dev, 0);
965 goto skip_normal_probe;
966 }
Alan Cox6e47e062009-06-11 12:37:06 +0100967
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 /* normal probing*/
969 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700970 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 return -EINVAL;
972 }
973
974 if (!buflen) {
Alan Cox6e47e062009-06-11 12:37:06 +0100975 if (intf->cur_altsetting->endpoint->extralen &&
976 intf->cur_altsetting->endpoint->extra) {
977 dev_dbg(&intf->dev,
978 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 buflen = intf->cur_altsetting->endpoint->extralen;
980 buffer = intf->cur_altsetting->endpoint->extra;
981 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700982 dev_err(&intf->dev,
983 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 return -EINVAL;
985 }
986 }
987
988 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +0100989 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700990 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 goto next_desc;
992 }
993
Alan Cox6e47e062009-06-11 12:37:06 +0100994 switch (buffer[2]) {
995 case USB_CDC_UNION_TYPE: /* we've found it */
996 if (union_header) {
997 dev_err(&intf->dev, "More than one "
998 "union descriptor, skipping ...\n");
999 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 }
Alan Cox6e47e062009-06-11 12:37:06 +01001001 union_header = (struct usb_cdc_union_desc *)buffer;
1002 break;
1003 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
1004 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1005 break;
1006 case USB_CDC_HEADER_TYPE: /* maybe check version */
1007 break; /* for now we ignore it */
1008 case USB_CDC_ACM_TYPE:
1009 ac_management_function = buffer[3];
1010 break;
1011 case USB_CDC_CALL_MANAGEMENT_TYPE:
1012 call_management_function = buffer[3];
1013 call_interface_num = buffer[4];
1014 if ((call_management_function & 3) != 3)
1015 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1016 break;
1017 default:
1018 /* there are LOTS more CDC descriptors that
1019 * could legitimately be found here.
1020 */
1021 dev_dbg(&intf->dev, "Ignoring descriptor: "
1022 "type %02x, length %d\n",
1023 buffer[2], buffer[0]);
1024 break;
1025 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026next_desc:
1027 buflen -= buffer[0];
1028 buffer += buffer[0];
1029 }
1030
1031 if (!union_header) {
1032 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001033 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
1035 control_interface = intf;
1036 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001037 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1038 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1039 return -ENODEV;
1040 } else {
1041 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1042 combined_interfaces = 1;
1043 control_interface = data_interface = intf;
1044 goto look_for_collapsed_interface;
1045 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 }
1047 } else {
1048 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1049 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1050 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001051 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 return -ENODEV;
1053 }
1054 }
Alan Cox6e47e062009-06-11 12:37:06 +01001055
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001057 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001059 if (control_interface == data_interface) {
1060 /* some broken devices designed for windows work this way */
1061 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1062 combined_interfaces = 1;
1063 /* a popular other OS doesn't use it */
1064 quirks |= NO_CAP_LINE;
1065 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1066 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1067 return -EINVAL;
1068 }
1069look_for_collapsed_interface:
1070 for (i = 0; i < 3; i++) {
1071 struct usb_endpoint_descriptor *ep;
1072 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1073
1074 if (usb_endpoint_is_int_in(ep))
1075 epctrl = ep;
1076 else if (usb_endpoint_is_bulk_out(ep))
1077 epwrite = ep;
1078 else if (usb_endpoint_is_bulk_in(ep))
1079 epread = ep;
1080 else
1081 return -EINVAL;
1082 }
1083 if (!epctrl || !epread || !epwrite)
1084 return -ENODEV;
1085 else
1086 goto made_compressed_probe;
1087 }
1088
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089skip_normal_probe:
1090
1091 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001092 if (data_interface->cur_altsetting->desc.bInterfaceClass
1093 != CDC_DATA_INTERFACE_TYPE) {
1094 if (control_interface->cur_altsetting->desc.bInterfaceClass
1095 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001097 dev_dbg(&intf->dev,
1098 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 t = control_interface;
1100 control_interface = data_interface;
1101 data_interface = t;
1102 } else {
1103 return -EINVAL;
1104 }
1105 }
Alan Stern74da5d62007-08-02 13:29:10 -04001106
1107 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001108 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001109 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001110
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001111 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1112 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001113 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 return -EBUSY;
1115 }
1116
1117
1118 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
1119 return -EINVAL;
1120
1121 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1122 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1123 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1124
1125
1126 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001127 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 /* descriptors are swapped */
1129 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001130 dev_dbg(&intf->dev,
1131 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 t = epread;
1133 epread = epwrite;
1134 epwrite = t;
1135 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001136made_compressed_probe:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 dbg("interfaces are valid");
1138 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
1139
1140 if (minor == ACM_TTY_MINORS) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001141 dev_err(&intf->dev, "no more free acm devices\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 return -ENODEV;
1143 }
1144
Alan Cox6e47e062009-06-11 12:37:06 +01001145 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1146 if (acm == NULL) {
Joe Perches898eb712007-10-18 03:06:30 -07001147 dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 goto alloc_fail;
1149 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150
1151 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Alan Cox6e47e062009-06-11 12:37:06 +01001152 readsize = le16_to_cpu(epread->wMaxPacketSize) *
1153 (quirks == SINGLE_RX_URB ? 1 : 2);
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001154 acm->combined_interfaces = combined_interfaces;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001155 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 acm->control = control_interface;
1157 acm->data = data_interface;
1158 acm->minor = minor;
1159 acm->dev = usb_dev;
1160 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001161 if (quirks & NO_CAP_LINE)
1162 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 acm->ctrlsize = ctrlsize;
1164 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001165 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001166 acm->urb_task.func = acm_rx_tasklet;
1167 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +00001168 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001169 INIT_WORK(&acm->waker, acm_waker);
David Brownelle5fbab52008-08-06 18:46:10 -07001170 init_waitqueue_head(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +02001172 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001173 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001174 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001175 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Alan Cox739e0282009-06-11 12:27:50 +01001176 tty_port_init(&acm->port);
1177 acm->port.ops = &acm_port_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178
1179 buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
1180 if (!buf) {
Joe Perches898eb712007-10-18 03:06:30 -07001181 dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 goto alloc_fail2;
1183 }
1184 acm->ctrl_buffer = buf;
1185
Oliver Neukum884b6002005-04-21 21:28:02 +02001186 if (acm_write_buffers_alloc(acm) < 0) {
Joe Perches898eb712007-10-18 03:06:30 -07001187 dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 goto alloc_fail4;
1189 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190
1191 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1192 if (!acm->ctrlurb) {
Joe Perches898eb712007-10-18 03:06:30 -07001193 dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 goto alloc_fail5;
1195 }
Oliver Neukum86478942006-05-13 22:50:47 +02001196 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001197 struct acm_ru *rcv = &(acm->ru[i]);
1198
Alan Cox6e47e062009-06-11 12:37:06 +01001199 rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
1200 if (rcv->urb == NULL) {
1201 dev_dbg(&intf->dev,
1202 "out of memory (read urbs usb_alloc_urb)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001203 goto alloc_fail7;
1204 }
1205
1206 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1207 rcv->instance = acm;
1208 }
Oliver Neukum86478942006-05-13 22:50:47 +02001209 for (i = 0; i < num_rx_buf; i++) {
David Brownell672c4e12008-08-06 18:41:12 -07001210 struct acm_rb *rb = &(acm->rb[i]);
David Kubicek61a87ad2005-11-01 18:51:34 +01001211
David Brownell672c4e12008-08-06 18:41:12 -07001212 rb->base = usb_buffer_alloc(acm->dev, readsize,
1213 GFP_KERNEL, &rb->dma);
1214 if (!rb->base) {
Alan Cox6e47e062009-06-11 12:37:06 +01001215 dev_dbg(&intf->dev,
1216 "out of memory (read bufs usb_buffer_alloc)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001217 goto alloc_fail7;
1218 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 }
Alan Cox6e47e062009-06-11 12:37:06 +01001220 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001221 struct acm_wb *snd = &(acm->wb[i]);
1222
Alan Cox6e47e062009-06-11 12:37:06 +01001223 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1224 if (snd->urb == NULL) {
1225 dev_dbg(&intf->dev,
1226 "out of memory (write urbs usb_alloc_urb)");
David Engrafe4cf3aa2008-03-20 10:01:34 +01001227 goto alloc_fail7;
1228 }
1229
Alan Cox6e47e062009-06-11 12:37:06 +01001230 usb_fill_bulk_urb(snd->urb, usb_dev,
1231 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1232 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001233 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1234 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 }
1236
Alan Cox6e47e062009-06-11 12:37:06 +01001237 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001238
1239 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1240 if (i < 0)
1241 goto alloc_fail8;
1242
1243 if (cfd) { /* export the country data */
1244 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1245 if (!acm->country_codes)
1246 goto skip_countries;
1247 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001248 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1249 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001250 acm->country_rel_date = cfd->iCountryCodeRelDate;
1251
1252 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1253 if (i < 0) {
1254 kfree(acm->country_codes);
1255 goto skip_countries;
1256 }
1257
Alan Cox6e47e062009-06-11 12:37:06 +01001258 i = device_create_file(&intf->dev,
1259 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001260 if (i < 0) {
1261 kfree(acm->country_codes);
1262 goto skip_countries;
1263 }
1264 }
1265
1266skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001267 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001268 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1269 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1270 /* works around buggy devices */
1271 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1273 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1274
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1276
1277 acm_set_control(acm, acm->ctrlout);
1278
1279 acm->line.dwDTERate = cpu_to_le32(9600);
1280 acm->line.bDataBits = 8;
1281 acm_set_line(acm, &acm->line);
1282
1283 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001284 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001286 usb_get_intf(control_interface);
1287 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288
1289 acm_table[minor] = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001291 return 0;
1292alloc_fail8:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001293 for (i = 0; i < ACM_NW; i++)
1294 usb_free_urb(acm->wb[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295alloc_fail7:
Oliver Neukum830f4022008-06-25 14:17:16 +02001296 acm_read_buffers_free(acm);
Oliver Neukum86478942006-05-13 22:50:47 +02001297 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001298 usb_free_urb(acm->ru[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 usb_free_urb(acm->ctrlurb);
1300alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001301 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302alloc_fail4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
1304alloc_fail2:
1305 kfree(acm);
1306alloc_fail:
1307 return -ENOMEM;
1308}
1309
Oliver Neukum1365baf2007-10-12 17:24:28 +02001310static void stop_data_traffic(struct acm *acm)
1311{
1312 int i;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001313 dbg("Entering stop_data_traffic");
Oliver Neukum1365baf2007-10-12 17:24:28 +02001314
1315 tasklet_disable(&acm->urb_task);
1316
1317 usb_kill_urb(acm->ctrlurb);
Alan Cox6e47e062009-06-11 12:37:06 +01001318 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001319 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001320 for (i = 0; i < acm->rx_buflimit; i++)
1321 usb_kill_urb(acm->ru[i].urb);
1322
Oliver Neukum1365baf2007-10-12 17:24:28 +02001323 tasklet_enable(&acm->urb_task);
1324
1325 cancel_work_sync(&acm->work);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001326 cancel_work_sync(&acm->waker);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001327}
1328
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329static void acm_disconnect(struct usb_interface *intf)
1330{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001331 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001333 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334
David Brownell672c4e12008-08-06 18:41:12 -07001335 /* sibling interface is already cleaning up */
1336 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001337 return;
David Brownell672c4e12008-08-06 18:41:12 -07001338
1339 mutex_lock(&open_mutex);
Alan Cox6e47e062009-06-11 12:37:06 +01001340 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001341 device_remove_file(&acm->control->dev,
1342 &dev_attr_wCountryCodes);
1343 device_remove_file(&acm->control->dev,
1344 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001345 }
Alan Stern74da5d62007-08-02 13:29:10 -04001346 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 acm->dev = NULL;
Oliver Neukum86067eea2006-01-08 12:39:13 +01001348 usb_set_intfdata(acm->control, NULL);
1349 usb_set_intfdata(acm->data, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350
Oliver Neukum1365baf2007-10-12 17:24:28 +02001351 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
Oliver Neukum884b6002005-04-21 21:28:02 +02001353 acm_write_buffers_free(acm);
Alan Cox6e47e062009-06-11 12:37:06 +01001354 usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
1355 acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001356 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001358 if (!acm->combined_interfaces)
1359 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001360 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
Alan Cox10077d42009-06-11 12:36:09 +01001362 if (acm->port.count == 0) {
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001363 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001364 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 return;
1366 }
1367
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001368 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001369 tty = tty_port_tty_get(&acm->port);
1370 if (tty) {
1371 tty_hangup(tty);
1372 tty_kref_put(tty);
1373 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374}
1375
Oliver Neukum35758582008-07-01 19:10:08 +02001376#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001377static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1378{
1379 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001380 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001381
Alan Stern65bfd292008-11-25 16:39:18 -05001382 if (message.event & PM_EVENT_AUTO) {
Oliver Neukum11ea8592008-06-20 11:25:57 +02001383 int b;
1384
1385 spin_lock_irq(&acm->read_lock);
1386 spin_lock(&acm->write_lock);
1387 b = acm->processing + acm->transmitting;
1388 spin_unlock(&acm->write_lock);
1389 spin_unlock_irq(&acm->read_lock);
1390 if (b)
1391 return -EBUSY;
1392 }
1393
1394 spin_lock_irq(&acm->read_lock);
1395 spin_lock(&acm->write_lock);
1396 cnt = acm->susp_count++;
1397 spin_unlock(&acm->write_lock);
1398 spin_unlock_irq(&acm->read_lock);
1399
1400 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001401 return 0;
1402 /*
1403 we treat opened interfaces differently,
1404 we must guard against open
1405 */
1406 mutex_lock(&acm->mutex);
1407
Alan Cox10077d42009-06-11 12:36:09 +01001408 if (acm->port.count)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001409 stop_data_traffic(acm);
1410
1411 mutex_unlock(&acm->mutex);
1412 return 0;
1413}
1414
1415static int acm_resume(struct usb_interface *intf)
1416{
1417 struct acm *acm = usb_get_intfdata(intf);
1418 int rv = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001419 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001420
Oliver Neukum11ea8592008-06-20 11:25:57 +02001421 spin_lock_irq(&acm->read_lock);
1422 acm->susp_count -= 1;
1423 cnt = acm->susp_count;
1424 spin_unlock_irq(&acm->read_lock);
1425
1426 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001427 return 0;
1428
1429 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001430 if (acm->port.count) {
Oliver Neukum1365baf2007-10-12 17:24:28 +02001431 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
1432 if (rv < 0)
Oliver Neukum11ea8592008-06-20 11:25:57 +02001433 goto err_out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001434
1435 tasklet_schedule(&acm->urb_task);
1436 }
1437
1438err_out:
1439 mutex_unlock(&acm->mutex);
1440 return rv;
1441}
Oliver Neukum35758582008-07-01 19:10:08 +02001442
1443#endif /* CONFIG_PM */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444/*
1445 * USB driver structure.
1446 */
1447
1448static struct usb_device_id acm_ids[] = {
1449 /* quirky and broken devices */
1450 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1451 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1452 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001453 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1454 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1455 },
Andrew Lunn0f9c7b42008-12-23 17:31:23 +01001456 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1457 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1458 },
Masahito Omote8753e652005-07-29 12:17:25 -07001459 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1460 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1461 },
Chris Malley91a9c922006-10-03 10:08:28 +01001462 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1463 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1464 },
Alan Cox7abcf202009-04-06 17:35:01 +01001465 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1466 .driver_info = SINGLE_RX_URB,
1467 },
Oliver Neukum86478942006-05-13 22:50:47 +02001468 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1469 .driver_info = SINGLE_RX_URB, /* firmware bug */
1470 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001471 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1472 .driver_info = SINGLE_RX_URB, /* firmware bug */
1473 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001474 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1475 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1476 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001477 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1478 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1479 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001480 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1481 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1482 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001483 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1484 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1485 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001486 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1487 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1488 },
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001489 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1490 },
Adam Richterc332b4e2009-02-18 16:17:15 -08001491 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1492 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1493 data interface instead of
1494 communications interface.
1495 Maybe we should define a new
1496 quirk for this. */
1497 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001498 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1499 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1500 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001501
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 /* control interfaces with various AT-command sets */
1503 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1504 USB_CDC_ACM_PROTO_AT_V25TER) },
1505 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1506 USB_CDC_ACM_PROTO_AT_PCCA101) },
1507 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1508 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1509 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1510 USB_CDC_ACM_PROTO_AT_GSM) },
1511 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001512 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1514 USB_CDC_ACM_PROTO_AT_CDMA) },
1515
1516 /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
1517 { }
1518};
1519
Alan Cox6e47e062009-06-11 12:37:06 +01001520MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521
1522static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 .name = "cdc_acm",
1524 .probe = acm_probe,
1525 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001526#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001527 .suspend = acm_suspend,
1528 .resume = acm_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001529#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001531#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001532 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001533#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534};
1535
1536/*
1537 * TTY driver structures.
1538 */
1539
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001540static const struct tty_operations acm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 .open = acm_tty_open,
1542 .close = acm_tty_close,
Alan Cox10077d42009-06-11 12:36:09 +01001543 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 .write = acm_tty_write,
1545 .write_room = acm_tty_write_room,
1546 .ioctl = acm_tty_ioctl,
1547 .throttle = acm_tty_throttle,
1548 .unthrottle = acm_tty_unthrottle,
1549 .chars_in_buffer = acm_tty_chars_in_buffer,
1550 .break_ctl = acm_tty_break_ctl,
1551 .set_termios = acm_tty_set_termios,
1552 .tiocmget = acm_tty_tiocmget,
1553 .tiocmset = acm_tty_tiocmset,
1554};
1555
1556/*
1557 * Init / exit.
1558 */
1559
1560static int __init acm_init(void)
1561{
1562 int retval;
1563 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1564 if (!acm_tty_driver)
1565 return -ENOMEM;
1566 acm_tty_driver->owner = THIS_MODULE,
1567 acm_tty_driver->driver_name = "acm",
1568 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 acm_tty_driver->major = ACM_TTY_MAJOR,
1570 acm_tty_driver->minor_start = 0,
1571 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1572 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001573 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001575 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1576 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577 tty_set_operations(acm_tty_driver, &acm_ops);
1578
1579 retval = tty_register_driver(acm_tty_driver);
1580 if (retval) {
1581 put_tty_driver(acm_tty_driver);
1582 return retval;
1583 }
1584
1585 retval = usb_register(&acm_driver);
1586 if (retval) {
1587 tty_unregister_driver(acm_tty_driver);
1588 put_tty_driver(acm_tty_driver);
1589 return retval;
1590 }
1591
Greg Kroah-Hartman5909f6e2008-08-18 13:21:04 -07001592 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
1593 DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594
1595 return 0;
1596}
1597
1598static void __exit acm_exit(void)
1599{
1600 usb_deregister(&acm_driver);
1601 tty_unregister_driver(acm_tty_driver);
1602 put_tty_driver(acm_tty_driver);
1603}
1604
1605module_init(acm_init);
1606module_exit(acm_exit);
1607
Alan Cox6e47e062009-06-11 12:37:06 +01001608MODULE_AUTHOR(DRIVER_AUTHOR);
1609MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01001611MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);