blob: 612b9ff0fcf22eacbb80ce87ddc1f7391e7ae04a [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>
Pavel Macheka2531292010-07-18 14:27:13 +02005 * Copyright (c) 1999 Pavel Machek <pavel@ucw.cz>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * 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 Neukum97d35f92009-12-16 17:05:57 +0100173 usb_autopm_put_interface_async(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200174}
175
176/*
177 * Poke write.
Oliver Neukum11ea8592008-06-20 11:25:57 +0200178 *
179 * the caller is responsible for locking
Oliver Neukum884b6002005-04-21 21:28:02 +0200180 */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200181
182static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
183{
184 int rc;
185
186 acm->transmitting++;
187
188 wb->urb->transfer_buffer = wb->buf;
189 wb->urb->transfer_dma = wb->dmah;
190 wb->urb->transfer_buffer_length = wb->len;
191 wb->urb->dev = acm->dev;
192
Alan Cox6e47e062009-06-11 12:37:06 +0100193 rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
194 if (rc < 0) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200195 dbg("usb_submit_urb(write bulk) failed: %d", rc);
196 acm_write_done(acm, wb);
197 }
198 return rc;
199}
200
David Engrafe4cf3aa2008-03-20 10:01:34 +0100201static int acm_write_start(struct acm *acm, int wbn)
Oliver Neukum884b6002005-04-21 21:28:02 +0200202{
203 unsigned long flags;
David Brownell934da462008-08-06 18:44:12 -0700204 struct acm_wb *wb = &acm->wb[wbn];
Oliver Neukum884b6002005-04-21 21:28:02 +0200205 int rc;
206
207 spin_lock_irqsave(&acm->write_lock, flags);
208 if (!acm->dev) {
David Brownell934da462008-08-06 18:44:12 -0700209 wb->use = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200210 spin_unlock_irqrestore(&acm->write_lock, flags);
211 return -ENODEV;
212 }
213
Oliver Neukum11ea8592008-06-20 11:25:57 +0200214 dbg("%s susp_count: %d", __func__, acm->susp_count);
Oliver Neukum97d35f92009-12-16 17:05:57 +0100215 usb_autopm_get_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200216 if (acm->susp_count) {
Oliver Neukum97d35f92009-12-16 17:05:57 +0100217 if (!acm->delayed_wb)
218 acm->delayed_wb = wb;
219 else
220 usb_autopm_put_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200221 spin_unlock_irqrestore(&acm->write_lock, flags);
222 return 0; /* A white lie */
223 }
224 usb_mark_last_busy(acm->dev);
225
Oliver Neukum11ea8592008-06-20 11:25:57 +0200226 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200227 spin_unlock_irqrestore(&acm->write_lock, flags);
228
Oliver Neukum884b6002005-04-21 21:28:02 +0200229 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200230
Oliver Neukum884b6002005-04-21 21:28:02 +0200231}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100232/*
233 * attributes exported through sysfs
234 */
235static ssize_t show_caps
236(struct device *dev, struct device_attribute *attr, char *buf)
237{
238 struct usb_interface *intf = to_usb_interface(dev);
239 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200240
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100241 return sprintf(buf, "%d", acm->ctrl_caps);
242}
243static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
244
245static ssize_t show_country_codes
246(struct device *dev, struct device_attribute *attr, char *buf)
247{
248 struct usb_interface *intf = to_usb_interface(dev);
249 struct acm *acm = usb_get_intfdata(intf);
250
251 memcpy(buf, acm->country_codes, acm->country_code_size);
252 return acm->country_code_size;
253}
254
255static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
256
257static ssize_t show_country_rel_date
258(struct device *dev, struct device_attribute *attr, char *buf)
259{
260 struct usb_interface *intf = to_usb_interface(dev);
261 struct acm *acm = usb_get_intfdata(intf);
262
263 return sprintf(buf, "%d", acm->country_rel_date);
264}
265
266static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200267/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 * Interrupt handlers for various ACM device responses
269 */
270
271/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100272static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273{
274 struct acm *acm = urb->context;
275 struct usb_cdc_notification *dr = urb->transfer_buffer;
Alan Cox10077d42009-06-11 12:36:09 +0100276 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 unsigned char *data;
278 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700279 int retval;
280 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700282 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 case 0:
284 /* success */
285 break;
286 case -ECONNRESET:
287 case -ENOENT:
288 case -ESHUTDOWN:
289 /* this urb is terminated, clean up */
Harvey Harrison441b62c2008-03-03 16:08:34 -0800290 dbg("%s - urb shutting down with status: %d", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 return;
292 default:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800293 dbg("%s - nonzero urb status received: %d", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 goto exit;
295 }
296
297 if (!ACM_READY(acm))
298 goto exit;
299
Johan Hovold7e7797e2011-03-22 11:12:11 +0100300 usb_mark_last_busy(acm->dev);
301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 data = (unsigned char *)(dr + 1);
303 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100304 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
305 dbg("%s network", dr->wValue ?
306 "connected to" : "disconnected from");
307 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
Alan Cox6e47e062009-06-11 12:37:06 +0100309 case USB_CDC_NOTIFY_SERIAL_STATE:
310 tty = tty_port_tty_get(&acm->port);
311 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
Alan Cox6e47e062009-06-11 12:37:06 +0100313 if (tty) {
314 if (!acm->clocal &&
315 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
316 dbg("calling hangup");
317 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 }
Alan Cox6e47e062009-06-11 12:37:06 +0100319 tty_kref_put(tty);
320 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
Alan Cox6e47e062009-06-11 12:37:06 +0100322 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323
Alan Cox6e47e062009-06-11 12:37:06 +0100324 dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
325 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
326 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
327 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
328 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
329 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
330 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
331 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 break;
333
Alan Cox6e47e062009-06-11 12:37:06 +0100334 default:
335 dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
336 dr->bNotificationType, dr->wIndex,
337 dr->wLength, data[0], data[1]);
338 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 }
340exit:
Alan Cox6e47e062009-06-11 12:37:06 +0100341 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700342 if (retval)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100343 dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
344 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345}
346
347/* data interface returns incoming bytes, or we got unthrottled */
David Howells7d12e782006-10-05 14:55:46 +0100348static void acm_read_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349{
David Kubicek61a87ad2005-11-01 18:51:34 +0100350 struct acm_rb *buf;
351 struct acm_ru *rcv = urb->context;
352 struct acm *acm = rcv->instance;
Oliver Neukum86478942006-05-13 22:50:47 +0200353 int status = urb->status;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700354
355 dbg("Entering acm_read_bulk with status %d", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
Oliver Neukum11ea8592008-06-20 11:25:57 +0200357 if (!ACM_READY(acm)) {
Johan Hovold1d9846e2011-03-22 11:12:14 +0100358 dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200360 }
361 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
Oliver Neukum86478942006-05-13 22:50:47 +0200363 if (status)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100364 dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
365 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
David Kubicek61a87ad2005-11-01 18:51:34 +0100367 buf = rcv->buffer;
368 buf->size = urb->actual_length;
369
Oliver Neukum86478942006-05-13 22:50:47 +0200370 if (likely(status == 0)) {
371 spin_lock(&acm->read_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200372 acm->processing++;
Oliver Neukum86478942006-05-13 22:50:47 +0200373 list_add_tail(&rcv->list, &acm->spare_read_urbs);
374 list_add_tail(&buf->list, &acm->filled_read_bufs);
375 spin_unlock(&acm->read_lock);
376 } else {
377 /* we drop the buffer due to an error */
378 spin_lock(&acm->read_lock);
379 list_add_tail(&rcv->list, &acm->spare_read_urbs);
380 list_add(&buf->list, &acm->spare_read_bufs);
381 spin_unlock(&acm->read_lock);
382 /* nevertheless the tasklet must be kicked unconditionally
383 so the queue cannot dry up */
384 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200385 if (likely(!acm->susp_count))
386 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387}
388
389static void acm_rx_tasklet(unsigned long _acm)
390{
391 struct acm *acm = (void *)_acm;
David Kubicek61a87ad2005-11-01 18:51:34 +0100392 struct acm_rb *buf;
Alan Cox10077d42009-06-11 12:36:09 +0100393 struct tty_struct *tty;
David Kubicek61a87ad2005-11-01 18:51:34 +0100394 struct acm_ru *rcv;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200395 unsigned long flags;
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100396 unsigned char throttled;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200397
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 dbg("Entering acm_rx_tasklet");
399
Alan Cox10077d42009-06-11 12:36:09 +0100400 if (!ACM_READY(acm)) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200401 dbg("acm_rx_tasklet: ACM not ready");
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100402 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200403 }
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100404
Oliver Neukum834dbca2007-03-06 10:47:04 +0100405 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100406 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100407 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Alan Cox10077d42009-06-11 12:36:09 +0100408 if (throttled) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200409 dbg("acm_rx_tasklet: throttled");
David Kubicek61a87ad2005-11-01 18:51:34 +0100410 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200411 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100412
Alan Cox10077d42009-06-11 12:36:09 +0100413 tty = tty_port_tty_get(&acm->port);
414
David Kubicek61a87ad2005-11-01 18:51:34 +0100415next_buffer:
Jarek Poplawski762f0072006-10-06 07:23:11 +0200416 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100417 if (list_empty(&acm->filled_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200418 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100419 goto urbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100421 buf = list_entry(acm->filled_read_bufs.next,
422 struct acm_rb, list);
423 list_del(&buf->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200424 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100425
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200426 dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
David Kubicek61a87ad2005-11-01 18:51:34 +0100427
Alan Cox10077d42009-06-11 12:36:09 +0100428 if (tty) {
429 spin_lock_irqsave(&acm->throttle_lock, flags);
430 throttled = acm->throttle;
431 spin_unlock_irqrestore(&acm->throttle_lock, flags);
432 if (!throttled) {
Alan Cox10077d42009-06-11 12:36:09 +0100433 tty_insert_flip_string(tty, buf->base, buf->size);
434 tty_flip_buffer_push(tty);
435 } else {
436 tty_kref_put(tty);
437 dbg("Throttling noticed");
438 spin_lock_irqsave(&acm->read_lock, flags);
439 list_add(&buf->list, &acm->filled_read_bufs);
440 spin_unlock_irqrestore(&acm->read_lock, flags);
441 return;
442 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
Jarek Poplawski762f0072006-10-06 07:23:11 +0200445 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100446 list_add(&buf->list, &acm->spare_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200447 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100448 goto next_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449
David Kubicek61a87ad2005-11-01 18:51:34 +0100450urbs:
Alan Cox10077d42009-06-11 12:36:09 +0100451 tty_kref_put(tty);
452
David Kubicek61a87ad2005-11-01 18:51:34 +0100453 while (!list_empty(&acm->spare_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200454 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100455 if (list_empty(&acm->spare_read_urbs)) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200456 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200457 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100458 return;
459 }
460 rcv = list_entry(acm->spare_read_urbs.next,
461 struct acm_ru, list);
462 list_del(&rcv->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200463 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100464
465 buf = list_entry(acm->spare_read_bufs.next,
466 struct acm_rb, list);
467 list_del(&buf->list);
468
469 rcv->buffer = buf;
470
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200471 if (acm->is_int_ep)
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400472 usb_fill_int_urb(rcv->urb, acm->dev,
473 acm->rx_endpoint,
474 buf->base,
475 acm->readsize,
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200476 acm_read_bulk, rcv, acm->bInterval);
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400477 else
478 usb_fill_bulk_urb(rcv->urb, acm->dev,
479 acm->rx_endpoint,
480 buf->base,
481 acm->readsize,
482 acm_read_bulk, rcv);
David Kubicek61a87ad2005-11-01 18:51:34 +0100483 rcv->urb->transfer_dma = buf->dma;
484 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
485
Alan Cox6e47e062009-06-11 12:37:06 +0100486 /* This shouldn't kill the driver as unsuccessful URBs are
487 returned to the free-urbs-pool and resubmited ASAP */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200488 spin_lock_irqsave(&acm->read_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100489 if (acm->susp_count ||
490 usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100491 list_add(&buf->list, &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100492 list_add(&rcv->list, &acm->spare_read_urbs);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200493 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200494 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100495 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200496 } else {
497 spin_unlock_irqrestore(&acm->read_lock, flags);
498 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 +0100499 }
500 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200501 spin_lock_irqsave(&acm->read_lock, flags);
502 acm->processing = 0;
503 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504}
505
506/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100507static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508{
Ming Leicdc97792008-02-24 18:41:47 +0800509 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700510 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800511 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200512
David Brownelle5fbab52008-08-06 18:46:10 -0700513 if (verbose || urb->status
514 || (urb->actual_length != urb->transfer_buffer_length))
Johan Hovold1d9846e2011-03-22 11:12:14 +0100515 dev_dbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
516 __func__,
David Brownelle5fbab52008-08-06 18:46:10 -0700517 urb->actual_length,
518 urb->transfer_buffer_length,
519 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800521 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100522 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800523 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200524 if (ACM_READY(acm))
525 schedule_work(&acm->work);
David Brownelle5fbab52008-08-06 18:46:10 -0700526 else
527 wake_up_interruptible(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528}
529
David Howellsc4028952006-11-22 14:57:56 +0000530static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531{
David Howellsc4028952006-11-22 14:57:56 +0000532 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100533 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700534
Johan Hovold1d9846e2011-03-22 11:12:14 +0100535 dev_vdbg(&acm->data->dev, "%s\n", __func__);
536
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 if (!ACM_READY(acm))
538 return;
Alan Cox10077d42009-06-11 12:36:09 +0100539 tty = tty_port_tty_get(&acm->port);
Johan Hovold15e5bee2011-03-22 11:12:10 +0100540 if (!tty)
541 return;
Alan Cox10077d42009-06-11 12:36:09 +0100542 tty_wakeup(tty);
543 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544}
545
546/*
547 * TTY handlers
548 */
549
550static int acm_tty_open(struct tty_struct *tty, struct file *filp)
551{
552 struct acm *acm;
Thadeu Lima de Souza Cascardo42dd2aa2009-06-25 14:41:24 +0100553 int rv = -ENODEV;
David Kubicek61a87ad2005-11-01 18:51:34 +0100554 int i;
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200555 dbg("Entering acm_tty_open.");
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100556
557 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
559 acm = acm_table[tty->index];
560 if (!acm || !acm->dev)
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100561 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 else
563 rv = 0;
564
David Engraf28d1dfa2008-03-20 10:53:52 +0100565 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
Alan Cox10077d42009-06-11 12:36:09 +0100566
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 tty->driver_data = acm;
Alan Cox10077d42009-06-11 12:36:09 +0100568 tty_port_tty_set(&acm->port, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
Oliver Neukum94409cc2008-02-11 15:22:29 +0100570 if (usb_autopm_get_interface(acm->control) < 0)
571 goto early_bail;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200572 else
573 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200574
575 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100576 if (acm->port.count++) {
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100577 mutex_unlock(&acm->mutex);
Oliver Neukum1365baf2007-10-12 17:24:28 +0200578 usb_autopm_put_interface(acm->control);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100579 goto out;
Alan Cox10077d42009-06-11 12:36:09 +0100580 }
Oliver Neukum1365baf2007-10-12 17:24:28 +0200581
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 acm->ctrlurb->dev = acm->dev;
583 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
584 dbg("usb_submit_urb(ctrl irq) failed");
585 goto bail_out;
586 }
587
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100588 if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
589 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 goto full_bailout;
Alan Cox10077d42009-06-11 12:36:09 +0100591
Oliver Neukum11ea8592008-06-20 11:25:57 +0200592 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
David Kubicek61a87ad2005-11-01 18:51:34 +0100594 INIT_LIST_HEAD(&acm->spare_read_urbs);
595 INIT_LIST_HEAD(&acm->spare_read_bufs);
596 INIT_LIST_HEAD(&acm->filled_read_bufs);
Alan Cox6e47e062009-06-11 12:37:06 +0100597
598 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100599 list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
Alan Cox6e47e062009-06-11 12:37:06 +0100600 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100601 list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100602
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100603 acm->throttle = 0;
604
Oliver Neukum7af25b42009-09-08 23:51:28 +0200605 set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
Alan Cox10077d42009-06-11 12:36:09 +0100606 rv = tty_port_block_til_ready(&acm->port, tty, filp);
Henry Gebhardt18a77b52009-11-04 11:19:28 +0100607 tasklet_schedule(&acm->urb_task);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100608
Oliver Neukum1365baf2007-10-12 17:24:28 +0200609 mutex_unlock(&acm->mutex);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100610out:
Oliver Neukum94409cc2008-02-11 15:22:29 +0100611 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 return rv;
613
614full_bailout:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 usb_kill_urb(acm->ctrlurb);
616bail_out:
Alan Cox10077d42009-06-11 12:36:09 +0100617 acm->port.count--;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200618 mutex_unlock(&acm->mutex);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100619 usb_autopm_put_interface(acm->control);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100620early_bail:
621 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100622 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 return -EIO;
624}
625
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700626static void acm_tty_unregister(struct acm *acm)
627{
Alan Cox10077d42009-06-11 12:36:09 +0100628 int i, nr;
David Kubicek61a87ad2005-11-01 18:51:34 +0100629
Oliver Neukum86478942006-05-13 22:50:47 +0200630 nr = acm->rx_buflimit;
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700631 tty_unregister_device(acm_tty_driver, acm->minor);
632 usb_put_intf(acm->control);
633 acm_table[acm->minor] = NULL;
634 usb_free_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100635 for (i = 0; i < ACM_NW; i++)
636 usb_free_urb(acm->wb[i].urb);
Oliver Neukum86478942006-05-13 22:50:47 +0200637 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100638 usb_free_urb(acm->ru[i].urb);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100639 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700640 kfree(acm);
641}
642
David Brownelle5fbab52008-08-06 18:46:10 -0700643static int acm_tty_chars_in_buffer(struct tty_struct *tty);
644
Arnd Bergmann4e608672010-06-01 22:53:04 +0200645static void acm_port_down(struct acm *acm)
Alan Cox10077d42009-06-11 12:36:09 +0100646{
647 int i, nr = acm->rx_buflimit;
648 mutex_lock(&open_mutex);
649 if (acm->dev) {
650 usb_autopm_get_interface(acm->control);
651 acm_set_control(acm, acm->ctrlout = 0);
Alan Cox10077d42009-06-11 12:36:09 +0100652 usb_kill_urb(acm->ctrlurb);
653 for (i = 0; i < ACM_NW; i++)
654 usb_kill_urb(acm->wb[i].urb);
Johan Hovold23b80552011-03-22 11:12:09 +0100655 tasklet_disable(&acm->urb_task);
Alan Cox10077d42009-06-11 12:36:09 +0100656 for (i = 0; i < nr; i++)
657 usb_kill_urb(acm->ru[i].urb);
Johan Hovold23b80552011-03-22 11:12:09 +0100658 tasklet_enable(&acm->urb_task);
Alan Cox10077d42009-06-11 12:36:09 +0100659 acm->control->needs_remote_wakeup = 0;
660 usb_autopm_put_interface(acm->control);
661 }
662 mutex_unlock(&open_mutex);
663}
664
665static void acm_tty_hangup(struct tty_struct *tty)
666{
667 struct acm *acm = tty->driver_data;
668 tty_port_hangup(&acm->port);
Arnd Bergmann4e608672010-06-01 22:53:04 +0200669 acm_port_down(acm);
Alan Cox10077d42009-06-11 12:36:09 +0100670}
671
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672static void acm_tty_close(struct tty_struct *tty, struct file *filp)
673{
674 struct acm *acm = tty->driver_data;
675
Alan Cox10077d42009-06-11 12:36:09 +0100676 /* Perform the closing process and see if we need to do the hardware
677 shutdown */
Francesco Lavra051522b2009-11-03 10:53:07 +0000678 if (!acm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 return;
Francesco Lavra051522b2009-11-03 10:53:07 +0000680 if (tty_port_close_start(&acm->port, tty, filp) == 0) {
681 mutex_lock(&open_mutex);
682 if (!acm->dev) {
683 tty_port_tty_set(&acm->port, NULL);
684 acm_tty_unregister(acm);
685 tty->driver_data = NULL;
686 }
687 mutex_unlock(&open_mutex);
688 return;
689 }
Arnd Bergmann4e608672010-06-01 22:53:04 +0200690 acm_port_down(acm);
Alan Cox10077d42009-06-11 12:36:09 +0100691 tty_port_close_end(&acm->port, tty);
Alan Cox10077d42009-06-11 12:36:09 +0100692 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693}
694
Alan Cox6e47e062009-06-11 12:37:06 +0100695static int acm_tty_write(struct tty_struct *tty,
696 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697{
698 struct acm *acm = tty->driver_data;
699 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200700 unsigned long flags;
701 int wbn;
702 struct acm_wb *wb;
703
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200704 dbg("Entering acm_tty_write to write %d bytes,", count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705
706 if (!ACM_READY(acm))
707 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 if (!count)
709 return 0;
710
Oliver Neukum884b6002005-04-21 21:28:02 +0200711 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100712 wbn = acm_wb_alloc(acm);
713 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200714 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200715 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200717 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718
Oliver Neukum884b6002005-04-21 21:28:02 +0200719 count = (count > acm->writesize) ? acm->writesize : count;
720 dbg("Get %d bytes...", count);
721 memcpy(wb->buf, buf, count);
722 wb->len = count;
723 spin_unlock_irqrestore(&acm->write_lock, flags);
724
Alan Cox6e47e062009-06-11 12:37:06 +0100725 stat = acm_write_start(acm, wbn);
726 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200727 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 return count;
729}
730
731static int acm_tty_write_room(struct tty_struct *tty)
732{
733 struct acm *acm = tty->driver_data;
734 if (!ACM_READY(acm))
735 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200736 /*
737 * Do not let the line discipline to know that we have a reserve,
738 * or it might get too enthusiastic.
739 */
David Brownell934da462008-08-06 18:44:12 -0700740 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741}
742
743static int acm_tty_chars_in_buffer(struct tty_struct *tty)
744{
745 struct acm *acm = tty->driver_data;
746 if (!ACM_READY(acm))
Alan Cox23198fd2009-07-20 16:05:27 +0100747 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200748 /*
749 * This is inaccurate (overcounts), but it works.
750 */
Oliver Neukum86478942006-05-13 22:50:47 +0200751 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752}
753
754static void acm_tty_throttle(struct tty_struct *tty)
755{
756 struct acm *acm = tty->driver_data;
757 if (!ACM_READY(acm))
758 return;
759 spin_lock_bh(&acm->throttle_lock);
760 acm->throttle = 1;
761 spin_unlock_bh(&acm->throttle_lock);
762}
763
764static void acm_tty_unthrottle(struct tty_struct *tty)
765{
766 struct acm *acm = tty->driver_data;
767 if (!ACM_READY(acm))
768 return;
769 spin_lock_bh(&acm->throttle_lock);
770 acm->throttle = 0;
771 spin_unlock_bh(&acm->throttle_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100772 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773}
774
Alan Cox9e989662008-07-22 11:18:03 +0100775static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776{
777 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100778 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 if (!ACM_READY(acm))
Alan Cox9e989662008-07-22 11:18:03 +0100780 return -EINVAL;
781 retval = acm_send_break(acm, state ? 0xffff : 0);
782 if (retval < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 dbg("send break failed");
Alan Cox9e989662008-07-22 11:18:03 +0100784 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785}
786
Alan Cox60b33c12011-02-14 16:26:14 +0000787static int acm_tty_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788{
789 struct acm *acm = tty->driver_data;
790
791 if (!ACM_READY(acm))
792 return -EINVAL;
793
794 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
795 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
796 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
797 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
798 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
799 TIOCM_CTS;
800}
801
Alan Cox20b9d172011-02-14 16:26:50 +0000802static int acm_tty_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 unsigned int set, unsigned int clear)
804{
805 struct acm *acm = tty->driver_data;
806 unsigned int newctrl;
807
808 if (!ACM_READY(acm))
809 return -EINVAL;
810
811 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100812 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
813 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
814 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
815 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
817 newctrl = (newctrl & ~clear) | set;
818
819 if (acm->ctrlout == newctrl)
820 return 0;
821 return acm_set_control(acm, acm->ctrlout = newctrl);
822}
823
Alan Cox6caa76b2011-02-14 16:27:22 +0000824static int acm_tty_ioctl(struct tty_struct *tty,
Alan Cox6e47e062009-06-11 12:37:06 +0100825 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826{
827 struct acm *acm = tty->driver_data;
828
829 if (!ACM_READY(acm))
830 return -EINVAL;
831
832 return -ENOIOCTLCMD;
833}
834
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100835static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 0, 50, 75, 110, 134, 150, 200, 300, 600,
837 1200, 1800, 2400, 4800, 9600, 19200, 38400,
838 57600, 115200, 230400, 460800, 500000, 576000,
839 921600, 1000000, 1152000, 1500000, 2000000,
840 2500000, 3000000, 3500000, 4000000
841};
842
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100843static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 5, 6, 7, 8
845};
846
Alan Cox6e47e062009-06-11 12:37:06 +0100847static void acm_tty_set_termios(struct tty_struct *tty,
848 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849{
850 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800851 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 struct usb_cdc_line_coding newline;
853 int newctrl = acm->ctrlout;
854
855 if (!ACM_READY(acm))
856 return;
857
Alan Cox9b80fee2009-09-19 13:13:23 -0700858 newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
860 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100861 (termios->c_cflag & PARODD ? 1 : 2) +
862 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
Alan Cox6e47e062009-06-11 12:37:06 +0100864 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
866
867 if (!newline.dwDTERate) {
868 newline.dwDTERate = acm->line.dwDTERate;
869 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +0100870 } else
871 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
873 if (newctrl != acm->ctrlout)
874 acm_set_control(acm, acm->ctrlout = newctrl);
875
876 if (memcmp(&acm->line, &newline, sizeof newline)) {
877 memcpy(&acm->line, &newline, sizeof newline);
878 dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
879 newline.bCharFormat, newline.bParityType,
880 newline.bDataBits);
881 acm_set_line(acm, &acm->line);
882 }
883}
884
885/*
886 * USB probe and disconnect routines.
887 */
888
Oliver Neukum830f4022008-06-25 14:17:16 +0200889/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +0200890static void acm_write_buffers_free(struct acm *acm)
891{
892 int i;
893 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +0200894 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200895
Alan Cox6e47e062009-06-11 12:37:06 +0100896 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Daniel Mack997ea582010-04-12 13:17:25 +0200897 usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +0200898}
899
Oliver Neukum830f4022008-06-25 14:17:16 +0200900static void acm_read_buffers_free(struct acm *acm)
901{
902 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
903 int i, n = acm->rx_buflimit;
904
905 for (i = 0; i < n; i++)
Daniel Mack997ea582010-04-12 13:17:25 +0200906 usb_free_coherent(usb_dev, acm->readsize,
907 acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +0200908}
909
Oliver Neukum884b6002005-04-21 21:28:02 +0200910/* Little helper: write buffers allocate */
911static int acm_write_buffers_alloc(struct acm *acm)
912{
913 int i;
914 struct acm_wb *wb;
915
Oliver Neukum86478942006-05-13 22:50:47 +0200916 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Daniel Mack997ea582010-04-12 13:17:25 +0200917 wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
Oliver Neukum884b6002005-04-21 21:28:02 +0200918 &wb->dmah);
919 if (!wb->buf) {
920 while (i != 0) {
921 --i;
922 --wb;
Daniel Mack997ea582010-04-12 13:17:25 +0200923 usb_free_coherent(acm->dev, acm->writesize,
Oliver Neukum884b6002005-04-21 21:28:02 +0200924 wb->buf, wb->dmah);
925 }
926 return -ENOMEM;
927 }
928 }
929 return 0;
930}
931
Alan Cox10077d42009-06-11 12:36:09 +0100932static int acm_probe(struct usb_interface *intf,
933 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934{
935 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100936 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -0700937 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 int buflen = intf->altsetting->extralen;
939 struct usb_interface *control_interface;
940 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200941 struct usb_endpoint_descriptor *epctrl = NULL;
942 struct usb_endpoint_descriptor *epread = NULL;
943 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 struct usb_device *usb_dev = interface_to_usbdev(intf);
945 struct acm *acm;
946 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +0100947 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 u8 *buf;
949 u8 ac_management_function = 0;
950 u8 call_management_function = 0;
951 int call_interface_num = -1;
952 int data_interface_num;
953 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200954 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100955 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200956 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957
Oliver Neukum86478942006-05-13 22:50:47 +0200958 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200960 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
961
962 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 if (quirks == NO_UNION_NORMAL) {
964 data_interface = usb_ifnum_to_if(usb_dev, 1);
965 control_interface = usb_ifnum_to_if(usb_dev, 0);
966 goto skip_normal_probe;
967 }
Alan Cox6e47e062009-06-11 12:37:06 +0100968
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 /* normal probing*/
970 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700971 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 return -EINVAL;
973 }
974
975 if (!buflen) {
Toby Gray577045c2010-09-02 10:46:20 +0100976 if (intf->cur_altsetting->endpoint &&
977 intf->cur_altsetting->endpoint->extralen &&
Alan Cox6e47e062009-06-11 12:37:06 +0100978 intf->cur_altsetting->endpoint->extra) {
979 dev_dbg(&intf->dev,
980 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 buflen = intf->cur_altsetting->endpoint->extralen;
982 buffer = intf->cur_altsetting->endpoint->extra;
983 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700984 dev_err(&intf->dev,
985 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 return -EINVAL;
987 }
988 }
989
990 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +0100991 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700992 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 goto next_desc;
994 }
995
Alan Cox6e47e062009-06-11 12:37:06 +0100996 switch (buffer[2]) {
997 case USB_CDC_UNION_TYPE: /* we've found it */
998 if (union_header) {
999 dev_err(&intf->dev, "More than one "
1000 "union descriptor, skipping ...\n");
1001 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 }
Alan Cox6e47e062009-06-11 12:37:06 +01001003 union_header = (struct usb_cdc_union_desc *)buffer;
1004 break;
1005 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
1006 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1007 break;
1008 case USB_CDC_HEADER_TYPE: /* maybe check version */
1009 break; /* for now we ignore it */
1010 case USB_CDC_ACM_TYPE:
1011 ac_management_function = buffer[3];
1012 break;
1013 case USB_CDC_CALL_MANAGEMENT_TYPE:
1014 call_management_function = buffer[3];
1015 call_interface_num = buffer[4];
Julian Calabyce126642010-01-05 23:58:20 +11001016 if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
Alan Cox6e47e062009-06-11 12:37:06 +01001017 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1018 break;
1019 default:
1020 /* there are LOTS more CDC descriptors that
1021 * could legitimately be found here.
1022 */
1023 dev_dbg(&intf->dev, "Ignoring descriptor: "
1024 "type %02x, length %d\n",
1025 buffer[2], buffer[0]);
1026 break;
1027 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028next_desc:
1029 buflen -= buffer[0];
1030 buffer += buffer[0];
1031 }
1032
1033 if (!union_header) {
1034 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001035 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
1037 control_interface = intf;
1038 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001039 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1040 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1041 return -ENODEV;
1042 } else {
1043 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1044 combined_interfaces = 1;
1045 control_interface = data_interface = intf;
1046 goto look_for_collapsed_interface;
1047 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 }
1049 } else {
1050 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1051 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1052 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001053 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 return -ENODEV;
1055 }
1056 }
Alan Cox6e47e062009-06-11 12:37:06 +01001057
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001059 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001061 if (control_interface == data_interface) {
1062 /* some broken devices designed for windows work this way */
1063 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1064 combined_interfaces = 1;
1065 /* a popular other OS doesn't use it */
1066 quirks |= NO_CAP_LINE;
1067 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1068 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1069 return -EINVAL;
1070 }
1071look_for_collapsed_interface:
1072 for (i = 0; i < 3; i++) {
1073 struct usb_endpoint_descriptor *ep;
1074 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1075
1076 if (usb_endpoint_is_int_in(ep))
1077 epctrl = ep;
1078 else if (usb_endpoint_is_bulk_out(ep))
1079 epwrite = ep;
1080 else if (usb_endpoint_is_bulk_in(ep))
1081 epread = ep;
1082 else
1083 return -EINVAL;
1084 }
1085 if (!epctrl || !epread || !epwrite)
1086 return -ENODEV;
1087 else
1088 goto made_compressed_probe;
1089 }
1090
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091skip_normal_probe:
1092
1093 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001094 if (data_interface->cur_altsetting->desc.bInterfaceClass
1095 != CDC_DATA_INTERFACE_TYPE) {
1096 if (control_interface->cur_altsetting->desc.bInterfaceClass
1097 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001099 dev_dbg(&intf->dev,
1100 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 t = control_interface;
1102 control_interface = data_interface;
1103 data_interface = t;
1104 } else {
1105 return -EINVAL;
1106 }
1107 }
Alan Stern74da5d62007-08-02 13:29:10 -04001108
1109 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001110 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001111 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001112
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001113 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1114 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001115 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 return -EBUSY;
1117 }
1118
1119
1120 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
1121 return -EINVAL;
1122
1123 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1124 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1125 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1126
1127
1128 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001129 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 /* descriptors are swapped */
1131 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001132 dev_dbg(&intf->dev,
1133 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 t = epread;
1135 epread = epwrite;
1136 epwrite = t;
1137 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001138made_compressed_probe:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 dbg("interfaces are valid");
1140 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
1141
1142 if (minor == ACM_TTY_MINORS) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001143 dev_err(&intf->dev, "no more free acm devices\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 return -ENODEV;
1145 }
1146
Alan Cox6e47e062009-06-11 12:37:06 +01001147 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1148 if (acm == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001149 dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 goto alloc_fail;
1151 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152
1153 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Alan Cox6e47e062009-06-11 12:37:06 +01001154 readsize = le16_to_cpu(epread->wMaxPacketSize) *
1155 (quirks == SINGLE_RX_URB ? 1 : 2);
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001156 acm->combined_interfaces = combined_interfaces;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001157 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 acm->control = control_interface;
1159 acm->data = data_interface;
1160 acm->minor = minor;
1161 acm->dev = usb_dev;
1162 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001163 if (quirks & NO_CAP_LINE)
1164 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 acm->ctrlsize = ctrlsize;
1166 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001167 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001168 acm->urb_task.func = acm_rx_tasklet;
1169 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +00001170 INIT_WORK(&acm->work, acm_softint);
David Brownelle5fbab52008-08-06 18:46:10 -07001171 init_waitqueue_head(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +02001173 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001174 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001175 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001176 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001177 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1178 if (acm->is_int_ep)
1179 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001180 tty_port_init(&acm->port);
1181 acm->port.ops = &acm_port_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182
Daniel Mack997ea582010-04-12 13:17:25 +02001183 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 if (!buf) {
Johan Hovold255ab562011-03-22 11:12:13 +01001185 dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 goto alloc_fail2;
1187 }
1188 acm->ctrl_buffer = buf;
1189
Oliver Neukum884b6002005-04-21 21:28:02 +02001190 if (acm_write_buffers_alloc(acm) < 0) {
Johan Hovold255ab562011-03-22 11:12:13 +01001191 dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 goto alloc_fail4;
1193 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194
1195 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1196 if (!acm->ctrlurb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001197 dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 goto alloc_fail5;
1199 }
Oliver Neukum86478942006-05-13 22:50:47 +02001200 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001201 struct acm_ru *rcv = &(acm->ru[i]);
1202
Alan Cox6e47e062009-06-11 12:37:06 +01001203 rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
1204 if (rcv->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001205 dev_err(&intf->dev,
Alan Cox6e47e062009-06-11 12:37:06 +01001206 "out of memory (read urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001207 goto alloc_fail6;
David Kubicek61a87ad2005-11-01 18:51:34 +01001208 }
1209
1210 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1211 rcv->instance = acm;
1212 }
Oliver Neukum86478942006-05-13 22:50:47 +02001213 for (i = 0; i < num_rx_buf; i++) {
David Brownell672c4e12008-08-06 18:41:12 -07001214 struct acm_rb *rb = &(acm->rb[i]);
David Kubicek61a87ad2005-11-01 18:51:34 +01001215
Daniel Mack997ea582010-04-12 13:17:25 +02001216 rb->base = usb_alloc_coherent(acm->dev, readsize,
David Brownell672c4e12008-08-06 18:41:12 -07001217 GFP_KERNEL, &rb->dma);
1218 if (!rb->base) {
Johan Hovold255ab562011-03-22 11:12:13 +01001219 dev_err(&intf->dev,
Daniel Mack997ea582010-04-12 13:17:25 +02001220 "out of memory (read bufs usb_alloc_coherent)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001221 goto alloc_fail7;
1222 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 }
Alan Cox6e47e062009-06-11 12:37:06 +01001224 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001225 struct acm_wb *snd = &(acm->wb[i]);
1226
Alan Cox6e47e062009-06-11 12:37:06 +01001227 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1228 if (snd->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001229 dev_err(&intf->dev,
Johan Hovold59d7fec2011-03-22 11:12:12 +01001230 "out of memory (write urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001231 goto alloc_fail8;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001232 }
1233
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001234 if (usb_endpoint_xfer_int(epwrite))
1235 usb_fill_int_urb(snd->urb, usb_dev,
1236 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1237 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1238 else
1239 usb_fill_bulk_urb(snd->urb, usb_dev,
1240 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1241 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001242 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1243 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 }
1245
Alan Cox6e47e062009-06-11 12:37:06 +01001246 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001247
1248 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1249 if (i < 0)
1250 goto alloc_fail8;
1251
1252 if (cfd) { /* export the country data */
1253 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1254 if (!acm->country_codes)
1255 goto skip_countries;
1256 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001257 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1258 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001259 acm->country_rel_date = cfd->iCountryCodeRelDate;
1260
1261 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1262 if (i < 0) {
1263 kfree(acm->country_codes);
1264 goto skip_countries;
1265 }
1266
Alan Cox6e47e062009-06-11 12:37:06 +01001267 i = device_create_file(&intf->dev,
1268 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001269 if (i < 0) {
Axel Linc2572b72010-05-31 08:04:47 +08001270 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001271 kfree(acm->country_codes);
1272 goto skip_countries;
1273 }
1274 }
1275
1276skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001277 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001278 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1279 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1280 /* works around buggy devices */
1281 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1283 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1284
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1286
1287 acm_set_control(acm, acm->ctrlout);
1288
1289 acm->line.dwDTERate = cpu_to_le32(9600);
1290 acm->line.bDataBits = 8;
1291 acm_set_line(acm, &acm->line);
1292
1293 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001294 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001296 usb_get_intf(control_interface);
1297 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298
1299 acm_table[minor] = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001301 return 0;
1302alloc_fail8:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001303 for (i = 0; i < ACM_NW; i++)
1304 usb_free_urb(acm->wb[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305alloc_fail7:
Oliver Neukum830f4022008-06-25 14:17:16 +02001306 acm_read_buffers_free(acm);
Axel Linc2572b72010-05-31 08:04:47 +08001307alloc_fail6:
Oliver Neukum86478942006-05-13 22:50:47 +02001308 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001309 usb_free_urb(acm->ru[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 usb_free_urb(acm->ctrlurb);
1311alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001312 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313alloc_fail4:
Daniel Mack997ea582010-04-12 13:17:25 +02001314 usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315alloc_fail2:
1316 kfree(acm);
1317alloc_fail:
1318 return -ENOMEM;
1319}
1320
Oliver Neukum1365baf2007-10-12 17:24:28 +02001321static void stop_data_traffic(struct acm *acm)
1322{
1323 int i;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001324 dbg("Entering stop_data_traffic");
Oliver Neukum1365baf2007-10-12 17:24:28 +02001325
1326 tasklet_disable(&acm->urb_task);
1327
1328 usb_kill_urb(acm->ctrlurb);
Alan Cox6e47e062009-06-11 12:37:06 +01001329 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001330 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001331 for (i = 0; i < acm->rx_buflimit; i++)
1332 usb_kill_urb(acm->ru[i].urb);
1333
Oliver Neukum1365baf2007-10-12 17:24:28 +02001334 tasklet_enable(&acm->urb_task);
1335
1336 cancel_work_sync(&acm->work);
1337}
1338
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339static void acm_disconnect(struct usb_interface *intf)
1340{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001341 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001343 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
David Brownell672c4e12008-08-06 18:41:12 -07001345 /* sibling interface is already cleaning up */
1346 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001347 return;
David Brownell672c4e12008-08-06 18:41:12 -07001348
1349 mutex_lock(&open_mutex);
Alan Cox6e47e062009-06-11 12:37:06 +01001350 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001351 device_remove_file(&acm->control->dev,
1352 &dev_attr_wCountryCodes);
1353 device_remove_file(&acm->control->dev,
1354 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001355 }
Alan Stern74da5d62007-08-02 13:29:10 -04001356 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 acm->dev = NULL;
Oliver Neukum86067eea2006-01-08 12:39:13 +01001358 usb_set_intfdata(acm->control, NULL);
1359 usb_set_intfdata(acm->data, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
Oliver Neukum1365baf2007-10-12 17:24:28 +02001361 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362
Oliver Neukum884b6002005-04-21 21:28:02 +02001363 acm_write_buffers_free(acm);
Daniel Mack997ea582010-04-12 13:17:25 +02001364 usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
1365 acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001366 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001368 if (!acm->combined_interfaces)
1369 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001370 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371
Alan Cox10077d42009-06-11 12:36:09 +01001372 if (acm->port.count == 0) {
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001373 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001374 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 return;
1376 }
1377
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001378 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001379 tty = tty_port_tty_get(&acm->port);
1380 if (tty) {
1381 tty_hangup(tty);
1382 tty_kref_put(tty);
1383 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384}
1385
Oliver Neukum35758582008-07-01 19:10:08 +02001386#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001387static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1388{
1389 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001390 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001391
Alan Stern65bfd292008-11-25 16:39:18 -05001392 if (message.event & PM_EVENT_AUTO) {
Oliver Neukum11ea8592008-06-20 11:25:57 +02001393 int b;
1394
1395 spin_lock_irq(&acm->read_lock);
1396 spin_lock(&acm->write_lock);
1397 b = acm->processing + acm->transmitting;
1398 spin_unlock(&acm->write_lock);
1399 spin_unlock_irq(&acm->read_lock);
1400 if (b)
1401 return -EBUSY;
1402 }
1403
1404 spin_lock_irq(&acm->read_lock);
1405 spin_lock(&acm->write_lock);
1406 cnt = acm->susp_count++;
1407 spin_unlock(&acm->write_lock);
1408 spin_unlock_irq(&acm->read_lock);
1409
1410 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001411 return 0;
1412 /*
1413 we treat opened interfaces differently,
1414 we must guard against open
1415 */
1416 mutex_lock(&acm->mutex);
1417
Alan Cox10077d42009-06-11 12:36:09 +01001418 if (acm->port.count)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001419 stop_data_traffic(acm);
1420
1421 mutex_unlock(&acm->mutex);
1422 return 0;
1423}
1424
1425static int acm_resume(struct usb_interface *intf)
1426{
1427 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001428 struct acm_wb *wb;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001429 int rv = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001430 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001431
Oliver Neukum11ea8592008-06-20 11:25:57 +02001432 spin_lock_irq(&acm->read_lock);
1433 acm->susp_count -= 1;
1434 cnt = acm->susp_count;
1435 spin_unlock_irq(&acm->read_lock);
1436
1437 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001438 return 0;
1439
1440 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001441 if (acm->port.count) {
Oliver Neukum1365baf2007-10-12 17:24:28 +02001442 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001443
1444 spin_lock_irq(&acm->write_lock);
1445 if (acm->delayed_wb) {
1446 wb = acm->delayed_wb;
1447 acm->delayed_wb = NULL;
1448 spin_unlock_irq(&acm->write_lock);
Oliver Neukumf0730922010-03-03 00:37:56 +01001449 acm_start_wb(acm, wb);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001450 } else {
1451 spin_unlock_irq(&acm->write_lock);
1452 }
1453
1454 /*
1455 * delayed error checking because we must
1456 * do the write path at all cost
1457 */
Oliver Neukum1365baf2007-10-12 17:24:28 +02001458 if (rv < 0)
Oliver Neukum11ea8592008-06-20 11:25:57 +02001459 goto err_out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001460
1461 tasklet_schedule(&acm->urb_task);
1462 }
1463
1464err_out:
1465 mutex_unlock(&acm->mutex);
1466 return rv;
1467}
Oliver Neukum35758582008-07-01 19:10:08 +02001468
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001469static int acm_reset_resume(struct usb_interface *intf)
1470{
1471 struct acm *acm = usb_get_intfdata(intf);
1472 struct tty_struct *tty;
1473
1474 mutex_lock(&acm->mutex);
1475 if (acm->port.count) {
1476 tty = tty_port_tty_get(&acm->port);
1477 if (tty) {
1478 tty_hangup(tty);
1479 tty_kref_put(tty);
1480 }
1481 }
1482 mutex_unlock(&acm->mutex);
1483 return acm_resume(intf);
1484}
1485
Oliver Neukum35758582008-07-01 19:10:08 +02001486#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001487
1488#define NOKIA_PCSUITE_ACM_INFO(x) \
1489 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1490 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1491 USB_CDC_ACM_PROTO_VENDOR)
1492
Toby Gray4035e452010-09-01 16:01:19 +01001493#define SAMSUNG_PCSUITE_ACM_INFO(x) \
1494 USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
1495 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1496 USB_CDC_ACM_PROTO_VENDOR)
1497
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498/*
1499 * USB driver structure.
1500 */
1501
Németh Márton6ef48522010-01-10 15:33:45 +01001502static const struct usb_device_id acm_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 /* quirky and broken devices */
1504 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1505 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1506 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001507 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1508 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1509 },
Andrew Lunn0f9c7b4a2008-12-23 17:31:23 +01001510 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1511 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1512 },
Masahito Omote8753e652005-07-29 12:17:25 -07001513 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1514 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1515 },
Chris Malley91a9c922006-10-03 10:08:28 +01001516 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1517 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1518 },
Alan Cox7abcf202009-04-06 17:35:01 +01001519 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1520 .driver_info = SINGLE_RX_URB,
1521 },
Oliver Neukum86478942006-05-13 22:50:47 +02001522 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1523 .driver_info = SINGLE_RX_URB, /* firmware bug */
1524 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001525 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1526 .driver_info = SINGLE_RX_URB, /* firmware bug */
1527 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001528 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1529 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1530 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001531 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1532 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1533 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001534 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1535 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1536 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001537 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1538 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1539 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001540 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1541 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1542 },
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001543 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1544 },
Adam Richterc332b4e2009-02-18 16:17:15 -08001545 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1546 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1547 data interface instead of
1548 communications interface.
1549 Maybe we should define a new
1550 quirk for this. */
1551 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001552 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1553 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1554 },
Russ Nelsonc3baa192010-04-21 23:07:03 -04001555 { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1556 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1557 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001558
Adrian Taylorc1479a92009-11-19 10:35:33 +00001559 /* Nokia S60 phones expose two ACM channels. The first is
1560 * a modem and is picked up by the standard AT-command
1561 * information below. The second is 'vendor-specific' but
1562 * is treated as a serial device at the S60 end, so we want
1563 * to expose it on Linux too. */
1564 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1565 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1566 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1567 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1568 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1569 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1570 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1571 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1572 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1573 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1574 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1575 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1576 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1577 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1578 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1579 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1580 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1581 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1582 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1583 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1584 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1585 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1586 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1587 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1588 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1589 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1590 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1591 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1592 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1593 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1594 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1595 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1596 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1597 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1598 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1599 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1600 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1601 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1602 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1603 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1604 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1605 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1606 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
Przemo Firszt83a4eae92010-06-28 21:29:34 +01001607 { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
Toby Gray4035e452010-09-01 16:01:19 +01001608 { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
1609 { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
1610 { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
1611 { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
1612 { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
1613 { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
1614 { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
1615 { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
1616 { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
1617 { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
Arvid Ephraim Picciani721d92f2011-01-25 15:58:40 +01001618 { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
Toby Gray4035e452010-09-01 16:01:19 +01001619 { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001620
1621 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1622
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001623 /* Support Lego NXT using pbLua firmware */
Julian Calabyce126642010-01-05 23:58:20 +11001624 { USB_DEVICE(0x0694, 0xff00),
1625 .driver_info = NOT_A_MODEM,
Otavio Salvador7893afc2010-09-26 23:35:05 -03001626 },
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001627
Philippe Corbes5b239f02010-08-31 19:31:32 +02001628 /* control interfaces without any protocol set */
1629 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1630 USB_CDC_PROTO_NONE) },
1631
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 /* control interfaces with various AT-command sets */
1633 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1634 USB_CDC_ACM_PROTO_AT_V25TER) },
1635 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1636 USB_CDC_ACM_PROTO_AT_PCCA101) },
1637 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1638 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1639 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1640 USB_CDC_ACM_PROTO_AT_GSM) },
1641 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001642 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1644 USB_CDC_ACM_PROTO_AT_CDMA) },
1645
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 { }
1647};
1648
Alan Cox6e47e062009-06-11 12:37:06 +01001649MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650
1651static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 .name = "cdc_acm",
1653 .probe = acm_probe,
1654 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001655#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001656 .suspend = acm_suspend,
1657 .resume = acm_resume,
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001658 .reset_resume = acm_reset_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001659#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001661#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001662 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001663#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664};
1665
1666/*
1667 * TTY driver structures.
1668 */
1669
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001670static const struct tty_operations acm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 .open = acm_tty_open,
1672 .close = acm_tty_close,
Alan Cox10077d42009-06-11 12:36:09 +01001673 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 .write = acm_tty_write,
1675 .write_room = acm_tty_write_room,
1676 .ioctl = acm_tty_ioctl,
1677 .throttle = acm_tty_throttle,
1678 .unthrottle = acm_tty_unthrottle,
1679 .chars_in_buffer = acm_tty_chars_in_buffer,
1680 .break_ctl = acm_tty_break_ctl,
1681 .set_termios = acm_tty_set_termios,
1682 .tiocmget = acm_tty_tiocmget,
1683 .tiocmset = acm_tty_tiocmset,
1684};
1685
1686/*
1687 * Init / exit.
1688 */
1689
1690static int __init acm_init(void)
1691{
1692 int retval;
1693 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1694 if (!acm_tty_driver)
1695 return -ENOMEM;
1696 acm_tty_driver->owner = THIS_MODULE,
1697 acm_tty_driver->driver_name = "acm",
1698 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 acm_tty_driver->major = ACM_TTY_MAJOR,
1700 acm_tty_driver->minor_start = 0,
1701 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1702 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001703 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001705 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1706 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 tty_set_operations(acm_tty_driver, &acm_ops);
1708
1709 retval = tty_register_driver(acm_tty_driver);
1710 if (retval) {
1711 put_tty_driver(acm_tty_driver);
1712 return retval;
1713 }
1714
1715 retval = usb_register(&acm_driver);
1716 if (retval) {
1717 tty_unregister_driver(acm_tty_driver);
1718 put_tty_driver(acm_tty_driver);
1719 return retval;
1720 }
1721
Greg Kroah-Hartman5909f6e2008-08-18 13:21:04 -07001722 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
1723 DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724
1725 return 0;
1726}
1727
1728static void __exit acm_exit(void)
1729{
1730 usb_deregister(&acm_driver);
1731 tty_unregister_driver(acm_tty_driver);
1732 put_tty_driver(acm_tty_driver);
1733}
1734
1735module_init(acm_init);
1736module_exit(acm_exit);
1737
Alan Cox6e47e062009-06-11 12:37:06 +01001738MODULE_AUTHOR(DRIVER_AUTHOR);
1739MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01001741MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);