blob: 2d8d6b8382356bf380d306892dc1cd5b68aad25e [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 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070015 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 */
29
30#undef DEBUG
David Brownelle5fbab52008-08-06 18:46:10 -070031#undef VERBOSE_DEBUG
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
33#include <linux/kernel.h>
34#include <linux/errno.h>
35#include <linux/init.h>
36#include <linux/slab.h>
37#include <linux/tty.h>
Oliver Neukum7af25b42009-09-08 23:51:28 +020038#include <linux/serial.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/tty_driver.h>
40#include <linux/tty_flip.h>
41#include <linux/module.h>
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010042#include <linux/mutex.h>
Alan Cox10077d42009-06-11 12:36:09 +010043#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <linux/usb.h>
David Brownella8c28f22006-06-13 09:57:47 -070045#include <linux/usb/cdc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <asm/byteorder.h>
47#include <asm/unaligned.h>
David Kubicek61a87ad2005-11-01 18:51:34 +010048#include <linux/list.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
50#include "cdc-acm.h"
51
David Brownelle5fbab52008-08-06 18:46:10 -070052
David Kubicek61a87ad2005-11-01 18:51:34 +010053#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
55
56static struct usb_driver acm_driver;
57static struct tty_driver *acm_tty_driver;
58static struct acm *acm_table[ACM_TTY_MINORS];
59
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010060static DEFINE_MUTEX(open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
Alan Cox10077d42009-06-11 12:36:09 +010062#define ACM_READY(acm) (acm && acm->dev && acm->port.count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070063
Alan Cox739e0282009-06-11 12:27:50 +010064static const struct tty_port_operations acm_port_ops = {
65};
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067/*
68 * Functions for ACM control messages.
69 */
70
Alan Cox6e47e062009-06-11 12:37:06 +010071static int acm_ctrl_msg(struct acm *acm, int request, int value,
72 void *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -070073{
74 int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
75 request, USB_RT_ACM, value,
76 acm->control->altsetting[0].desc.bInterfaceNumber,
77 buf, len, 5000);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +010078 dev_dbg(&acm->control->dev,
79 "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
80 __func__, request, value, len, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 return retval < 0 ? retval : 0;
82}
83
84/* devices aren't required to support these requests.
85 * the cdc acm descriptor tells whether they do...
86 */
87#define acm_set_control(acm, control) \
88 acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
89#define acm_set_line(acm, line) \
90 acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
91#define acm_send_break(acm, ms) \
92 acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
93
94/*
Oliver Neukum884b6002005-04-21 21:28:02 +020095 * Write buffer management.
96 * All of these assume proper locks taken by the caller.
97 */
98
99static int acm_wb_alloc(struct acm *acm)
100{
101 int i, wbn;
102 struct acm_wb *wb;
103
David Engrafe4cf3aa2008-03-20 10:01:34 +0100104 wbn = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200105 i = 0;
106 for (;;) {
107 wb = &acm->wb[wbn];
108 if (!wb->use) {
109 wb->use = 1;
110 return wbn;
111 }
Oliver Neukum86478942006-05-13 22:50:47 +0200112 wbn = (wbn + 1) % ACM_NW;
113 if (++i >= ACM_NW)
Oliver Neukum884b6002005-04-21 21:28:02 +0200114 return -1;
115 }
116}
117
Oliver Neukum884b6002005-04-21 21:28:02 +0200118static int acm_wb_is_avail(struct acm *acm)
119{
120 int i, n;
David Brownelle5fbab52008-08-06 18:46:10 -0700121 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200122
Oliver Neukum86478942006-05-13 22:50:47 +0200123 n = ACM_NW;
David Brownelle5fbab52008-08-06 18:46:10 -0700124 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100125 for (i = 0; i < ACM_NW; i++)
Oliver Neukum86478942006-05-13 22:50:47 +0200126 n -= acm->wb[i].use;
David Brownelle5fbab52008-08-06 18:46:10 -0700127 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200128 return n;
129}
130
Oliver Neukum884b6002005-04-21 21:28:02 +0200131/*
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800132 * Finish write. Caller must hold acm->write_lock
Oliver Neukum884b6002005-04-21 21:28:02 +0200133 */
David Engrafe4cf3aa2008-03-20 10:01:34 +0100134static void acm_write_done(struct acm *acm, struct acm_wb *wb)
Oliver Neukum884b6002005-04-21 21:28:02 +0200135{
David Engrafe4cf3aa2008-03-20 10:01:34 +0100136 wb->use = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200137 acm->transmitting--;
Oliver Neukum97d35f92009-12-16 17:05:57 +0100138 usb_autopm_put_interface_async(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200139}
140
141/*
142 * Poke write.
Oliver Neukum11ea8592008-06-20 11:25:57 +0200143 *
144 * the caller is responsible for locking
Oliver Neukum884b6002005-04-21 21:28:02 +0200145 */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200146
147static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
148{
149 int rc;
150
151 acm->transmitting++;
152
153 wb->urb->transfer_buffer = wb->buf;
154 wb->urb->transfer_dma = wb->dmah;
155 wb->urb->transfer_buffer_length = wb->len;
156 wb->urb->dev = acm->dev;
157
Alan Cox6e47e062009-06-11 12:37:06 +0100158 rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
159 if (rc < 0) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100160 dev_err(&acm->data->dev,
161 "%s - usb_submit_urb(write bulk) failed: %d\n",
162 __func__, rc);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200163 acm_write_done(acm, wb);
164 }
165 return rc;
166}
167
David Engrafe4cf3aa2008-03-20 10:01:34 +0100168static int acm_write_start(struct acm *acm, int wbn)
Oliver Neukum884b6002005-04-21 21:28:02 +0200169{
170 unsigned long flags;
David Brownell934da462008-08-06 18:44:12 -0700171 struct acm_wb *wb = &acm->wb[wbn];
Oliver Neukum884b6002005-04-21 21:28:02 +0200172 int rc;
173
174 spin_lock_irqsave(&acm->write_lock, flags);
175 if (!acm->dev) {
David Brownell934da462008-08-06 18:44:12 -0700176 wb->use = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200177 spin_unlock_irqrestore(&acm->write_lock, flags);
178 return -ENODEV;
179 }
180
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100181 dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100182 acm->susp_count);
Oliver Neukum97d35f92009-12-16 17:05:57 +0100183 usb_autopm_get_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200184 if (acm->susp_count) {
Oliver Neukum97d35f92009-12-16 17:05:57 +0100185 if (!acm->delayed_wb)
186 acm->delayed_wb = wb;
187 else
188 usb_autopm_put_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200189 spin_unlock_irqrestore(&acm->write_lock, flags);
190 return 0; /* A white lie */
191 }
192 usb_mark_last_busy(acm->dev);
193
Oliver Neukum11ea8592008-06-20 11:25:57 +0200194 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200195 spin_unlock_irqrestore(&acm->write_lock, flags);
196
Oliver Neukum884b6002005-04-21 21:28:02 +0200197 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200198
Oliver Neukum884b6002005-04-21 21:28:02 +0200199}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100200/*
201 * attributes exported through sysfs
202 */
203static ssize_t show_caps
204(struct device *dev, struct device_attribute *attr, char *buf)
205{
206 struct usb_interface *intf = to_usb_interface(dev);
207 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200208
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100209 return sprintf(buf, "%d", acm->ctrl_caps);
210}
211static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
212
213static ssize_t show_country_codes
214(struct device *dev, struct device_attribute *attr, char *buf)
215{
216 struct usb_interface *intf = to_usb_interface(dev);
217 struct acm *acm = usb_get_intfdata(intf);
218
219 memcpy(buf, acm->country_codes, acm->country_code_size);
220 return acm->country_code_size;
221}
222
223static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
224
225static ssize_t show_country_rel_date
226(struct device *dev, struct device_attribute *attr, char *buf)
227{
228 struct usb_interface *intf = to_usb_interface(dev);
229 struct acm *acm = usb_get_intfdata(intf);
230
231 return sprintf(buf, "%d", acm->country_rel_date);
232}
233
234static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200235/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 * Interrupt handlers for various ACM device responses
237 */
238
239/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100240static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241{
242 struct acm *acm = urb->context;
243 struct usb_cdc_notification *dr = urb->transfer_buffer;
Alan Cox10077d42009-06-11 12:36:09 +0100244 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 unsigned char *data;
246 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700247 int retval;
248 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700250 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 case 0:
252 /* success */
253 break;
254 case -ECONNRESET:
255 case -ENOENT:
256 case -ESHUTDOWN:
257 /* this urb is terminated, clean up */
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100258 dev_dbg(&acm->control->dev,
259 "%s - urb shutting down with status: %d\n",
260 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 return;
262 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100263 dev_dbg(&acm->control->dev,
264 "%s - nonzero urb status received: %d\n",
265 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 goto exit;
267 }
268
269 if (!ACM_READY(acm))
270 goto exit;
271
Johan Hovold7e7797e2011-03-22 11:12:11 +0100272 usb_mark_last_busy(acm->dev);
273
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 data = (unsigned char *)(dr + 1);
275 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100276 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100277 dev_dbg(&acm->control->dev, "%s - network connection: %d\n",
278 __func__, dr->wValue);
Alan Cox6e47e062009-06-11 12:37:06 +0100279 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280
Alan Cox6e47e062009-06-11 12:37:06 +0100281 case USB_CDC_NOTIFY_SERIAL_STATE:
282 tty = tty_port_tty_get(&acm->port);
283 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284
Alan Cox6e47e062009-06-11 12:37:06 +0100285 if (tty) {
286 if (!acm->clocal &&
287 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100288 dev_dbg(&acm->control->dev,
289 "%s - calling hangup\n", __func__);
Alan Cox6e47e062009-06-11 12:37:06 +0100290 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 }
Alan Cox6e47e062009-06-11 12:37:06 +0100292 tty_kref_put(tty);
293 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
Alan Cox6e47e062009-06-11 12:37:06 +0100295 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100297 dev_dbg(&acm->control->dev,
298 "%s - input control lines: dcd%c dsr%c break%c "
299 "ring%c framing%c parity%c overrun%c\n",
300 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100301 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
302 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
303 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
304 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
305 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
306 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
307 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 break;
309
Alan Cox6e47e062009-06-11 12:37:06 +0100310 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100311 dev_dbg(&acm->control->dev,
312 "%s - unknown notification %d received: index %d "
313 "len %d data0 %d data1 %d\n",
314 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100315 dr->bNotificationType, dr->wIndex,
316 dr->wLength, data[0], data[1]);
317 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 }
319exit:
Alan Cox6e47e062009-06-11 12:37:06 +0100320 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700321 if (retval)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100322 dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
323 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324}
325
326/* data interface returns incoming bytes, or we got unthrottled */
David Howells7d12e782006-10-05 14:55:46 +0100327static void acm_read_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328{
David Kubicek61a87ad2005-11-01 18:51:34 +0100329 struct acm_rb *buf;
330 struct acm_ru *rcv = urb->context;
331 struct acm *acm = rcv->instance;
Oliver Neukum86478942006-05-13 22:50:47 +0200332 int status = urb->status;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700333
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100334 dev_vdbg(&acm->data->dev, "%s - status %d\n", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
Oliver Neukum11ea8592008-06-20 11:25:57 +0200336 if (!ACM_READY(acm)) {
Johan Hovold1d9846e2011-03-22 11:12:14 +0100337 dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200339 }
340 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341
Oliver Neukum86478942006-05-13 22:50:47 +0200342 if (status)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100343 dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
344 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345
David Kubicek61a87ad2005-11-01 18:51:34 +0100346 buf = rcv->buffer;
347 buf->size = urb->actual_length;
348
Oliver Neukum86478942006-05-13 22:50:47 +0200349 if (likely(status == 0)) {
350 spin_lock(&acm->read_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200351 acm->processing++;
Oliver Neukum86478942006-05-13 22:50:47 +0200352 list_add_tail(&rcv->list, &acm->spare_read_urbs);
353 list_add_tail(&buf->list, &acm->filled_read_bufs);
354 spin_unlock(&acm->read_lock);
355 } else {
356 /* we drop the buffer due to an error */
357 spin_lock(&acm->read_lock);
358 list_add_tail(&rcv->list, &acm->spare_read_urbs);
359 list_add(&buf->list, &acm->spare_read_bufs);
360 spin_unlock(&acm->read_lock);
361 /* nevertheless the tasklet must be kicked unconditionally
362 so the queue cannot dry up */
363 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200364 if (likely(!acm->susp_count))
365 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366}
367
368static void acm_rx_tasklet(unsigned long _acm)
369{
370 struct acm *acm = (void *)_acm;
David Kubicek61a87ad2005-11-01 18:51:34 +0100371 struct acm_rb *buf;
Alan Cox10077d42009-06-11 12:36:09 +0100372 struct tty_struct *tty;
David Kubicek61a87ad2005-11-01 18:51:34 +0100373 struct acm_ru *rcv;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200374 unsigned long flags;
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100375 unsigned char throttled;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200376
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100377 dev_vdbg(&acm->data->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
Alan Cox10077d42009-06-11 12:36:09 +0100379 if (!ACM_READY(acm)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100380 dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100381 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200382 }
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100383
Oliver Neukum834dbca2007-03-06 10:47:04 +0100384 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100385 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100386 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Alan Cox10077d42009-06-11 12:36:09 +0100387 if (throttled) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100388 dev_dbg(&acm->data->dev, "%s - throttled\n", __func__);
David Kubicek61a87ad2005-11-01 18:51:34 +0100389 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200390 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100391
Alan Cox10077d42009-06-11 12:36:09 +0100392 tty = tty_port_tty_get(&acm->port);
393
David Kubicek61a87ad2005-11-01 18:51:34 +0100394next_buffer:
Jarek Poplawski762f0072006-10-06 07:23:11 +0200395 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100396 if (list_empty(&acm->filled_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200397 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100398 goto urbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100400 buf = list_entry(acm->filled_read_bufs.next,
401 struct acm_rb, list);
402 list_del(&buf->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200403 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100404
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100405 dev_vdbg(&acm->data->dev, "%s - processing buf 0x%p, size = %d\n",
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100406 __func__, buf, buf->size);
Alan Cox10077d42009-06-11 12:36:09 +0100407 if (tty) {
408 spin_lock_irqsave(&acm->throttle_lock, flags);
409 throttled = acm->throttle;
410 spin_unlock_irqrestore(&acm->throttle_lock, flags);
411 if (!throttled) {
Alan Cox10077d42009-06-11 12:36:09 +0100412 tty_insert_flip_string(tty, buf->base, buf->size);
413 tty_flip_buffer_push(tty);
414 } else {
415 tty_kref_put(tty);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100416 dev_dbg(&acm->data->dev, "%s - throttling noticed\n",
417 __func__);
Alan Cox10077d42009-06-11 12:36:09 +0100418 spin_lock_irqsave(&acm->read_lock, flags);
419 list_add(&buf->list, &acm->filled_read_bufs);
420 spin_unlock_irqrestore(&acm->read_lock, flags);
421 return;
422 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424
Jarek Poplawski762f0072006-10-06 07:23:11 +0200425 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100426 list_add(&buf->list, &acm->spare_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 next_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429
David Kubicek61a87ad2005-11-01 18:51:34 +0100430urbs:
Alan Cox10077d42009-06-11 12:36:09 +0100431 tty_kref_put(tty);
432
David Kubicek61a87ad2005-11-01 18:51:34 +0100433 while (!list_empty(&acm->spare_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200434 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100435 if (list_empty(&acm->spare_read_urbs)) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200436 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200437 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100438 return;
439 }
440 rcv = list_entry(acm->spare_read_urbs.next,
441 struct acm_ru, list);
442 list_del(&rcv->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200443 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100444
445 buf = list_entry(acm->spare_read_bufs.next,
446 struct acm_rb, list);
447 list_del(&buf->list);
448
449 rcv->buffer = buf;
450
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200451 if (acm->is_int_ep)
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400452 usb_fill_int_urb(rcv->urb, acm->dev,
453 acm->rx_endpoint,
454 buf->base,
455 acm->readsize,
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200456 acm_read_bulk, rcv, acm->bInterval);
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400457 else
458 usb_fill_bulk_urb(rcv->urb, acm->dev,
459 acm->rx_endpoint,
460 buf->base,
461 acm->readsize,
462 acm_read_bulk, rcv);
David Kubicek61a87ad2005-11-01 18:51:34 +0100463 rcv->urb->transfer_dma = buf->dma;
464 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
465
Alan Cox6e47e062009-06-11 12:37:06 +0100466 /* This shouldn't kill the driver as unsuccessful URBs are
467 returned to the free-urbs-pool and resubmited ASAP */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200468 spin_lock_irqsave(&acm->read_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100469 if (acm->susp_count ||
470 usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100471 list_add(&buf->list, &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100472 list_add(&rcv->list, &acm->spare_read_urbs);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200473 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200474 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100475 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200476 } else {
477 spin_unlock_irqrestore(&acm->read_lock, flags);
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100478 dev_vdbg(&acm->data->dev,
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100479 "%s - sending urb 0x%p, rcv 0x%p, buf 0x%p\n",
480 __func__, rcv->urb, rcv, buf);
David Kubicek61a87ad2005-11-01 18:51:34 +0100481 }
482 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200483 spin_lock_irqsave(&acm->read_lock, flags);
484 acm->processing = 0;
485 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486}
487
488/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100489static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490{
Ming Leicdc97792008-02-24 18:41:47 +0800491 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700492 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800493 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200494
Johan Hovold4fa46262011-03-22 11:12:16 +0100495 if (urb->status || (urb->actual_length != urb->transfer_buffer_length))
496 dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
Johan Hovold1d9846e2011-03-22 11:12:14 +0100497 __func__,
David Brownelle5fbab52008-08-06 18:46:10 -0700498 urb->actual_length,
499 urb->transfer_buffer_length,
500 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800502 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100503 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800504 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200505 if (ACM_READY(acm))
506 schedule_work(&acm->work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507}
508
David Howellsc4028952006-11-22 14:57:56 +0000509static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510{
David Howellsc4028952006-11-22 14:57:56 +0000511 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100512 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700513
Johan Hovold1d9846e2011-03-22 11:12:14 +0100514 dev_vdbg(&acm->data->dev, "%s\n", __func__);
515
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 if (!ACM_READY(acm))
517 return;
Alan Cox10077d42009-06-11 12:36:09 +0100518 tty = tty_port_tty_get(&acm->port);
Johan Hovold15e5bee2011-03-22 11:12:10 +0100519 if (!tty)
520 return;
Alan Cox10077d42009-06-11 12:36:09 +0100521 tty_wakeup(tty);
522 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523}
524
525/*
526 * TTY handlers
527 */
528
529static int acm_tty_open(struct tty_struct *tty, struct file *filp)
530{
531 struct acm *acm;
Thadeu Lima de Souza Cascardo42dd2aa2009-06-25 14:41:24 +0100532 int rv = -ENODEV;
David Kubicek61a87ad2005-11-01 18:51:34 +0100533 int i;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100534
535 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
537 acm = acm_table[tty->index];
538 if (!acm || !acm->dev)
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100539 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 else
541 rv = 0;
542
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100543 dev_dbg(&acm->control->dev, "%s\n", __func__);
544
David Engraf28d1dfa2008-03-20 10:53:52 +0100545 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
Alan Cox10077d42009-06-11 12:36:09 +0100546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 tty->driver_data = acm;
Alan Cox10077d42009-06-11 12:36:09 +0100548 tty_port_tty_set(&acm->port, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
Oliver Neukum94409cc2008-02-11 15:22:29 +0100550 if (usb_autopm_get_interface(acm->control) < 0)
551 goto early_bail;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200552 else
553 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200554
555 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100556 if (acm->port.count++) {
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100557 mutex_unlock(&acm->mutex);
Oliver Neukum1365baf2007-10-12 17:24:28 +0200558 usb_autopm_put_interface(acm->control);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100559 goto out;
Alan Cox10077d42009-06-11 12:36:09 +0100560 }
Oliver Neukum1365baf2007-10-12 17:24:28 +0200561
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 acm->ctrlurb->dev = acm->dev;
563 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100564 dev_err(&acm->control->dev,
565 "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 goto bail_out;
567 }
568
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100569 if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
570 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 goto full_bailout;
Alan Cox10077d42009-06-11 12:36:09 +0100572
Oliver Neukum11ea8592008-06-20 11:25:57 +0200573 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
David Kubicek61a87ad2005-11-01 18:51:34 +0100575 INIT_LIST_HEAD(&acm->spare_read_urbs);
576 INIT_LIST_HEAD(&acm->spare_read_bufs);
577 INIT_LIST_HEAD(&acm->filled_read_bufs);
Alan Cox6e47e062009-06-11 12:37:06 +0100578
579 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100580 list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
Alan Cox6e47e062009-06-11 12:37:06 +0100581 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100582 list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100583
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100584 acm->throttle = 0;
585
Oliver Neukum7af25b42009-09-08 23:51:28 +0200586 set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
Alan Cox10077d42009-06-11 12:36:09 +0100587 rv = tty_port_block_til_ready(&acm->port, tty, filp);
Henry Gebhardt18a77b52009-11-04 11:19:28 +0100588 tasklet_schedule(&acm->urb_task);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100589
Oliver Neukum1365baf2007-10-12 17:24:28 +0200590 mutex_unlock(&acm->mutex);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100591out:
Oliver Neukum94409cc2008-02-11 15:22:29 +0100592 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 return rv;
594
595full_bailout:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 usb_kill_urb(acm->ctrlurb);
597bail_out:
Alan Cox10077d42009-06-11 12:36:09 +0100598 acm->port.count--;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200599 mutex_unlock(&acm->mutex);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100600 usb_autopm_put_interface(acm->control);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100601early_bail:
602 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100603 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 return -EIO;
605}
606
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700607static void acm_tty_unregister(struct acm *acm)
608{
Johan Hovolddab54c92011-03-22 11:12:21 +0100609 int i;
David Kubicek61a87ad2005-11-01 18:51:34 +0100610
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700611 tty_unregister_device(acm_tty_driver, acm->minor);
612 usb_put_intf(acm->control);
613 acm_table[acm->minor] = NULL;
614 usb_free_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100615 for (i = 0; i < ACM_NW; i++)
616 usb_free_urb(acm->wb[i].urb);
Johan Hovolddab54c92011-03-22 11:12:21 +0100617 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100618 usb_free_urb(acm->ru[i].urb);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100619 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700620 kfree(acm);
621}
622
Arnd Bergmann4e608672010-06-01 22:53:04 +0200623static void acm_port_down(struct acm *acm)
Alan Cox10077d42009-06-11 12:36:09 +0100624{
Johan Hovolddab54c92011-03-22 11:12:21 +0100625 int i;
626
Alan Cox10077d42009-06-11 12:36:09 +0100627 mutex_lock(&open_mutex);
628 if (acm->dev) {
629 usb_autopm_get_interface(acm->control);
630 acm_set_control(acm, acm->ctrlout = 0);
Alan Cox10077d42009-06-11 12:36:09 +0100631 usb_kill_urb(acm->ctrlurb);
632 for (i = 0; i < ACM_NW; i++)
633 usb_kill_urb(acm->wb[i].urb);
Johan Hovold23b80552011-03-22 11:12:09 +0100634 tasklet_disable(&acm->urb_task);
Johan Hovolddab54c92011-03-22 11:12:21 +0100635 for (i = 0; i < acm->rx_buflimit; i++)
Alan Cox10077d42009-06-11 12:36:09 +0100636 usb_kill_urb(acm->ru[i].urb);
Johan Hovold23b80552011-03-22 11:12:09 +0100637 tasklet_enable(&acm->urb_task);
Alan Cox10077d42009-06-11 12:36:09 +0100638 acm->control->needs_remote_wakeup = 0;
639 usb_autopm_put_interface(acm->control);
640 }
641 mutex_unlock(&open_mutex);
642}
643
644static void acm_tty_hangup(struct tty_struct *tty)
645{
646 struct acm *acm = tty->driver_data;
647 tty_port_hangup(&acm->port);
Arnd Bergmann4e608672010-06-01 22:53:04 +0200648 acm_port_down(acm);
Alan Cox10077d42009-06-11 12:36:09 +0100649}
650
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651static void acm_tty_close(struct tty_struct *tty, struct file *filp)
652{
653 struct acm *acm = tty->driver_data;
654
Alan Cox10077d42009-06-11 12:36:09 +0100655 /* Perform the closing process and see if we need to do the hardware
656 shutdown */
Francesco Lavra051522b2009-11-03 10:53:07 +0000657 if (!acm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 return;
Francesco Lavra051522b2009-11-03 10:53:07 +0000659 if (tty_port_close_start(&acm->port, tty, filp) == 0) {
660 mutex_lock(&open_mutex);
661 if (!acm->dev) {
662 tty_port_tty_set(&acm->port, NULL);
663 acm_tty_unregister(acm);
664 tty->driver_data = NULL;
665 }
666 mutex_unlock(&open_mutex);
667 return;
668 }
Arnd Bergmann4e608672010-06-01 22:53:04 +0200669 acm_port_down(acm);
Alan Cox10077d42009-06-11 12:36:09 +0100670 tty_port_close_end(&acm->port, tty);
Alan Cox10077d42009-06-11 12:36:09 +0100671 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672}
673
Alan Cox6e47e062009-06-11 12:37:06 +0100674static int acm_tty_write(struct tty_struct *tty,
675 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676{
677 struct acm *acm = tty->driver_data;
678 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200679 unsigned long flags;
680 int wbn;
681 struct acm_wb *wb;
682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 if (!ACM_READY(acm))
684 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 if (!count)
686 return 0;
687
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100688 dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100689
Oliver Neukum884b6002005-04-21 21:28:02 +0200690 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100691 wbn = acm_wb_alloc(acm);
692 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200693 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200694 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200696 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Oliver Neukum884b6002005-04-21 21:28:02 +0200698 count = (count > acm->writesize) ? acm->writesize : count;
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100699 dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
Oliver Neukum884b6002005-04-21 21:28:02 +0200700 memcpy(wb->buf, buf, count);
701 wb->len = count;
702 spin_unlock_irqrestore(&acm->write_lock, flags);
703
Alan Cox6e47e062009-06-11 12:37:06 +0100704 stat = acm_write_start(acm, wbn);
705 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200706 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 return count;
708}
709
710static int acm_tty_write_room(struct tty_struct *tty)
711{
712 struct acm *acm = tty->driver_data;
713 if (!ACM_READY(acm))
714 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200715 /*
716 * Do not let the line discipline to know that we have a reserve,
717 * or it might get too enthusiastic.
718 */
David Brownell934da462008-08-06 18:44:12 -0700719 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720}
721
722static int acm_tty_chars_in_buffer(struct tty_struct *tty)
723{
724 struct acm *acm = tty->driver_data;
725 if (!ACM_READY(acm))
Alan Cox23198fd2009-07-20 16:05:27 +0100726 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200727 /*
728 * This is inaccurate (overcounts), but it works.
729 */
Oliver Neukum86478942006-05-13 22:50:47 +0200730 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731}
732
733static void acm_tty_throttle(struct tty_struct *tty)
734{
735 struct acm *acm = tty->driver_data;
736 if (!ACM_READY(acm))
737 return;
738 spin_lock_bh(&acm->throttle_lock);
739 acm->throttle = 1;
740 spin_unlock_bh(&acm->throttle_lock);
741}
742
743static void acm_tty_unthrottle(struct tty_struct *tty)
744{
745 struct acm *acm = tty->driver_data;
746 if (!ACM_READY(acm))
747 return;
748 spin_lock_bh(&acm->throttle_lock);
749 acm->throttle = 0;
750 spin_unlock_bh(&acm->throttle_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100751 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752}
753
Alan Cox9e989662008-07-22 11:18:03 +0100754static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755{
756 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100757 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 if (!ACM_READY(acm))
Alan Cox9e989662008-07-22 11:18:03 +0100759 return -EINVAL;
760 retval = acm_send_break(acm, state ? 0xffff : 0);
761 if (retval < 0)
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100762 dev_dbg(&acm->control->dev, "%s - send break failed\n",
763 __func__);
Alan Cox9e989662008-07-22 11:18:03 +0100764 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765}
766
Alan Cox60b33c12011-02-14 16:26:14 +0000767static int acm_tty_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768{
769 struct acm *acm = tty->driver_data;
770
771 if (!ACM_READY(acm))
772 return -EINVAL;
773
774 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
775 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
776 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
777 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
778 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
779 TIOCM_CTS;
780}
781
Alan Cox20b9d172011-02-14 16:26:50 +0000782static int acm_tty_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 unsigned int set, unsigned int clear)
784{
785 struct acm *acm = tty->driver_data;
786 unsigned int newctrl;
787
788 if (!ACM_READY(acm))
789 return -EINVAL;
790
791 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100792 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
793 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
794 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
795 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
797 newctrl = (newctrl & ~clear) | set;
798
799 if (acm->ctrlout == newctrl)
800 return 0;
801 return acm_set_control(acm, acm->ctrlout = newctrl);
802}
803
Alan Cox6caa76b2011-02-14 16:27:22 +0000804static int acm_tty_ioctl(struct tty_struct *tty,
Alan Cox6e47e062009-06-11 12:37:06 +0100805 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806{
807 struct acm *acm = tty->driver_data;
808
809 if (!ACM_READY(acm))
810 return -EINVAL;
811
812 return -ENOIOCTLCMD;
813}
814
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100815static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 0, 50, 75, 110, 134, 150, 200, 300, 600,
817 1200, 1800, 2400, 4800, 9600, 19200, 38400,
818 57600, 115200, 230400, 460800, 500000, 576000,
819 921600, 1000000, 1152000, 1500000, 2000000,
820 2500000, 3000000, 3500000, 4000000
821};
822
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100823static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 5, 6, 7, 8
825};
826
Alan Cox6e47e062009-06-11 12:37:06 +0100827static void acm_tty_set_termios(struct tty_struct *tty,
828 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829{
830 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800831 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 struct usb_cdc_line_coding newline;
833 int newctrl = acm->ctrlout;
834
835 if (!ACM_READY(acm))
836 return;
837
Alan Cox9b80fee2009-09-19 13:13:23 -0700838 newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
840 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100841 (termios->c_cflag & PARODD ? 1 : 2) +
842 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
Alan Cox6e47e062009-06-11 12:37:06 +0100844 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
846
847 if (!newline.dwDTERate) {
848 newline.dwDTERate = acm->line.dwDTERate;
849 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +0100850 } else
851 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852
853 if (newctrl != acm->ctrlout)
854 acm_set_control(acm, acm->ctrlout = newctrl);
855
856 if (memcmp(&acm->line, &newline, sizeof newline)) {
857 memcpy(&acm->line, &newline, sizeof newline);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100858 dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
859 __func__,
860 le32_to_cpu(newline.dwDTERate),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 newline.bCharFormat, newline.bParityType,
862 newline.bDataBits);
863 acm_set_line(acm, &acm->line);
864 }
865}
866
867/*
868 * USB probe and disconnect routines.
869 */
870
Oliver Neukum830f4022008-06-25 14:17:16 +0200871/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +0200872static void acm_write_buffers_free(struct acm *acm)
873{
874 int i;
875 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +0200876 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200877
Alan Cox6e47e062009-06-11 12:37:06 +0100878 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Daniel Mack997ea582010-04-12 13:17:25 +0200879 usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +0200880}
881
Oliver Neukum830f4022008-06-25 14:17:16 +0200882static void acm_read_buffers_free(struct acm *acm)
883{
884 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Johan Hovolddab54c92011-03-22 11:12:21 +0100885 int i;
Oliver Neukum830f4022008-06-25 14:17:16 +0200886
Johan Hovolddab54c92011-03-22 11:12:21 +0100887 for (i = 0; i < acm->rx_buflimit; i++)
Daniel Mack997ea582010-04-12 13:17:25 +0200888 usb_free_coherent(usb_dev, acm->readsize,
889 acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +0200890}
891
Oliver Neukum884b6002005-04-21 21:28:02 +0200892/* Little helper: write buffers allocate */
893static int acm_write_buffers_alloc(struct acm *acm)
894{
895 int i;
896 struct acm_wb *wb;
897
Oliver Neukum86478942006-05-13 22:50:47 +0200898 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Daniel Mack997ea582010-04-12 13:17:25 +0200899 wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
Oliver Neukum884b6002005-04-21 21:28:02 +0200900 &wb->dmah);
901 if (!wb->buf) {
902 while (i != 0) {
903 --i;
904 --wb;
Daniel Mack997ea582010-04-12 13:17:25 +0200905 usb_free_coherent(acm->dev, acm->writesize,
Oliver Neukum884b6002005-04-21 21:28:02 +0200906 wb->buf, wb->dmah);
907 }
908 return -ENOMEM;
909 }
910 }
911 return 0;
912}
913
Alan Cox10077d42009-06-11 12:36:09 +0100914static int acm_probe(struct usb_interface *intf,
915 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916{
917 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100918 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -0700919 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 int buflen = intf->altsetting->extralen;
921 struct usb_interface *control_interface;
922 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200923 struct usb_endpoint_descriptor *epctrl = NULL;
924 struct usb_endpoint_descriptor *epread = NULL;
925 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 struct usb_device *usb_dev = interface_to_usbdev(intf);
927 struct acm *acm;
928 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +0100929 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 u8 *buf;
931 u8 ac_management_function = 0;
932 u8 call_management_function = 0;
933 int call_interface_num = -1;
934 int data_interface_num;
935 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200936 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100937 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200938 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939
Oliver Neukum86478942006-05-13 22:50:47 +0200940 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200942 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
943
944 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 if (quirks == NO_UNION_NORMAL) {
946 data_interface = usb_ifnum_to_if(usb_dev, 1);
947 control_interface = usb_ifnum_to_if(usb_dev, 0);
948 goto skip_normal_probe;
949 }
Alan Cox6e47e062009-06-11 12:37:06 +0100950
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 /* normal probing*/
952 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700953 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 return -EINVAL;
955 }
956
957 if (!buflen) {
Toby Gray577045c2010-09-02 10:46:20 +0100958 if (intf->cur_altsetting->endpoint &&
959 intf->cur_altsetting->endpoint->extralen &&
Alan Cox6e47e062009-06-11 12:37:06 +0100960 intf->cur_altsetting->endpoint->extra) {
961 dev_dbg(&intf->dev,
962 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 buflen = intf->cur_altsetting->endpoint->extralen;
964 buffer = intf->cur_altsetting->endpoint->extra;
965 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700966 dev_err(&intf->dev,
967 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 return -EINVAL;
969 }
970 }
971
972 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +0100973 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700974 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 goto next_desc;
976 }
977
Alan Cox6e47e062009-06-11 12:37:06 +0100978 switch (buffer[2]) {
979 case USB_CDC_UNION_TYPE: /* we've found it */
980 if (union_header) {
981 dev_err(&intf->dev, "More than one "
982 "union descriptor, skipping ...\n");
983 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 }
Alan Cox6e47e062009-06-11 12:37:06 +0100985 union_header = (struct usb_cdc_union_desc *)buffer;
986 break;
987 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
988 cfd = (struct usb_cdc_country_functional_desc *)buffer;
989 break;
990 case USB_CDC_HEADER_TYPE: /* maybe check version */
991 break; /* for now we ignore it */
992 case USB_CDC_ACM_TYPE:
993 ac_management_function = buffer[3];
994 break;
995 case USB_CDC_CALL_MANAGEMENT_TYPE:
996 call_management_function = buffer[3];
997 call_interface_num = buffer[4];
Julian Calabyce126642010-01-05 23:58:20 +1100998 if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
Alan Cox6e47e062009-06-11 12:37:06 +0100999 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1000 break;
1001 default:
1002 /* there are LOTS more CDC descriptors that
1003 * could legitimately be found here.
1004 */
1005 dev_dbg(&intf->dev, "Ignoring descriptor: "
1006 "type %02x, length %d\n",
1007 buffer[2], buffer[0]);
1008 break;
1009 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010next_desc:
1011 buflen -= buffer[0];
1012 buffer += buffer[0];
1013 }
1014
1015 if (!union_header) {
1016 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001017 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
1019 control_interface = intf;
1020 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001021 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1022 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1023 return -ENODEV;
1024 } else {
1025 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1026 combined_interfaces = 1;
1027 control_interface = data_interface = intf;
1028 goto look_for_collapsed_interface;
1029 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 }
1031 } else {
1032 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1033 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1034 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001035 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 return -ENODEV;
1037 }
1038 }
Alan Cox6e47e062009-06-11 12:37:06 +01001039
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001041 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001043 if (control_interface == data_interface) {
1044 /* some broken devices designed for windows work this way */
1045 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1046 combined_interfaces = 1;
1047 /* a popular other OS doesn't use it */
1048 quirks |= NO_CAP_LINE;
1049 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1050 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1051 return -EINVAL;
1052 }
1053look_for_collapsed_interface:
1054 for (i = 0; i < 3; i++) {
1055 struct usb_endpoint_descriptor *ep;
1056 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1057
1058 if (usb_endpoint_is_int_in(ep))
1059 epctrl = ep;
1060 else if (usb_endpoint_is_bulk_out(ep))
1061 epwrite = ep;
1062 else if (usb_endpoint_is_bulk_in(ep))
1063 epread = ep;
1064 else
1065 return -EINVAL;
1066 }
1067 if (!epctrl || !epread || !epwrite)
1068 return -ENODEV;
1069 else
1070 goto made_compressed_probe;
1071 }
1072
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073skip_normal_probe:
1074
1075 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001076 if (data_interface->cur_altsetting->desc.bInterfaceClass
1077 != CDC_DATA_INTERFACE_TYPE) {
1078 if (control_interface->cur_altsetting->desc.bInterfaceClass
1079 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001081 dev_dbg(&intf->dev,
1082 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 t = control_interface;
1084 control_interface = data_interface;
1085 data_interface = t;
1086 } else {
1087 return -EINVAL;
1088 }
1089 }
Alan Stern74da5d62007-08-02 13:29:10 -04001090
1091 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001092 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001093 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001094
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001095 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1096 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001097 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 return -EBUSY;
1099 }
1100
1101
1102 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
1103 return -EINVAL;
1104
1105 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1106 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1107 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1108
1109
1110 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001111 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 /* descriptors are swapped */
1113 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001114 dev_dbg(&intf->dev,
1115 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 t = epread;
1117 epread = epwrite;
1118 epwrite = t;
1119 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001120made_compressed_probe:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001121 dev_dbg(&intf->dev, "interfaces are valid\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
1123
1124 if (minor == ACM_TTY_MINORS) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001125 dev_err(&intf->dev, "no more free acm devices\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 return -ENODEV;
1127 }
1128
Alan Cox6e47e062009-06-11 12:37:06 +01001129 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1130 if (acm == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001131 dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 goto alloc_fail;
1133 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134
1135 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Alan Cox6e47e062009-06-11 12:37:06 +01001136 readsize = le16_to_cpu(epread->wMaxPacketSize) *
1137 (quirks == SINGLE_RX_URB ? 1 : 2);
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001138 acm->combined_interfaces = combined_interfaces;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001139 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 acm->control = control_interface;
1141 acm->data = data_interface;
1142 acm->minor = minor;
1143 acm->dev = usb_dev;
1144 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001145 if (quirks & NO_CAP_LINE)
1146 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 acm->ctrlsize = ctrlsize;
1148 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001149 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001150 acm->urb_task.func = acm_rx_tasklet;
1151 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +00001152 INIT_WORK(&acm->work, acm_softint);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +02001154 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001155 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001156 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001157 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001158 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1159 if (acm->is_int_ep)
1160 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001161 tty_port_init(&acm->port);
1162 acm->port.ops = &acm_port_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163
Daniel Mack997ea582010-04-12 13:17:25 +02001164 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 if (!buf) {
Johan Hovold255ab562011-03-22 11:12:13 +01001166 dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 goto alloc_fail2;
1168 }
1169 acm->ctrl_buffer = buf;
1170
Oliver Neukum884b6002005-04-21 21:28:02 +02001171 if (acm_write_buffers_alloc(acm) < 0) {
Johan Hovold255ab562011-03-22 11:12:13 +01001172 dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 goto alloc_fail4;
1174 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175
1176 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1177 if (!acm->ctrlurb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001178 dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 goto alloc_fail5;
1180 }
Oliver Neukum86478942006-05-13 22:50:47 +02001181 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001182 struct acm_ru *rcv = &(acm->ru[i]);
1183
Alan Cox6e47e062009-06-11 12:37:06 +01001184 rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
1185 if (rcv->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001186 dev_err(&intf->dev,
Alan Cox6e47e062009-06-11 12:37:06 +01001187 "out of memory (read urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001188 goto alloc_fail6;
David Kubicek61a87ad2005-11-01 18:51:34 +01001189 }
1190
1191 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1192 rcv->instance = acm;
1193 }
Oliver Neukum86478942006-05-13 22:50:47 +02001194 for (i = 0; i < num_rx_buf; i++) {
David Brownell672c4e12008-08-06 18:41:12 -07001195 struct acm_rb *rb = &(acm->rb[i]);
David Kubicek61a87ad2005-11-01 18:51:34 +01001196
Daniel Mack997ea582010-04-12 13:17:25 +02001197 rb->base = usb_alloc_coherent(acm->dev, readsize,
David Brownell672c4e12008-08-06 18:41:12 -07001198 GFP_KERNEL, &rb->dma);
1199 if (!rb->base) {
Johan Hovold255ab562011-03-22 11:12:13 +01001200 dev_err(&intf->dev,
Daniel Mack997ea582010-04-12 13:17:25 +02001201 "out of memory (read bufs usb_alloc_coherent)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001202 goto alloc_fail7;
1203 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 }
Alan Cox6e47e062009-06-11 12:37:06 +01001205 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001206 struct acm_wb *snd = &(acm->wb[i]);
1207
Alan Cox6e47e062009-06-11 12:37:06 +01001208 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1209 if (snd->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001210 dev_err(&intf->dev,
Johan Hovold59d7fec2011-03-22 11:12:12 +01001211 "out of memory (write urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001212 goto alloc_fail8;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001213 }
1214
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001215 if (usb_endpoint_xfer_int(epwrite))
1216 usb_fill_int_urb(snd->urb, usb_dev,
1217 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1218 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1219 else
1220 usb_fill_bulk_urb(snd->urb, usb_dev,
1221 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1222 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001223 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1224 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 }
1226
Alan Cox6e47e062009-06-11 12:37:06 +01001227 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001228
1229 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1230 if (i < 0)
1231 goto alloc_fail8;
1232
1233 if (cfd) { /* export the country data */
1234 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1235 if (!acm->country_codes)
1236 goto skip_countries;
1237 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001238 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1239 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001240 acm->country_rel_date = cfd->iCountryCodeRelDate;
1241
1242 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1243 if (i < 0) {
1244 kfree(acm->country_codes);
1245 goto skip_countries;
1246 }
1247
Alan Cox6e47e062009-06-11 12:37:06 +01001248 i = device_create_file(&intf->dev,
1249 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001250 if (i < 0) {
Axel Linc2572b72010-05-31 08:04:47 +08001251 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001252 kfree(acm->country_codes);
1253 goto skip_countries;
1254 }
1255 }
1256
1257skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001258 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001259 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1260 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1261 /* works around buggy devices */
1262 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1264 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1265
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1267
1268 acm_set_control(acm, acm->ctrlout);
1269
1270 acm->line.dwDTERate = cpu_to_le32(9600);
1271 acm->line.bDataBits = 8;
1272 acm_set_line(acm, &acm->line);
1273
1274 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001275 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001277 usb_get_intf(control_interface);
1278 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279
1280 acm_table[minor] = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001282 return 0;
1283alloc_fail8:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001284 for (i = 0; i < ACM_NW; i++)
1285 usb_free_urb(acm->wb[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286alloc_fail7:
Oliver Neukum830f4022008-06-25 14:17:16 +02001287 acm_read_buffers_free(acm);
Axel Linc2572b72010-05-31 08:04:47 +08001288alloc_fail6:
Oliver Neukum86478942006-05-13 22:50:47 +02001289 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001290 usb_free_urb(acm->ru[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 usb_free_urb(acm->ctrlurb);
1292alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001293 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294alloc_fail4:
Daniel Mack997ea582010-04-12 13:17:25 +02001295 usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296alloc_fail2:
1297 kfree(acm);
1298alloc_fail:
1299 return -ENOMEM;
1300}
1301
Oliver Neukum1365baf2007-10-12 17:24:28 +02001302static void stop_data_traffic(struct acm *acm)
1303{
1304 int i;
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001305
1306 dev_dbg(&acm->control->dev, "%s\n", __func__);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001307
1308 tasklet_disable(&acm->urb_task);
1309
1310 usb_kill_urb(acm->ctrlurb);
Alan Cox6e47e062009-06-11 12:37:06 +01001311 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001312 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001313 for (i = 0; i < acm->rx_buflimit; i++)
1314 usb_kill_urb(acm->ru[i].urb);
1315
Oliver Neukum1365baf2007-10-12 17:24:28 +02001316 tasklet_enable(&acm->urb_task);
1317
1318 cancel_work_sync(&acm->work);
1319}
1320
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321static void acm_disconnect(struct usb_interface *intf)
1322{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001323 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001325 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326
David Brownell672c4e12008-08-06 18:41:12 -07001327 /* sibling interface is already cleaning up */
1328 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001329 return;
David Brownell672c4e12008-08-06 18:41:12 -07001330
1331 mutex_lock(&open_mutex);
Alan Cox6e47e062009-06-11 12:37:06 +01001332 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001333 device_remove_file(&acm->control->dev,
1334 &dev_attr_wCountryCodes);
1335 device_remove_file(&acm->control->dev,
1336 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001337 }
Alan Stern74da5d62007-08-02 13:29:10 -04001338 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 acm->dev = NULL;
Oliver Neukum86067eea2006-01-08 12:39:13 +01001340 usb_set_intfdata(acm->control, NULL);
1341 usb_set_intfdata(acm->data, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342
Oliver Neukum1365baf2007-10-12 17:24:28 +02001343 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
Oliver Neukum884b6002005-04-21 21:28:02 +02001345 acm_write_buffers_free(acm);
Daniel Mack997ea582010-04-12 13:17:25 +02001346 usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
1347 acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001348 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001350 if (!acm->combined_interfaces)
1351 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001352 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353
Alan Cox10077d42009-06-11 12:36:09 +01001354 if (acm->port.count == 0) {
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001355 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001356 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 return;
1358 }
1359
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001360 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001361 tty = tty_port_tty_get(&acm->port);
1362 if (tty) {
1363 tty_hangup(tty);
1364 tty_kref_put(tty);
1365 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366}
1367
Oliver Neukum35758582008-07-01 19:10:08 +02001368#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001369static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1370{
1371 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001372 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001373
Alan Stern65bfd292008-11-25 16:39:18 -05001374 if (message.event & PM_EVENT_AUTO) {
Oliver Neukum11ea8592008-06-20 11:25:57 +02001375 int b;
1376
1377 spin_lock_irq(&acm->read_lock);
1378 spin_lock(&acm->write_lock);
1379 b = acm->processing + acm->transmitting;
1380 spin_unlock(&acm->write_lock);
1381 spin_unlock_irq(&acm->read_lock);
1382 if (b)
1383 return -EBUSY;
1384 }
1385
1386 spin_lock_irq(&acm->read_lock);
1387 spin_lock(&acm->write_lock);
1388 cnt = acm->susp_count++;
1389 spin_unlock(&acm->write_lock);
1390 spin_unlock_irq(&acm->read_lock);
1391
1392 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001393 return 0;
1394 /*
1395 we treat opened interfaces differently,
1396 we must guard against open
1397 */
1398 mutex_lock(&acm->mutex);
1399
Alan Cox10077d42009-06-11 12:36:09 +01001400 if (acm->port.count)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001401 stop_data_traffic(acm);
1402
1403 mutex_unlock(&acm->mutex);
1404 return 0;
1405}
1406
1407static int acm_resume(struct usb_interface *intf)
1408{
1409 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001410 struct acm_wb *wb;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001411 int rv = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001412 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001413
Oliver Neukum11ea8592008-06-20 11:25:57 +02001414 spin_lock_irq(&acm->read_lock);
1415 acm->susp_count -= 1;
1416 cnt = acm->susp_count;
1417 spin_unlock_irq(&acm->read_lock);
1418
1419 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001420 return 0;
1421
1422 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001423 if (acm->port.count) {
Oliver Neukum1365baf2007-10-12 17:24:28 +02001424 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001425
1426 spin_lock_irq(&acm->write_lock);
1427 if (acm->delayed_wb) {
1428 wb = acm->delayed_wb;
1429 acm->delayed_wb = NULL;
1430 spin_unlock_irq(&acm->write_lock);
Oliver Neukumf0730922010-03-03 00:37:56 +01001431 acm_start_wb(acm, wb);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001432 } else {
1433 spin_unlock_irq(&acm->write_lock);
1434 }
1435
1436 /*
1437 * delayed error checking because we must
1438 * do the write path at all cost
1439 */
Oliver Neukum1365baf2007-10-12 17:24:28 +02001440 if (rv < 0)
Oliver Neukum11ea8592008-06-20 11:25:57 +02001441 goto err_out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001442
1443 tasklet_schedule(&acm->urb_task);
1444 }
1445
1446err_out:
1447 mutex_unlock(&acm->mutex);
1448 return rv;
1449}
Oliver Neukum35758582008-07-01 19:10:08 +02001450
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001451static int acm_reset_resume(struct usb_interface *intf)
1452{
1453 struct acm *acm = usb_get_intfdata(intf);
1454 struct tty_struct *tty;
1455
1456 mutex_lock(&acm->mutex);
1457 if (acm->port.count) {
1458 tty = tty_port_tty_get(&acm->port);
1459 if (tty) {
1460 tty_hangup(tty);
1461 tty_kref_put(tty);
1462 }
1463 }
1464 mutex_unlock(&acm->mutex);
1465 return acm_resume(intf);
1466}
1467
Oliver Neukum35758582008-07-01 19:10:08 +02001468#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001469
1470#define NOKIA_PCSUITE_ACM_INFO(x) \
1471 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1472 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1473 USB_CDC_ACM_PROTO_VENDOR)
1474
Toby Gray4035e452010-09-01 16:01:19 +01001475#define SAMSUNG_PCSUITE_ACM_INFO(x) \
1476 USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
1477 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1478 USB_CDC_ACM_PROTO_VENDOR)
1479
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480/*
1481 * USB driver structure.
1482 */
1483
Németh Márton6ef48522010-01-10 15:33:45 +01001484static const struct usb_device_id acm_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 /* quirky and broken devices */
1486 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1487 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1488 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001489 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1490 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1491 },
Andrew Lunn0f9c7b42008-12-23 17:31:23 +01001492 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1493 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1494 },
Masahito Omote8753e652005-07-29 12:17:25 -07001495 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1496 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1497 },
Chris Malley91a9c922006-10-03 10:08:28 +01001498 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1499 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1500 },
Alan Cox7abcf202009-04-06 17:35:01 +01001501 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1502 .driver_info = SINGLE_RX_URB,
1503 },
Oliver Neukum86478942006-05-13 22:50:47 +02001504 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1505 .driver_info = SINGLE_RX_URB, /* firmware bug */
1506 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001507 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1508 .driver_info = SINGLE_RX_URB, /* firmware bug */
1509 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001510 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1511 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1512 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001513 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1514 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1515 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001516 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1517 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1518 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001519 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1520 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1521 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001522 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1523 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1524 },
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001525 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1526 },
Adam Richterc332b4e2009-02-18 16:17:15 -08001527 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1528 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1529 data interface instead of
1530 communications interface.
1531 Maybe we should define a new
1532 quirk for this. */
1533 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001534 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1535 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1536 },
Russ Nelsonc3baa192010-04-21 23:07:03 -04001537 { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1538 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1539 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001540
Adrian Taylorc1479a92009-11-19 10:35:33 +00001541 /* Nokia S60 phones expose two ACM channels. The first is
1542 * a modem and is picked up by the standard AT-command
1543 * information below. The second is 'vendor-specific' but
1544 * is treated as a serial device at the S60 end, so we want
1545 * to expose it on Linux too. */
1546 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1547 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1548 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1549 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1550 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1551 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1552 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1553 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1554 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1555 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1556 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1557 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1558 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1559 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1560 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1561 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1562 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1563 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1564 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1565 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1566 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1567 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1568 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1569 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1570 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1571 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1572 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1573 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1574 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1575 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1576 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1577 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1578 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1579 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1580 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1581 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1582 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1583 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1584 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1585 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1586 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1587 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1588 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
Przemo Firszt83a4eae2010-06-28 21:29:34 +01001589 { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
Toby Gray4035e452010-09-01 16:01:19 +01001590 { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
1591 { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
1592 { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
1593 { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
1594 { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
1595 { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
1596 { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
1597 { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
1598 { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
1599 { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
Arvid Ephraim Picciani721d92f2011-01-25 15:58:40 +01001600 { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
Toby Gray4035e452010-09-01 16:01:19 +01001601 { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001602
1603 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1604
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001605 /* Support Lego NXT using pbLua firmware */
Julian Calabyce126642010-01-05 23:58:20 +11001606 { USB_DEVICE(0x0694, 0xff00),
1607 .driver_info = NOT_A_MODEM,
Otavio Salvador7893afc2010-09-26 23:35:05 -03001608 },
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001609
Philippe Corbes5b239f02010-08-31 19:31:32 +02001610 /* control interfaces without any protocol set */
1611 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1612 USB_CDC_PROTO_NONE) },
1613
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 /* control interfaces with various AT-command sets */
1615 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1616 USB_CDC_ACM_PROTO_AT_V25TER) },
1617 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1618 USB_CDC_ACM_PROTO_AT_PCCA101) },
1619 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1620 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1621 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1622 USB_CDC_ACM_PROTO_AT_GSM) },
1623 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001624 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1626 USB_CDC_ACM_PROTO_AT_CDMA) },
1627
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 { }
1629};
1630
Alan Cox6e47e062009-06-11 12:37:06 +01001631MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632
1633static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 .name = "cdc_acm",
1635 .probe = acm_probe,
1636 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001637#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001638 .suspend = acm_suspend,
1639 .resume = acm_resume,
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001640 .reset_resume = acm_reset_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001641#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001643#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001644 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001645#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646};
1647
1648/*
1649 * TTY driver structures.
1650 */
1651
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001652static const struct tty_operations acm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 .open = acm_tty_open,
1654 .close = acm_tty_close,
Alan Cox10077d42009-06-11 12:36:09 +01001655 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656 .write = acm_tty_write,
1657 .write_room = acm_tty_write_room,
1658 .ioctl = acm_tty_ioctl,
1659 .throttle = acm_tty_throttle,
1660 .unthrottle = acm_tty_unthrottle,
1661 .chars_in_buffer = acm_tty_chars_in_buffer,
1662 .break_ctl = acm_tty_break_ctl,
1663 .set_termios = acm_tty_set_termios,
1664 .tiocmget = acm_tty_tiocmget,
1665 .tiocmset = acm_tty_tiocmset,
1666};
1667
1668/*
1669 * Init / exit.
1670 */
1671
1672static int __init acm_init(void)
1673{
1674 int retval;
1675 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1676 if (!acm_tty_driver)
1677 return -ENOMEM;
1678 acm_tty_driver->owner = THIS_MODULE,
1679 acm_tty_driver->driver_name = "acm",
1680 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 acm_tty_driver->major = ACM_TTY_MAJOR,
1682 acm_tty_driver->minor_start = 0,
1683 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1684 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001685 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001687 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1688 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689 tty_set_operations(acm_tty_driver, &acm_ops);
1690
1691 retval = tty_register_driver(acm_tty_driver);
1692 if (retval) {
1693 put_tty_driver(acm_tty_driver);
1694 return retval;
1695 }
1696
1697 retval = usb_register(&acm_driver);
1698 if (retval) {
1699 tty_unregister_driver(acm_tty_driver);
1700 put_tty_driver(acm_tty_driver);
1701 return retval;
1702 }
1703
Johan Hovolda2c7b932011-03-22 11:12:18 +01001704 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705
1706 return 0;
1707}
1708
1709static void __exit acm_exit(void)
1710{
1711 usb_deregister(&acm_driver);
1712 tty_unregister_driver(acm_tty_driver);
1713 put_tty_driver(acm_tty_driver);
1714}
1715
1716module_init(acm_init);
1717module_exit(acm_exit);
1718
Alan Cox6e47e062009-06-11 12:37:06 +01001719MODULE_AUTHOR(DRIVER_AUTHOR);
1720MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01001722MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);