blob: e3861b21e776600fb721699c8ede4e62dc51f8fc [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>
Oliver Neukum7af25b42009-09-08 23:51:28 +020062#include <linux/serial.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#include <linux/tty_driver.h>
64#include <linux/tty_flip.h>
65#include <linux/module.h>
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010066#include <linux/mutex.h>
Alan Cox10077d42009-06-11 12:36:09 +010067#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068#include <linux/usb.h>
David Brownella8c28f22006-06-13 09:57:47 -070069#include <linux/usb/cdc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#include <asm/byteorder.h>
71#include <asm/unaligned.h>
David Kubicek61a87ad2005-11-01 18:51:34 +010072#include <linux/list.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74#include "cdc-acm.h"
75
David Brownelle5fbab52008-08-06 18:46:10 -070076
77#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */
78
Linus Torvalds1da177e2005-04-16 15:20:36 -070079/*
80 * Version Information
81 */
David Engrafe4cf3aa2008-03-20 10:01:34 +010082#define DRIVER_VERSION "v0.26"
David Kubicek61a87ad2005-11-01 18:51:34 +010083#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
Linus Torvalds1da177e2005-04-16 15:20:36 -070084#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
85
86static struct usb_driver acm_driver;
87static struct tty_driver *acm_tty_driver;
88static struct acm *acm_table[ACM_TTY_MINORS];
89
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010090static DEFINE_MUTEX(open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
Alan Cox10077d42009-06-11 12:36:09 +010092#define ACM_READY(acm) (acm && acm->dev && acm->port.count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
Alan Cox739e0282009-06-11 12:27:50 +010094static const struct tty_port_operations acm_port_ops = {
95};
96
David Brownelle5fbab52008-08-06 18:46:10 -070097#ifdef VERBOSE_DEBUG
98#define verbose 1
99#else
100#define verbose 0
101#endif
102
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103/*
104 * Functions for ACM control messages.
105 */
106
Alan Cox6e47e062009-06-11 12:37:06 +0100107static int acm_ctrl_msg(struct acm *acm, int request, int value,
108 void *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109{
110 int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
111 request, USB_RT_ACM, value,
112 acm->control->altsetting[0].desc.bInterfaceNumber,
113 buf, len, 5000);
Alan Cox6e47e062009-06-11 12:37:06 +0100114 dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d",
115 request, value, len, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 return retval < 0 ? retval : 0;
117}
118
119/* devices aren't required to support these requests.
120 * the cdc acm descriptor tells whether they do...
121 */
122#define acm_set_control(acm, control) \
123 acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
124#define acm_set_line(acm, line) \
125 acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
126#define acm_send_break(acm, ms) \
127 acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
128
129/*
Oliver Neukum884b6002005-04-21 21:28:02 +0200130 * Write buffer management.
131 * All of these assume proper locks taken by the caller.
132 */
133
134static int acm_wb_alloc(struct acm *acm)
135{
136 int i, wbn;
137 struct acm_wb *wb;
138
David Engrafe4cf3aa2008-03-20 10:01:34 +0100139 wbn = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200140 i = 0;
141 for (;;) {
142 wb = &acm->wb[wbn];
143 if (!wb->use) {
144 wb->use = 1;
145 return wbn;
146 }
Oliver Neukum86478942006-05-13 22:50:47 +0200147 wbn = (wbn + 1) % ACM_NW;
148 if (++i >= ACM_NW)
Oliver Neukum884b6002005-04-21 21:28:02 +0200149 return -1;
150 }
151}
152
Oliver Neukum884b6002005-04-21 21:28:02 +0200153static int acm_wb_is_avail(struct acm *acm)
154{
155 int i, n;
David Brownelle5fbab52008-08-06 18:46:10 -0700156 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200157
Oliver Neukum86478942006-05-13 22:50:47 +0200158 n = ACM_NW;
David Brownelle5fbab52008-08-06 18:46:10 -0700159 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100160 for (i = 0; i < ACM_NW; i++)
Oliver Neukum86478942006-05-13 22:50:47 +0200161 n -= acm->wb[i].use;
David Brownelle5fbab52008-08-06 18:46:10 -0700162 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200163 return n;
164}
165
Oliver Neukum884b6002005-04-21 21:28:02 +0200166/*
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800167 * Finish write. Caller must hold acm->write_lock
Oliver Neukum884b6002005-04-21 21:28:02 +0200168 */
David Engrafe4cf3aa2008-03-20 10:01:34 +0100169static void acm_write_done(struct acm *acm, struct acm_wb *wb)
Oliver Neukum884b6002005-04-21 21:28:02 +0200170{
David Engrafe4cf3aa2008-03-20 10:01:34 +0100171 wb->use = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200172 acm->transmitting--;
Oliver Neukum884b6002005-04-21 21:28:02 +0200173}
174
175/*
176 * Poke write.
Oliver Neukum11ea8592008-06-20 11:25:57 +0200177 *
178 * the caller is responsible for locking
Oliver Neukum884b6002005-04-21 21:28:02 +0200179 */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200180
181static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
182{
183 int rc;
184
185 acm->transmitting++;
186
187 wb->urb->transfer_buffer = wb->buf;
188 wb->urb->transfer_dma = wb->dmah;
189 wb->urb->transfer_buffer_length = wb->len;
190 wb->urb->dev = acm->dev;
191
Alan Cox6e47e062009-06-11 12:37:06 +0100192 rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
193 if (rc < 0) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200194 dbg("usb_submit_urb(write bulk) failed: %d", rc);
195 acm_write_done(acm, wb);
196 }
197 return rc;
198}
199
David Engrafe4cf3aa2008-03-20 10:01:34 +0100200static int acm_write_start(struct acm *acm, int wbn)
Oliver Neukum884b6002005-04-21 21:28:02 +0200201{
202 unsigned long flags;
David Brownell934da462008-08-06 18:44:12 -0700203 struct acm_wb *wb = &acm->wb[wbn];
Oliver Neukum884b6002005-04-21 21:28:02 +0200204 int rc;
205
206 spin_lock_irqsave(&acm->write_lock, flags);
207 if (!acm->dev) {
David Brownell934da462008-08-06 18:44:12 -0700208 wb->use = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200209 spin_unlock_irqrestore(&acm->write_lock, flags);
210 return -ENODEV;
211 }
212
Oliver Neukum11ea8592008-06-20 11:25:57 +0200213 dbg("%s susp_count: %d", __func__, acm->susp_count);
214 if (acm->susp_count) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200215 acm->delayed_wb = wb;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200216 schedule_work(&acm->waker);
217 spin_unlock_irqrestore(&acm->write_lock, flags);
218 return 0; /* A white lie */
219 }
220 usb_mark_last_busy(acm->dev);
221
Oliver Neukum11ea8592008-06-20 11:25:57 +0200222 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200223 spin_unlock_irqrestore(&acm->write_lock, flags);
224
Oliver Neukum884b6002005-04-21 21:28:02 +0200225 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200226
Oliver Neukum884b6002005-04-21 21:28:02 +0200227}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100228/*
229 * attributes exported through sysfs
230 */
231static ssize_t show_caps
232(struct device *dev, struct device_attribute *attr, char *buf)
233{
234 struct usb_interface *intf = to_usb_interface(dev);
235 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200236
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100237 return sprintf(buf, "%d", acm->ctrl_caps);
238}
239static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
240
241static ssize_t show_country_codes
242(struct device *dev, struct device_attribute *attr, char *buf)
243{
244 struct usb_interface *intf = to_usb_interface(dev);
245 struct acm *acm = usb_get_intfdata(intf);
246
247 memcpy(buf, acm->country_codes, acm->country_code_size);
248 return acm->country_code_size;
249}
250
251static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
252
253static ssize_t show_country_rel_date
254(struct device *dev, struct device_attribute *attr, char *buf)
255{
256 struct usb_interface *intf = to_usb_interface(dev);
257 struct acm *acm = usb_get_intfdata(intf);
258
259 return sprintf(buf, "%d", acm->country_rel_date);
260}
261
262static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200263/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 * Interrupt handlers for various ACM device responses
265 */
266
267/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100268static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269{
270 struct acm *acm = urb->context;
271 struct usb_cdc_notification *dr = urb->transfer_buffer;
Alan Cox10077d42009-06-11 12:36:09 +0100272 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 unsigned char *data;
274 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700275 int retval;
276 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700278 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 case 0:
280 /* success */
281 break;
282 case -ECONNRESET:
283 case -ENOENT:
284 case -ESHUTDOWN:
285 /* this urb is terminated, clean up */
Harvey Harrison441b62c2008-03-03 16:08:34 -0800286 dbg("%s - urb shutting down with status: %d", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 return;
288 default:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800289 dbg("%s - nonzero urb status received: %d", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 goto exit;
291 }
292
293 if (!ACM_READY(acm))
294 goto exit;
295
296 data = (unsigned char *)(dr + 1);
297 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100298 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
299 dbg("%s network", dr->wValue ?
300 "connected to" : "disconnected from");
301 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302
Alan Cox6e47e062009-06-11 12:37:06 +0100303 case USB_CDC_NOTIFY_SERIAL_STATE:
304 tty = tty_port_tty_get(&acm->port);
305 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
Alan Cox6e47e062009-06-11 12:37:06 +0100307 if (tty) {
308 if (!acm->clocal &&
309 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
310 dbg("calling hangup");
311 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 }
Alan Cox6e47e062009-06-11 12:37:06 +0100313 tty_kref_put(tty);
314 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
Alan Cox6e47e062009-06-11 12:37:06 +0100316 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
Alan Cox6e47e062009-06-11 12:37:06 +0100318 dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
319 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
320 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
321 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
322 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
323 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
324 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
325 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 break;
327
Alan Cox6e47e062009-06-11 12:37:06 +0100328 default:
329 dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
330 dr->bNotificationType, dr->wIndex,
331 dr->wLength, data[0], data[1]);
332 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 }
334exit:
Oliver Neukum11ea8592008-06-20 11:25:57 +0200335 usb_mark_last_busy(acm->dev);
Alan Cox6e47e062009-06-11 12:37:06 +0100336 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700337 if (retval)
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700338 dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
339 "result %d", __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340}
341
342/* data interface returns incoming bytes, or we got unthrottled */
David Howells7d12e782006-10-05 14:55:46 +0100343static void acm_read_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344{
David Kubicek61a87ad2005-11-01 18:51:34 +0100345 struct acm_rb *buf;
346 struct acm_ru *rcv = urb->context;
347 struct acm *acm = rcv->instance;
Oliver Neukum86478942006-05-13 22:50:47 +0200348 int status = urb->status;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700349
350 dbg("Entering acm_read_bulk with status %d", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351
Oliver Neukum11ea8592008-06-20 11:25:57 +0200352 if (!ACM_READY(acm)) {
353 dev_dbg(&acm->data->dev, "Aborting, acm not ready");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200355 }
356 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
Oliver Neukum86478942006-05-13 22:50:47 +0200358 if (status)
Joe Perches898eb712007-10-18 03:06:30 -0700359 dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360
David Kubicek61a87ad2005-11-01 18:51:34 +0100361 buf = rcv->buffer;
362 buf->size = urb->actual_length;
363
Oliver Neukum86478942006-05-13 22:50:47 +0200364 if (likely(status == 0)) {
365 spin_lock(&acm->read_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200366 acm->processing++;
Oliver Neukum86478942006-05-13 22:50:47 +0200367 list_add_tail(&rcv->list, &acm->spare_read_urbs);
368 list_add_tail(&buf->list, &acm->filled_read_bufs);
369 spin_unlock(&acm->read_lock);
370 } else {
371 /* we drop the buffer due to an error */
372 spin_lock(&acm->read_lock);
373 list_add_tail(&rcv->list, &acm->spare_read_urbs);
374 list_add(&buf->list, &acm->spare_read_bufs);
375 spin_unlock(&acm->read_lock);
376 /* nevertheless the tasklet must be kicked unconditionally
377 so the queue cannot dry up */
378 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200379 if (likely(!acm->susp_count))
380 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381}
382
383static void acm_rx_tasklet(unsigned long _acm)
384{
385 struct acm *acm = (void *)_acm;
David Kubicek61a87ad2005-11-01 18:51:34 +0100386 struct acm_rb *buf;
Alan Cox10077d42009-06-11 12:36:09 +0100387 struct tty_struct *tty;
David Kubicek61a87ad2005-11-01 18:51:34 +0100388 struct acm_ru *rcv;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200389 unsigned long flags;
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100390 unsigned char throttled;
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
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200466 if (acm->is_int_ep)
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400467 usb_fill_int_urb(rcv->urb, acm->dev,
468 acm->rx_endpoint,
469 buf->base,
470 acm->readsize,
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200471 acm_read_bulk, rcv, acm->bInterval);
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400472 else
473 usb_fill_bulk_urb(rcv->urb, acm->dev,
474 acm->rx_endpoint,
475 buf->base,
476 acm->readsize,
477 acm_read_bulk, rcv);
David Kubicek61a87ad2005-11-01 18:51:34 +0100478 rcv->urb->transfer_dma = buf->dma;
479 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
480
Alan Cox6e47e062009-06-11 12:37:06 +0100481 /* This shouldn't kill the driver as unsuccessful URBs are
482 returned to the free-urbs-pool and resubmited ASAP */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200483 spin_lock_irqsave(&acm->read_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100484 if (acm->susp_count ||
485 usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100486 list_add(&buf->list, &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100487 list_add(&rcv->list, &acm->spare_read_urbs);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200488 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200489 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100490 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200491 } else {
492 spin_unlock_irqrestore(&acm->read_lock, flags);
493 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 +0100494 }
495 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200496 spin_lock_irqsave(&acm->read_lock, flags);
497 acm->processing = 0;
498 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499}
500
501/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100502static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503{
Ming Leicdc97792008-02-24 18:41:47 +0800504 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700505 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800506 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200507
David Brownelle5fbab52008-08-06 18:46:10 -0700508 if (verbose || urb->status
509 || (urb->actual_length != urb->transfer_buffer_length))
510 dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
511 urb->actual_length,
512 urb->transfer_buffer_length,
513 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800515 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100516 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800517 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200518 if (ACM_READY(acm))
519 schedule_work(&acm->work);
David Brownelle5fbab52008-08-06 18:46:10 -0700520 else
521 wake_up_interruptible(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522}
523
David Howellsc4028952006-11-22 14:57:56 +0000524static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525{
David Howellsc4028952006-11-22 14:57:56 +0000526 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100527 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700528
529 dev_vdbg(&acm->data->dev, "tx work\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 if (!ACM_READY(acm))
531 return;
Alan Cox10077d42009-06-11 12:36:09 +0100532 tty = tty_port_tty_get(&acm->port);
533 tty_wakeup(tty);
534 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535}
536
Oliver Neukum11ea8592008-06-20 11:25:57 +0200537static void acm_waker(struct work_struct *waker)
538{
539 struct acm *acm = container_of(waker, struct acm, waker);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200540 int rv;
541
542 rv = usb_autopm_get_interface(acm->control);
543 if (rv < 0) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700544 dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200545 return;
546 }
547 if (acm->delayed_wb) {
548 acm_start_wb(acm, acm->delayed_wb);
549 acm->delayed_wb = NULL;
550 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200551 usb_autopm_put_interface(acm->control);
552}
553
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554/*
555 * TTY handlers
556 */
557
558static int acm_tty_open(struct tty_struct *tty, struct file *filp)
559{
560 struct acm *acm;
Thadeu Lima de Souza Cascardo42dd2aa2009-06-25 14:41:24 +0100561 int rv = -ENODEV;
David Kubicek61a87ad2005-11-01 18:51:34 +0100562 int i;
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200563 dbg("Entering acm_tty_open.");
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100564
565 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
567 acm = acm_table[tty->index];
568 if (!acm || !acm->dev)
569 goto err_out;
570 else
571 rv = 0;
572
David Engraf28d1dfa2008-03-20 10:53:52 +0100573 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
Alan Cox10077d42009-06-11 12:36:09 +0100574
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 tty->driver_data = acm;
Alan Cox10077d42009-06-11 12:36:09 +0100576 tty_port_tty_set(&acm->port, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Oliver Neukum94409cc2008-02-11 15:22:29 +0100578 if (usb_autopm_get_interface(acm->control) < 0)
579 goto early_bail;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200580 else
581 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf72007-10-12 17:24:28 +0200582
583 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100584 if (acm->port.count++) {
Oliver Neukum1365baf72007-10-12 17:24:28 +0200585 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 goto done;
Alan Cox10077d42009-06-11 12:36:09 +0100587 }
Oliver Neukum1365baf72007-10-12 17:24:28 +0200588
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 acm->ctrlurb->dev = acm->dev;
590 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
591 dbg("usb_submit_urb(ctrl irq) failed");
592 goto bail_out;
593 }
594
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100595 if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
596 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 goto full_bailout;
Alan Cox10077d42009-06-11 12:36:09 +0100598
Oliver Neukum11ea8592008-06-20 11:25:57 +0200599 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
David Kubicek61a87ad2005-11-01 18:51:34 +0100601 INIT_LIST_HEAD(&acm->spare_read_urbs);
602 INIT_LIST_HEAD(&acm->spare_read_bufs);
603 INIT_LIST_HEAD(&acm->filled_read_bufs);
Alan Cox6e47e062009-06-11 12:37:06 +0100604
605 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100606 list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
Alan Cox6e47e062009-06-11 12:37:06 +0100607 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100608 list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100609
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100610 acm->throttle = 0;
611
David Kubicek61a87ad2005-11-01 18:51:34 +0100612 tasklet_schedule(&acm->urb_task);
Oliver Neukum7af25b42009-09-08 23:51:28 +0200613 set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
Alan Cox10077d42009-06-11 12:36:09 +0100614 rv = tty_port_block_til_ready(&acm->port, tty, filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615done:
Oliver Neukum1365baf72007-10-12 17:24:28 +0200616 mutex_unlock(&acm->mutex);
Alexey Dobriyan74573ee2008-08-20 16:56:04 -0700617err_out:
Oliver Neukum94409cc2008-02-11 15:22:29 +0100618 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 return rv;
620
621full_bailout:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 usb_kill_urb(acm->ctrlurb);
623bail_out:
Oliver Neukum1365baf72007-10-12 17:24:28 +0200624 usb_autopm_put_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100625 acm->port.count--;
Oliver Neukum1365baf72007-10-12 17:24:28 +0200626 mutex_unlock(&acm->mutex);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100627early_bail:
628 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100629 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 return -EIO;
631}
632
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700633static void acm_tty_unregister(struct acm *acm)
634{
Alan Cox10077d42009-06-11 12:36:09 +0100635 int i, nr;
David Kubicek61a87ad2005-11-01 18:51:34 +0100636
Oliver Neukum86478942006-05-13 22:50:47 +0200637 nr = acm->rx_buflimit;
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700638 tty_unregister_device(acm_tty_driver, acm->minor);
639 usb_put_intf(acm->control);
640 acm_table[acm->minor] = NULL;
641 usb_free_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100642 for (i = 0; i < ACM_NW; i++)
643 usb_free_urb(acm->wb[i].urb);
Oliver Neukum86478942006-05-13 22:50:47 +0200644 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100645 usb_free_urb(acm->ru[i].urb);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100646 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700647 kfree(acm);
648}
649
David Brownelle5fbab52008-08-06 18:46:10 -0700650static int acm_tty_chars_in_buffer(struct tty_struct *tty);
651
Alan Cox10077d42009-06-11 12:36:09 +0100652static void acm_port_down(struct acm *acm, int drain)
653{
654 int i, nr = acm->rx_buflimit;
655 mutex_lock(&open_mutex);
656 if (acm->dev) {
657 usb_autopm_get_interface(acm->control);
658 acm_set_control(acm, acm->ctrlout = 0);
659 /* try letting the last writes drain naturally */
660 if (drain) {
661 wait_event_interruptible_timeout(acm->drain_wait,
662 (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
663 ACM_CLOSE_TIMEOUT * HZ);
664 }
665 usb_kill_urb(acm->ctrlurb);
666 for (i = 0; i < ACM_NW; i++)
667 usb_kill_urb(acm->wb[i].urb);
668 for (i = 0; i < nr; i++)
669 usb_kill_urb(acm->ru[i].urb);
670 acm->control->needs_remote_wakeup = 0;
671 usb_autopm_put_interface(acm->control);
672 }
673 mutex_unlock(&open_mutex);
674}
675
676static void acm_tty_hangup(struct tty_struct *tty)
677{
678 struct acm *acm = tty->driver_data;
679 tty_port_hangup(&acm->port);
680 acm_port_down(acm, 0);
681}
682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683static void acm_tty_close(struct tty_struct *tty, struct file *filp)
684{
685 struct acm *acm = tty->driver_data;
686
Alan Cox10077d42009-06-11 12:36:09 +0100687 /* Perform the closing process and see if we need to do the hardware
688 shutdown */
Thadeu Lima de Souza Cascardo922b1352009-06-25 14:41:30 +0100689 if (!acm || tty_port_close_start(&acm->port, tty, filp) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 return;
Alan Cox10077d42009-06-11 12:36:09 +0100691 acm_port_down(acm, 0);
692 tty_port_close_end(&acm->port, tty);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100693 mutex_lock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100694 tty_port_tty_set(&acm->port, NULL);
695 if (!acm->dev)
696 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100697 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698}
699
Alan Cox6e47e062009-06-11 12:37:06 +0100700static int acm_tty_write(struct tty_struct *tty,
701 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702{
703 struct acm *acm = tty->driver_data;
704 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200705 unsigned long flags;
706 int wbn;
707 struct acm_wb *wb;
708
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200709 dbg("Entering acm_tty_write to write %d bytes,", count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
711 if (!ACM_READY(acm))
712 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 if (!count)
714 return 0;
715
Oliver Neukum884b6002005-04-21 21:28:02 +0200716 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100717 wbn = acm_wb_alloc(acm);
718 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200719 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200720 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200722 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
Oliver Neukum884b6002005-04-21 21:28:02 +0200724 count = (count > acm->writesize) ? acm->writesize : count;
725 dbg("Get %d bytes...", count);
726 memcpy(wb->buf, buf, count);
727 wb->len = count;
728 spin_unlock_irqrestore(&acm->write_lock, flags);
729
Alan Cox6e47e062009-06-11 12:37:06 +0100730 stat = acm_write_start(acm, wbn);
731 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200732 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 return count;
734}
735
736static int acm_tty_write_room(struct tty_struct *tty)
737{
738 struct acm *acm = tty->driver_data;
739 if (!ACM_READY(acm))
740 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200741 /*
742 * Do not let the line discipline to know that we have a reserve,
743 * or it might get too enthusiastic.
744 */
David Brownell934da462008-08-06 18:44:12 -0700745 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746}
747
748static int acm_tty_chars_in_buffer(struct tty_struct *tty)
749{
750 struct acm *acm = tty->driver_data;
751 if (!ACM_READY(acm))
Alan Cox23198fd2009-07-20 16:05:27 +0100752 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200753 /*
754 * This is inaccurate (overcounts), but it works.
755 */
Oliver Neukum86478942006-05-13 22:50:47 +0200756 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757}
758
759static void acm_tty_throttle(struct tty_struct *tty)
760{
761 struct acm *acm = tty->driver_data;
762 if (!ACM_READY(acm))
763 return;
764 spin_lock_bh(&acm->throttle_lock);
765 acm->throttle = 1;
766 spin_unlock_bh(&acm->throttle_lock);
767}
768
769static void acm_tty_unthrottle(struct tty_struct *tty)
770{
771 struct acm *acm = tty->driver_data;
772 if (!ACM_READY(acm))
773 return;
774 spin_lock_bh(&acm->throttle_lock);
775 acm->throttle = 0;
776 spin_unlock_bh(&acm->throttle_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100777 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778}
779
Alan Cox9e989662008-07-22 11:18:03 +0100780static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781{
782 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100783 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 if (!ACM_READY(acm))
Alan Cox9e989662008-07-22 11:18:03 +0100785 return -EINVAL;
786 retval = acm_send_break(acm, state ? 0xffff : 0);
787 if (retval < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 dbg("send break failed");
Alan Cox9e989662008-07-22 11:18:03 +0100789 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790}
791
792static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
793{
794 struct acm *acm = tty->driver_data;
795
796 if (!ACM_READY(acm))
797 return -EINVAL;
798
799 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
800 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
801 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
802 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
803 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
804 TIOCM_CTS;
805}
806
807static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
808 unsigned int set, unsigned int clear)
809{
810 struct acm *acm = tty->driver_data;
811 unsigned int newctrl;
812
813 if (!ACM_READY(acm))
814 return -EINVAL;
815
816 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100817 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
818 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
819 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
820 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
822 newctrl = (newctrl & ~clear) | set;
823
824 if (acm->ctrlout == newctrl)
825 return 0;
826 return acm_set_control(acm, acm->ctrlout = newctrl);
827}
828
Alan Cox6e47e062009-06-11 12:37:06 +0100829static int acm_tty_ioctl(struct tty_struct *tty, struct file *file,
830 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831{
832 struct acm *acm = tty->driver_data;
833
834 if (!ACM_READY(acm))
835 return -EINVAL;
836
837 return -ENOIOCTLCMD;
838}
839
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100840static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 0, 50, 75, 110, 134, 150, 200, 300, 600,
842 1200, 1800, 2400, 4800, 9600, 19200, 38400,
843 57600, 115200, 230400, 460800, 500000, 576000,
844 921600, 1000000, 1152000, 1500000, 2000000,
845 2500000, 3000000, 3500000, 4000000
846};
847
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100848static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 5, 6, 7, 8
850};
851
Alan Cox6e47e062009-06-11 12:37:06 +0100852static void acm_tty_set_termios(struct tty_struct *tty,
853 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854{
855 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800856 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 struct usb_cdc_line_coding newline;
858 int newctrl = acm->ctrlout;
859
860 if (!ACM_READY(acm))
861 return;
862
Alan Cox9b80fee2009-09-19 13:13:23 -0700863 newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
865 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100866 (termios->c_cflag & PARODD ? 1 : 2) +
867 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
Alan Cox6e47e062009-06-11 12:37:06 +0100869 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
871
872 if (!newline.dwDTERate) {
873 newline.dwDTERate = acm->line.dwDTERate;
874 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +0100875 } else
876 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
878 if (newctrl != acm->ctrlout)
879 acm_set_control(acm, acm->ctrlout = newctrl);
880
881 if (memcmp(&acm->line, &newline, sizeof newline)) {
882 memcpy(&acm->line, &newline, sizeof newline);
883 dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
884 newline.bCharFormat, newline.bParityType,
885 newline.bDataBits);
886 acm_set_line(acm, &acm->line);
887 }
888}
889
890/*
891 * USB probe and disconnect routines.
892 */
893
Oliver Neukum830f4022008-06-25 14:17:16 +0200894/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +0200895static void acm_write_buffers_free(struct acm *acm)
896{
897 int i;
898 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +0200899 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200900
Alan Cox6e47e062009-06-11 12:37:06 +0100901 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Oliver Neukuma496c642008-10-21 10:39:04 +0200902 usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +0200903}
904
Oliver Neukum830f4022008-06-25 14:17:16 +0200905static void acm_read_buffers_free(struct acm *acm)
906{
907 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
908 int i, n = acm->rx_buflimit;
909
910 for (i = 0; i < n; i++)
Alan Cox6e47e062009-06-11 12:37:06 +0100911 usb_buffer_free(usb_dev, acm->readsize,
912 acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +0200913}
914
Oliver Neukum884b6002005-04-21 21:28:02 +0200915/* Little helper: write buffers allocate */
916static int acm_write_buffers_alloc(struct acm *acm)
917{
918 int i;
919 struct acm_wb *wb;
920
Oliver Neukum86478942006-05-13 22:50:47 +0200921 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200922 wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
923 &wb->dmah);
924 if (!wb->buf) {
925 while (i != 0) {
926 --i;
927 --wb;
928 usb_buffer_free(acm->dev, acm->writesize,
929 wb->buf, wb->dmah);
930 }
931 return -ENOMEM;
932 }
933 }
934 return 0;
935}
936
Alan Cox10077d42009-06-11 12:36:09 +0100937static int acm_probe(struct usb_interface *intf,
938 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939{
940 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100941 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -0700942 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 int buflen = intf->altsetting->extralen;
944 struct usb_interface *control_interface;
945 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200946 struct usb_endpoint_descriptor *epctrl = NULL;
947 struct usb_endpoint_descriptor *epread = NULL;
948 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 struct usb_device *usb_dev = interface_to_usbdev(intf);
950 struct acm *acm;
951 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +0100952 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 u8 *buf;
954 u8 ac_management_function = 0;
955 u8 call_management_function = 0;
956 int call_interface_num = -1;
957 int data_interface_num;
958 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200959 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100960 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200961 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962
Oliver Neukum86478942006-05-13 22:50:47 +0200963 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200965 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
966
967 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 if (quirks == NO_UNION_NORMAL) {
969 data_interface = usb_ifnum_to_if(usb_dev, 1);
970 control_interface = usb_ifnum_to_if(usb_dev, 0);
971 goto skip_normal_probe;
972 }
Alan Cox6e47e062009-06-11 12:37:06 +0100973
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 /* normal probing*/
975 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700976 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 return -EINVAL;
978 }
979
980 if (!buflen) {
Alan Cox6e47e062009-06-11 12:37:06 +0100981 if (intf->cur_altsetting->endpoint->extralen &&
982 intf->cur_altsetting->endpoint->extra) {
983 dev_dbg(&intf->dev,
984 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 buflen = intf->cur_altsetting->endpoint->extralen;
986 buffer = intf->cur_altsetting->endpoint->extra;
987 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700988 dev_err(&intf->dev,
989 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 return -EINVAL;
991 }
992 }
993
994 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +0100995 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700996 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 goto next_desc;
998 }
999
Alan Cox6e47e062009-06-11 12:37:06 +01001000 switch (buffer[2]) {
1001 case USB_CDC_UNION_TYPE: /* we've found it */
1002 if (union_header) {
1003 dev_err(&intf->dev, "More than one "
1004 "union descriptor, skipping ...\n");
1005 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 }
Alan Cox6e47e062009-06-11 12:37:06 +01001007 union_header = (struct usb_cdc_union_desc *)buffer;
1008 break;
1009 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
1010 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1011 break;
1012 case USB_CDC_HEADER_TYPE: /* maybe check version */
1013 break; /* for now we ignore it */
1014 case USB_CDC_ACM_TYPE:
1015 ac_management_function = buffer[3];
1016 break;
1017 case USB_CDC_CALL_MANAGEMENT_TYPE:
1018 call_management_function = buffer[3];
1019 call_interface_num = buffer[4];
1020 if ((call_management_function & 3) != 3)
1021 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1022 break;
1023 default:
1024 /* there are LOTS more CDC descriptors that
1025 * could legitimately be found here.
1026 */
1027 dev_dbg(&intf->dev, "Ignoring descriptor: "
1028 "type %02x, length %d\n",
1029 buffer[2], buffer[0]);
1030 break;
1031 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032next_desc:
1033 buflen -= buffer[0];
1034 buffer += buffer[0];
1035 }
1036
1037 if (!union_header) {
1038 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001039 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
1041 control_interface = intf;
1042 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001043 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1044 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1045 return -ENODEV;
1046 } else {
1047 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1048 combined_interfaces = 1;
1049 control_interface = data_interface = intf;
1050 goto look_for_collapsed_interface;
1051 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 }
1053 } else {
1054 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1055 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1056 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001057 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 return -ENODEV;
1059 }
1060 }
Alan Cox6e47e062009-06-11 12:37:06 +01001061
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001063 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001065 if (control_interface == data_interface) {
1066 /* some broken devices designed for windows work this way */
1067 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1068 combined_interfaces = 1;
1069 /* a popular other OS doesn't use it */
1070 quirks |= NO_CAP_LINE;
1071 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1072 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1073 return -EINVAL;
1074 }
1075look_for_collapsed_interface:
1076 for (i = 0; i < 3; i++) {
1077 struct usb_endpoint_descriptor *ep;
1078 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1079
1080 if (usb_endpoint_is_int_in(ep))
1081 epctrl = ep;
1082 else if (usb_endpoint_is_bulk_out(ep))
1083 epwrite = ep;
1084 else if (usb_endpoint_is_bulk_in(ep))
1085 epread = ep;
1086 else
1087 return -EINVAL;
1088 }
1089 if (!epctrl || !epread || !epwrite)
1090 return -ENODEV;
1091 else
1092 goto made_compressed_probe;
1093 }
1094
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095skip_normal_probe:
1096
1097 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001098 if (data_interface->cur_altsetting->desc.bInterfaceClass
1099 != CDC_DATA_INTERFACE_TYPE) {
1100 if (control_interface->cur_altsetting->desc.bInterfaceClass
1101 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001103 dev_dbg(&intf->dev,
1104 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 t = control_interface;
1106 control_interface = data_interface;
1107 data_interface = t;
1108 } else {
1109 return -EINVAL;
1110 }
1111 }
Alan Stern74da5d62007-08-02 13:29:10 -04001112
1113 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001114 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001115 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001116
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001117 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1118 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001119 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 return -EBUSY;
1121 }
1122
1123
1124 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
1125 return -EINVAL;
1126
1127 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1128 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1129 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1130
1131
1132 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001133 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 /* descriptors are swapped */
1135 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001136 dev_dbg(&intf->dev,
1137 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 t = epread;
1139 epread = epwrite;
1140 epwrite = t;
1141 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001142made_compressed_probe:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 dbg("interfaces are valid");
1144 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
1145
1146 if (minor == ACM_TTY_MINORS) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001147 dev_err(&intf->dev, "no more free acm devices\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 return -ENODEV;
1149 }
1150
Alan Cox6e47e062009-06-11 12:37:06 +01001151 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1152 if (acm == NULL) {
Joe Perches898eb712007-10-18 03:06:30 -07001153 dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 goto alloc_fail;
1155 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156
1157 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Alan Cox6e47e062009-06-11 12:37:06 +01001158 readsize = le16_to_cpu(epread->wMaxPacketSize) *
1159 (quirks == SINGLE_RX_URB ? 1 : 2);
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001160 acm->combined_interfaces = combined_interfaces;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001161 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 acm->control = control_interface;
1163 acm->data = data_interface;
1164 acm->minor = minor;
1165 acm->dev = usb_dev;
1166 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001167 if (quirks & NO_CAP_LINE)
1168 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 acm->ctrlsize = ctrlsize;
1170 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001171 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001172 acm->urb_task.func = acm_rx_tasklet;
1173 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +00001174 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001175 INIT_WORK(&acm->waker, acm_waker);
David Brownelle5fbab52008-08-06 18:46:10 -07001176 init_waitqueue_head(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +02001178 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001179 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf72007-10-12 17:24:28 +02001180 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001181 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001182 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1183 if (acm->is_int_ep)
1184 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001185 tty_port_init(&acm->port);
1186 acm->port.ops = &acm_port_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187
1188 buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
1189 if (!buf) {
Joe Perches898eb712007-10-18 03:06:30 -07001190 dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 goto alloc_fail2;
1192 }
1193 acm->ctrl_buffer = buf;
1194
Oliver Neukum884b6002005-04-21 21:28:02 +02001195 if (acm_write_buffers_alloc(acm) < 0) {
Joe Perches898eb712007-10-18 03:06:30 -07001196 dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 goto alloc_fail4;
1198 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199
1200 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1201 if (!acm->ctrlurb) {
Joe Perches898eb712007-10-18 03:06:30 -07001202 dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 goto alloc_fail5;
1204 }
Oliver Neukum86478942006-05-13 22:50:47 +02001205 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001206 struct acm_ru *rcv = &(acm->ru[i]);
1207
Alan Cox6e47e062009-06-11 12:37:06 +01001208 rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
1209 if (rcv->urb == NULL) {
1210 dev_dbg(&intf->dev,
1211 "out of memory (read urbs usb_alloc_urb)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001212 goto alloc_fail7;
1213 }
1214
1215 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1216 rcv->instance = acm;
1217 }
Oliver Neukum86478942006-05-13 22:50:47 +02001218 for (i = 0; i < num_rx_buf; i++) {
David Brownell672c4e12008-08-06 18:41:12 -07001219 struct acm_rb *rb = &(acm->rb[i]);
David Kubicek61a87ad2005-11-01 18:51:34 +01001220
David Brownell672c4e12008-08-06 18:41:12 -07001221 rb->base = usb_buffer_alloc(acm->dev, readsize,
1222 GFP_KERNEL, &rb->dma);
1223 if (!rb->base) {
Alan Cox6e47e062009-06-11 12:37:06 +01001224 dev_dbg(&intf->dev,
1225 "out of memory (read bufs usb_buffer_alloc)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001226 goto alloc_fail7;
1227 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 }
Alan Cox6e47e062009-06-11 12:37:06 +01001229 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001230 struct acm_wb *snd = &(acm->wb[i]);
1231
Alan Cox6e47e062009-06-11 12:37:06 +01001232 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1233 if (snd->urb == NULL) {
1234 dev_dbg(&intf->dev,
1235 "out of memory (write urbs usb_alloc_urb)");
David Engrafe4cf3aa2008-03-20 10:01:34 +01001236 goto alloc_fail7;
1237 }
1238
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001239 if (usb_endpoint_xfer_int(epwrite))
1240 usb_fill_int_urb(snd->urb, usb_dev,
1241 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1242 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1243 else
1244 usb_fill_bulk_urb(snd->urb, usb_dev,
1245 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1246 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001247 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1248 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 }
1250
Alan Cox6e47e062009-06-11 12:37:06 +01001251 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001252
1253 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1254 if (i < 0)
1255 goto alloc_fail8;
1256
1257 if (cfd) { /* export the country data */
1258 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1259 if (!acm->country_codes)
1260 goto skip_countries;
1261 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001262 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1263 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001264 acm->country_rel_date = cfd->iCountryCodeRelDate;
1265
1266 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1267 if (i < 0) {
1268 kfree(acm->country_codes);
1269 goto skip_countries;
1270 }
1271
Alan Cox6e47e062009-06-11 12:37:06 +01001272 i = device_create_file(&intf->dev,
1273 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001274 if (i < 0) {
1275 kfree(acm->country_codes);
1276 goto skip_countries;
1277 }
1278 }
1279
1280skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001281 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001282 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1283 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1284 /* works around buggy devices */
1285 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1287 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1288
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1290
1291 acm_set_control(acm, acm->ctrlout);
1292
1293 acm->line.dwDTERate = cpu_to_le32(9600);
1294 acm->line.bDataBits = 8;
1295 acm_set_line(acm, &acm->line);
1296
1297 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001298 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001300 usb_get_intf(control_interface);
1301 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302
1303 acm_table[minor] = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001305 return 0;
1306alloc_fail8:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001307 for (i = 0; i < ACM_NW; i++)
1308 usb_free_urb(acm->wb[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309alloc_fail7:
Oliver Neukum830f4022008-06-25 14:17:16 +02001310 acm_read_buffers_free(acm);
Oliver Neukum86478942006-05-13 22:50:47 +02001311 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001312 usb_free_urb(acm->ru[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 usb_free_urb(acm->ctrlurb);
1314alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001315 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316alloc_fail4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
1318alloc_fail2:
1319 kfree(acm);
1320alloc_fail:
1321 return -ENOMEM;
1322}
1323
Oliver Neukum1365baf72007-10-12 17:24:28 +02001324static void stop_data_traffic(struct acm *acm)
1325{
1326 int i;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001327 dbg("Entering stop_data_traffic");
Oliver Neukum1365baf72007-10-12 17:24:28 +02001328
1329 tasklet_disable(&acm->urb_task);
1330
1331 usb_kill_urb(acm->ctrlurb);
Alan Cox6e47e062009-06-11 12:37:06 +01001332 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001333 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf72007-10-12 17:24:28 +02001334 for (i = 0; i < acm->rx_buflimit; i++)
1335 usb_kill_urb(acm->ru[i].urb);
1336
Oliver Neukum1365baf72007-10-12 17:24:28 +02001337 tasklet_enable(&acm->urb_task);
1338
1339 cancel_work_sync(&acm->work);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001340 cancel_work_sync(&acm->waker);
Oliver Neukum1365baf72007-10-12 17:24:28 +02001341}
1342
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343static void acm_disconnect(struct usb_interface *intf)
1344{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001345 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001347 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348
David Brownell672c4e12008-08-06 18:41:12 -07001349 /* sibling interface is already cleaning up */
1350 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001351 return;
David Brownell672c4e12008-08-06 18:41:12 -07001352
1353 mutex_lock(&open_mutex);
Alan Cox6e47e062009-06-11 12:37:06 +01001354 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001355 device_remove_file(&acm->control->dev,
1356 &dev_attr_wCountryCodes);
1357 device_remove_file(&acm->control->dev,
1358 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001359 }
Alan Stern74da5d62007-08-02 13:29:10 -04001360 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 acm->dev = NULL;
Oliver Neukum86067eea2006-01-08 12:39:13 +01001362 usb_set_intfdata(acm->control, NULL);
1363 usb_set_intfdata(acm->data, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
Oliver Neukum1365baf72007-10-12 17:24:28 +02001365 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366
Oliver Neukum884b6002005-04-21 21:28:02 +02001367 acm_write_buffers_free(acm);
Alan Cox6e47e062009-06-11 12:37:06 +01001368 usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
1369 acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001370 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001372 if (!acm->combined_interfaces)
1373 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001374 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
Alan Cox10077d42009-06-11 12:36:09 +01001376 if (acm->port.count == 0) {
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001377 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001378 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 return;
1380 }
1381
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001382 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001383 tty = tty_port_tty_get(&acm->port);
1384 if (tty) {
1385 tty_hangup(tty);
1386 tty_kref_put(tty);
1387 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388}
1389
Oliver Neukum35758582008-07-01 19:10:08 +02001390#ifdef CONFIG_PM
Oliver Neukum1365baf72007-10-12 17:24:28 +02001391static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1392{
1393 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001394 int cnt;
Oliver Neukum1365baf72007-10-12 17:24:28 +02001395
Alan Stern65bfd292008-11-25 16:39:18 -05001396 if (message.event & PM_EVENT_AUTO) {
Oliver Neukum11ea8592008-06-20 11:25:57 +02001397 int b;
1398
1399 spin_lock_irq(&acm->read_lock);
1400 spin_lock(&acm->write_lock);
1401 b = acm->processing + acm->transmitting;
1402 spin_unlock(&acm->write_lock);
1403 spin_unlock_irq(&acm->read_lock);
1404 if (b)
1405 return -EBUSY;
1406 }
1407
1408 spin_lock_irq(&acm->read_lock);
1409 spin_lock(&acm->write_lock);
1410 cnt = acm->susp_count++;
1411 spin_unlock(&acm->write_lock);
1412 spin_unlock_irq(&acm->read_lock);
1413
1414 if (cnt)
Oliver Neukum1365baf72007-10-12 17:24:28 +02001415 return 0;
1416 /*
1417 we treat opened interfaces differently,
1418 we must guard against open
1419 */
1420 mutex_lock(&acm->mutex);
1421
Alan Cox10077d42009-06-11 12:36:09 +01001422 if (acm->port.count)
Oliver Neukum1365baf72007-10-12 17:24:28 +02001423 stop_data_traffic(acm);
1424
1425 mutex_unlock(&acm->mutex);
1426 return 0;
1427}
1428
1429static int acm_resume(struct usb_interface *intf)
1430{
1431 struct acm *acm = usb_get_intfdata(intf);
1432 int rv = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001433 int cnt;
Oliver Neukum1365baf72007-10-12 17:24:28 +02001434
Oliver Neukum11ea8592008-06-20 11:25:57 +02001435 spin_lock_irq(&acm->read_lock);
1436 acm->susp_count -= 1;
1437 cnt = acm->susp_count;
1438 spin_unlock_irq(&acm->read_lock);
1439
1440 if (cnt)
Oliver Neukum1365baf72007-10-12 17:24:28 +02001441 return 0;
1442
1443 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001444 if (acm->port.count) {
Oliver Neukum1365baf72007-10-12 17:24:28 +02001445 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
1446 if (rv < 0)
Oliver Neukum11ea8592008-06-20 11:25:57 +02001447 goto err_out;
Oliver Neukum1365baf72007-10-12 17:24:28 +02001448
1449 tasklet_schedule(&acm->urb_task);
1450 }
1451
1452err_out:
1453 mutex_unlock(&acm->mutex);
1454 return rv;
1455}
Oliver Neukum35758582008-07-01 19:10:08 +02001456
1457#endif /* CONFIG_PM */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458/*
1459 * USB driver structure.
1460 */
1461
1462static struct usb_device_id acm_ids[] = {
1463 /* quirky and broken devices */
1464 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1465 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1466 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001467 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1468 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1469 },
Andrew Lunn0f9c7b42008-12-23 17:31:23 +01001470 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1471 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1472 },
Masahito Omote8753e652005-07-29 12:17:25 -07001473 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1474 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1475 },
Chris Malley91a9c922006-10-03 10:08:28 +01001476 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1477 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1478 },
Alan Cox7abcf202009-04-06 17:35:01 +01001479 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1480 .driver_info = SINGLE_RX_URB,
1481 },
Oliver Neukum86478942006-05-13 22:50:47 +02001482 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1483 .driver_info = SINGLE_RX_URB, /* firmware bug */
1484 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001485 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1486 .driver_info = SINGLE_RX_URB, /* firmware bug */
1487 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001488 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1489 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1490 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001491 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1492 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1493 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001494 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1495 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1496 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001497 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1498 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1499 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001500 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1501 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1502 },
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001503 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1504 },
Adam Richterc332b4e2009-02-18 16:17:15 -08001505 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1506 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1507 data interface instead of
1508 communications interface.
1509 Maybe we should define a new
1510 quirk for this. */
1511 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001512 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1513 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1514 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001515
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 /* control interfaces with various AT-command sets */
1517 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1518 USB_CDC_ACM_PROTO_AT_V25TER) },
1519 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1520 USB_CDC_ACM_PROTO_AT_PCCA101) },
1521 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1522 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1523 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1524 USB_CDC_ACM_PROTO_AT_GSM) },
1525 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001526 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1528 USB_CDC_ACM_PROTO_AT_CDMA) },
1529
1530 /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
1531 { }
1532};
1533
Alan Cox6e47e062009-06-11 12:37:06 +01001534MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535
1536static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 .name = "cdc_acm",
1538 .probe = acm_probe,
1539 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001540#ifdef CONFIG_PM
Oliver Neukum1365baf72007-10-12 17:24:28 +02001541 .suspend = acm_suspend,
1542 .resume = acm_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001543#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001545#ifdef CONFIG_PM
Oliver Neukum1365baf72007-10-12 17:24:28 +02001546 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001547#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548};
1549
1550/*
1551 * TTY driver structures.
1552 */
1553
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001554static const struct tty_operations acm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555 .open = acm_tty_open,
1556 .close = acm_tty_close,
Alan Cox10077d42009-06-11 12:36:09 +01001557 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 .write = acm_tty_write,
1559 .write_room = acm_tty_write_room,
1560 .ioctl = acm_tty_ioctl,
1561 .throttle = acm_tty_throttle,
1562 .unthrottle = acm_tty_unthrottle,
1563 .chars_in_buffer = acm_tty_chars_in_buffer,
1564 .break_ctl = acm_tty_break_ctl,
1565 .set_termios = acm_tty_set_termios,
1566 .tiocmget = acm_tty_tiocmget,
1567 .tiocmset = acm_tty_tiocmset,
1568};
1569
1570/*
1571 * Init / exit.
1572 */
1573
1574static int __init acm_init(void)
1575{
1576 int retval;
1577 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1578 if (!acm_tty_driver)
1579 return -ENOMEM;
1580 acm_tty_driver->owner = THIS_MODULE,
1581 acm_tty_driver->driver_name = "acm",
1582 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 acm_tty_driver->major = ACM_TTY_MAJOR,
1584 acm_tty_driver->minor_start = 0,
1585 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1586 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001587 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001589 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1590 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 tty_set_operations(acm_tty_driver, &acm_ops);
1592
1593 retval = tty_register_driver(acm_tty_driver);
1594 if (retval) {
1595 put_tty_driver(acm_tty_driver);
1596 return retval;
1597 }
1598
1599 retval = usb_register(&acm_driver);
1600 if (retval) {
1601 tty_unregister_driver(acm_tty_driver);
1602 put_tty_driver(acm_tty_driver);
1603 return retval;
1604 }
1605
Greg Kroah-Hartman5909f6e2008-08-18 13:21:04 -07001606 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
1607 DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608
1609 return 0;
1610}
1611
1612static void __exit acm_exit(void)
1613{
1614 usb_deregister(&acm_driver);
1615 tty_unregister_driver(acm_tty_driver);
1616 put_tty_driver(acm_tty_driver);
1617}
1618
1619module_init(acm_init);
1620module_exit(acm_exit);
1621
Alan Cox6e47e062009-06-11 12:37:06 +01001622MODULE_AUTHOR(DRIVER_AUTHOR);
1623MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01001625MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);