blob: 2bfc41ece0e1d2df04ab058f742e76fd7b7749e9 [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
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200465 if (acm->is_int_ep)
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400466 usb_fill_int_urb(rcv->urb, acm->dev,
467 acm->rx_endpoint,
468 buf->base,
469 acm->readsize,
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200470 acm_read_bulk, rcv, acm->bInterval);
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400471 else
472 usb_fill_bulk_urb(rcv->urb, acm->dev,
473 acm->rx_endpoint,
474 buf->base,
475 acm->readsize,
476 acm_read_bulk, rcv);
David Kubicek61a87ad2005-11-01 18:51:34 +0100477 rcv->urb->transfer_dma = buf->dma;
478 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
479
Alan Cox6e47e062009-06-11 12:37:06 +0100480 /* This shouldn't kill the driver as unsuccessful URBs are
481 returned to the free-urbs-pool and resubmited ASAP */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200482 spin_lock_irqsave(&acm->read_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100483 if (acm->susp_count ||
484 usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100485 list_add(&buf->list, &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100486 list_add(&rcv->list, &acm->spare_read_urbs);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200487 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200488 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100489 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200490 } else {
491 spin_unlock_irqrestore(&acm->read_lock, flags);
492 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 +0100493 }
494 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200495 spin_lock_irqsave(&acm->read_lock, flags);
496 acm->processing = 0;
497 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498}
499
500/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100501static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502{
Ming Leicdc97792008-02-24 18:41:47 +0800503 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700504 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800505 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200506
David Brownelle5fbab52008-08-06 18:46:10 -0700507 if (verbose || urb->status
508 || (urb->actual_length != urb->transfer_buffer_length))
509 dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
510 urb->actual_length,
511 urb->transfer_buffer_length,
512 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800514 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100515 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800516 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200517 if (ACM_READY(acm))
518 schedule_work(&acm->work);
David Brownelle5fbab52008-08-06 18:46:10 -0700519 else
520 wake_up_interruptible(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521}
522
David Howellsc4028952006-11-22 14:57:56 +0000523static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524{
David Howellsc4028952006-11-22 14:57:56 +0000525 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100526 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700527
528 dev_vdbg(&acm->data->dev, "tx work\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 if (!ACM_READY(acm))
530 return;
Alan Cox10077d42009-06-11 12:36:09 +0100531 tty = tty_port_tty_get(&acm->port);
532 tty_wakeup(tty);
533 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534}
535
Oliver Neukum11ea8592008-06-20 11:25:57 +0200536static void acm_waker(struct work_struct *waker)
537{
538 struct acm *acm = container_of(waker, struct acm, waker);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200539 int rv;
540
541 rv = usb_autopm_get_interface(acm->control);
542 if (rv < 0) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700543 dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200544 return;
545 }
546 if (acm->delayed_wb) {
547 acm_start_wb(acm, acm->delayed_wb);
548 acm->delayed_wb = NULL;
549 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200550 usb_autopm_put_interface(acm->control);
551}
552
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553/*
554 * TTY handlers
555 */
556
557static int acm_tty_open(struct tty_struct *tty, struct file *filp)
558{
559 struct acm *acm;
Thadeu Lima de Souza Cascardo42dd2aa2009-06-25 14:41:24 +0100560 int rv = -ENODEV;
David Kubicek61a87ad2005-11-01 18:51:34 +0100561 int i;
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200562 dbg("Entering acm_tty_open.");
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100563
564 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
566 acm = acm_table[tty->index];
567 if (!acm || !acm->dev)
568 goto err_out;
569 else
570 rv = 0;
571
David Engraf28d1dfa2008-03-20 10:53:52 +0100572 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
Alan Cox10077d42009-06-11 12:36:09 +0100573
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 tty->driver_data = acm;
Alan Cox10077d42009-06-11 12:36:09 +0100575 tty_port_tty_set(&acm->port, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Oliver Neukum94409cc2008-02-11 15:22:29 +0100577 if (usb_autopm_get_interface(acm->control) < 0)
578 goto early_bail;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200579 else
580 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf72007-10-12 17:24:28 +0200581
582 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100583 if (acm->port.count++) {
Oliver Neukum1365baf72007-10-12 17:24:28 +0200584 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 goto done;
Alan Cox10077d42009-06-11 12:36:09 +0100586 }
Oliver Neukum1365baf72007-10-12 17:24:28 +0200587
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 acm->ctrlurb->dev = acm->dev;
589 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
590 dbg("usb_submit_urb(ctrl irq) failed");
591 goto bail_out;
592 }
593
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100594 if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
595 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 goto full_bailout;
Alan Cox10077d42009-06-11 12:36:09 +0100597
Oliver Neukum11ea8592008-06-20 11:25:57 +0200598 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599
David Kubicek61a87ad2005-11-01 18:51:34 +0100600 INIT_LIST_HEAD(&acm->spare_read_urbs);
601 INIT_LIST_HEAD(&acm->spare_read_bufs);
602 INIT_LIST_HEAD(&acm->filled_read_bufs);
Alan Cox6e47e062009-06-11 12:37:06 +0100603
604 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100605 list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
Alan Cox6e47e062009-06-11 12:37:06 +0100606 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100607 list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100608
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100609 acm->throttle = 0;
610
David Kubicek61a87ad2005-11-01 18:51:34 +0100611 tasklet_schedule(&acm->urb_task);
Alan Cox10077d42009-06-11 12:36:09 +0100612 rv = tty_port_block_til_ready(&acm->port, tty, filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613done:
Oliver Neukum1365baf72007-10-12 17:24:28 +0200614 mutex_unlock(&acm->mutex);
Alexey Dobriyan74573ee2008-08-20 16:56:04 -0700615err_out:
Oliver Neukum94409cc2008-02-11 15:22:29 +0100616 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 return rv;
618
619full_bailout:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 usb_kill_urb(acm->ctrlurb);
621bail_out:
Oliver Neukum1365baf72007-10-12 17:24:28 +0200622 usb_autopm_put_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100623 acm->port.count--;
Oliver Neukum1365baf72007-10-12 17:24:28 +0200624 mutex_unlock(&acm->mutex);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100625early_bail:
626 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100627 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 return -EIO;
629}
630
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700631static void acm_tty_unregister(struct acm *acm)
632{
Alan Cox10077d42009-06-11 12:36:09 +0100633 int i, nr;
David Kubicek61a87ad2005-11-01 18:51:34 +0100634
Oliver Neukum86478942006-05-13 22:50:47 +0200635 nr = acm->rx_buflimit;
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700636 tty_unregister_device(acm_tty_driver, acm->minor);
637 usb_put_intf(acm->control);
638 acm_table[acm->minor] = NULL;
639 usb_free_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100640 for (i = 0; i < ACM_NW; i++)
641 usb_free_urb(acm->wb[i].urb);
Oliver Neukum86478942006-05-13 22:50:47 +0200642 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100643 usb_free_urb(acm->ru[i].urb);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100644 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700645 kfree(acm);
646}
647
David Brownelle5fbab52008-08-06 18:46:10 -0700648static int acm_tty_chars_in_buffer(struct tty_struct *tty);
649
Alan Cox10077d42009-06-11 12:36:09 +0100650static void acm_port_down(struct acm *acm, int drain)
651{
652 int i, nr = acm->rx_buflimit;
653 mutex_lock(&open_mutex);
654 if (acm->dev) {
655 usb_autopm_get_interface(acm->control);
656 acm_set_control(acm, acm->ctrlout = 0);
657 /* try letting the last writes drain naturally */
658 if (drain) {
659 wait_event_interruptible_timeout(acm->drain_wait,
660 (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
661 ACM_CLOSE_TIMEOUT * HZ);
662 }
663 usb_kill_urb(acm->ctrlurb);
664 for (i = 0; i < ACM_NW; i++)
665 usb_kill_urb(acm->wb[i].urb);
666 for (i = 0; i < nr; i++)
667 usb_kill_urb(acm->ru[i].urb);
668 acm->control->needs_remote_wakeup = 0;
669 usb_autopm_put_interface(acm->control);
670 }
671 mutex_unlock(&open_mutex);
672}
673
674static void acm_tty_hangup(struct tty_struct *tty)
675{
676 struct acm *acm = tty->driver_data;
677 tty_port_hangup(&acm->port);
678 acm_port_down(acm, 0);
679}
680
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681static void acm_tty_close(struct tty_struct *tty, struct file *filp)
682{
683 struct acm *acm = tty->driver_data;
684
Alan Cox10077d42009-06-11 12:36:09 +0100685 /* Perform the closing process and see if we need to do the hardware
686 shutdown */
Thadeu Lima de Souza Cascardo922b1352009-06-25 14:41:30 +0100687 if (!acm || tty_port_close_start(&acm->port, tty, filp) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 return;
Alan Cox10077d42009-06-11 12:36:09 +0100689 acm_port_down(acm, 0);
690 tty_port_close_end(&acm->port, tty);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100691 mutex_lock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100692 tty_port_tty_set(&acm->port, NULL);
693 if (!acm->dev)
694 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100695 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696}
697
Alan Cox6e47e062009-06-11 12:37:06 +0100698static int acm_tty_write(struct tty_struct *tty,
699 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700{
701 struct acm *acm = tty->driver_data;
702 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200703 unsigned long flags;
704 int wbn;
705 struct acm_wb *wb;
706
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200707 dbg("Entering acm_tty_write to write %d bytes,", count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
709 if (!ACM_READY(acm))
710 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 if (!count)
712 return 0;
713
Oliver Neukum884b6002005-04-21 21:28:02 +0200714 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100715 wbn = acm_wb_alloc(acm);
716 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200717 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200718 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200720 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
Oliver Neukum884b6002005-04-21 21:28:02 +0200722 count = (count > acm->writesize) ? acm->writesize : count;
723 dbg("Get %d bytes...", count);
724 memcpy(wb->buf, buf, count);
725 wb->len = count;
726 spin_unlock_irqrestore(&acm->write_lock, flags);
727
Alan Cox6e47e062009-06-11 12:37:06 +0100728 stat = acm_write_start(acm, wbn);
729 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200730 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 return count;
732}
733
734static int acm_tty_write_room(struct tty_struct *tty)
735{
736 struct acm *acm = tty->driver_data;
737 if (!ACM_READY(acm))
738 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200739 /*
740 * Do not let the line discipline to know that we have a reserve,
741 * or it might get too enthusiastic.
742 */
David Brownell934da462008-08-06 18:44:12 -0700743 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744}
745
746static int acm_tty_chars_in_buffer(struct tty_struct *tty)
747{
748 struct acm *acm = tty->driver_data;
749 if (!ACM_READY(acm))
Alan Cox23198fd2009-07-20 16:05:27 +0100750 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200751 /*
752 * This is inaccurate (overcounts), but it works.
753 */
Oliver Neukum86478942006-05-13 22:50:47 +0200754 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755}
756
757static void acm_tty_throttle(struct tty_struct *tty)
758{
759 struct acm *acm = tty->driver_data;
760 if (!ACM_READY(acm))
761 return;
762 spin_lock_bh(&acm->throttle_lock);
763 acm->throttle = 1;
764 spin_unlock_bh(&acm->throttle_lock);
765}
766
767static void acm_tty_unthrottle(struct tty_struct *tty)
768{
769 struct acm *acm = tty->driver_data;
770 if (!ACM_READY(acm))
771 return;
772 spin_lock_bh(&acm->throttle_lock);
773 acm->throttle = 0;
774 spin_unlock_bh(&acm->throttle_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100775 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776}
777
Alan Cox9e989662008-07-22 11:18:03 +0100778static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779{
780 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100781 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 if (!ACM_READY(acm))
Alan Cox9e989662008-07-22 11:18:03 +0100783 return -EINVAL;
784 retval = acm_send_break(acm, state ? 0xffff : 0);
785 if (retval < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 dbg("send break failed");
Alan Cox9e989662008-07-22 11:18:03 +0100787 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788}
789
790static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
791{
792 struct acm *acm = tty->driver_data;
793
794 if (!ACM_READY(acm))
795 return -EINVAL;
796
797 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
798 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
799 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
800 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
801 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
802 TIOCM_CTS;
803}
804
805static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
806 unsigned int set, unsigned int clear)
807{
808 struct acm *acm = tty->driver_data;
809 unsigned int newctrl;
810
811 if (!ACM_READY(acm))
812 return -EINVAL;
813
814 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100815 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
816 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
817 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
818 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
820 newctrl = (newctrl & ~clear) | set;
821
822 if (acm->ctrlout == newctrl)
823 return 0;
824 return acm_set_control(acm, acm->ctrlout = newctrl);
825}
826
Alan Cox6e47e062009-06-11 12:37:06 +0100827static int acm_tty_ioctl(struct tty_struct *tty, struct file *file,
828 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829{
830 struct acm *acm = tty->driver_data;
831
832 if (!ACM_READY(acm))
833 return -EINVAL;
834
835 return -ENOIOCTLCMD;
836}
837
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100838static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 0, 50, 75, 110, 134, 150, 200, 300, 600,
840 1200, 1800, 2400, 4800, 9600, 19200, 38400,
841 57600, 115200, 230400, 460800, 500000, 576000,
842 921600, 1000000, 1152000, 1500000, 2000000,
843 2500000, 3000000, 3500000, 4000000
844};
845
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100846static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 5, 6, 7, 8
848};
849
Alan Cox6e47e062009-06-11 12:37:06 +0100850static void acm_tty_set_termios(struct tty_struct *tty,
851 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852{
853 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800854 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 struct usb_cdc_line_coding newline;
856 int newctrl = acm->ctrlout;
857
858 if (!ACM_READY(acm))
859 return;
860
Alan Cox6e47e062009-06-11 12:37:06 +0100861 /* FIXME: Needs to support the tty_baud interface */
862 /* FIXME: Broken on sparc */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
864 (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
865 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
866 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100867 (termios->c_cflag & PARODD ? 1 : 2) +
868 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
Alan Cox6e47e062009-06-11 12:37:06 +0100870 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
872
873 if (!newline.dwDTERate) {
874 newline.dwDTERate = acm->line.dwDTERate;
875 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +0100876 } else
877 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
879 if (newctrl != acm->ctrlout)
880 acm_set_control(acm, acm->ctrlout = newctrl);
881
882 if (memcmp(&acm->line, &newline, sizeof newline)) {
883 memcpy(&acm->line, &newline, sizeof newline);
884 dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
885 newline.bCharFormat, newline.bParityType,
886 newline.bDataBits);
887 acm_set_line(acm, &acm->line);
888 }
889}
890
891/*
892 * USB probe and disconnect routines.
893 */
894
Oliver Neukum830f4022008-06-25 14:17:16 +0200895/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +0200896static void acm_write_buffers_free(struct acm *acm)
897{
898 int i;
899 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +0200900 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200901
Alan Cox6e47e062009-06-11 12:37:06 +0100902 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Oliver Neukuma496c642008-10-21 10:39:04 +0200903 usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +0200904}
905
Oliver Neukum830f4022008-06-25 14:17:16 +0200906static void acm_read_buffers_free(struct acm *acm)
907{
908 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
909 int i, n = acm->rx_buflimit;
910
911 for (i = 0; i < n; i++)
Alan Cox6e47e062009-06-11 12:37:06 +0100912 usb_buffer_free(usb_dev, acm->readsize,
913 acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +0200914}
915
Oliver Neukum884b6002005-04-21 21:28:02 +0200916/* Little helper: write buffers allocate */
917static int acm_write_buffers_alloc(struct acm *acm)
918{
919 int i;
920 struct acm_wb *wb;
921
Oliver Neukum86478942006-05-13 22:50:47 +0200922 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200923 wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
924 &wb->dmah);
925 if (!wb->buf) {
926 while (i != 0) {
927 --i;
928 --wb;
929 usb_buffer_free(acm->dev, acm->writesize,
930 wb->buf, wb->dmah);
931 }
932 return -ENOMEM;
933 }
934 }
935 return 0;
936}
937
Alan Cox10077d42009-06-11 12:36:09 +0100938static int acm_probe(struct usb_interface *intf,
939 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940{
941 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100942 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -0700943 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 int buflen = intf->altsetting->extralen;
945 struct usb_interface *control_interface;
946 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200947 struct usb_endpoint_descriptor *epctrl = NULL;
948 struct usb_endpoint_descriptor *epread = NULL;
949 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 struct usb_device *usb_dev = interface_to_usbdev(intf);
951 struct acm *acm;
952 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +0100953 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 u8 *buf;
955 u8 ac_management_function = 0;
956 u8 call_management_function = 0;
957 int call_interface_num = -1;
958 int data_interface_num;
959 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200960 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100961 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200962 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963
Oliver Neukum86478942006-05-13 22:50:47 +0200964 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200966 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
967
968 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 if (quirks == NO_UNION_NORMAL) {
970 data_interface = usb_ifnum_to_if(usb_dev, 1);
971 control_interface = usb_ifnum_to_if(usb_dev, 0);
972 goto skip_normal_probe;
973 }
Alan Cox6e47e062009-06-11 12:37:06 +0100974
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 /* normal probing*/
976 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700977 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 return -EINVAL;
979 }
980
981 if (!buflen) {
Alan Cox6e47e062009-06-11 12:37:06 +0100982 if (intf->cur_altsetting->endpoint->extralen &&
983 intf->cur_altsetting->endpoint->extra) {
984 dev_dbg(&intf->dev,
985 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 buflen = intf->cur_altsetting->endpoint->extralen;
987 buffer = intf->cur_altsetting->endpoint->extra;
988 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700989 dev_err(&intf->dev,
990 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 return -EINVAL;
992 }
993 }
994
995 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +0100996 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700997 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 goto next_desc;
999 }
1000
Alan Cox6e47e062009-06-11 12:37:06 +01001001 switch (buffer[2]) {
1002 case USB_CDC_UNION_TYPE: /* we've found it */
1003 if (union_header) {
1004 dev_err(&intf->dev, "More than one "
1005 "union descriptor, skipping ...\n");
1006 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 }
Alan Cox6e47e062009-06-11 12:37:06 +01001008 union_header = (struct usb_cdc_union_desc *)buffer;
1009 break;
1010 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
1011 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1012 break;
1013 case USB_CDC_HEADER_TYPE: /* maybe check version */
1014 break; /* for now we ignore it */
1015 case USB_CDC_ACM_TYPE:
1016 ac_management_function = buffer[3];
1017 break;
1018 case USB_CDC_CALL_MANAGEMENT_TYPE:
1019 call_management_function = buffer[3];
1020 call_interface_num = buffer[4];
1021 if ((call_management_function & 3) != 3)
1022 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1023 break;
1024 default:
1025 /* there are LOTS more CDC descriptors that
1026 * could legitimately be found here.
1027 */
1028 dev_dbg(&intf->dev, "Ignoring descriptor: "
1029 "type %02x, length %d\n",
1030 buffer[2], buffer[0]);
1031 break;
1032 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033next_desc:
1034 buflen -= buffer[0];
1035 buffer += buffer[0];
1036 }
1037
1038 if (!union_header) {
1039 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001040 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
1042 control_interface = intf;
1043 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001044 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1045 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1046 return -ENODEV;
1047 } else {
1048 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1049 combined_interfaces = 1;
1050 control_interface = data_interface = intf;
1051 goto look_for_collapsed_interface;
1052 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 }
1054 } else {
1055 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1056 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1057 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001058 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 return -ENODEV;
1060 }
1061 }
Alan Cox6e47e062009-06-11 12:37:06 +01001062
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001064 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001066 if (control_interface == data_interface) {
1067 /* some broken devices designed for windows work this way */
1068 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1069 combined_interfaces = 1;
1070 /* a popular other OS doesn't use it */
1071 quirks |= NO_CAP_LINE;
1072 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1073 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1074 return -EINVAL;
1075 }
1076look_for_collapsed_interface:
1077 for (i = 0; i < 3; i++) {
1078 struct usb_endpoint_descriptor *ep;
1079 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1080
1081 if (usb_endpoint_is_int_in(ep))
1082 epctrl = ep;
1083 else if (usb_endpoint_is_bulk_out(ep))
1084 epwrite = ep;
1085 else if (usb_endpoint_is_bulk_in(ep))
1086 epread = ep;
1087 else
1088 return -EINVAL;
1089 }
1090 if (!epctrl || !epread || !epwrite)
1091 return -ENODEV;
1092 else
1093 goto made_compressed_probe;
1094 }
1095
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096skip_normal_probe:
1097
1098 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001099 if (data_interface->cur_altsetting->desc.bInterfaceClass
1100 != CDC_DATA_INTERFACE_TYPE) {
1101 if (control_interface->cur_altsetting->desc.bInterfaceClass
1102 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001104 dev_dbg(&intf->dev,
1105 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 t = control_interface;
1107 control_interface = data_interface;
1108 data_interface = t;
1109 } else {
1110 return -EINVAL;
1111 }
1112 }
Alan Stern74da5d62007-08-02 13:29:10 -04001113
1114 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001115 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001116 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001117
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001118 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1119 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001120 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 return -EBUSY;
1122 }
1123
1124
1125 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
1126 return -EINVAL;
1127
1128 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1129 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1130 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1131
1132
1133 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001134 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 /* descriptors are swapped */
1136 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001137 dev_dbg(&intf->dev,
1138 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 t = epread;
1140 epread = epwrite;
1141 epwrite = t;
1142 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001143made_compressed_probe:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 dbg("interfaces are valid");
1145 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
1146
1147 if (minor == ACM_TTY_MINORS) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001148 dev_err(&intf->dev, "no more free acm devices\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 return -ENODEV;
1150 }
1151
Alan Cox6e47e062009-06-11 12:37:06 +01001152 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1153 if (acm == NULL) {
Joe Perches898eb712007-10-18 03:06:30 -07001154 dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 goto alloc_fail;
1156 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157
1158 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Alan Cox6e47e062009-06-11 12:37:06 +01001159 readsize = le16_to_cpu(epread->wMaxPacketSize) *
1160 (quirks == SINGLE_RX_URB ? 1 : 2);
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001161 acm->combined_interfaces = combined_interfaces;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001162 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 acm->control = control_interface;
1164 acm->data = data_interface;
1165 acm->minor = minor;
1166 acm->dev = usb_dev;
1167 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001168 if (quirks & NO_CAP_LINE)
1169 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 acm->ctrlsize = ctrlsize;
1171 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001172 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001173 acm->urb_task.func = acm_rx_tasklet;
1174 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +00001175 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001176 INIT_WORK(&acm->waker, acm_waker);
David Brownelle5fbab52008-08-06 18:46:10 -07001177 init_waitqueue_head(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +02001179 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001180 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf72007-10-12 17:24:28 +02001181 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001182 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001183 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1184 if (acm->is_int_ep)
1185 acm->bInterval = epread->bInterval;
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 Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-10-12 17:24:28 +02001335 for (i = 0; i < acm->rx_buflimit; i++)
1336 usb_kill_urb(acm->ru[i].urb);
1337
Oliver Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-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);