blob: 44a5291c5699e990edb4140be3dd7127c82ad792 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070097/*
98 * Functions for ACM control messages.
99 */
100
Alan Cox6e47e062009-06-11 12:37:06 +0100101static int acm_ctrl_msg(struct acm *acm, int request, int value,
102 void *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103{
104 int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
105 request, USB_RT_ACM, value,
106 acm->control->altsetting[0].desc.bInterfaceNumber,
107 buf, len, 5000);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100108 dev_dbg(&acm->control->dev,
109 "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
110 __func__, request, value, len, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 return retval < 0 ? retval : 0;
112}
113
114/* devices aren't required to support these requests.
115 * the cdc acm descriptor tells whether they do...
116 */
117#define acm_set_control(acm, control) \
118 acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
119#define acm_set_line(acm, line) \
120 acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
121#define acm_send_break(acm, ms) \
122 acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
123
124/*
Oliver Neukum884b6002005-04-21 21:28:02 +0200125 * Write buffer management.
126 * All of these assume proper locks taken by the caller.
127 */
128
129static int acm_wb_alloc(struct acm *acm)
130{
131 int i, wbn;
132 struct acm_wb *wb;
133
David Engrafe4cf3aa2008-03-20 10:01:34 +0100134 wbn = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200135 i = 0;
136 for (;;) {
137 wb = &acm->wb[wbn];
138 if (!wb->use) {
139 wb->use = 1;
140 return wbn;
141 }
Oliver Neukum86478942006-05-13 22:50:47 +0200142 wbn = (wbn + 1) % ACM_NW;
143 if (++i >= ACM_NW)
Oliver Neukum884b6002005-04-21 21:28:02 +0200144 return -1;
145 }
146}
147
Oliver Neukum884b6002005-04-21 21:28:02 +0200148static int acm_wb_is_avail(struct acm *acm)
149{
150 int i, n;
David Brownelle5fbab52008-08-06 18:46:10 -0700151 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200152
Oliver Neukum86478942006-05-13 22:50:47 +0200153 n = ACM_NW;
David Brownelle5fbab52008-08-06 18:46:10 -0700154 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100155 for (i = 0; i < ACM_NW; i++)
Oliver Neukum86478942006-05-13 22:50:47 +0200156 n -= acm->wb[i].use;
David Brownelle5fbab52008-08-06 18:46:10 -0700157 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200158 return n;
159}
160
Oliver Neukum884b6002005-04-21 21:28:02 +0200161/*
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800162 * Finish write. Caller must hold acm->write_lock
Oliver Neukum884b6002005-04-21 21:28:02 +0200163 */
David Engrafe4cf3aa2008-03-20 10:01:34 +0100164static void acm_write_done(struct acm *acm, struct acm_wb *wb)
Oliver Neukum884b6002005-04-21 21:28:02 +0200165{
David Engrafe4cf3aa2008-03-20 10:01:34 +0100166 wb->use = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200167 acm->transmitting--;
Oliver Neukum97d35f92009-12-16 17:05:57 +0100168 usb_autopm_put_interface_async(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200169}
170
171/*
172 * Poke write.
Oliver Neukum11ea8592008-06-20 11:25:57 +0200173 *
174 * the caller is responsible for locking
Oliver Neukum884b6002005-04-21 21:28:02 +0200175 */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200176
177static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
178{
179 int rc;
180
181 acm->transmitting++;
182
183 wb->urb->transfer_buffer = wb->buf;
184 wb->urb->transfer_dma = wb->dmah;
185 wb->urb->transfer_buffer_length = wb->len;
186 wb->urb->dev = acm->dev;
187
Alan Cox6e47e062009-06-11 12:37:06 +0100188 rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
189 if (rc < 0) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100190 dev_err(&acm->data->dev,
191 "%s - usb_submit_urb(write bulk) failed: %d\n",
192 __func__, rc);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200193 acm_write_done(acm, wb);
194 }
195 return rc;
196}
197
David Engrafe4cf3aa2008-03-20 10:01:34 +0100198static int acm_write_start(struct acm *acm, int wbn)
Oliver Neukum884b6002005-04-21 21:28:02 +0200199{
200 unsigned long flags;
David Brownell934da462008-08-06 18:44:12 -0700201 struct acm_wb *wb = &acm->wb[wbn];
Oliver Neukum884b6002005-04-21 21:28:02 +0200202 int rc;
203
204 spin_lock_irqsave(&acm->write_lock, flags);
205 if (!acm->dev) {
David Brownell934da462008-08-06 18:44:12 -0700206 wb->use = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200207 spin_unlock_irqrestore(&acm->write_lock, flags);
208 return -ENODEV;
209 }
210
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100211 dev_dbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
212 acm->susp_count);
Oliver Neukum97d35f92009-12-16 17:05:57 +0100213 usb_autopm_get_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200214 if (acm->susp_count) {
Oliver Neukum97d35f92009-12-16 17:05:57 +0100215 if (!acm->delayed_wb)
216 acm->delayed_wb = wb;
217 else
218 usb_autopm_put_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200219 spin_unlock_irqrestore(&acm->write_lock, flags);
220 return 0; /* A white lie */
221 }
222 usb_mark_last_busy(acm->dev);
223
Oliver Neukum11ea8592008-06-20 11:25:57 +0200224 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200225 spin_unlock_irqrestore(&acm->write_lock, flags);
226
Oliver Neukum884b6002005-04-21 21:28:02 +0200227 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200228
Oliver Neukum884b6002005-04-21 21:28:02 +0200229}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100230/*
231 * attributes exported through sysfs
232 */
233static ssize_t show_caps
234(struct device *dev, struct device_attribute *attr, char *buf)
235{
236 struct usb_interface *intf = to_usb_interface(dev);
237 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200238
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100239 return sprintf(buf, "%d", acm->ctrl_caps);
240}
241static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
242
243static ssize_t show_country_codes
244(struct device *dev, struct device_attribute *attr, char *buf)
245{
246 struct usb_interface *intf = to_usb_interface(dev);
247 struct acm *acm = usb_get_intfdata(intf);
248
249 memcpy(buf, acm->country_codes, acm->country_code_size);
250 return acm->country_code_size;
251}
252
253static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
254
255static ssize_t show_country_rel_date
256(struct device *dev, struct device_attribute *attr, char *buf)
257{
258 struct usb_interface *intf = to_usb_interface(dev);
259 struct acm *acm = usb_get_intfdata(intf);
260
261 return sprintf(buf, "%d", acm->country_rel_date);
262}
263
264static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200265/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 * Interrupt handlers for various ACM device responses
267 */
268
269/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100270static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{
272 struct acm *acm = urb->context;
273 struct usb_cdc_notification *dr = urb->transfer_buffer;
Alan Cox10077d42009-06-11 12:36:09 +0100274 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 unsigned char *data;
276 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700277 int retval;
278 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700280 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 case 0:
282 /* success */
283 break;
284 case -ECONNRESET:
285 case -ENOENT:
286 case -ESHUTDOWN:
287 /* this urb is terminated, clean up */
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100288 dev_dbg(&acm->control->dev,
289 "%s - urb shutting down with status: %d\n",
290 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 return;
292 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100293 dev_dbg(&acm->control->dev,
294 "%s - nonzero urb status received: %d\n",
295 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 goto exit;
297 }
298
299 if (!ACM_READY(acm))
300 goto exit;
301
Johan Hovold7e7797e2011-03-22 11:12:11 +0100302 usb_mark_last_busy(acm->dev);
303
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 data = (unsigned char *)(dr + 1);
305 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100306 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100307 dev_dbg(&acm->control->dev, "%s - network connection: %d\n",
308 __func__, dr->wValue);
Alan Cox6e47e062009-06-11 12:37:06 +0100309 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
Alan Cox6e47e062009-06-11 12:37:06 +0100311 case USB_CDC_NOTIFY_SERIAL_STATE:
312 tty = tty_port_tty_get(&acm->port);
313 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
Alan Cox6e47e062009-06-11 12:37:06 +0100315 if (tty) {
316 if (!acm->clocal &&
317 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100318 dev_dbg(&acm->control->dev,
319 "%s - calling hangup\n", __func__);
Alan Cox6e47e062009-06-11 12:37:06 +0100320 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 }
Alan Cox6e47e062009-06-11 12:37:06 +0100322 tty_kref_put(tty);
323 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324
Alan Cox6e47e062009-06-11 12:37:06 +0100325 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100327 dev_dbg(&acm->control->dev,
328 "%s - input control lines: dcd%c dsr%c break%c "
329 "ring%c framing%c parity%c overrun%c\n",
330 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100331 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
332 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
333 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
334 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
335 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
336 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
337 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 break;
339
Alan Cox6e47e062009-06-11 12:37:06 +0100340 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100341 dev_dbg(&acm->control->dev,
342 "%s - unknown notification %d received: index %d "
343 "len %d data0 %d data1 %d\n",
344 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100345 dr->bNotificationType, dr->wIndex,
346 dr->wLength, data[0], data[1]);
347 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 }
349exit:
Alan Cox6e47e062009-06-11 12:37:06 +0100350 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700351 if (retval)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100352 dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
353 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354}
355
356/* data interface returns incoming bytes, or we got unthrottled */
David Howells7d12e782006-10-05 14:55:46 +0100357static void acm_read_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358{
David Kubicek61a87ad2005-11-01 18:51:34 +0100359 struct acm_rb *buf;
360 struct acm_ru *rcv = urb->context;
361 struct acm *acm = rcv->instance;
Oliver Neukum86478942006-05-13 22:50:47 +0200362 int status = urb->status;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700363
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100364 dev_dbg(&acm->data->dev, "%s - status %d\n", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
Oliver Neukum11ea8592008-06-20 11:25:57 +0200366 if (!ACM_READY(acm)) {
Johan Hovold1d9846e2011-03-22 11:12:14 +0100367 dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200369 }
370 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
Oliver Neukum86478942006-05-13 22:50:47 +0200372 if (status)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100373 dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
374 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375
David Kubicek61a87ad2005-11-01 18:51:34 +0100376 buf = rcv->buffer;
377 buf->size = urb->actual_length;
378
Oliver Neukum86478942006-05-13 22:50:47 +0200379 if (likely(status == 0)) {
380 spin_lock(&acm->read_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200381 acm->processing++;
Oliver Neukum86478942006-05-13 22:50:47 +0200382 list_add_tail(&rcv->list, &acm->spare_read_urbs);
383 list_add_tail(&buf->list, &acm->filled_read_bufs);
384 spin_unlock(&acm->read_lock);
385 } else {
386 /* we drop the buffer due to an error */
387 spin_lock(&acm->read_lock);
388 list_add_tail(&rcv->list, &acm->spare_read_urbs);
389 list_add(&buf->list, &acm->spare_read_bufs);
390 spin_unlock(&acm->read_lock);
391 /* nevertheless the tasklet must be kicked unconditionally
392 so the queue cannot dry up */
393 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200394 if (likely(!acm->susp_count))
395 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396}
397
398static void acm_rx_tasklet(unsigned long _acm)
399{
400 struct acm *acm = (void *)_acm;
David Kubicek61a87ad2005-11-01 18:51:34 +0100401 struct acm_rb *buf;
Alan Cox10077d42009-06-11 12:36:09 +0100402 struct tty_struct *tty;
David Kubicek61a87ad2005-11-01 18:51:34 +0100403 struct acm_ru *rcv;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200404 unsigned long flags;
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100405 unsigned char throttled;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200406
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100407 dev_dbg(&acm->data->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
Alan Cox10077d42009-06-11 12:36:09 +0100409 if (!ACM_READY(acm)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100410 dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100411 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200412 }
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100413
Oliver Neukum834dbca2007-03-06 10:47:04 +0100414 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100415 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100416 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Alan Cox10077d42009-06-11 12:36:09 +0100417 if (throttled) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100418 dev_dbg(&acm->data->dev, "%s - throttled\n", __func__);
David Kubicek61a87ad2005-11-01 18:51:34 +0100419 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200420 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100421
Alan Cox10077d42009-06-11 12:36:09 +0100422 tty = tty_port_tty_get(&acm->port);
423
David Kubicek61a87ad2005-11-01 18:51:34 +0100424next_buffer:
Jarek Poplawski762f0072006-10-06 07:23:11 +0200425 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100426 if (list_empty(&acm->filled_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200427 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100428 goto urbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100430 buf = list_entry(acm->filled_read_bufs.next,
431 struct acm_rb, list);
432 list_del(&buf->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200433 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100434
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100435 dev_dbg(&acm->data->dev, "%s - processing buf 0x%p, size = %d\n",
436 __func__, buf, buf->size);
Alan Cox10077d42009-06-11 12:36:09 +0100437 if (tty) {
438 spin_lock_irqsave(&acm->throttle_lock, flags);
439 throttled = acm->throttle;
440 spin_unlock_irqrestore(&acm->throttle_lock, flags);
441 if (!throttled) {
Alan Cox10077d42009-06-11 12:36:09 +0100442 tty_insert_flip_string(tty, buf->base, buf->size);
443 tty_flip_buffer_push(tty);
444 } else {
445 tty_kref_put(tty);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100446 dev_dbg(&acm->data->dev, "%s - throttling noticed\n",
447 __func__);
Alan Cox10077d42009-06-11 12:36:09 +0100448 spin_lock_irqsave(&acm->read_lock, flags);
449 list_add(&buf->list, &acm->filled_read_bufs);
450 spin_unlock_irqrestore(&acm->read_lock, flags);
451 return;
452 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
Jarek Poplawski762f0072006-10-06 07:23:11 +0200455 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100456 list_add(&buf->list, &acm->spare_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200457 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100458 goto next_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
David Kubicek61a87ad2005-11-01 18:51:34 +0100460urbs:
Alan Cox10077d42009-06-11 12:36:09 +0100461 tty_kref_put(tty);
462
David Kubicek61a87ad2005-11-01 18:51:34 +0100463 while (!list_empty(&acm->spare_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200464 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100465 if (list_empty(&acm->spare_read_urbs)) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200466 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200467 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100468 return;
469 }
470 rcv = list_entry(acm->spare_read_urbs.next,
471 struct acm_ru, list);
472 list_del(&rcv->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200473 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100474
475 buf = list_entry(acm->spare_read_bufs.next,
476 struct acm_rb, list);
477 list_del(&buf->list);
478
479 rcv->buffer = buf;
480
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200481 if (acm->is_int_ep)
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400482 usb_fill_int_urb(rcv->urb, acm->dev,
483 acm->rx_endpoint,
484 buf->base,
485 acm->readsize,
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200486 acm_read_bulk, rcv, acm->bInterval);
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400487 else
488 usb_fill_bulk_urb(rcv->urb, acm->dev,
489 acm->rx_endpoint,
490 buf->base,
491 acm->readsize,
492 acm_read_bulk, rcv);
David Kubicek61a87ad2005-11-01 18:51:34 +0100493 rcv->urb->transfer_dma = buf->dma;
494 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
495
Alan Cox6e47e062009-06-11 12:37:06 +0100496 /* This shouldn't kill the driver as unsuccessful URBs are
497 returned to the free-urbs-pool and resubmited ASAP */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200498 spin_lock_irqsave(&acm->read_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100499 if (acm->susp_count ||
500 usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100501 list_add(&buf->list, &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100502 list_add(&rcv->list, &acm->spare_read_urbs);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200503 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200504 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100505 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200506 } else {
507 spin_unlock_irqrestore(&acm->read_lock, flags);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100508 dev_dbg(&acm->data->dev,
509 "%s - sending urb 0x%p, rcv 0x%p, buf 0x%p\n",
510 __func__, rcv->urb, rcv, buf);
David Kubicek61a87ad2005-11-01 18:51:34 +0100511 }
512 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200513 spin_lock_irqsave(&acm->read_lock, flags);
514 acm->processing = 0;
515 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516}
517
518/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100519static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520{
Ming Leicdc97792008-02-24 18:41:47 +0800521 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700522 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800523 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200524
Johan Hovold4fa46262011-03-22 11:12:16 +0100525 if (urb->status || (urb->actual_length != urb->transfer_buffer_length))
526 dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
Johan Hovold1d9846e2011-03-22 11:12:14 +0100527 __func__,
David Brownelle5fbab52008-08-06 18:46:10 -0700528 urb->actual_length,
529 urb->transfer_buffer_length,
530 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800532 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100533 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800534 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200535 if (ACM_READY(acm))
536 schedule_work(&acm->work);
David Brownelle5fbab52008-08-06 18:46:10 -0700537 else
538 wake_up_interruptible(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539}
540
David Howellsc4028952006-11-22 14:57:56 +0000541static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542{
David Howellsc4028952006-11-22 14:57:56 +0000543 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100544 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700545
Johan Hovold1d9846e2011-03-22 11:12:14 +0100546 dev_vdbg(&acm->data->dev, "%s\n", __func__);
547
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 if (!ACM_READY(acm))
549 return;
Alan Cox10077d42009-06-11 12:36:09 +0100550 tty = tty_port_tty_get(&acm->port);
Johan Hovold15e5bee2011-03-22 11:12:10 +0100551 if (!tty)
552 return;
Alan Cox10077d42009-06-11 12:36:09 +0100553 tty_wakeup(tty);
554 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555}
556
557/*
558 * TTY handlers
559 */
560
561static int acm_tty_open(struct tty_struct *tty, struct file *filp)
562{
563 struct acm *acm;
Thadeu Lima de Souza Cascardo42dd2aa2009-06-25 14:41:24 +0100564 int rv = -ENODEV;
David Kubicek61a87ad2005-11-01 18:51:34 +0100565 int i;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100566
567 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
569 acm = acm_table[tty->index];
570 if (!acm || !acm->dev)
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100571 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 else
573 rv = 0;
574
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100575 dev_dbg(&acm->control->dev, "%s\n", __func__);
576
David Engraf28d1dfa2008-03-20 10:53:52 +0100577 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
Alan Cox10077d42009-06-11 12:36:09 +0100578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 tty->driver_data = acm;
Alan Cox10077d42009-06-11 12:36:09 +0100580 tty_port_tty_set(&acm->port, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Oliver Neukum94409cc2008-02-11 15:22:29 +0100582 if (usb_autopm_get_interface(acm->control) < 0)
583 goto early_bail;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200584 else
585 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200586
587 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100588 if (acm->port.count++) {
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100589 mutex_unlock(&acm->mutex);
Oliver Neukum1365baf2007-10-12 17:24:28 +0200590 usb_autopm_put_interface(acm->control);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100591 goto out;
Alan Cox10077d42009-06-11 12:36:09 +0100592 }
Oliver Neukum1365baf2007-10-12 17:24:28 +0200593
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 acm->ctrlurb->dev = acm->dev;
595 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100596 dev_err(&acm->control->dev,
597 "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 goto bail_out;
599 }
600
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100601 if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
602 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 goto full_bailout;
Alan Cox10077d42009-06-11 12:36:09 +0100604
Oliver Neukum11ea8592008-06-20 11:25:57 +0200605 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
David Kubicek61a87ad2005-11-01 18:51:34 +0100607 INIT_LIST_HEAD(&acm->spare_read_urbs);
608 INIT_LIST_HEAD(&acm->spare_read_bufs);
609 INIT_LIST_HEAD(&acm->filled_read_bufs);
Alan Cox6e47e062009-06-11 12:37:06 +0100610
611 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100612 list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
Alan Cox6e47e062009-06-11 12:37:06 +0100613 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100614 list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100615
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100616 acm->throttle = 0;
617
Oliver Neukum7af25b42009-09-08 23:51:28 +0200618 set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
Alan Cox10077d42009-06-11 12:36:09 +0100619 rv = tty_port_block_til_ready(&acm->port, tty, filp);
Henry Gebhardt18a77b52009-11-04 11:19:28 +0100620 tasklet_schedule(&acm->urb_task);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100621
Oliver Neukum1365baf2007-10-12 17:24:28 +0200622 mutex_unlock(&acm->mutex);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100623out:
Oliver Neukum94409cc2008-02-11 15:22:29 +0100624 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 return rv;
626
627full_bailout:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 usb_kill_urb(acm->ctrlurb);
629bail_out:
Alan Cox10077d42009-06-11 12:36:09 +0100630 acm->port.count--;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200631 mutex_unlock(&acm->mutex);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100632 usb_autopm_put_interface(acm->control);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100633early_bail:
634 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100635 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 return -EIO;
637}
638
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700639static void acm_tty_unregister(struct acm *acm)
640{
Alan Cox10077d42009-06-11 12:36:09 +0100641 int i, nr;
David Kubicek61a87ad2005-11-01 18:51:34 +0100642
Oliver Neukum86478942006-05-13 22:50:47 +0200643 nr = acm->rx_buflimit;
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700644 tty_unregister_device(acm_tty_driver, acm->minor);
645 usb_put_intf(acm->control);
646 acm_table[acm->minor] = NULL;
647 usb_free_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100648 for (i = 0; i < ACM_NW; i++)
649 usb_free_urb(acm->wb[i].urb);
Oliver Neukum86478942006-05-13 22:50:47 +0200650 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100651 usb_free_urb(acm->ru[i].urb);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100652 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700653 kfree(acm);
654}
655
David Brownelle5fbab52008-08-06 18:46:10 -0700656static int acm_tty_chars_in_buffer(struct tty_struct *tty);
657
Arnd Bergmann4e608672010-06-01 22:53:04 +0200658static void acm_port_down(struct acm *acm)
Alan Cox10077d42009-06-11 12:36:09 +0100659{
660 int i, nr = acm->rx_buflimit;
661 mutex_lock(&open_mutex);
662 if (acm->dev) {
663 usb_autopm_get_interface(acm->control);
664 acm_set_control(acm, acm->ctrlout = 0);
Alan Cox10077d42009-06-11 12:36:09 +0100665 usb_kill_urb(acm->ctrlurb);
666 for (i = 0; i < ACM_NW; i++)
667 usb_kill_urb(acm->wb[i].urb);
Johan Hovold23b80552011-03-22 11:12:09 +0100668 tasklet_disable(&acm->urb_task);
Alan Cox10077d42009-06-11 12:36:09 +0100669 for (i = 0; i < nr; i++)
670 usb_kill_urb(acm->ru[i].urb);
Johan Hovold23b80552011-03-22 11:12:09 +0100671 tasklet_enable(&acm->urb_task);
Alan Cox10077d42009-06-11 12:36:09 +0100672 acm->control->needs_remote_wakeup = 0;
673 usb_autopm_put_interface(acm->control);
674 }
675 mutex_unlock(&open_mutex);
676}
677
678static void acm_tty_hangup(struct tty_struct *tty)
679{
680 struct acm *acm = tty->driver_data;
681 tty_port_hangup(&acm->port);
Arnd Bergmann4e608672010-06-01 22:53:04 +0200682 acm_port_down(acm);
Alan Cox10077d42009-06-11 12:36:09 +0100683}
684
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685static void acm_tty_close(struct tty_struct *tty, struct file *filp)
686{
687 struct acm *acm = tty->driver_data;
688
Alan Cox10077d42009-06-11 12:36:09 +0100689 /* Perform the closing process and see if we need to do the hardware
690 shutdown */
Francesco Lavra051522b2009-11-03 10:53:07 +0000691 if (!acm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 return;
Francesco Lavra051522b2009-11-03 10:53:07 +0000693 if (tty_port_close_start(&acm->port, tty, filp) == 0) {
694 mutex_lock(&open_mutex);
695 if (!acm->dev) {
696 tty_port_tty_set(&acm->port, NULL);
697 acm_tty_unregister(acm);
698 tty->driver_data = NULL;
699 }
700 mutex_unlock(&open_mutex);
701 return;
702 }
Arnd Bergmann4e608672010-06-01 22:53:04 +0200703 acm_port_down(acm);
Alan Cox10077d42009-06-11 12:36:09 +0100704 tty_port_close_end(&acm->port, tty);
Alan Cox10077d42009-06-11 12:36:09 +0100705 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706}
707
Alan Cox6e47e062009-06-11 12:37:06 +0100708static int acm_tty_write(struct tty_struct *tty,
709 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710{
711 struct acm *acm = tty->driver_data;
712 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200713 unsigned long flags;
714 int wbn;
715 struct acm_wb *wb;
716
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 if (!ACM_READY(acm))
718 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 if (!count)
720 return 0;
721
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100722 dev_dbg(&acm->data->dev, "%s - count %d\n", __func__, count);
723
Oliver Neukum884b6002005-04-21 21:28:02 +0200724 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100725 wbn = acm_wb_alloc(acm);
726 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200727 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200728 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200730 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731
Oliver Neukum884b6002005-04-21 21:28:02 +0200732 count = (count > acm->writesize) ? acm->writesize : count;
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100733 dev_dbg(&acm->data->dev, "%s - write %d\n", __func__, count);
Oliver Neukum884b6002005-04-21 21:28:02 +0200734 memcpy(wb->buf, buf, count);
735 wb->len = count;
736 spin_unlock_irqrestore(&acm->write_lock, flags);
737
Alan Cox6e47e062009-06-11 12:37:06 +0100738 stat = acm_write_start(acm, wbn);
739 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200740 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 return count;
742}
743
744static int acm_tty_write_room(struct tty_struct *tty)
745{
746 struct acm *acm = tty->driver_data;
747 if (!ACM_READY(acm))
748 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200749 /*
750 * Do not let the line discipline to know that we have a reserve,
751 * or it might get too enthusiastic.
752 */
David Brownell934da462008-08-06 18:44:12 -0700753 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754}
755
756static int acm_tty_chars_in_buffer(struct tty_struct *tty)
757{
758 struct acm *acm = tty->driver_data;
759 if (!ACM_READY(acm))
Alan Cox23198fd2009-07-20 16:05:27 +0100760 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200761 /*
762 * This is inaccurate (overcounts), but it works.
763 */
Oliver Neukum86478942006-05-13 22:50:47 +0200764 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765}
766
767static void acm_tty_throttle(struct tty_struct *tty)
768{
769 struct acm *acm = tty->driver_data;
770 if (!ACM_READY(acm))
771 return;
772 spin_lock_bh(&acm->throttle_lock);
773 acm->throttle = 1;
774 spin_unlock_bh(&acm->throttle_lock);
775}
776
777static void acm_tty_unthrottle(struct tty_struct *tty)
778{
779 struct acm *acm = tty->driver_data;
780 if (!ACM_READY(acm))
781 return;
782 spin_lock_bh(&acm->throttle_lock);
783 acm->throttle = 0;
784 spin_unlock_bh(&acm->throttle_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100785 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786}
787
Alan Cox9e989662008-07-22 11:18:03 +0100788static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789{
790 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100791 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 if (!ACM_READY(acm))
Alan Cox9e989662008-07-22 11:18:03 +0100793 return -EINVAL;
794 retval = acm_send_break(acm, state ? 0xffff : 0);
795 if (retval < 0)
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100796 dev_dbg(&acm->control->dev, "%s - send break failed\n",
797 __func__);
Alan Cox9e989662008-07-22 11:18:03 +0100798 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799}
800
Alan Cox60b33c12011-02-14 16:26:14 +0000801static int acm_tty_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802{
803 struct acm *acm = tty->driver_data;
804
805 if (!ACM_READY(acm))
806 return -EINVAL;
807
808 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
809 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
810 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
811 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
812 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
813 TIOCM_CTS;
814}
815
Alan Cox20b9d172011-02-14 16:26:50 +0000816static int acm_tty_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 unsigned int set, unsigned int clear)
818{
819 struct acm *acm = tty->driver_data;
820 unsigned int newctrl;
821
822 if (!ACM_READY(acm))
823 return -EINVAL;
824
825 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100826 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
827 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
828 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
829 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
831 newctrl = (newctrl & ~clear) | set;
832
833 if (acm->ctrlout == newctrl)
834 return 0;
835 return acm_set_control(acm, acm->ctrlout = newctrl);
836}
837
Alan Cox6caa76b2011-02-14 16:27:22 +0000838static int acm_tty_ioctl(struct tty_struct *tty,
Alan Cox6e47e062009-06-11 12:37:06 +0100839 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840{
841 struct acm *acm = tty->driver_data;
842
843 if (!ACM_READY(acm))
844 return -EINVAL;
845
846 return -ENOIOCTLCMD;
847}
848
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100849static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 0, 50, 75, 110, 134, 150, 200, 300, 600,
851 1200, 1800, 2400, 4800, 9600, 19200, 38400,
852 57600, 115200, 230400, 460800, 500000, 576000,
853 921600, 1000000, 1152000, 1500000, 2000000,
854 2500000, 3000000, 3500000, 4000000
855};
856
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100857static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 5, 6, 7, 8
859};
860
Alan Cox6e47e062009-06-11 12:37:06 +0100861static void acm_tty_set_termios(struct tty_struct *tty,
862 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863{
864 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800865 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 struct usb_cdc_line_coding newline;
867 int newctrl = acm->ctrlout;
868
869 if (!ACM_READY(acm))
870 return;
871
Alan Cox9b80fee2009-09-19 13:13:23 -0700872 newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
874 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100875 (termios->c_cflag & PARODD ? 1 : 2) +
876 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
Alan Cox6e47e062009-06-11 12:37:06 +0100878 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
880
881 if (!newline.dwDTERate) {
882 newline.dwDTERate = acm->line.dwDTERate;
883 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +0100884 } else
885 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886
887 if (newctrl != acm->ctrlout)
888 acm_set_control(acm, acm->ctrlout = newctrl);
889
890 if (memcmp(&acm->line, &newline, sizeof newline)) {
891 memcpy(&acm->line, &newline, sizeof newline);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100892 dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
893 __func__,
894 le32_to_cpu(newline.dwDTERate),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 newline.bCharFormat, newline.bParityType,
896 newline.bDataBits);
897 acm_set_line(acm, &acm->line);
898 }
899}
900
901/*
902 * USB probe and disconnect routines.
903 */
904
Oliver Neukum830f4022008-06-25 14:17:16 +0200905/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +0200906static void acm_write_buffers_free(struct acm *acm)
907{
908 int i;
909 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +0200910 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200911
Alan Cox6e47e062009-06-11 12:37:06 +0100912 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Daniel Mack997ea582010-04-12 13:17:25 +0200913 usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +0200914}
915
Oliver Neukum830f4022008-06-25 14:17:16 +0200916static void acm_read_buffers_free(struct acm *acm)
917{
918 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
919 int i, n = acm->rx_buflimit;
920
921 for (i = 0; i < n; i++)
Daniel Mack997ea582010-04-12 13:17:25 +0200922 usb_free_coherent(usb_dev, acm->readsize,
923 acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +0200924}
925
Oliver Neukum884b6002005-04-21 21:28:02 +0200926/* Little helper: write buffers allocate */
927static int acm_write_buffers_alloc(struct acm *acm)
928{
929 int i;
930 struct acm_wb *wb;
931
Oliver Neukum86478942006-05-13 22:50:47 +0200932 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Daniel Mack997ea582010-04-12 13:17:25 +0200933 wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
Oliver Neukum884b6002005-04-21 21:28:02 +0200934 &wb->dmah);
935 if (!wb->buf) {
936 while (i != 0) {
937 --i;
938 --wb;
Daniel Mack997ea582010-04-12 13:17:25 +0200939 usb_free_coherent(acm->dev, acm->writesize,
Oliver Neukum884b6002005-04-21 21:28:02 +0200940 wb->buf, wb->dmah);
941 }
942 return -ENOMEM;
943 }
944 }
945 return 0;
946}
947
Alan Cox10077d42009-06-11 12:36:09 +0100948static int acm_probe(struct usb_interface *intf,
949 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950{
951 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100952 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -0700953 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 int buflen = intf->altsetting->extralen;
955 struct usb_interface *control_interface;
956 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200957 struct usb_endpoint_descriptor *epctrl = NULL;
958 struct usb_endpoint_descriptor *epread = NULL;
959 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 struct usb_device *usb_dev = interface_to_usbdev(intf);
961 struct acm *acm;
962 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +0100963 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 u8 *buf;
965 u8 ac_management_function = 0;
966 u8 call_management_function = 0;
967 int call_interface_num = -1;
968 int data_interface_num;
969 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200970 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100971 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200972 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973
Oliver Neukum86478942006-05-13 22:50:47 +0200974 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200976 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
977
978 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 if (quirks == NO_UNION_NORMAL) {
980 data_interface = usb_ifnum_to_if(usb_dev, 1);
981 control_interface = usb_ifnum_to_if(usb_dev, 0);
982 goto skip_normal_probe;
983 }
Alan Cox6e47e062009-06-11 12:37:06 +0100984
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 /* normal probing*/
986 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700987 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 return -EINVAL;
989 }
990
991 if (!buflen) {
Toby Gray577045c2010-09-02 10:46:20 +0100992 if (intf->cur_altsetting->endpoint &&
993 intf->cur_altsetting->endpoint->extralen &&
Alan Cox6e47e062009-06-11 12:37:06 +0100994 intf->cur_altsetting->endpoint->extra) {
995 dev_dbg(&intf->dev,
996 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 buflen = intf->cur_altsetting->endpoint->extralen;
998 buffer = intf->cur_altsetting->endpoint->extra;
999 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001000 dev_err(&intf->dev,
1001 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 return -EINVAL;
1003 }
1004 }
1005
1006 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001007 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001008 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 goto next_desc;
1010 }
1011
Alan Cox6e47e062009-06-11 12:37:06 +01001012 switch (buffer[2]) {
1013 case USB_CDC_UNION_TYPE: /* we've found it */
1014 if (union_header) {
1015 dev_err(&intf->dev, "More than one "
1016 "union descriptor, skipping ...\n");
1017 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 }
Alan Cox6e47e062009-06-11 12:37:06 +01001019 union_header = (struct usb_cdc_union_desc *)buffer;
1020 break;
1021 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
1022 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1023 break;
1024 case USB_CDC_HEADER_TYPE: /* maybe check version */
1025 break; /* for now we ignore it */
1026 case USB_CDC_ACM_TYPE:
1027 ac_management_function = buffer[3];
1028 break;
1029 case USB_CDC_CALL_MANAGEMENT_TYPE:
1030 call_management_function = buffer[3];
1031 call_interface_num = buffer[4];
Julian Calabyce126642010-01-05 23:58:20 +11001032 if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
Alan Cox6e47e062009-06-11 12:37:06 +01001033 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1034 break;
1035 default:
1036 /* there are LOTS more CDC descriptors that
1037 * could legitimately be found here.
1038 */
1039 dev_dbg(&intf->dev, "Ignoring descriptor: "
1040 "type %02x, length %d\n",
1041 buffer[2], buffer[0]);
1042 break;
1043 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044next_desc:
1045 buflen -= buffer[0];
1046 buffer += buffer[0];
1047 }
1048
1049 if (!union_header) {
1050 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001051 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
1053 control_interface = intf;
1054 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001055 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1056 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1057 return -ENODEV;
1058 } else {
1059 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1060 combined_interfaces = 1;
1061 control_interface = data_interface = intf;
1062 goto look_for_collapsed_interface;
1063 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 }
1065 } else {
1066 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1067 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1068 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001069 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 return -ENODEV;
1071 }
1072 }
Alan Cox6e47e062009-06-11 12:37:06 +01001073
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001075 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001077 if (control_interface == data_interface) {
1078 /* some broken devices designed for windows work this way */
1079 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1080 combined_interfaces = 1;
1081 /* a popular other OS doesn't use it */
1082 quirks |= NO_CAP_LINE;
1083 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1084 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1085 return -EINVAL;
1086 }
1087look_for_collapsed_interface:
1088 for (i = 0; i < 3; i++) {
1089 struct usb_endpoint_descriptor *ep;
1090 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1091
1092 if (usb_endpoint_is_int_in(ep))
1093 epctrl = ep;
1094 else if (usb_endpoint_is_bulk_out(ep))
1095 epwrite = ep;
1096 else if (usb_endpoint_is_bulk_in(ep))
1097 epread = ep;
1098 else
1099 return -EINVAL;
1100 }
1101 if (!epctrl || !epread || !epwrite)
1102 return -ENODEV;
1103 else
1104 goto made_compressed_probe;
1105 }
1106
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107skip_normal_probe:
1108
1109 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001110 if (data_interface->cur_altsetting->desc.bInterfaceClass
1111 != CDC_DATA_INTERFACE_TYPE) {
1112 if (control_interface->cur_altsetting->desc.bInterfaceClass
1113 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001115 dev_dbg(&intf->dev,
1116 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 t = control_interface;
1118 control_interface = data_interface;
1119 data_interface = t;
1120 } else {
1121 return -EINVAL;
1122 }
1123 }
Alan Stern74da5d62007-08-02 13:29:10 -04001124
1125 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001126 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001127 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001128
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001129 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1130 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001131 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 return -EBUSY;
1133 }
1134
1135
1136 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
1137 return -EINVAL;
1138
1139 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1140 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1141 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1142
1143
1144 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001145 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 /* descriptors are swapped */
1147 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001148 dev_dbg(&intf->dev,
1149 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 t = epread;
1151 epread = epwrite;
1152 epwrite = t;
1153 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001154made_compressed_probe:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001155 dev_dbg(&intf->dev, "interfaces are valid\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
1157
1158 if (minor == ACM_TTY_MINORS) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001159 dev_err(&intf->dev, "no more free acm devices\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 return -ENODEV;
1161 }
1162
Alan Cox6e47e062009-06-11 12:37:06 +01001163 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1164 if (acm == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001165 dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 goto alloc_fail;
1167 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168
1169 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Alan Cox6e47e062009-06-11 12:37:06 +01001170 readsize = le16_to_cpu(epread->wMaxPacketSize) *
1171 (quirks == SINGLE_RX_URB ? 1 : 2);
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001172 acm->combined_interfaces = combined_interfaces;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001173 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 acm->control = control_interface;
1175 acm->data = data_interface;
1176 acm->minor = minor;
1177 acm->dev = usb_dev;
1178 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001179 if (quirks & NO_CAP_LINE)
1180 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 acm->ctrlsize = ctrlsize;
1182 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001183 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001184 acm->urb_task.func = acm_rx_tasklet;
1185 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +00001186 INIT_WORK(&acm->work, acm_softint);
David Brownelle5fbab52008-08-06 18:46:10 -07001187 init_waitqueue_head(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +02001189 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001190 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001191 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001192 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001193 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1194 if (acm->is_int_ep)
1195 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001196 tty_port_init(&acm->port);
1197 acm->port.ops = &acm_port_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198
Daniel Mack997ea582010-04-12 13:17:25 +02001199 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 if (!buf) {
Johan Hovold255ab562011-03-22 11:12:13 +01001201 dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 goto alloc_fail2;
1203 }
1204 acm->ctrl_buffer = buf;
1205
Oliver Neukum884b6002005-04-21 21:28:02 +02001206 if (acm_write_buffers_alloc(acm) < 0) {
Johan Hovold255ab562011-03-22 11:12:13 +01001207 dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 goto alloc_fail4;
1209 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210
1211 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1212 if (!acm->ctrlurb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001213 dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 goto alloc_fail5;
1215 }
Oliver Neukum86478942006-05-13 22:50:47 +02001216 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001217 struct acm_ru *rcv = &(acm->ru[i]);
1218
Alan Cox6e47e062009-06-11 12:37:06 +01001219 rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
1220 if (rcv->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001221 dev_err(&intf->dev,
Alan Cox6e47e062009-06-11 12:37:06 +01001222 "out of memory (read urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001223 goto alloc_fail6;
David Kubicek61a87ad2005-11-01 18:51:34 +01001224 }
1225
1226 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1227 rcv->instance = acm;
1228 }
Oliver Neukum86478942006-05-13 22:50:47 +02001229 for (i = 0; i < num_rx_buf; i++) {
David Brownell672c4e12008-08-06 18:41:12 -07001230 struct acm_rb *rb = &(acm->rb[i]);
David Kubicek61a87ad2005-11-01 18:51:34 +01001231
Daniel Mack997ea582010-04-12 13:17:25 +02001232 rb->base = usb_alloc_coherent(acm->dev, readsize,
David Brownell672c4e12008-08-06 18:41:12 -07001233 GFP_KERNEL, &rb->dma);
1234 if (!rb->base) {
Johan Hovold255ab562011-03-22 11:12:13 +01001235 dev_err(&intf->dev,
Daniel Mack997ea582010-04-12 13:17:25 +02001236 "out of memory (read bufs usb_alloc_coherent)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001237 goto alloc_fail7;
1238 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 }
Alan Cox6e47e062009-06-11 12:37:06 +01001240 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001241 struct acm_wb *snd = &(acm->wb[i]);
1242
Alan Cox6e47e062009-06-11 12:37:06 +01001243 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1244 if (snd->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001245 dev_err(&intf->dev,
Johan Hovold59d7fec2011-03-22 11:12:12 +01001246 "out of memory (write urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001247 goto alloc_fail8;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001248 }
1249
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001250 if (usb_endpoint_xfer_int(epwrite))
1251 usb_fill_int_urb(snd->urb, usb_dev,
1252 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1253 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1254 else
1255 usb_fill_bulk_urb(snd->urb, usb_dev,
1256 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1257 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001258 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1259 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 }
1261
Alan Cox6e47e062009-06-11 12:37:06 +01001262 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001263
1264 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1265 if (i < 0)
1266 goto alloc_fail8;
1267
1268 if (cfd) { /* export the country data */
1269 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1270 if (!acm->country_codes)
1271 goto skip_countries;
1272 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001273 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1274 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001275 acm->country_rel_date = cfd->iCountryCodeRelDate;
1276
1277 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1278 if (i < 0) {
1279 kfree(acm->country_codes);
1280 goto skip_countries;
1281 }
1282
Alan Cox6e47e062009-06-11 12:37:06 +01001283 i = device_create_file(&intf->dev,
1284 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001285 if (i < 0) {
Axel Linc2572b72010-05-31 08:04:47 +08001286 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001287 kfree(acm->country_codes);
1288 goto skip_countries;
1289 }
1290 }
1291
1292skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001293 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001294 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1295 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1296 /* works around buggy devices */
1297 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1299 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1300
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1302
1303 acm_set_control(acm, acm->ctrlout);
1304
1305 acm->line.dwDTERate = cpu_to_le32(9600);
1306 acm->line.bDataBits = 8;
1307 acm_set_line(acm, &acm->line);
1308
1309 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001310 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001312 usb_get_intf(control_interface);
1313 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314
1315 acm_table[minor] = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001317 return 0;
1318alloc_fail8:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001319 for (i = 0; i < ACM_NW; i++)
1320 usb_free_urb(acm->wb[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321alloc_fail7:
Oliver Neukum830f4022008-06-25 14:17:16 +02001322 acm_read_buffers_free(acm);
Axel Linc2572b72010-05-31 08:04:47 +08001323alloc_fail6:
Oliver Neukum86478942006-05-13 22:50:47 +02001324 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001325 usb_free_urb(acm->ru[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 usb_free_urb(acm->ctrlurb);
1327alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001328 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329alloc_fail4:
Daniel Mack997ea582010-04-12 13:17:25 +02001330 usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331alloc_fail2:
1332 kfree(acm);
1333alloc_fail:
1334 return -ENOMEM;
1335}
1336
Oliver Neukum1365baf2007-10-12 17:24:28 +02001337static void stop_data_traffic(struct acm *acm)
1338{
1339 int i;
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001340
1341 dev_dbg(&acm->control->dev, "%s\n", __func__);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001342
1343 tasklet_disable(&acm->urb_task);
1344
1345 usb_kill_urb(acm->ctrlurb);
Alan Cox6e47e062009-06-11 12:37:06 +01001346 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001347 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001348 for (i = 0; i < acm->rx_buflimit; i++)
1349 usb_kill_urb(acm->ru[i].urb);
1350
Oliver Neukum1365baf2007-10-12 17:24:28 +02001351 tasklet_enable(&acm->urb_task);
1352
1353 cancel_work_sync(&acm->work);
1354}
1355
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356static void acm_disconnect(struct usb_interface *intf)
1357{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001358 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001360 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
David Brownell672c4e12008-08-06 18:41:12 -07001362 /* sibling interface is already cleaning up */
1363 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001364 return;
David Brownell672c4e12008-08-06 18:41:12 -07001365
1366 mutex_lock(&open_mutex);
Alan Cox6e47e062009-06-11 12:37:06 +01001367 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001368 device_remove_file(&acm->control->dev,
1369 &dev_attr_wCountryCodes);
1370 device_remove_file(&acm->control->dev,
1371 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001372 }
Alan Stern74da5d62007-08-02 13:29:10 -04001373 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 acm->dev = NULL;
Oliver Neukum86067eea2006-01-08 12:39:13 +01001375 usb_set_intfdata(acm->control, NULL);
1376 usb_set_intfdata(acm->data, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
Oliver Neukum1365baf2007-10-12 17:24:28 +02001378 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379
Oliver Neukum884b6002005-04-21 21:28:02 +02001380 acm_write_buffers_free(acm);
Daniel Mack997ea582010-04-12 13:17:25 +02001381 usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
1382 acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001383 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001385 if (!acm->combined_interfaces)
1386 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001387 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388
Alan Cox10077d42009-06-11 12:36:09 +01001389 if (acm->port.count == 0) {
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001390 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001391 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 return;
1393 }
1394
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001395 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001396 tty = tty_port_tty_get(&acm->port);
1397 if (tty) {
1398 tty_hangup(tty);
1399 tty_kref_put(tty);
1400 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401}
1402
Oliver Neukum35758582008-07-01 19:10:08 +02001403#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001404static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1405{
1406 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001407 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001408
Alan Stern65bfd292008-11-25 16:39:18 -05001409 if (message.event & PM_EVENT_AUTO) {
Oliver Neukum11ea8592008-06-20 11:25:57 +02001410 int b;
1411
1412 spin_lock_irq(&acm->read_lock);
1413 spin_lock(&acm->write_lock);
1414 b = acm->processing + acm->transmitting;
1415 spin_unlock(&acm->write_lock);
1416 spin_unlock_irq(&acm->read_lock);
1417 if (b)
1418 return -EBUSY;
1419 }
1420
1421 spin_lock_irq(&acm->read_lock);
1422 spin_lock(&acm->write_lock);
1423 cnt = acm->susp_count++;
1424 spin_unlock(&acm->write_lock);
1425 spin_unlock_irq(&acm->read_lock);
1426
1427 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001428 return 0;
1429 /*
1430 we treat opened interfaces differently,
1431 we must guard against open
1432 */
1433 mutex_lock(&acm->mutex);
1434
Alan Cox10077d42009-06-11 12:36:09 +01001435 if (acm->port.count)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001436 stop_data_traffic(acm);
1437
1438 mutex_unlock(&acm->mutex);
1439 return 0;
1440}
1441
1442static int acm_resume(struct usb_interface *intf)
1443{
1444 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001445 struct acm_wb *wb;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001446 int rv = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001447 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001448
Oliver Neukum11ea8592008-06-20 11:25:57 +02001449 spin_lock_irq(&acm->read_lock);
1450 acm->susp_count -= 1;
1451 cnt = acm->susp_count;
1452 spin_unlock_irq(&acm->read_lock);
1453
1454 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001455 return 0;
1456
1457 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001458 if (acm->port.count) {
Oliver Neukum1365baf2007-10-12 17:24:28 +02001459 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001460
1461 spin_lock_irq(&acm->write_lock);
1462 if (acm->delayed_wb) {
1463 wb = acm->delayed_wb;
1464 acm->delayed_wb = NULL;
1465 spin_unlock_irq(&acm->write_lock);
Oliver Neukumf0730922010-03-03 00:37:56 +01001466 acm_start_wb(acm, wb);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001467 } else {
1468 spin_unlock_irq(&acm->write_lock);
1469 }
1470
1471 /*
1472 * delayed error checking because we must
1473 * do the write path at all cost
1474 */
Oliver Neukum1365baf2007-10-12 17:24:28 +02001475 if (rv < 0)
Oliver Neukum11ea8592008-06-20 11:25:57 +02001476 goto err_out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001477
1478 tasklet_schedule(&acm->urb_task);
1479 }
1480
1481err_out:
1482 mutex_unlock(&acm->mutex);
1483 return rv;
1484}
Oliver Neukum35758582008-07-01 19:10:08 +02001485
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001486static int acm_reset_resume(struct usb_interface *intf)
1487{
1488 struct acm *acm = usb_get_intfdata(intf);
1489 struct tty_struct *tty;
1490
1491 mutex_lock(&acm->mutex);
1492 if (acm->port.count) {
1493 tty = tty_port_tty_get(&acm->port);
1494 if (tty) {
1495 tty_hangup(tty);
1496 tty_kref_put(tty);
1497 }
1498 }
1499 mutex_unlock(&acm->mutex);
1500 return acm_resume(intf);
1501}
1502
Oliver Neukum35758582008-07-01 19:10:08 +02001503#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001504
1505#define NOKIA_PCSUITE_ACM_INFO(x) \
1506 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1507 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1508 USB_CDC_ACM_PROTO_VENDOR)
1509
Toby Gray4035e452010-09-01 16:01:19 +01001510#define SAMSUNG_PCSUITE_ACM_INFO(x) \
1511 USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
1512 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1513 USB_CDC_ACM_PROTO_VENDOR)
1514
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515/*
1516 * USB driver structure.
1517 */
1518
Németh Márton6ef48522010-01-10 15:33:45 +01001519static const struct usb_device_id acm_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 /* quirky and broken devices */
1521 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1522 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1523 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001524 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1525 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1526 },
Andrew Lunn0f9c7b4a2008-12-23 17:31:23 +01001527 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1528 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1529 },
Masahito Omote8753e652005-07-29 12:17:25 -07001530 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1531 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1532 },
Chris Malley91a9c922006-10-03 10:08:28 +01001533 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1534 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1535 },
Alan Cox7abcf202009-04-06 17:35:01 +01001536 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1537 .driver_info = SINGLE_RX_URB,
1538 },
Oliver Neukum86478942006-05-13 22:50:47 +02001539 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1540 .driver_info = SINGLE_RX_URB, /* firmware bug */
1541 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001542 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1543 .driver_info = SINGLE_RX_URB, /* firmware bug */
1544 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001545 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1546 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1547 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001548 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1549 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1550 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001551 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1552 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1553 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001554 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1555 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1556 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001557 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1558 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1559 },
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001560 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1561 },
Adam Richterc332b4e2009-02-18 16:17:15 -08001562 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1563 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1564 data interface instead of
1565 communications interface.
1566 Maybe we should define a new
1567 quirk for this. */
1568 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001569 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1570 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1571 },
Russ Nelsonc3baa192010-04-21 23:07:03 -04001572 { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1573 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1574 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001575
Adrian Taylorc1479a92009-11-19 10:35:33 +00001576 /* Nokia S60 phones expose two ACM channels. The first is
1577 * a modem and is picked up by the standard AT-command
1578 * information below. The second is 'vendor-specific' but
1579 * is treated as a serial device at the S60 end, so we want
1580 * to expose it on Linux too. */
1581 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1582 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1583 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1584 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1585 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1586 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1587 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1588 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1589 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1590 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1591 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1592 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1593 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1594 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1595 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1596 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1597 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1598 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1599 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1600 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1601 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1602 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1603 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1604 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1605 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1606 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1607 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1608 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1609 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1610 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1611 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1612 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1613 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1614 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1615 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1616 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1617 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1618 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1619 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1620 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1621 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1622 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1623 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
Przemo Firszt83a4eae2010-06-28 21:29:34 +01001624 { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
Toby Gray4035e452010-09-01 16:01:19 +01001625 { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
1626 { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
1627 { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
1628 { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
1629 { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
1630 { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
1631 { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
1632 { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
1633 { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
1634 { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
Arvid Ephraim Picciani721d92f2011-01-25 15:58:40 +01001635 { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
Toby Gray4035e452010-09-01 16:01:19 +01001636 { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001637
1638 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1639
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001640 /* Support Lego NXT using pbLua firmware */
Julian Calabyce126642010-01-05 23:58:20 +11001641 { USB_DEVICE(0x0694, 0xff00),
1642 .driver_info = NOT_A_MODEM,
Otavio Salvador7893afc2010-09-26 23:35:05 -03001643 },
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001644
Philippe Corbes5b239f02010-08-31 19:31:32 +02001645 /* control interfaces without any protocol set */
1646 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1647 USB_CDC_PROTO_NONE) },
1648
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649 /* control interfaces with various AT-command sets */
1650 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1651 USB_CDC_ACM_PROTO_AT_V25TER) },
1652 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1653 USB_CDC_ACM_PROTO_AT_PCCA101) },
1654 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1655 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1656 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1657 USB_CDC_ACM_PROTO_AT_GSM) },
1658 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001659 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1661 USB_CDC_ACM_PROTO_AT_CDMA) },
1662
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 { }
1664};
1665
Alan Cox6e47e062009-06-11 12:37:06 +01001666MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667
1668static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 .name = "cdc_acm",
1670 .probe = acm_probe,
1671 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001672#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001673 .suspend = acm_suspend,
1674 .resume = acm_resume,
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001675 .reset_resume = acm_reset_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001676#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001678#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001679 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001680#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681};
1682
1683/*
1684 * TTY driver structures.
1685 */
1686
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001687static const struct tty_operations acm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688 .open = acm_tty_open,
1689 .close = acm_tty_close,
Alan Cox10077d42009-06-11 12:36:09 +01001690 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691 .write = acm_tty_write,
1692 .write_room = acm_tty_write_room,
1693 .ioctl = acm_tty_ioctl,
1694 .throttle = acm_tty_throttle,
1695 .unthrottle = acm_tty_unthrottle,
1696 .chars_in_buffer = acm_tty_chars_in_buffer,
1697 .break_ctl = acm_tty_break_ctl,
1698 .set_termios = acm_tty_set_termios,
1699 .tiocmget = acm_tty_tiocmget,
1700 .tiocmset = acm_tty_tiocmset,
1701};
1702
1703/*
1704 * Init / exit.
1705 */
1706
1707static int __init acm_init(void)
1708{
1709 int retval;
1710 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1711 if (!acm_tty_driver)
1712 return -ENOMEM;
1713 acm_tty_driver->owner = THIS_MODULE,
1714 acm_tty_driver->driver_name = "acm",
1715 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716 acm_tty_driver->major = ACM_TTY_MAJOR,
1717 acm_tty_driver->minor_start = 0,
1718 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1719 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001720 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001722 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1723 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724 tty_set_operations(acm_tty_driver, &acm_ops);
1725
1726 retval = tty_register_driver(acm_tty_driver);
1727 if (retval) {
1728 put_tty_driver(acm_tty_driver);
1729 return retval;
1730 }
1731
1732 retval = usb_register(&acm_driver);
1733 if (retval) {
1734 tty_unregister_driver(acm_tty_driver);
1735 put_tty_driver(acm_tty_driver);
1736 return retval;
1737 }
1738
Greg Kroah-Hartman5909f6e2008-08-18 13:21:04 -07001739 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
1740 DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741
1742 return 0;
1743}
1744
1745static void __exit acm_exit(void)
1746{
1747 usb_deregister(&acm_driver);
1748 tty_unregister_driver(acm_tty_driver);
1749 put_tty_driver(acm_tty_driver);
1750}
1751
1752module_init(acm_init);
1753module_exit(acm_exit);
1754
Alan Cox6e47e062009-06-11 12:37:06 +01001755MODULE_AUTHOR(DRIVER_AUTHOR);
1756MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001757MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01001758MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);