blob: e1f89416ef8cdf6235b2cfcebdbda23ab213f7e7 [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;
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400390 struct usb_host_endpoint *ep;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200391
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 dbg("Entering acm_rx_tasklet");
393
Alan Cox10077d42009-06-11 12:36:09 +0100394 if (!ACM_READY(acm)) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200395 dbg("acm_rx_tasklet: ACM not ready");
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100396 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200397 }
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100398
Oliver Neukum834dbca2007-03-06 10:47:04 +0100399 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100400 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100401 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Alan Cox10077d42009-06-11 12:36:09 +0100402 if (throttled) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200403 dbg("acm_rx_tasklet: throttled");
David Kubicek61a87ad2005-11-01 18:51:34 +0100404 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200405 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100406
Alan Cox10077d42009-06-11 12:36:09 +0100407 tty = tty_port_tty_get(&acm->port);
408
David Kubicek61a87ad2005-11-01 18:51:34 +0100409next_buffer:
Jarek Poplawski762f0072006-10-06 07:23:11 +0200410 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100411 if (list_empty(&acm->filled_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200412 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100413 goto urbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100415 buf = list_entry(acm->filled_read_bufs.next,
416 struct acm_rb, list);
417 list_del(&buf->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200418 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100419
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200420 dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
David Kubicek61a87ad2005-11-01 18:51:34 +0100421
Alan Cox10077d42009-06-11 12:36:09 +0100422 if (tty) {
423 spin_lock_irqsave(&acm->throttle_lock, flags);
424 throttled = acm->throttle;
425 spin_unlock_irqrestore(&acm->throttle_lock, flags);
426 if (!throttled) {
427 tty_buffer_request_room(tty, buf->size);
428 tty_insert_flip_string(tty, buf->base, buf->size);
429 tty_flip_buffer_push(tty);
430 } else {
431 tty_kref_put(tty);
432 dbg("Throttling noticed");
433 spin_lock_irqsave(&acm->read_lock, flags);
434 list_add(&buf->list, &acm->filled_read_bufs);
435 spin_unlock_irqrestore(&acm->read_lock, flags);
436 return;
437 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439
Jarek Poplawski762f0072006-10-06 07:23:11 +0200440 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100441 list_add(&buf->list, &acm->spare_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200442 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100443 goto next_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
David Kubicek61a87ad2005-11-01 18:51:34 +0100445urbs:
Alan Cox10077d42009-06-11 12:36:09 +0100446 tty_kref_put(tty);
447
David Kubicek61a87ad2005-11-01 18:51:34 +0100448 while (!list_empty(&acm->spare_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200449 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100450 if (list_empty(&acm->spare_read_urbs)) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200451 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200452 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100453 return;
454 }
455 rcv = list_entry(acm->spare_read_urbs.next,
456 struct acm_ru, list);
457 list_del(&rcv->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200458 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100459
460 buf = list_entry(acm->spare_read_bufs.next,
461 struct acm_rb, list);
462 list_del(&buf->list);
463
464 rcv->buffer = buf;
465
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400466 ep = (usb_pipein(acm->rx_endpoint) ? acm->dev->ep_in : acm->dev->ep_out)
467 [usb_pipeendpoint(acm->rx_endpoint)];
468 if (usb_endpoint_xfer_int(&ep->desc))
469 usb_fill_int_urb(rcv->urb, acm->dev,
470 acm->rx_endpoint,
471 buf->base,
472 acm->readsize,
473 acm_read_bulk, rcv, ep->desc.bInterval);
474 else
475 usb_fill_bulk_urb(rcv->urb, acm->dev,
476 acm->rx_endpoint,
477 buf->base,
478 acm->readsize,
479 acm_read_bulk, rcv);
David Kubicek61a87ad2005-11-01 18:51:34 +0100480 rcv->urb->transfer_dma = buf->dma;
481 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
482
Alan Cox6e47e062009-06-11 12:37:06 +0100483 /* This shouldn't kill the driver as unsuccessful URBs are
484 returned to the free-urbs-pool and resubmited ASAP */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200485 spin_lock_irqsave(&acm->read_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100486 if (acm->susp_count ||
487 usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100488 list_add(&buf->list, &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100489 list_add(&rcv->list, &acm->spare_read_urbs);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200490 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200491 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100492 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200493 } else {
494 spin_unlock_irqrestore(&acm->read_lock, flags);
495 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 +0100496 }
497 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200498 spin_lock_irqsave(&acm->read_lock, flags);
499 acm->processing = 0;
500 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501}
502
503/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100504static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505{
Ming Leicdc97792008-02-24 18:41:47 +0800506 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700507 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800508 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200509
David Brownelle5fbab52008-08-06 18:46:10 -0700510 if (verbose || urb->status
511 || (urb->actual_length != urb->transfer_buffer_length))
512 dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
513 urb->actual_length,
514 urb->transfer_buffer_length,
515 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800517 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100518 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800519 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200520 if (ACM_READY(acm))
521 schedule_work(&acm->work);
David Brownelle5fbab52008-08-06 18:46:10 -0700522 else
523 wake_up_interruptible(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524}
525
David Howellsc4028952006-11-22 14:57:56 +0000526static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527{
David Howellsc4028952006-11-22 14:57:56 +0000528 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100529 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700530
531 dev_vdbg(&acm->data->dev, "tx work\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 if (!ACM_READY(acm))
533 return;
Alan Cox10077d42009-06-11 12:36:09 +0100534 tty = tty_port_tty_get(&acm->port);
535 tty_wakeup(tty);
536 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537}
538
Oliver Neukum11ea8592008-06-20 11:25:57 +0200539static void acm_waker(struct work_struct *waker)
540{
541 struct acm *acm = container_of(waker, struct acm, waker);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200542 int rv;
543
544 rv = usb_autopm_get_interface(acm->control);
545 if (rv < 0) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700546 dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200547 return;
548 }
549 if (acm->delayed_wb) {
550 acm_start_wb(acm, acm->delayed_wb);
551 acm->delayed_wb = NULL;
552 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200553 usb_autopm_put_interface(acm->control);
554}
555
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556/*
557 * TTY handlers
558 */
559
560static int acm_tty_open(struct tty_struct *tty, struct file *filp)
561{
562 struct acm *acm;
Thadeu Lima de Souza Cascardo42dd2aa2009-06-25 14:41:24 +0100563 int rv = -ENODEV;
David Kubicek61a87ad2005-11-01 18:51:34 +0100564 int i;
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200565 dbg("Entering acm_tty_open.");
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100566
567 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
569 acm = acm_table[tty->index];
570 if (!acm || !acm->dev)
571 goto err_out;
572 else
573 rv = 0;
574
David Engraf28d1dfa2008-03-20 10:53:52 +0100575 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
Alan Cox10077d42009-06-11 12:36:09 +0100576
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 tty->driver_data = acm;
Alan Cox10077d42009-06-11 12:36:09 +0100578 tty_port_tty_set(&acm->port, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
Oliver Neukum94409cc2008-02-11 15:22:29 +0100580 if (usb_autopm_get_interface(acm->control) < 0)
581 goto early_bail;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200582 else
583 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200584
585 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100586 if (acm->port.count++) {
Oliver Neukum1365baf2007-10-12 17:24:28 +0200587 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 goto done;
Alan Cox10077d42009-06-11 12:36:09 +0100589 }
Oliver Neukum1365baf2007-10-12 17:24:28 +0200590
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 acm->ctrlurb->dev = acm->dev;
592 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
593 dbg("usb_submit_urb(ctrl irq) failed");
594 goto bail_out;
595 }
596
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100597 if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
598 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 goto full_bailout;
Alan Cox10077d42009-06-11 12:36:09 +0100600
Oliver Neukum11ea8592008-06-20 11:25:57 +0200601 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
David Kubicek61a87ad2005-11-01 18:51:34 +0100603 INIT_LIST_HEAD(&acm->spare_read_urbs);
604 INIT_LIST_HEAD(&acm->spare_read_bufs);
605 INIT_LIST_HEAD(&acm->filled_read_bufs);
Alan Cox6e47e062009-06-11 12:37:06 +0100606
607 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100608 list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
Alan Cox6e47e062009-06-11 12:37:06 +0100609 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100610 list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100611
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100612 acm->throttle = 0;
613
David Kubicek61a87ad2005-11-01 18:51:34 +0100614 tasklet_schedule(&acm->urb_task);
Alan Cox10077d42009-06-11 12:36:09 +0100615 rv = tty_port_block_til_ready(&acm->port, tty, filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616done:
Oliver Neukum1365baf2007-10-12 17:24:28 +0200617 mutex_unlock(&acm->mutex);
Alexey Dobriyan74573ee2008-08-20 16:56:04 -0700618err_out:
Oliver Neukum94409cc2008-02-11 15:22:29 +0100619 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 return rv;
621
622full_bailout:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 usb_kill_urb(acm->ctrlurb);
624bail_out:
Oliver Neukum1365baf2007-10-12 17:24:28 +0200625 usb_autopm_put_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100626 acm->port.count--;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200627 mutex_unlock(&acm->mutex);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100628early_bail:
629 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100630 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 return -EIO;
632}
633
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700634static void acm_tty_unregister(struct acm *acm)
635{
Alan Cox10077d42009-06-11 12:36:09 +0100636 int i, nr;
David Kubicek61a87ad2005-11-01 18:51:34 +0100637
Oliver Neukum86478942006-05-13 22:50:47 +0200638 nr = acm->rx_buflimit;
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700639 tty_unregister_device(acm_tty_driver, acm->minor);
640 usb_put_intf(acm->control);
641 acm_table[acm->minor] = NULL;
642 usb_free_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100643 for (i = 0; i < ACM_NW; i++)
644 usb_free_urb(acm->wb[i].urb);
Oliver Neukum86478942006-05-13 22:50:47 +0200645 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100646 usb_free_urb(acm->ru[i].urb);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100647 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700648 kfree(acm);
649}
650
David Brownelle5fbab52008-08-06 18:46:10 -0700651static int acm_tty_chars_in_buffer(struct tty_struct *tty);
652
Alan Cox10077d42009-06-11 12:36:09 +0100653static void acm_port_down(struct acm *acm, int drain)
654{
655 int i, nr = acm->rx_buflimit;
656 mutex_lock(&open_mutex);
657 if (acm->dev) {
658 usb_autopm_get_interface(acm->control);
659 acm_set_control(acm, acm->ctrlout = 0);
660 /* try letting the last writes drain naturally */
661 if (drain) {
662 wait_event_interruptible_timeout(acm->drain_wait,
663 (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
664 ACM_CLOSE_TIMEOUT * HZ);
665 }
666 usb_kill_urb(acm->ctrlurb);
667 for (i = 0; i < ACM_NW; i++)
668 usb_kill_urb(acm->wb[i].urb);
669 for (i = 0; i < nr; i++)
670 usb_kill_urb(acm->ru[i].urb);
671 acm->control->needs_remote_wakeup = 0;
672 usb_autopm_put_interface(acm->control);
673 }
674 mutex_unlock(&open_mutex);
675}
676
677static void acm_tty_hangup(struct tty_struct *tty)
678{
679 struct acm *acm = tty->driver_data;
680 tty_port_hangup(&acm->port);
681 acm_port_down(acm, 0);
682}
683
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684static void acm_tty_close(struct tty_struct *tty, struct file *filp)
685{
686 struct acm *acm = tty->driver_data;
687
Alan Cox10077d42009-06-11 12:36:09 +0100688 /* Perform the closing process and see if we need to do the hardware
689 shutdown */
Thadeu Lima de Souza Cascardo922b1352009-06-25 14:41:30 +0100690 if (!acm || tty_port_close_start(&acm->port, tty, filp) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 return;
Alan Cox10077d42009-06-11 12:36:09 +0100692 acm_port_down(acm, 0);
693 tty_port_close_end(&acm->port, tty);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100694 mutex_lock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100695 tty_port_tty_set(&acm->port, NULL);
696 if (!acm->dev)
697 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100698 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699}
700
Alan Cox6e47e062009-06-11 12:37:06 +0100701static int acm_tty_write(struct tty_struct *tty,
702 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703{
704 struct acm *acm = tty->driver_data;
705 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200706 unsigned long flags;
707 int wbn;
708 struct acm_wb *wb;
709
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200710 dbg("Entering acm_tty_write to write %d bytes,", count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
712 if (!ACM_READY(acm))
713 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 if (!count)
715 return 0;
716
Oliver Neukum884b6002005-04-21 21:28:02 +0200717 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100718 wbn = acm_wb_alloc(acm);
719 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200720 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200721 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200723 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
Oliver Neukum884b6002005-04-21 21:28:02 +0200725 count = (count > acm->writesize) ? acm->writesize : count;
726 dbg("Get %d bytes...", count);
727 memcpy(wb->buf, buf, count);
728 wb->len = count;
729 spin_unlock_irqrestore(&acm->write_lock, flags);
730
Alan Cox6e47e062009-06-11 12:37:06 +0100731 stat = acm_write_start(acm, wbn);
732 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200733 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 return count;
735}
736
737static int acm_tty_write_room(struct tty_struct *tty)
738{
739 struct acm *acm = tty->driver_data;
740 if (!ACM_READY(acm))
741 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200742 /*
743 * Do not let the line discipline to know that we have a reserve,
744 * or it might get too enthusiastic.
745 */
David Brownell934da462008-08-06 18:44:12 -0700746 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747}
748
749static int acm_tty_chars_in_buffer(struct tty_struct *tty)
750{
751 struct acm *acm = tty->driver_data;
752 if (!ACM_READY(acm))
Alan Cox23198fd2009-07-20 16:05:27 +0100753 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200754 /*
755 * This is inaccurate (overcounts), but it works.
756 */
Oliver Neukum86478942006-05-13 22:50:47 +0200757 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758}
759
760static void acm_tty_throttle(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 = 1;
767 spin_unlock_bh(&acm->throttle_lock);
768}
769
770static void acm_tty_unthrottle(struct tty_struct *tty)
771{
772 struct acm *acm = tty->driver_data;
773 if (!ACM_READY(acm))
774 return;
775 spin_lock_bh(&acm->throttle_lock);
776 acm->throttle = 0;
777 spin_unlock_bh(&acm->throttle_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100778 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779}
780
Alan Cox9e989662008-07-22 11:18:03 +0100781static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782{
783 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100784 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 if (!ACM_READY(acm))
Alan Cox9e989662008-07-22 11:18:03 +0100786 return -EINVAL;
787 retval = acm_send_break(acm, state ? 0xffff : 0);
788 if (retval < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 dbg("send break failed");
Alan Cox9e989662008-07-22 11:18:03 +0100790 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791}
792
793static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
794{
795 struct acm *acm = tty->driver_data;
796
797 if (!ACM_READY(acm))
798 return -EINVAL;
799
800 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
801 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
802 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
803 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
804 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
805 TIOCM_CTS;
806}
807
808static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
809 unsigned int set, unsigned int clear)
810{
811 struct acm *acm = tty->driver_data;
812 unsigned int newctrl;
813
814 if (!ACM_READY(acm))
815 return -EINVAL;
816
817 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100818 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
819 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
820 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
821 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
823 newctrl = (newctrl & ~clear) | set;
824
825 if (acm->ctrlout == newctrl)
826 return 0;
827 return acm_set_control(acm, acm->ctrlout = newctrl);
828}
829
Alan Cox6e47e062009-06-11 12:37:06 +0100830static int acm_tty_ioctl(struct tty_struct *tty, struct file *file,
831 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832{
833 struct acm *acm = tty->driver_data;
834
835 if (!ACM_READY(acm))
836 return -EINVAL;
837
838 return -ENOIOCTLCMD;
839}
840
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100841static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 0, 50, 75, 110, 134, 150, 200, 300, 600,
843 1200, 1800, 2400, 4800, 9600, 19200, 38400,
844 57600, 115200, 230400, 460800, 500000, 576000,
845 921600, 1000000, 1152000, 1500000, 2000000,
846 2500000, 3000000, 3500000, 4000000
847};
848
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100849static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 5, 6, 7, 8
851};
852
Alan Cox6e47e062009-06-11 12:37:06 +0100853static void acm_tty_set_termios(struct tty_struct *tty,
854 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855{
856 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800857 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 struct usb_cdc_line_coding newline;
859 int newctrl = acm->ctrlout;
860
861 if (!ACM_READY(acm))
862 return;
863
Alan Cox6e47e062009-06-11 12:37:06 +0100864 /* FIXME: Needs to support the tty_baud interface */
865 /* FIXME: Broken on sparc */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
867 (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
868 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
869 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100870 (termios->c_cflag & PARODD ? 1 : 2) +
871 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
Alan Cox6e47e062009-06-11 12:37:06 +0100873 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
875
876 if (!newline.dwDTERate) {
877 newline.dwDTERate = acm->line.dwDTERate;
878 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +0100879 } else
880 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881
882 if (newctrl != acm->ctrlout)
883 acm_set_control(acm, acm->ctrlout = newctrl);
884
885 if (memcmp(&acm->line, &newline, sizeof newline)) {
886 memcpy(&acm->line, &newline, sizeof newline);
887 dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
888 newline.bCharFormat, newline.bParityType,
889 newline.bDataBits);
890 acm_set_line(acm, &acm->line);
891 }
892}
893
894/*
895 * USB probe and disconnect routines.
896 */
897
Oliver Neukum830f4022008-06-25 14:17:16 +0200898/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +0200899static void acm_write_buffers_free(struct acm *acm)
900{
901 int i;
902 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +0200903 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200904
Alan Cox6e47e062009-06-11 12:37:06 +0100905 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Oliver Neukuma496c642008-10-21 10:39:04 +0200906 usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +0200907}
908
Oliver Neukum830f4022008-06-25 14:17:16 +0200909static void acm_read_buffers_free(struct acm *acm)
910{
911 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
912 int i, n = acm->rx_buflimit;
913
914 for (i = 0; i < n; i++)
Alan Cox6e47e062009-06-11 12:37:06 +0100915 usb_buffer_free(usb_dev, acm->readsize,
916 acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +0200917}
918
Oliver Neukum884b6002005-04-21 21:28:02 +0200919/* Little helper: write buffers allocate */
920static int acm_write_buffers_alloc(struct acm *acm)
921{
922 int i;
923 struct acm_wb *wb;
924
Oliver Neukum86478942006-05-13 22:50:47 +0200925 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200926 wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
927 &wb->dmah);
928 if (!wb->buf) {
929 while (i != 0) {
930 --i;
931 --wb;
932 usb_buffer_free(acm->dev, acm->writesize,
933 wb->buf, wb->dmah);
934 }
935 return -ENOMEM;
936 }
937 }
938 return 0;
939}
940
Alan Cox10077d42009-06-11 12:36:09 +0100941static int acm_probe(struct usb_interface *intf,
942 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943{
944 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100945 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -0700946 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 int buflen = intf->altsetting->extralen;
948 struct usb_interface *control_interface;
949 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200950 struct usb_endpoint_descriptor *epctrl = NULL;
951 struct usb_endpoint_descriptor *epread = NULL;
952 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 struct usb_device *usb_dev = interface_to_usbdev(intf);
954 struct acm *acm;
955 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +0100956 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 u8 *buf;
958 u8 ac_management_function = 0;
959 u8 call_management_function = 0;
960 int call_interface_num = -1;
961 int data_interface_num;
962 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200963 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100964 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200965 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
Oliver Neukum86478942006-05-13 22:50:47 +0200967 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200969 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
970
971 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 if (quirks == NO_UNION_NORMAL) {
973 data_interface = usb_ifnum_to_if(usb_dev, 1);
974 control_interface = usb_ifnum_to_if(usb_dev, 0);
975 goto skip_normal_probe;
976 }
Alan Cox6e47e062009-06-11 12:37:06 +0100977
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 /* normal probing*/
979 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700980 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 return -EINVAL;
982 }
983
984 if (!buflen) {
Alan Cox6e47e062009-06-11 12:37:06 +0100985 if (intf->cur_altsetting->endpoint->extralen &&
986 intf->cur_altsetting->endpoint->extra) {
987 dev_dbg(&intf->dev,
988 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 buflen = intf->cur_altsetting->endpoint->extralen;
990 buffer = intf->cur_altsetting->endpoint->extra;
991 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700992 dev_err(&intf->dev,
993 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 return -EINVAL;
995 }
996 }
997
998 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +0100999 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001000 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 goto next_desc;
1002 }
1003
Alan Cox6e47e062009-06-11 12:37:06 +01001004 switch (buffer[2]) {
1005 case USB_CDC_UNION_TYPE: /* we've found it */
1006 if (union_header) {
1007 dev_err(&intf->dev, "More than one "
1008 "union descriptor, skipping ...\n");
1009 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 }
Alan Cox6e47e062009-06-11 12:37:06 +01001011 union_header = (struct usb_cdc_union_desc *)buffer;
1012 break;
1013 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
1014 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1015 break;
1016 case USB_CDC_HEADER_TYPE: /* maybe check version */
1017 break; /* for now we ignore it */
1018 case USB_CDC_ACM_TYPE:
1019 ac_management_function = buffer[3];
1020 break;
1021 case USB_CDC_CALL_MANAGEMENT_TYPE:
1022 call_management_function = buffer[3];
1023 call_interface_num = buffer[4];
1024 if ((call_management_function & 3) != 3)
1025 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1026 break;
1027 default:
1028 /* there are LOTS more CDC descriptors that
1029 * could legitimately be found here.
1030 */
1031 dev_dbg(&intf->dev, "Ignoring descriptor: "
1032 "type %02x, length %d\n",
1033 buffer[2], buffer[0]);
1034 break;
1035 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036next_desc:
1037 buflen -= buffer[0];
1038 buffer += buffer[0];
1039 }
1040
1041 if (!union_header) {
1042 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001043 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
1045 control_interface = intf;
1046 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001047 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1048 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1049 return -ENODEV;
1050 } else {
1051 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1052 combined_interfaces = 1;
1053 control_interface = data_interface = intf;
1054 goto look_for_collapsed_interface;
1055 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 }
1057 } else {
1058 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1059 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1060 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001061 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 return -ENODEV;
1063 }
1064 }
Alan Cox6e47e062009-06-11 12:37:06 +01001065
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001067 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001069 if (control_interface == data_interface) {
1070 /* some broken devices designed for windows work this way */
1071 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1072 combined_interfaces = 1;
1073 /* a popular other OS doesn't use it */
1074 quirks |= NO_CAP_LINE;
1075 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1076 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1077 return -EINVAL;
1078 }
1079look_for_collapsed_interface:
1080 for (i = 0; i < 3; i++) {
1081 struct usb_endpoint_descriptor *ep;
1082 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1083
1084 if (usb_endpoint_is_int_in(ep))
1085 epctrl = ep;
1086 else if (usb_endpoint_is_bulk_out(ep))
1087 epwrite = ep;
1088 else if (usb_endpoint_is_bulk_in(ep))
1089 epread = ep;
1090 else
1091 return -EINVAL;
1092 }
1093 if (!epctrl || !epread || !epwrite)
1094 return -ENODEV;
1095 else
1096 goto made_compressed_probe;
1097 }
1098
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099skip_normal_probe:
1100
1101 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001102 if (data_interface->cur_altsetting->desc.bInterfaceClass
1103 != CDC_DATA_INTERFACE_TYPE) {
1104 if (control_interface->cur_altsetting->desc.bInterfaceClass
1105 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001107 dev_dbg(&intf->dev,
1108 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 t = control_interface;
1110 control_interface = data_interface;
1111 data_interface = t;
1112 } else {
1113 return -EINVAL;
1114 }
1115 }
Alan Stern74da5d62007-08-02 13:29:10 -04001116
1117 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001118 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001119 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001120
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001121 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1122 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001123 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 return -EBUSY;
1125 }
1126
1127
1128 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
1129 return -EINVAL;
1130
1131 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1132 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1133 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1134
1135
1136 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001137 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 /* descriptors are swapped */
1139 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001140 dev_dbg(&intf->dev,
1141 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 t = epread;
1143 epread = epwrite;
1144 epwrite = t;
1145 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001146made_compressed_probe:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 dbg("interfaces are valid");
1148 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
1149
1150 if (minor == ACM_TTY_MINORS) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001151 dev_err(&intf->dev, "no more free acm devices\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 return -ENODEV;
1153 }
1154
Alan Cox6e47e062009-06-11 12:37:06 +01001155 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1156 if (acm == NULL) {
Joe Perches898eb712007-10-18 03:06:30 -07001157 dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 goto alloc_fail;
1159 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160
1161 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Alan Cox6e47e062009-06-11 12:37:06 +01001162 readsize = le16_to_cpu(epread->wMaxPacketSize) *
1163 (quirks == SINGLE_RX_URB ? 1 : 2);
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001164 acm->combined_interfaces = combined_interfaces;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001165 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 acm->control = control_interface;
1167 acm->data = data_interface;
1168 acm->minor = minor;
1169 acm->dev = usb_dev;
1170 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001171 if (quirks & NO_CAP_LINE)
1172 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 acm->ctrlsize = ctrlsize;
1174 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001175 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001176 acm->urb_task.func = acm_rx_tasklet;
1177 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +00001178 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001179 INIT_WORK(&acm->waker, acm_waker);
David Brownelle5fbab52008-08-06 18:46:10 -07001180 init_waitqueue_head(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +02001182 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001183 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001184 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001185 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Alan Cox739e0282009-06-11 12:27:50 +01001186 tty_port_init(&acm->port);
1187 acm->port.ops = &acm_port_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188
1189 buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
1190 if (!buf) {
Joe Perches898eb712007-10-18 03:06:30 -07001191 dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 goto alloc_fail2;
1193 }
1194 acm->ctrl_buffer = buf;
1195
Oliver Neukum884b6002005-04-21 21:28:02 +02001196 if (acm_write_buffers_alloc(acm) < 0) {
Joe Perches898eb712007-10-18 03:06:30 -07001197 dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 goto alloc_fail4;
1199 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200
1201 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1202 if (!acm->ctrlurb) {
Joe Perches898eb712007-10-18 03:06:30 -07001203 dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 goto alloc_fail5;
1205 }
Oliver Neukum86478942006-05-13 22:50:47 +02001206 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001207 struct acm_ru *rcv = &(acm->ru[i]);
1208
Alan Cox6e47e062009-06-11 12:37:06 +01001209 rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
1210 if (rcv->urb == NULL) {
1211 dev_dbg(&intf->dev,
1212 "out of memory (read urbs usb_alloc_urb)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001213 goto alloc_fail7;
1214 }
1215
1216 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1217 rcv->instance = acm;
1218 }
Oliver Neukum86478942006-05-13 22:50:47 +02001219 for (i = 0; i < num_rx_buf; i++) {
David Brownell672c4e12008-08-06 18:41:12 -07001220 struct acm_rb *rb = &(acm->rb[i]);
David Kubicek61a87ad2005-11-01 18:51:34 +01001221
David Brownell672c4e12008-08-06 18:41:12 -07001222 rb->base = usb_buffer_alloc(acm->dev, readsize,
1223 GFP_KERNEL, &rb->dma);
1224 if (!rb->base) {
Alan Cox6e47e062009-06-11 12:37:06 +01001225 dev_dbg(&intf->dev,
1226 "out of memory (read bufs usb_buffer_alloc)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001227 goto alloc_fail7;
1228 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 }
Alan Cox6e47e062009-06-11 12:37:06 +01001230 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001231 struct acm_wb *snd = &(acm->wb[i]);
1232
Alan Cox6e47e062009-06-11 12:37:06 +01001233 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1234 if (snd->urb == NULL) {
1235 dev_dbg(&intf->dev,
1236 "out of memory (write urbs usb_alloc_urb)");
David Engrafe4cf3aa2008-03-20 10:01:34 +01001237 goto alloc_fail7;
1238 }
1239
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001240 if (usb_endpoint_xfer_int(epwrite))
1241 usb_fill_int_urb(snd->urb, usb_dev,
1242 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1243 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1244 else
1245 usb_fill_bulk_urb(snd->urb, usb_dev,
1246 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1247 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001248 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1249 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 }
1251
Alan Cox6e47e062009-06-11 12:37:06 +01001252 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001253
1254 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1255 if (i < 0)
1256 goto alloc_fail8;
1257
1258 if (cfd) { /* export the country data */
1259 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1260 if (!acm->country_codes)
1261 goto skip_countries;
1262 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001263 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1264 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001265 acm->country_rel_date = cfd->iCountryCodeRelDate;
1266
1267 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1268 if (i < 0) {
1269 kfree(acm->country_codes);
1270 goto skip_countries;
1271 }
1272
Alan Cox6e47e062009-06-11 12:37:06 +01001273 i = device_create_file(&intf->dev,
1274 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001275 if (i < 0) {
1276 kfree(acm->country_codes);
1277 goto skip_countries;
1278 }
1279 }
1280
1281skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001282 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001283 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1284 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1285 /* works around buggy devices */
1286 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1288 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1289
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1291
1292 acm_set_control(acm, acm->ctrlout);
1293
1294 acm->line.dwDTERate = cpu_to_le32(9600);
1295 acm->line.bDataBits = 8;
1296 acm_set_line(acm, &acm->line);
1297
1298 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001299 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001301 usb_get_intf(control_interface);
1302 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303
1304 acm_table[minor] = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001306 return 0;
1307alloc_fail8:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001308 for (i = 0; i < ACM_NW; i++)
1309 usb_free_urb(acm->wb[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310alloc_fail7:
Oliver Neukum830f4022008-06-25 14:17:16 +02001311 acm_read_buffers_free(acm);
Oliver Neukum86478942006-05-13 22:50:47 +02001312 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001313 usb_free_urb(acm->ru[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 usb_free_urb(acm->ctrlurb);
1315alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001316 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317alloc_fail4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
1319alloc_fail2:
1320 kfree(acm);
1321alloc_fail:
1322 return -ENOMEM;
1323}
1324
Oliver Neukum1365baf2007-10-12 17:24:28 +02001325static void stop_data_traffic(struct acm *acm)
1326{
1327 int i;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001328 dbg("Entering stop_data_traffic");
Oliver Neukum1365baf2007-10-12 17:24:28 +02001329
1330 tasklet_disable(&acm->urb_task);
1331
1332 usb_kill_urb(acm->ctrlurb);
Alan Cox6e47e062009-06-11 12:37:06 +01001333 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001334 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001335 for (i = 0; i < acm->rx_buflimit; i++)
1336 usb_kill_urb(acm->ru[i].urb);
1337
Oliver Neukum1365baf2007-10-12 17:24:28 +02001338 tasklet_enable(&acm->urb_task);
1339
1340 cancel_work_sync(&acm->work);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001341 cancel_work_sync(&acm->waker);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001342}
1343
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344static void acm_disconnect(struct usb_interface *intf)
1345{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001346 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001348 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
David Brownell672c4e12008-08-06 18:41:12 -07001350 /* sibling interface is already cleaning up */
1351 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001352 return;
David Brownell672c4e12008-08-06 18:41:12 -07001353
1354 mutex_lock(&open_mutex);
Alan Cox6e47e062009-06-11 12:37:06 +01001355 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001356 device_remove_file(&acm->control->dev,
1357 &dev_attr_wCountryCodes);
1358 device_remove_file(&acm->control->dev,
1359 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001360 }
Alan Stern74da5d62007-08-02 13:29:10 -04001361 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 acm->dev = NULL;
Oliver Neukum86067eea2006-01-08 12:39:13 +01001363 usb_set_intfdata(acm->control, NULL);
1364 usb_set_intfdata(acm->data, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365
Oliver Neukum1365baf2007-10-12 17:24:28 +02001366 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Oliver Neukum884b6002005-04-21 21:28:02 +02001368 acm_write_buffers_free(acm);
Alan Cox6e47e062009-06-11 12:37:06 +01001369 usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
1370 acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001371 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001373 if (!acm->combined_interfaces)
1374 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001375 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376
Alan Cox10077d42009-06-11 12:36:09 +01001377 if (acm->port.count == 0) {
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001378 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001379 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 return;
1381 }
1382
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001383 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001384 tty = tty_port_tty_get(&acm->port);
1385 if (tty) {
1386 tty_hangup(tty);
1387 tty_kref_put(tty);
1388 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389}
1390
Oliver Neukum35758582008-07-01 19:10:08 +02001391#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001392static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1393{
1394 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001395 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001396
Alan Stern65bfd292008-11-25 16:39:18 -05001397 if (message.event & PM_EVENT_AUTO) {
Oliver Neukum11ea8592008-06-20 11:25:57 +02001398 int b;
1399
1400 spin_lock_irq(&acm->read_lock);
1401 spin_lock(&acm->write_lock);
1402 b = acm->processing + acm->transmitting;
1403 spin_unlock(&acm->write_lock);
1404 spin_unlock_irq(&acm->read_lock);
1405 if (b)
1406 return -EBUSY;
1407 }
1408
1409 spin_lock_irq(&acm->read_lock);
1410 spin_lock(&acm->write_lock);
1411 cnt = acm->susp_count++;
1412 spin_unlock(&acm->write_lock);
1413 spin_unlock_irq(&acm->read_lock);
1414
1415 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001416 return 0;
1417 /*
1418 we treat opened interfaces differently,
1419 we must guard against open
1420 */
1421 mutex_lock(&acm->mutex);
1422
Alan Cox10077d42009-06-11 12:36:09 +01001423 if (acm->port.count)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001424 stop_data_traffic(acm);
1425
1426 mutex_unlock(&acm->mutex);
1427 return 0;
1428}
1429
1430static int acm_resume(struct usb_interface *intf)
1431{
1432 struct acm *acm = usb_get_intfdata(intf);
1433 int rv = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001434 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001435
Oliver Neukum11ea8592008-06-20 11:25:57 +02001436 spin_lock_irq(&acm->read_lock);
1437 acm->susp_count -= 1;
1438 cnt = acm->susp_count;
1439 spin_unlock_irq(&acm->read_lock);
1440
1441 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001442 return 0;
1443
1444 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001445 if (acm->port.count) {
Oliver Neukum1365baf2007-10-12 17:24:28 +02001446 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
1447 if (rv < 0)
Oliver Neukum11ea8592008-06-20 11:25:57 +02001448 goto err_out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001449
1450 tasklet_schedule(&acm->urb_task);
1451 }
1452
1453err_out:
1454 mutex_unlock(&acm->mutex);
1455 return rv;
1456}
Oliver Neukum35758582008-07-01 19:10:08 +02001457
1458#endif /* CONFIG_PM */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459/*
1460 * USB driver structure.
1461 */
1462
1463static struct usb_device_id acm_ids[] = {
1464 /* quirky and broken devices */
1465 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1466 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1467 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001468 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1469 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1470 },
Andrew Lunn0f9c7b42008-12-23 17:31:23 +01001471 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1472 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1473 },
Masahito Omote8753e652005-07-29 12:17:25 -07001474 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1475 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1476 },
Chris Malley91a9c922006-10-03 10:08:28 +01001477 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1478 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1479 },
Alan Cox7abcf202009-04-06 17:35:01 +01001480 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1481 .driver_info = SINGLE_RX_URB,
1482 },
Oliver Neukum86478942006-05-13 22:50:47 +02001483 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1484 .driver_info = SINGLE_RX_URB, /* firmware bug */
1485 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001486 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1487 .driver_info = SINGLE_RX_URB, /* firmware bug */
1488 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001489 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1490 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1491 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001492 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1493 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1494 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001495 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1496 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1497 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001498 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1499 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1500 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001501 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1502 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1503 },
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001504 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1505 },
Adam Richterc332b4e2009-02-18 16:17:15 -08001506 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1507 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1508 data interface instead of
1509 communications interface.
1510 Maybe we should define a new
1511 quirk for this. */
1512 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001513 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1514 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1515 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001516
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 /* control interfaces with various AT-command sets */
1518 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1519 USB_CDC_ACM_PROTO_AT_V25TER) },
1520 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1521 USB_CDC_ACM_PROTO_AT_PCCA101) },
1522 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1523 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1524 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1525 USB_CDC_ACM_PROTO_AT_GSM) },
1526 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001527 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1529 USB_CDC_ACM_PROTO_AT_CDMA) },
1530
1531 /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
1532 { }
1533};
1534
Alan Cox6e47e062009-06-11 12:37:06 +01001535MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
1537static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 .name = "cdc_acm",
1539 .probe = acm_probe,
1540 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001541#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001542 .suspend = acm_suspend,
1543 .resume = acm_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001544#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001546#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001547 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001548#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549};
1550
1551/*
1552 * TTY driver structures.
1553 */
1554
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001555static const struct tty_operations acm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 .open = acm_tty_open,
1557 .close = acm_tty_close,
Alan Cox10077d42009-06-11 12:36:09 +01001558 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 .write = acm_tty_write,
1560 .write_room = acm_tty_write_room,
1561 .ioctl = acm_tty_ioctl,
1562 .throttle = acm_tty_throttle,
1563 .unthrottle = acm_tty_unthrottle,
1564 .chars_in_buffer = acm_tty_chars_in_buffer,
1565 .break_ctl = acm_tty_break_ctl,
1566 .set_termios = acm_tty_set_termios,
1567 .tiocmget = acm_tty_tiocmget,
1568 .tiocmset = acm_tty_tiocmset,
1569};
1570
1571/*
1572 * Init / exit.
1573 */
1574
1575static int __init acm_init(void)
1576{
1577 int retval;
1578 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1579 if (!acm_tty_driver)
1580 return -ENOMEM;
1581 acm_tty_driver->owner = THIS_MODULE,
1582 acm_tty_driver->driver_name = "acm",
1583 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584 acm_tty_driver->major = ACM_TTY_MAJOR,
1585 acm_tty_driver->minor_start = 0,
1586 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1587 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001588 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001590 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1591 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592 tty_set_operations(acm_tty_driver, &acm_ops);
1593
1594 retval = tty_register_driver(acm_tty_driver);
1595 if (retval) {
1596 put_tty_driver(acm_tty_driver);
1597 return retval;
1598 }
1599
1600 retval = usb_register(&acm_driver);
1601 if (retval) {
1602 tty_unregister_driver(acm_tty_driver);
1603 put_tty_driver(acm_tty_driver);
1604 return retval;
1605 }
1606
Greg Kroah-Hartman5909f6e2008-08-18 13:21:04 -07001607 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
1608 DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609
1610 return 0;
1611}
1612
1613static void __exit acm_exit(void)
1614{
1615 usb_deregister(&acm_driver);
1616 tty_unregister_driver(acm_tty_driver);
1617 put_tty_driver(acm_tty_driver);
1618}
1619
1620module_init(acm_init);
1621module_exit(acm_exit);
1622
Alan Cox6e47e062009-06-11 12:37:06 +01001623MODULE_AUTHOR(DRIVER_AUTHOR);
1624MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01001626MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);