blob: 34d4eb98829e0db97271e6e6e5f5adc6bffe3e33 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * cdc-acm.c
3 *
4 * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de>
5 * Copyright (c) 1999 Pavel Machek <pavel@suse.cz>
6 * Copyright (c) 1999 Johannes Erdfelt <johannes@erdfelt.com>
7 * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
8 * Copyright (c) 2004 Oliver Neukum <oliver@neukum.name>
David Kubicek61a87ad2005-11-01 18:51:34 +01009 * Copyright (c) 2005 David Kubicek <dave@awk.cz>
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 *
11 * USB Abstract Control Model driver for USB modems and ISDN adapters
12 *
13 * Sponsored by SuSE
14 *
15 * ChangeLog:
16 * v0.9 - thorough cleaning, URBification, almost a rewrite
17 * v0.10 - some more cleanups
18 * v0.11 - fixed flow control, read error doesn't stop reads
Alan Cox6e47e062009-06-11 12:37:06 +010019 * v0.12 - added TIOCM ioctls, added break handling, made struct acm
20 * kmalloced
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 * v0.13 - added termios, added hangup
22 * v0.14 - sized down struct acm
23 * v0.15 - fixed flow control again - characters could be lost
24 * v0.16 - added code for modems with swapped data and control interfaces
25 * v0.17 - added new style probing
26 * v0.18 - fixed new style probing for devices with more configurations
27 * v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
28 * v0.20 - switched to probing on interface (rather than device) class
29 * v0.21 - revert to probing on device for devices with multiple configs
30 * v0.22 - probe only the control interface. if usbcore doesn't choose the
31 * config we want, sysadmin changes bConfigurationValue in sysfs.
32 * v0.23 - use softirq for rx processing, as needed by tty layer
33 * v0.24 - change probe method to evaluate CDC union descriptor
David Kubicek61a87ad2005-11-01 18:51:34 +010034 * v0.25 - downstream tasks paralelized to maximize throughput
David Engrafe4cf3aa2008-03-20 10:01:34 +010035 * v0.26 - multiple write urbs, writesize increased
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 */
37
38/*
39 * This program is free software; you can redistribute it and/or modify
40 * it under the terms of the GNU General Public License as published by
41 * the Free Software Foundation; either version 2 of the License, or
42 * (at your option) any later version.
43 *
44 * This program is distributed in the hope that it will be useful,
45 * but WITHOUT ANY WARRANTY; without even the implied warranty of
46 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 * GNU General Public License for more details.
48 *
49 * You should have received a copy of the GNU General Public License
50 * along with this program; if not, write to the Free Software
51 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
52 */
53
54#undef DEBUG
David Brownelle5fbab52008-08-06 18:46:10 -070055#undef VERBOSE_DEBUG
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
57#include <linux/kernel.h>
58#include <linux/errno.h>
59#include <linux/init.h>
60#include <linux/slab.h>
61#include <linux/tty.h>
Oliver Neukum7af25b42009-09-08 23:51:28 +020062#include <linux/serial.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#include <linux/tty_driver.h>
64#include <linux/tty_flip.h>
65#include <linux/module.h>
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010066#include <linux/mutex.h>
Alan Cox10077d42009-06-11 12:36:09 +010067#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068#include <linux/usb.h>
David Brownella8c28f22006-06-13 09:57:47 -070069#include <linux/usb/cdc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#include <asm/byteorder.h>
71#include <asm/unaligned.h>
David Kubicek61a87ad2005-11-01 18:51:34 +010072#include <linux/list.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74#include "cdc-acm.h"
75
David Brownelle5fbab52008-08-06 18:46:10 -070076
77#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */
78
Linus Torvalds1da177e2005-04-16 15:20:36 -070079/*
80 * Version Information
81 */
David Engrafe4cf3aa2008-03-20 10:01:34 +010082#define DRIVER_VERSION "v0.26"
David Kubicek61a87ad2005-11-01 18:51:34 +010083#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
Linus Torvalds1da177e2005-04-16 15:20:36 -070084#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
85
86static struct usb_driver acm_driver;
87static struct tty_driver *acm_tty_driver;
88static struct acm *acm_table[ACM_TTY_MINORS];
89
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010090static DEFINE_MUTEX(open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
Alan Cox10077d42009-06-11 12:36:09 +010092#define ACM_READY(acm) (acm && acm->dev && acm->port.count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
Alan Cox739e0282009-06-11 12:27:50 +010094static const struct tty_port_operations acm_port_ops = {
95};
96
David Brownelle5fbab52008-08-06 18:46:10 -070097#ifdef VERBOSE_DEBUG
98#define verbose 1
99#else
100#define verbose 0
101#endif
102
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103/*
104 * Functions for ACM control messages.
105 */
106
Alan Cox6e47e062009-06-11 12:37:06 +0100107static int acm_ctrl_msg(struct acm *acm, int request, int value,
108 void *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109{
110 int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
111 request, USB_RT_ACM, value,
112 acm->control->altsetting[0].desc.bInterfaceNumber,
113 buf, len, 5000);
Alan Cox6e47e062009-06-11 12:37:06 +0100114 dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d",
115 request, value, len, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 return retval < 0 ? retval : 0;
117}
118
119/* devices aren't required to support these requests.
120 * the cdc acm descriptor tells whether they do...
121 */
122#define acm_set_control(acm, control) \
123 acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
124#define acm_set_line(acm, line) \
125 acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
126#define acm_send_break(acm, ms) \
127 acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
128
129/*
Oliver Neukum884b6002005-04-21 21:28:02 +0200130 * Write buffer management.
131 * All of these assume proper locks taken by the caller.
132 */
133
134static int acm_wb_alloc(struct acm *acm)
135{
136 int i, wbn;
137 struct acm_wb *wb;
138
David Engrafe4cf3aa2008-03-20 10:01:34 +0100139 wbn = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200140 i = 0;
141 for (;;) {
142 wb = &acm->wb[wbn];
143 if (!wb->use) {
144 wb->use = 1;
145 return wbn;
146 }
Oliver Neukum86478942006-05-13 22:50:47 +0200147 wbn = (wbn + 1) % ACM_NW;
148 if (++i >= ACM_NW)
Oliver Neukum884b6002005-04-21 21:28:02 +0200149 return -1;
150 }
151}
152
Oliver Neukum884b6002005-04-21 21:28:02 +0200153static int acm_wb_is_avail(struct acm *acm)
154{
155 int i, n;
David Brownelle5fbab52008-08-06 18:46:10 -0700156 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200157
Oliver Neukum86478942006-05-13 22:50:47 +0200158 n = ACM_NW;
David Brownelle5fbab52008-08-06 18:46:10 -0700159 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100160 for (i = 0; i < ACM_NW; i++)
Oliver Neukum86478942006-05-13 22:50:47 +0200161 n -= acm->wb[i].use;
David Brownelle5fbab52008-08-06 18:46:10 -0700162 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200163 return n;
164}
165
Oliver Neukum884b6002005-04-21 21:28:02 +0200166/*
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800167 * Finish write. Caller must hold acm->write_lock
Oliver Neukum884b6002005-04-21 21:28:02 +0200168 */
David Engrafe4cf3aa2008-03-20 10:01:34 +0100169static void acm_write_done(struct acm *acm, struct acm_wb *wb)
Oliver Neukum884b6002005-04-21 21:28:02 +0200170{
David Engrafe4cf3aa2008-03-20 10:01:34 +0100171 wb->use = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200172 acm->transmitting--;
Oliver Neukum884b6002005-04-21 21:28:02 +0200173}
174
175/*
176 * Poke write.
Oliver Neukum11ea8592008-06-20 11:25:57 +0200177 *
178 * the caller is responsible for locking
Oliver Neukum884b6002005-04-21 21:28:02 +0200179 */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200180
181static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
182{
183 int rc;
184
185 acm->transmitting++;
186
187 wb->urb->transfer_buffer = wb->buf;
188 wb->urb->transfer_dma = wb->dmah;
189 wb->urb->transfer_buffer_length = wb->len;
190 wb->urb->dev = acm->dev;
191
Alan Cox6e47e062009-06-11 12:37:06 +0100192 rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
193 if (rc < 0) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200194 dbg("usb_submit_urb(write bulk) failed: %d", rc);
195 acm_write_done(acm, wb);
196 }
197 return rc;
198}
199
David Engrafe4cf3aa2008-03-20 10:01:34 +0100200static int acm_write_start(struct acm *acm, int wbn)
Oliver Neukum884b6002005-04-21 21:28:02 +0200201{
202 unsigned long flags;
David Brownell934da462008-08-06 18:44:12 -0700203 struct acm_wb *wb = &acm->wb[wbn];
Oliver Neukum884b6002005-04-21 21:28:02 +0200204 int rc;
205
206 spin_lock_irqsave(&acm->write_lock, flags);
207 if (!acm->dev) {
David Brownell934da462008-08-06 18:44:12 -0700208 wb->use = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200209 spin_unlock_irqrestore(&acm->write_lock, flags);
210 return -ENODEV;
211 }
212
Oliver Neukum11ea8592008-06-20 11:25:57 +0200213 dbg("%s susp_count: %d", __func__, acm->susp_count);
214 if (acm->susp_count) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200215 acm->delayed_wb = wb;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200216 schedule_work(&acm->waker);
217 spin_unlock_irqrestore(&acm->write_lock, flags);
218 return 0; /* A white lie */
219 }
220 usb_mark_last_busy(acm->dev);
221
Oliver Neukum11ea8592008-06-20 11:25:57 +0200222 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200223 spin_unlock_irqrestore(&acm->write_lock, flags);
224
Oliver Neukum884b6002005-04-21 21:28:02 +0200225 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200226
Oliver Neukum884b6002005-04-21 21:28:02 +0200227}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100228/*
229 * attributes exported through sysfs
230 */
231static ssize_t show_caps
232(struct device *dev, struct device_attribute *attr, char *buf)
233{
234 struct usb_interface *intf = to_usb_interface(dev);
235 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200236
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100237 return sprintf(buf, "%d", acm->ctrl_caps);
238}
239static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
240
241static ssize_t show_country_codes
242(struct device *dev, struct device_attribute *attr, char *buf)
243{
244 struct usb_interface *intf = to_usb_interface(dev);
245 struct acm *acm = usb_get_intfdata(intf);
246
247 memcpy(buf, acm->country_codes, acm->country_code_size);
248 return acm->country_code_size;
249}
250
251static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
252
253static ssize_t show_country_rel_date
254(struct device *dev, struct device_attribute *attr, char *buf)
255{
256 struct usb_interface *intf = to_usb_interface(dev);
257 struct acm *acm = usb_get_intfdata(intf);
258
259 return sprintf(buf, "%d", acm->country_rel_date);
260}
261
262static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200263/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 * Interrupt handlers for various ACM device responses
265 */
266
267/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100268static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269{
270 struct acm *acm = urb->context;
271 struct usb_cdc_notification *dr = urb->transfer_buffer;
Alan Cox10077d42009-06-11 12:36:09 +0100272 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 unsigned char *data;
274 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700275 int retval;
276 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700278 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 case 0:
280 /* success */
281 break;
282 case -ECONNRESET:
283 case -ENOENT:
284 case -ESHUTDOWN:
285 /* this urb is terminated, clean up */
Harvey Harrison441b62c2008-03-03 16:08:34 -0800286 dbg("%s - urb shutting down with status: %d", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 return;
288 default:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800289 dbg("%s - nonzero urb status received: %d", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 goto exit;
291 }
292
293 if (!ACM_READY(acm))
294 goto exit;
295
296 data = (unsigned char *)(dr + 1);
297 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100298 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
299 dbg("%s network", dr->wValue ?
300 "connected to" : "disconnected from");
301 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302
Alan Cox6e47e062009-06-11 12:37:06 +0100303 case USB_CDC_NOTIFY_SERIAL_STATE:
304 tty = tty_port_tty_get(&acm->port);
305 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
Alan Cox6e47e062009-06-11 12:37:06 +0100307 if (tty) {
308 if (!acm->clocal &&
309 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
310 dbg("calling hangup");
311 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 }
Alan Cox6e47e062009-06-11 12:37:06 +0100313 tty_kref_put(tty);
314 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
Alan Cox6e47e062009-06-11 12:37:06 +0100316 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
Alan Cox6e47e062009-06-11 12:37:06 +0100318 dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
319 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
320 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
321 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
322 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
323 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
324 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
325 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 break;
327
Alan Cox6e47e062009-06-11 12:37:06 +0100328 default:
329 dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
330 dr->bNotificationType, dr->wIndex,
331 dr->wLength, data[0], data[1]);
332 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 }
334exit:
Oliver Neukum11ea8592008-06-20 11:25:57 +0200335 usb_mark_last_busy(acm->dev);
Alan Cox6e47e062009-06-11 12:37:06 +0100336 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700337 if (retval)
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700338 dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
339 "result %d", __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340}
341
342/* data interface returns incoming bytes, or we got unthrottled */
David Howells7d12e782006-10-05 14:55:46 +0100343static void acm_read_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344{
David Kubicek61a87ad2005-11-01 18:51:34 +0100345 struct acm_rb *buf;
346 struct acm_ru *rcv = urb->context;
347 struct acm *acm = rcv->instance;
Oliver Neukum86478942006-05-13 22:50:47 +0200348 int status = urb->status;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700349
350 dbg("Entering acm_read_bulk with status %d", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351
Oliver Neukum11ea8592008-06-20 11:25:57 +0200352 if (!ACM_READY(acm)) {
353 dev_dbg(&acm->data->dev, "Aborting, acm not ready");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200355 }
356 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
Oliver Neukum86478942006-05-13 22:50:47 +0200358 if (status)
Joe Perches898eb712007-10-18 03:06:30 -0700359 dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360
David Kubicek61a87ad2005-11-01 18:51:34 +0100361 buf = rcv->buffer;
362 buf->size = urb->actual_length;
363
Oliver Neukum86478942006-05-13 22:50:47 +0200364 if (likely(status == 0)) {
365 spin_lock(&acm->read_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200366 acm->processing++;
Oliver Neukum86478942006-05-13 22:50:47 +0200367 list_add_tail(&rcv->list, &acm->spare_read_urbs);
368 list_add_tail(&buf->list, &acm->filled_read_bufs);
369 spin_unlock(&acm->read_lock);
370 } else {
371 /* we drop the buffer due to an error */
372 spin_lock(&acm->read_lock);
373 list_add_tail(&rcv->list, &acm->spare_read_urbs);
374 list_add(&buf->list, &acm->spare_read_bufs);
375 spin_unlock(&acm->read_lock);
376 /* nevertheless the tasklet must be kicked unconditionally
377 so the queue cannot dry up */
378 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200379 if (likely(!acm->susp_count))
380 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381}
382
383static void acm_rx_tasklet(unsigned long _acm)
384{
385 struct acm *acm = (void *)_acm;
David Kubicek61a87ad2005-11-01 18:51:34 +0100386 struct acm_rb *buf;
Alan Cox10077d42009-06-11 12:36:09 +0100387 struct tty_struct *tty;
David Kubicek61a87ad2005-11-01 18:51:34 +0100388 struct acm_ru *rcv;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200389 unsigned long flags;
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100390 unsigned char throttled;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200391
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 dbg("Entering acm_rx_tasklet");
393
Alan Cox10077d42009-06-11 12:36:09 +0100394 if (!ACM_READY(acm)) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200395 dbg("acm_rx_tasklet: ACM not ready");
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100396 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200397 }
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100398
Oliver Neukum834dbca2007-03-06 10:47:04 +0100399 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100400 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100401 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Alan Cox10077d42009-06-11 12:36:09 +0100402 if (throttled) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200403 dbg("acm_rx_tasklet: throttled");
David Kubicek61a87ad2005-11-01 18:51:34 +0100404 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200405 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100406
Alan Cox10077d42009-06-11 12:36:09 +0100407 tty = tty_port_tty_get(&acm->port);
408
David Kubicek61a87ad2005-11-01 18:51:34 +0100409next_buffer:
Jarek Poplawski762f0072006-10-06 07:23:11 +0200410 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100411 if (list_empty(&acm->filled_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200412 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100413 goto urbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100415 buf = list_entry(acm->filled_read_bufs.next,
416 struct acm_rb, list);
417 list_del(&buf->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200418 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100419
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200420 dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
David Kubicek61a87ad2005-11-01 18:51:34 +0100421
Alan Cox10077d42009-06-11 12:36:09 +0100422 if (tty) {
423 spin_lock_irqsave(&acm->throttle_lock, flags);
424 throttled = acm->throttle;
425 spin_unlock_irqrestore(&acm->throttle_lock, flags);
426 if (!throttled) {
427 tty_buffer_request_room(tty, buf->size);
428 tty_insert_flip_string(tty, buf->base, buf->size);
429 tty_flip_buffer_push(tty);
430 } else {
431 tty_kref_put(tty);
432 dbg("Throttling noticed");
433 spin_lock_irqsave(&acm->read_lock, flags);
434 list_add(&buf->list, &acm->filled_read_bufs);
435 spin_unlock_irqrestore(&acm->read_lock, flags);
436 return;
437 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439
Jarek Poplawski762f0072006-10-06 07:23:11 +0200440 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100441 list_add(&buf->list, &acm->spare_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200442 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100443 goto next_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
David Kubicek61a87ad2005-11-01 18:51:34 +0100445urbs:
Alan Cox10077d42009-06-11 12:36:09 +0100446 tty_kref_put(tty);
447
David Kubicek61a87ad2005-11-01 18:51:34 +0100448 while (!list_empty(&acm->spare_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200449 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100450 if (list_empty(&acm->spare_read_urbs)) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200451 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200452 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100453 return;
454 }
455 rcv = list_entry(acm->spare_read_urbs.next,
456 struct acm_ru, list);
457 list_del(&rcv->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200458 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100459
460 buf = list_entry(acm->spare_read_bufs.next,
461 struct acm_rb, list);
462 list_del(&buf->list);
463
464 rcv->buffer = buf;
465
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200466 if (acm->is_int_ep)
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400467 usb_fill_int_urb(rcv->urb, acm->dev,
468 acm->rx_endpoint,
469 buf->base,
470 acm->readsize,
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200471 acm_read_bulk, rcv, acm->bInterval);
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400472 else
473 usb_fill_bulk_urb(rcv->urb, acm->dev,
474 acm->rx_endpoint,
475 buf->base,
476 acm->readsize,
477 acm_read_bulk, rcv);
David Kubicek61a87ad2005-11-01 18:51:34 +0100478 rcv->urb->transfer_dma = buf->dma;
479 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
480
Alan Cox6e47e062009-06-11 12:37:06 +0100481 /* This shouldn't kill the driver as unsuccessful URBs are
482 returned to the free-urbs-pool and resubmited ASAP */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200483 spin_lock_irqsave(&acm->read_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100484 if (acm->susp_count ||
485 usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100486 list_add(&buf->list, &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100487 list_add(&rcv->list, &acm->spare_read_urbs);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200488 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200489 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100490 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200491 } else {
492 spin_unlock_irqrestore(&acm->read_lock, flags);
493 dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
David Kubicek61a87ad2005-11-01 18:51:34 +0100494 }
495 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200496 spin_lock_irqsave(&acm->read_lock, flags);
497 acm->processing = 0;
498 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499}
500
501/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100502static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503{
Ming Leicdc97792008-02-24 18:41:47 +0800504 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700505 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800506 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200507
David Brownelle5fbab52008-08-06 18:46:10 -0700508 if (verbose || urb->status
509 || (urb->actual_length != urb->transfer_buffer_length))
510 dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
511 urb->actual_length,
512 urb->transfer_buffer_length,
513 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800515 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100516 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800517 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200518 if (ACM_READY(acm))
519 schedule_work(&acm->work);
David Brownelle5fbab52008-08-06 18:46:10 -0700520 else
521 wake_up_interruptible(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522}
523
David Howellsc4028952006-11-22 14:57:56 +0000524static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525{
David Howellsc4028952006-11-22 14:57:56 +0000526 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100527 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700528
529 dev_vdbg(&acm->data->dev, "tx work\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 if (!ACM_READY(acm))
531 return;
Alan Cox10077d42009-06-11 12:36:09 +0100532 tty = tty_port_tty_get(&acm->port);
533 tty_wakeup(tty);
534 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535}
536
Oliver Neukum11ea8592008-06-20 11:25:57 +0200537static void acm_waker(struct work_struct *waker)
538{
539 struct acm *acm = container_of(waker, struct acm, waker);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200540 int rv;
541
542 rv = usb_autopm_get_interface(acm->control);
543 if (rv < 0) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700544 dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200545 return;
546 }
547 if (acm->delayed_wb) {
548 acm_start_wb(acm, acm->delayed_wb);
549 acm->delayed_wb = NULL;
550 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200551 usb_autopm_put_interface(acm->control);
552}
553
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554/*
555 * TTY handlers
556 */
557
558static int acm_tty_open(struct tty_struct *tty, struct file *filp)
559{
560 struct acm *acm;
Thadeu Lima de Souza Cascardo42dd2aa2009-06-25 14:41:24 +0100561 int rv = -ENODEV;
David Kubicek61a87ad2005-11-01 18:51:34 +0100562 int i;
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200563 dbg("Entering acm_tty_open.");
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100564
565 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
567 acm = acm_table[tty->index];
568 if (!acm || !acm->dev)
569 goto err_out;
570 else
571 rv = 0;
572
David Engraf28d1dfa2008-03-20 10:53:52 +0100573 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
Alan Cox10077d42009-06-11 12:36:09 +0100574
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 tty->driver_data = acm;
Alan Cox10077d42009-06-11 12:36:09 +0100576 tty_port_tty_set(&acm->port, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Oliver Neukum94409cc2008-02-11 15:22:29 +0100578 if (usb_autopm_get_interface(acm->control) < 0)
579 goto early_bail;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200580 else
581 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200582
583 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100584 if (acm->port.count++) {
Oliver Neukum1365baf2007-10-12 17:24:28 +0200585 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 goto done;
Alan Cox10077d42009-06-11 12:36:09 +0100587 }
Oliver Neukum1365baf2007-10-12 17:24:28 +0200588
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 acm->ctrlurb->dev = acm->dev;
590 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
591 dbg("usb_submit_urb(ctrl irq) failed");
592 goto bail_out;
593 }
594
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100595 if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
596 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 goto full_bailout;
Alan Cox10077d42009-06-11 12:36:09 +0100598
Oliver Neukum11ea8592008-06-20 11:25:57 +0200599 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
David Kubicek61a87ad2005-11-01 18:51:34 +0100601 INIT_LIST_HEAD(&acm->spare_read_urbs);
602 INIT_LIST_HEAD(&acm->spare_read_bufs);
603 INIT_LIST_HEAD(&acm->filled_read_bufs);
Alan Cox6e47e062009-06-11 12:37:06 +0100604
605 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100606 list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
Alan Cox6e47e062009-06-11 12:37:06 +0100607 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100608 list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100609
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100610 acm->throttle = 0;
611
Oliver Neukum7af25b42009-09-08 23:51:28 +0200612 set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
Alan Cox10077d42009-06-11 12:36:09 +0100613 rv = tty_port_block_til_ready(&acm->port, tty, filp);
Henry Gebhardt18a77b52009-11-04 11:19:28 +0100614 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615done:
Oliver Neukum1365baf2007-10-12 17:24:28 +0200616 mutex_unlock(&acm->mutex);
Alexey Dobriyan74573ee2008-08-20 16:56:04 -0700617err_out:
Oliver Neukum94409cc2008-02-11 15:22:29 +0100618 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 return rv;
620
621full_bailout:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 usb_kill_urb(acm->ctrlurb);
623bail_out:
Oliver Neukum1365baf2007-10-12 17:24:28 +0200624 usb_autopm_put_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100625 acm->port.count--;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200626 mutex_unlock(&acm->mutex);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100627early_bail:
628 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100629 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 return -EIO;
631}
632
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700633static void acm_tty_unregister(struct acm *acm)
634{
Alan Cox10077d42009-06-11 12:36:09 +0100635 int i, nr;
David Kubicek61a87ad2005-11-01 18:51:34 +0100636
Oliver Neukum86478942006-05-13 22:50:47 +0200637 nr = acm->rx_buflimit;
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700638 tty_unregister_device(acm_tty_driver, acm->minor);
639 usb_put_intf(acm->control);
640 acm_table[acm->minor] = NULL;
641 usb_free_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100642 for (i = 0; i < ACM_NW; i++)
643 usb_free_urb(acm->wb[i].urb);
Oliver Neukum86478942006-05-13 22:50:47 +0200644 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100645 usb_free_urb(acm->ru[i].urb);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100646 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700647 kfree(acm);
648}
649
David Brownelle5fbab52008-08-06 18:46:10 -0700650static int acm_tty_chars_in_buffer(struct tty_struct *tty);
651
Alan Cox10077d42009-06-11 12:36:09 +0100652static void acm_port_down(struct acm *acm, int drain)
653{
654 int i, nr = acm->rx_buflimit;
655 mutex_lock(&open_mutex);
656 if (acm->dev) {
657 usb_autopm_get_interface(acm->control);
658 acm_set_control(acm, acm->ctrlout = 0);
659 /* try letting the last writes drain naturally */
660 if (drain) {
661 wait_event_interruptible_timeout(acm->drain_wait,
662 (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
663 ACM_CLOSE_TIMEOUT * HZ);
664 }
665 usb_kill_urb(acm->ctrlurb);
666 for (i = 0; i < ACM_NW; i++)
667 usb_kill_urb(acm->wb[i].urb);
668 for (i = 0; i < nr; i++)
669 usb_kill_urb(acm->ru[i].urb);
670 acm->control->needs_remote_wakeup = 0;
671 usb_autopm_put_interface(acm->control);
672 }
673 mutex_unlock(&open_mutex);
674}
675
676static void acm_tty_hangup(struct tty_struct *tty)
677{
678 struct acm *acm = tty->driver_data;
679 tty_port_hangup(&acm->port);
680 acm_port_down(acm, 0);
681}
682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683static void acm_tty_close(struct tty_struct *tty, struct file *filp)
684{
685 struct acm *acm = tty->driver_data;
686
Alan Cox10077d42009-06-11 12:36:09 +0100687 /* Perform the closing process and see if we need to do the hardware
688 shutdown */
Francesco Lavra051522b2009-11-03 10:53:07 +0000689 if (!acm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 return;
Francesco Lavra051522b2009-11-03 10:53:07 +0000691 if (tty_port_close_start(&acm->port, tty, filp) == 0) {
692 mutex_lock(&open_mutex);
693 if (!acm->dev) {
694 tty_port_tty_set(&acm->port, NULL);
695 acm_tty_unregister(acm);
696 tty->driver_data = NULL;
697 }
698 mutex_unlock(&open_mutex);
699 return;
700 }
Alan Cox10077d42009-06-11 12:36:09 +0100701 acm_port_down(acm, 0);
702 tty_port_close_end(&acm->port, tty);
Alan Cox10077d42009-06-11 12:36:09 +0100703 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704}
705
Alan Cox6e47e062009-06-11 12:37:06 +0100706static int acm_tty_write(struct tty_struct *tty,
707 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708{
709 struct acm *acm = tty->driver_data;
710 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200711 unsigned long flags;
712 int wbn;
713 struct acm_wb *wb;
714
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200715 dbg("Entering acm_tty_write to write %d bytes,", count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
717 if (!ACM_READY(acm))
718 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 if (!count)
720 return 0;
721
Oliver Neukum884b6002005-04-21 21:28:02 +0200722 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100723 wbn = acm_wb_alloc(acm);
724 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200725 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200726 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200728 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729
Oliver Neukum884b6002005-04-21 21:28:02 +0200730 count = (count > acm->writesize) ? acm->writesize : count;
731 dbg("Get %d bytes...", count);
732 memcpy(wb->buf, buf, count);
733 wb->len = count;
734 spin_unlock_irqrestore(&acm->write_lock, flags);
735
Alan Cox6e47e062009-06-11 12:37:06 +0100736 stat = acm_write_start(acm, wbn);
737 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200738 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 return count;
740}
741
742static int acm_tty_write_room(struct tty_struct *tty)
743{
744 struct acm *acm = tty->driver_data;
745 if (!ACM_READY(acm))
746 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200747 /*
748 * Do not let the line discipline to know that we have a reserve,
749 * or it might get too enthusiastic.
750 */
David Brownell934da462008-08-06 18:44:12 -0700751 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752}
753
754static int acm_tty_chars_in_buffer(struct tty_struct *tty)
755{
756 struct acm *acm = tty->driver_data;
757 if (!ACM_READY(acm))
Alan Cox23198fd2009-07-20 16:05:27 +0100758 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200759 /*
760 * This is inaccurate (overcounts), but it works.
761 */
Oliver Neukum86478942006-05-13 22:50:47 +0200762 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763}
764
765static void acm_tty_throttle(struct tty_struct *tty)
766{
767 struct acm *acm = tty->driver_data;
768 if (!ACM_READY(acm))
769 return;
770 spin_lock_bh(&acm->throttle_lock);
771 acm->throttle = 1;
772 spin_unlock_bh(&acm->throttle_lock);
773}
774
775static void acm_tty_unthrottle(struct tty_struct *tty)
776{
777 struct acm *acm = tty->driver_data;
778 if (!ACM_READY(acm))
779 return;
780 spin_lock_bh(&acm->throttle_lock);
781 acm->throttle = 0;
782 spin_unlock_bh(&acm->throttle_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100783 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784}
785
Alan Cox9e989662008-07-22 11:18:03 +0100786static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787{
788 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100789 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 if (!ACM_READY(acm))
Alan Cox9e989662008-07-22 11:18:03 +0100791 return -EINVAL;
792 retval = acm_send_break(acm, state ? 0xffff : 0);
793 if (retval < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 dbg("send break failed");
Alan Cox9e989662008-07-22 11:18:03 +0100795 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796}
797
798static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
799{
800 struct acm *acm = tty->driver_data;
801
802 if (!ACM_READY(acm))
803 return -EINVAL;
804
805 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
806 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
807 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
808 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
809 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
810 TIOCM_CTS;
811}
812
813static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
814 unsigned int set, unsigned int clear)
815{
816 struct acm *acm = tty->driver_data;
817 unsigned int newctrl;
818
819 if (!ACM_READY(acm))
820 return -EINVAL;
821
822 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100823 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
824 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
825 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
826 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
828 newctrl = (newctrl & ~clear) | set;
829
830 if (acm->ctrlout == newctrl)
831 return 0;
832 return acm_set_control(acm, acm->ctrlout = newctrl);
833}
834
Alan Cox6e47e062009-06-11 12:37:06 +0100835static int acm_tty_ioctl(struct tty_struct *tty, struct file *file,
836 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837{
838 struct acm *acm = tty->driver_data;
839
840 if (!ACM_READY(acm))
841 return -EINVAL;
842
843 return -ENOIOCTLCMD;
844}
845
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100846static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 0, 50, 75, 110, 134, 150, 200, 300, 600,
848 1200, 1800, 2400, 4800, 9600, 19200, 38400,
849 57600, 115200, 230400, 460800, 500000, 576000,
850 921600, 1000000, 1152000, 1500000, 2000000,
851 2500000, 3000000, 3500000, 4000000
852};
853
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100854static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 5, 6, 7, 8
856};
857
Alan Cox6e47e062009-06-11 12:37:06 +0100858static void acm_tty_set_termios(struct tty_struct *tty,
859 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860{
861 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800862 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 struct usb_cdc_line_coding newline;
864 int newctrl = acm->ctrlout;
865
866 if (!ACM_READY(acm))
867 return;
868
Alan Cox9b80fee2009-09-19 13:13:23 -0700869 newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
871 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100872 (termios->c_cflag & PARODD ? 1 : 2) +
873 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
Alan Cox6e47e062009-06-11 12:37:06 +0100875 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
877
878 if (!newline.dwDTERate) {
879 newline.dwDTERate = acm->line.dwDTERate;
880 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +0100881 } else
882 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
884 if (newctrl != acm->ctrlout)
885 acm_set_control(acm, acm->ctrlout = newctrl);
886
887 if (memcmp(&acm->line, &newline, sizeof newline)) {
888 memcpy(&acm->line, &newline, sizeof newline);
889 dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
890 newline.bCharFormat, newline.bParityType,
891 newline.bDataBits);
892 acm_set_line(acm, &acm->line);
893 }
894}
895
896/*
897 * USB probe and disconnect routines.
898 */
899
Oliver Neukum830f4022008-06-25 14:17:16 +0200900/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +0200901static void acm_write_buffers_free(struct acm *acm)
902{
903 int i;
904 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +0200905 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200906
Alan Cox6e47e062009-06-11 12:37:06 +0100907 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Oliver Neukuma496c642008-10-21 10:39:04 +0200908 usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +0200909}
910
Oliver Neukum830f4022008-06-25 14:17:16 +0200911static void acm_read_buffers_free(struct acm *acm)
912{
913 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
914 int i, n = acm->rx_buflimit;
915
916 for (i = 0; i < n; i++)
Alan Cox6e47e062009-06-11 12:37:06 +0100917 usb_buffer_free(usb_dev, acm->readsize,
918 acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +0200919}
920
Oliver Neukum884b6002005-04-21 21:28:02 +0200921/* Little helper: write buffers allocate */
922static int acm_write_buffers_alloc(struct acm *acm)
923{
924 int i;
925 struct acm_wb *wb;
926
Oliver Neukum86478942006-05-13 22:50:47 +0200927 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200928 wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
929 &wb->dmah);
930 if (!wb->buf) {
931 while (i != 0) {
932 --i;
933 --wb;
934 usb_buffer_free(acm->dev, acm->writesize,
935 wb->buf, wb->dmah);
936 }
937 return -ENOMEM;
938 }
939 }
940 return 0;
941}
942
Alan Cox10077d42009-06-11 12:36:09 +0100943static int acm_probe(struct usb_interface *intf,
944 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945{
946 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100947 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -0700948 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 int buflen = intf->altsetting->extralen;
950 struct usb_interface *control_interface;
951 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200952 struct usb_endpoint_descriptor *epctrl = NULL;
953 struct usb_endpoint_descriptor *epread = NULL;
954 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 struct usb_device *usb_dev = interface_to_usbdev(intf);
956 struct acm *acm;
957 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +0100958 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 u8 *buf;
960 u8 ac_management_function = 0;
961 u8 call_management_function = 0;
962 int call_interface_num = -1;
963 int data_interface_num;
964 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200965 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100966 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200967 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968
Oliver Neukum86478942006-05-13 22:50:47 +0200969 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200971 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
972
973 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 if (quirks == NO_UNION_NORMAL) {
975 data_interface = usb_ifnum_to_if(usb_dev, 1);
976 control_interface = usb_ifnum_to_if(usb_dev, 0);
977 goto skip_normal_probe;
978 }
Alan Cox6e47e062009-06-11 12:37:06 +0100979
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 /* normal probing*/
981 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700982 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 return -EINVAL;
984 }
985
986 if (!buflen) {
Alan Cox6e47e062009-06-11 12:37:06 +0100987 if (intf->cur_altsetting->endpoint->extralen &&
988 intf->cur_altsetting->endpoint->extra) {
989 dev_dbg(&intf->dev,
990 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 buflen = intf->cur_altsetting->endpoint->extralen;
992 buffer = intf->cur_altsetting->endpoint->extra;
993 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700994 dev_err(&intf->dev,
995 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 return -EINVAL;
997 }
998 }
999
1000 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001001 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001002 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 goto next_desc;
1004 }
1005
Alan Cox6e47e062009-06-11 12:37:06 +01001006 switch (buffer[2]) {
1007 case USB_CDC_UNION_TYPE: /* we've found it */
1008 if (union_header) {
1009 dev_err(&intf->dev, "More than one "
1010 "union descriptor, skipping ...\n");
1011 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 }
Alan Cox6e47e062009-06-11 12:37:06 +01001013 union_header = (struct usb_cdc_union_desc *)buffer;
1014 break;
1015 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
1016 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1017 break;
1018 case USB_CDC_HEADER_TYPE: /* maybe check version */
1019 break; /* for now we ignore it */
1020 case USB_CDC_ACM_TYPE:
1021 ac_management_function = buffer[3];
1022 break;
1023 case USB_CDC_CALL_MANAGEMENT_TYPE:
1024 call_management_function = buffer[3];
1025 call_interface_num = buffer[4];
1026 if ((call_management_function & 3) != 3)
1027 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1028 break;
1029 default:
1030 /* there are LOTS more CDC descriptors that
1031 * could legitimately be found here.
1032 */
1033 dev_dbg(&intf->dev, "Ignoring descriptor: "
1034 "type %02x, length %d\n",
1035 buffer[2], buffer[0]);
1036 break;
1037 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038next_desc:
1039 buflen -= buffer[0];
1040 buffer += buffer[0];
1041 }
1042
1043 if (!union_header) {
1044 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001045 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
1047 control_interface = intf;
1048 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001049 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1050 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1051 return -ENODEV;
1052 } else {
1053 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1054 combined_interfaces = 1;
1055 control_interface = data_interface = intf;
1056 goto look_for_collapsed_interface;
1057 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 }
1059 } else {
1060 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1061 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1062 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001063 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 return -ENODEV;
1065 }
1066 }
Alan Cox6e47e062009-06-11 12:37:06 +01001067
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001069 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001071 if (control_interface == data_interface) {
1072 /* some broken devices designed for windows work this way */
1073 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1074 combined_interfaces = 1;
1075 /* a popular other OS doesn't use it */
1076 quirks |= NO_CAP_LINE;
1077 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1078 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1079 return -EINVAL;
1080 }
1081look_for_collapsed_interface:
1082 for (i = 0; i < 3; i++) {
1083 struct usb_endpoint_descriptor *ep;
1084 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1085
1086 if (usb_endpoint_is_int_in(ep))
1087 epctrl = ep;
1088 else if (usb_endpoint_is_bulk_out(ep))
1089 epwrite = ep;
1090 else if (usb_endpoint_is_bulk_in(ep))
1091 epread = ep;
1092 else
1093 return -EINVAL;
1094 }
1095 if (!epctrl || !epread || !epwrite)
1096 return -ENODEV;
1097 else
1098 goto made_compressed_probe;
1099 }
1100
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101skip_normal_probe:
1102
1103 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001104 if (data_interface->cur_altsetting->desc.bInterfaceClass
1105 != CDC_DATA_INTERFACE_TYPE) {
1106 if (control_interface->cur_altsetting->desc.bInterfaceClass
1107 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001109 dev_dbg(&intf->dev,
1110 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 t = control_interface;
1112 control_interface = data_interface;
1113 data_interface = t;
1114 } else {
1115 return -EINVAL;
1116 }
1117 }
Alan Stern74da5d62007-08-02 13:29:10 -04001118
1119 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001120 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001121 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001122
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001123 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1124 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001125 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 return -EBUSY;
1127 }
1128
1129
1130 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
1131 return -EINVAL;
1132
1133 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1134 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1135 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1136
1137
1138 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001139 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 /* descriptors are swapped */
1141 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001142 dev_dbg(&intf->dev,
1143 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 t = epread;
1145 epread = epwrite;
1146 epwrite = t;
1147 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001148made_compressed_probe:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 dbg("interfaces are valid");
1150 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
1151
1152 if (minor == ACM_TTY_MINORS) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001153 dev_err(&intf->dev, "no more free acm devices\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 return -ENODEV;
1155 }
1156
Alan Cox6e47e062009-06-11 12:37:06 +01001157 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1158 if (acm == NULL) {
Joe Perches898eb712007-10-18 03:06:30 -07001159 dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 goto alloc_fail;
1161 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162
1163 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Alan Cox6e47e062009-06-11 12:37:06 +01001164 readsize = le16_to_cpu(epread->wMaxPacketSize) *
1165 (quirks == SINGLE_RX_URB ? 1 : 2);
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001166 acm->combined_interfaces = combined_interfaces;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001167 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 acm->control = control_interface;
1169 acm->data = data_interface;
1170 acm->minor = minor;
1171 acm->dev = usb_dev;
1172 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001173 if (quirks & NO_CAP_LINE)
1174 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 acm->ctrlsize = ctrlsize;
1176 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001177 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001178 acm->urb_task.func = acm_rx_tasklet;
1179 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +00001180 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001181 INIT_WORK(&acm->waker, acm_waker);
David Brownelle5fbab52008-08-06 18:46:10 -07001182 init_waitqueue_head(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +02001184 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001185 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001186 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001187 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001188 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1189 if (acm->is_int_ep)
1190 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001191 tty_port_init(&acm->port);
1192 acm->port.ops = &acm_port_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193
1194 buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
1195 if (!buf) {
Joe Perches898eb712007-10-18 03:06:30 -07001196 dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 goto alloc_fail2;
1198 }
1199 acm->ctrl_buffer = buf;
1200
Oliver Neukum884b6002005-04-21 21:28:02 +02001201 if (acm_write_buffers_alloc(acm) < 0) {
Joe Perches898eb712007-10-18 03:06:30 -07001202 dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 goto alloc_fail4;
1204 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205
1206 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1207 if (!acm->ctrlurb) {
Joe Perches898eb712007-10-18 03:06:30 -07001208 dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 goto alloc_fail5;
1210 }
Oliver Neukum86478942006-05-13 22:50:47 +02001211 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001212 struct acm_ru *rcv = &(acm->ru[i]);
1213
Alan Cox6e47e062009-06-11 12:37:06 +01001214 rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
1215 if (rcv->urb == NULL) {
1216 dev_dbg(&intf->dev,
1217 "out of memory (read urbs usb_alloc_urb)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001218 goto alloc_fail7;
1219 }
1220
1221 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1222 rcv->instance = acm;
1223 }
Oliver Neukum86478942006-05-13 22:50:47 +02001224 for (i = 0; i < num_rx_buf; i++) {
David Brownell672c4e12008-08-06 18:41:12 -07001225 struct acm_rb *rb = &(acm->rb[i]);
David Kubicek61a87ad2005-11-01 18:51:34 +01001226
David Brownell672c4e12008-08-06 18:41:12 -07001227 rb->base = usb_buffer_alloc(acm->dev, readsize,
1228 GFP_KERNEL, &rb->dma);
1229 if (!rb->base) {
Alan Cox6e47e062009-06-11 12:37:06 +01001230 dev_dbg(&intf->dev,
1231 "out of memory (read bufs usb_buffer_alloc)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001232 goto alloc_fail7;
1233 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 }
Alan Cox6e47e062009-06-11 12:37:06 +01001235 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001236 struct acm_wb *snd = &(acm->wb[i]);
1237
Alan Cox6e47e062009-06-11 12:37:06 +01001238 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1239 if (snd->urb == NULL) {
1240 dev_dbg(&intf->dev,
1241 "out of memory (write urbs usb_alloc_urb)");
David Engrafe4cf3aa2008-03-20 10:01:34 +01001242 goto alloc_fail7;
1243 }
1244
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001245 if (usb_endpoint_xfer_int(epwrite))
1246 usb_fill_int_urb(snd->urb, usb_dev,
1247 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1248 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1249 else
1250 usb_fill_bulk_urb(snd->urb, usb_dev,
1251 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1252 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001253 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1254 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 }
1256
Alan Cox6e47e062009-06-11 12:37:06 +01001257 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001258
1259 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1260 if (i < 0)
1261 goto alloc_fail8;
1262
1263 if (cfd) { /* export the country data */
1264 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1265 if (!acm->country_codes)
1266 goto skip_countries;
1267 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001268 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1269 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001270 acm->country_rel_date = cfd->iCountryCodeRelDate;
1271
1272 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1273 if (i < 0) {
1274 kfree(acm->country_codes);
1275 goto skip_countries;
1276 }
1277
Alan Cox6e47e062009-06-11 12:37:06 +01001278 i = device_create_file(&intf->dev,
1279 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001280 if (i < 0) {
1281 kfree(acm->country_codes);
1282 goto skip_countries;
1283 }
1284 }
1285
1286skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001287 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001288 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1289 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1290 /* works around buggy devices */
1291 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1293 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1294
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1296
1297 acm_set_control(acm, acm->ctrlout);
1298
1299 acm->line.dwDTERate = cpu_to_le32(9600);
1300 acm->line.bDataBits = 8;
1301 acm_set_line(acm, &acm->line);
1302
1303 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001304 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001306 usb_get_intf(control_interface);
1307 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308
1309 acm_table[minor] = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001311 return 0;
1312alloc_fail8:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001313 for (i = 0; i < ACM_NW; i++)
1314 usb_free_urb(acm->wb[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315alloc_fail7:
Oliver Neukum830f4022008-06-25 14:17:16 +02001316 acm_read_buffers_free(acm);
Oliver Neukum86478942006-05-13 22:50:47 +02001317 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001318 usb_free_urb(acm->ru[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 usb_free_urb(acm->ctrlurb);
1320alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001321 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322alloc_fail4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
1324alloc_fail2:
1325 kfree(acm);
1326alloc_fail:
1327 return -ENOMEM;
1328}
1329
Oliver Neukum1365baf2007-10-12 17:24:28 +02001330static void stop_data_traffic(struct acm *acm)
1331{
1332 int i;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001333 dbg("Entering stop_data_traffic");
Oliver Neukum1365baf2007-10-12 17:24:28 +02001334
1335 tasklet_disable(&acm->urb_task);
1336
1337 usb_kill_urb(acm->ctrlurb);
Alan Cox6e47e062009-06-11 12:37:06 +01001338 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001339 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001340 for (i = 0; i < acm->rx_buflimit; i++)
1341 usb_kill_urb(acm->ru[i].urb);
1342
Oliver Neukum1365baf2007-10-12 17:24:28 +02001343 tasklet_enable(&acm->urb_task);
1344
1345 cancel_work_sync(&acm->work);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001346 cancel_work_sync(&acm->waker);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001347}
1348
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349static void acm_disconnect(struct usb_interface *intf)
1350{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001351 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001353 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354
David Brownell672c4e12008-08-06 18:41:12 -07001355 /* sibling interface is already cleaning up */
1356 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001357 return;
David Brownell672c4e12008-08-06 18:41:12 -07001358
1359 mutex_lock(&open_mutex);
Alan Cox6e47e062009-06-11 12:37:06 +01001360 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001361 device_remove_file(&acm->control->dev,
1362 &dev_attr_wCountryCodes);
1363 device_remove_file(&acm->control->dev,
1364 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001365 }
Alan Stern74da5d62007-08-02 13:29:10 -04001366 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 acm->dev = NULL;
Oliver Neukum86067eea2006-01-08 12:39:13 +01001368 usb_set_intfdata(acm->control, NULL);
1369 usb_set_intfdata(acm->data, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
Oliver Neukum1365baf2007-10-12 17:24:28 +02001371 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
Oliver Neukum884b6002005-04-21 21:28:02 +02001373 acm_write_buffers_free(acm);
Alan Cox6e47e062009-06-11 12:37:06 +01001374 usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
1375 acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001376 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001378 if (!acm->combined_interfaces)
1379 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001380 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381
Alan Cox10077d42009-06-11 12:36:09 +01001382 if (acm->port.count == 0) {
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001383 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001384 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 return;
1386 }
1387
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001388 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001389 tty = tty_port_tty_get(&acm->port);
1390 if (tty) {
1391 tty_hangup(tty);
1392 tty_kref_put(tty);
1393 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394}
1395
Oliver Neukum35758582008-07-01 19:10:08 +02001396#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001397static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1398{
1399 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001400 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001401
Alan Stern65bfd292008-11-25 16:39:18 -05001402 if (message.event & PM_EVENT_AUTO) {
Oliver Neukum11ea8592008-06-20 11:25:57 +02001403 int b;
1404
1405 spin_lock_irq(&acm->read_lock);
1406 spin_lock(&acm->write_lock);
1407 b = acm->processing + acm->transmitting;
1408 spin_unlock(&acm->write_lock);
1409 spin_unlock_irq(&acm->read_lock);
1410 if (b)
1411 return -EBUSY;
1412 }
1413
1414 spin_lock_irq(&acm->read_lock);
1415 spin_lock(&acm->write_lock);
1416 cnt = acm->susp_count++;
1417 spin_unlock(&acm->write_lock);
1418 spin_unlock_irq(&acm->read_lock);
1419
1420 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001421 return 0;
1422 /*
1423 we treat opened interfaces differently,
1424 we must guard against open
1425 */
1426 mutex_lock(&acm->mutex);
1427
Alan Cox10077d42009-06-11 12:36:09 +01001428 if (acm->port.count)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001429 stop_data_traffic(acm);
1430
1431 mutex_unlock(&acm->mutex);
1432 return 0;
1433}
1434
1435static int acm_resume(struct usb_interface *intf)
1436{
1437 struct acm *acm = usb_get_intfdata(intf);
1438 int rv = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001439 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001440
Oliver Neukum11ea8592008-06-20 11:25:57 +02001441 spin_lock_irq(&acm->read_lock);
1442 acm->susp_count -= 1;
1443 cnt = acm->susp_count;
1444 spin_unlock_irq(&acm->read_lock);
1445
1446 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001447 return 0;
1448
1449 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001450 if (acm->port.count) {
Oliver Neukum1365baf2007-10-12 17:24:28 +02001451 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
1452 if (rv < 0)
Oliver Neukum11ea8592008-06-20 11:25:57 +02001453 goto err_out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001454
1455 tasklet_schedule(&acm->urb_task);
1456 }
1457
1458err_out:
1459 mutex_unlock(&acm->mutex);
1460 return rv;
1461}
Oliver Neukum35758582008-07-01 19:10:08 +02001462
1463#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001464
1465#define NOKIA_PCSUITE_ACM_INFO(x) \
1466 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1467 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1468 USB_CDC_ACM_PROTO_VENDOR)
1469
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470/*
1471 * USB driver structure.
1472 */
1473
1474static struct usb_device_id acm_ids[] = {
1475 /* quirky and broken devices */
1476 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1477 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1478 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001479 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1480 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1481 },
Andrew Lunn0f9c7b4a2008-12-23 17:31:23 +01001482 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1483 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1484 },
Masahito Omote8753e652005-07-29 12:17:25 -07001485 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1486 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1487 },
Chris Malley91a9c922006-10-03 10:08:28 +01001488 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1489 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1490 },
Alan Cox7abcf202009-04-06 17:35:01 +01001491 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1492 .driver_info = SINGLE_RX_URB,
1493 },
Oliver Neukum86478942006-05-13 22:50:47 +02001494 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1495 .driver_info = SINGLE_RX_URB, /* firmware bug */
1496 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001497 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1498 .driver_info = SINGLE_RX_URB, /* firmware bug */
1499 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001500 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1501 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1502 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001503 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1504 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1505 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001506 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1507 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1508 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001509 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1510 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1511 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001512 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1513 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1514 },
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001515 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1516 },
Adam Richterc332b4e2009-02-18 16:17:15 -08001517 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1518 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1519 data interface instead of
1520 communications interface.
1521 Maybe we should define a new
1522 quirk for this. */
1523 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001524 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1525 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1526 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001527
Adrian Taylorc1479a92009-11-19 10:35:33 +00001528 /* Nokia S60 phones expose two ACM channels. The first is
1529 * a modem and is picked up by the standard AT-command
1530 * information below. The second is 'vendor-specific' but
1531 * is treated as a serial device at the S60 end, so we want
1532 * to expose it on Linux too. */
1533 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1534 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1535 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1536 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1537 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1538 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1539 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1540 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1541 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1542 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1543 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1544 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1545 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1546 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1547 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1548 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1549 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1550 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1551 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1552 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1553 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1554 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1555 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1556 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1557 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1558 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1559 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1560 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1561 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1562 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1563 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1564 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1565 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1566 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1567 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1568 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1569 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1570 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1571 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1572 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1573 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1574 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1575 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
1576
1577 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1578
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 /* control interfaces with various AT-command sets */
1580 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1581 USB_CDC_ACM_PROTO_AT_V25TER) },
1582 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1583 USB_CDC_ACM_PROTO_AT_PCCA101) },
1584 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1585 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1586 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1587 USB_CDC_ACM_PROTO_AT_GSM) },
1588 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001589 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1591 USB_CDC_ACM_PROTO_AT_CDMA) },
1592
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 { }
1594};
1595
Alan Cox6e47e062009-06-11 12:37:06 +01001596MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597
1598static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 .name = "cdc_acm",
1600 .probe = acm_probe,
1601 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001602#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001603 .suspend = acm_suspend,
1604 .resume = acm_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001605#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001607#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001608 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001609#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610};
1611
1612/*
1613 * TTY driver structures.
1614 */
1615
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001616static const struct tty_operations acm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617 .open = acm_tty_open,
1618 .close = acm_tty_close,
Alan Cox10077d42009-06-11 12:36:09 +01001619 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 .write = acm_tty_write,
1621 .write_room = acm_tty_write_room,
1622 .ioctl = acm_tty_ioctl,
1623 .throttle = acm_tty_throttle,
1624 .unthrottle = acm_tty_unthrottle,
1625 .chars_in_buffer = acm_tty_chars_in_buffer,
1626 .break_ctl = acm_tty_break_ctl,
1627 .set_termios = acm_tty_set_termios,
1628 .tiocmget = acm_tty_tiocmget,
1629 .tiocmset = acm_tty_tiocmset,
1630};
1631
1632/*
1633 * Init / exit.
1634 */
1635
1636static int __init acm_init(void)
1637{
1638 int retval;
1639 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1640 if (!acm_tty_driver)
1641 return -ENOMEM;
1642 acm_tty_driver->owner = THIS_MODULE,
1643 acm_tty_driver->driver_name = "acm",
1644 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645 acm_tty_driver->major = ACM_TTY_MAJOR,
1646 acm_tty_driver->minor_start = 0,
1647 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1648 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001649 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001651 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1652 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 tty_set_operations(acm_tty_driver, &acm_ops);
1654
1655 retval = tty_register_driver(acm_tty_driver);
1656 if (retval) {
1657 put_tty_driver(acm_tty_driver);
1658 return retval;
1659 }
1660
1661 retval = usb_register(&acm_driver);
1662 if (retval) {
1663 tty_unregister_driver(acm_tty_driver);
1664 put_tty_driver(acm_tty_driver);
1665 return retval;
1666 }
1667
Greg Kroah-Hartman5909f6e2008-08-18 13:21:04 -07001668 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
1669 DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670
1671 return 0;
1672}
1673
1674static void __exit acm_exit(void)
1675{
1676 usb_deregister(&acm_driver);
1677 tty_unregister_driver(acm_tty_driver);
1678 put_tty_driver(acm_tty_driver);
1679}
1680
1681module_init(acm_init);
1682module_exit(acm_exit);
1683
Alan Cox6e47e062009-06-11 12:37:06 +01001684MODULE_AUTHOR(DRIVER_AUTHOR);
1685MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01001687MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);