blob: b84ccec8fc7b8775689639e28a7a76d81a662e07 [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
53#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */
54
Johan Hovolda2c7b932011-03-22 11:12:18 +010055
David Kubicek61a87ad2005-11-01 18:51:34 +010056#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
Linus Torvalds1da177e2005-04-16 15:20:36 -070057#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
58
59static struct usb_driver acm_driver;
60static struct tty_driver *acm_tty_driver;
61static struct acm *acm_table[ACM_TTY_MINORS];
62
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010063static DEFINE_MUTEX(open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
Alan Cox10077d42009-06-11 12:36:09 +010065#define ACM_READY(acm) (acm && acm->dev && acm->port.count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Alan Cox739e0282009-06-11 12:27:50 +010067static const struct tty_port_operations acm_port_ops = {
68};
69
Linus Torvalds1da177e2005-04-16 15:20:36 -070070/*
71 * Functions for ACM control messages.
72 */
73
Alan Cox6e47e062009-06-11 12:37:06 +010074static int acm_ctrl_msg(struct acm *acm, int request, int value,
75 void *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -070076{
77 int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
78 request, USB_RT_ACM, value,
79 acm->control->altsetting[0].desc.bInterfaceNumber,
80 buf, len, 5000);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +010081 dev_dbg(&acm->control->dev,
82 "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
83 __func__, request, value, len, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 return retval < 0 ? retval : 0;
85}
86
87/* devices aren't required to support these requests.
88 * the cdc acm descriptor tells whether they do...
89 */
90#define acm_set_control(acm, control) \
91 acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
92#define acm_set_line(acm, line) \
93 acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
94#define acm_send_break(acm, ms) \
95 acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
96
97/*
Oliver Neukum884b6002005-04-21 21:28:02 +020098 * Write buffer management.
99 * All of these assume proper locks taken by the caller.
100 */
101
102static int acm_wb_alloc(struct acm *acm)
103{
104 int i, wbn;
105 struct acm_wb *wb;
106
David Engrafe4cf3aa2008-03-20 10:01:34 +0100107 wbn = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200108 i = 0;
109 for (;;) {
110 wb = &acm->wb[wbn];
111 if (!wb->use) {
112 wb->use = 1;
113 return wbn;
114 }
Oliver Neukum86478942006-05-13 22:50:47 +0200115 wbn = (wbn + 1) % ACM_NW;
116 if (++i >= ACM_NW)
Oliver Neukum884b6002005-04-21 21:28:02 +0200117 return -1;
118 }
119}
120
Oliver Neukum884b6002005-04-21 21:28:02 +0200121static int acm_wb_is_avail(struct acm *acm)
122{
123 int i, n;
David Brownelle5fbab52008-08-06 18:46:10 -0700124 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200125
Oliver Neukum86478942006-05-13 22:50:47 +0200126 n = ACM_NW;
David Brownelle5fbab52008-08-06 18:46:10 -0700127 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100128 for (i = 0; i < ACM_NW; i++)
Oliver Neukum86478942006-05-13 22:50:47 +0200129 n -= acm->wb[i].use;
David Brownelle5fbab52008-08-06 18:46:10 -0700130 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200131 return n;
132}
133
Oliver Neukum884b6002005-04-21 21:28:02 +0200134/*
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800135 * Finish write. Caller must hold acm->write_lock
Oliver Neukum884b6002005-04-21 21:28:02 +0200136 */
David Engrafe4cf3aa2008-03-20 10:01:34 +0100137static void acm_write_done(struct acm *acm, struct acm_wb *wb)
Oliver Neukum884b6002005-04-21 21:28:02 +0200138{
David Engrafe4cf3aa2008-03-20 10:01:34 +0100139 wb->use = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200140 acm->transmitting--;
Oliver Neukum97d35f92009-12-16 17:05:57 +0100141 usb_autopm_put_interface_async(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200142}
143
144/*
145 * Poke write.
Oliver Neukum11ea8592008-06-20 11:25:57 +0200146 *
147 * the caller is responsible for locking
Oliver Neukum884b6002005-04-21 21:28:02 +0200148 */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200149
150static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
151{
152 int rc;
153
154 acm->transmitting++;
155
156 wb->urb->transfer_buffer = wb->buf;
157 wb->urb->transfer_dma = wb->dmah;
158 wb->urb->transfer_buffer_length = wb->len;
159 wb->urb->dev = acm->dev;
160
Alan Cox6e47e062009-06-11 12:37:06 +0100161 rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
162 if (rc < 0) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100163 dev_err(&acm->data->dev,
164 "%s - usb_submit_urb(write bulk) failed: %d\n",
165 __func__, rc);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200166 acm_write_done(acm, wb);
167 }
168 return rc;
169}
170
David Engrafe4cf3aa2008-03-20 10:01:34 +0100171static int acm_write_start(struct acm *acm, int wbn)
Oliver Neukum884b6002005-04-21 21:28:02 +0200172{
173 unsigned long flags;
David Brownell934da462008-08-06 18:44:12 -0700174 struct acm_wb *wb = &acm->wb[wbn];
Oliver Neukum884b6002005-04-21 21:28:02 +0200175 int rc;
176
177 spin_lock_irqsave(&acm->write_lock, flags);
178 if (!acm->dev) {
David Brownell934da462008-08-06 18:44:12 -0700179 wb->use = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200180 spin_unlock_irqrestore(&acm->write_lock, flags);
181 return -ENODEV;
182 }
183
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100184 dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100185 acm->susp_count);
Oliver Neukum97d35f92009-12-16 17:05:57 +0100186 usb_autopm_get_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200187 if (acm->susp_count) {
Oliver Neukum97d35f92009-12-16 17:05:57 +0100188 if (!acm->delayed_wb)
189 acm->delayed_wb = wb;
190 else
191 usb_autopm_put_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200192 spin_unlock_irqrestore(&acm->write_lock, flags);
193 return 0; /* A white lie */
194 }
195 usb_mark_last_busy(acm->dev);
196
Oliver Neukum11ea8592008-06-20 11:25:57 +0200197 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200198 spin_unlock_irqrestore(&acm->write_lock, flags);
199
Oliver Neukum884b6002005-04-21 21:28:02 +0200200 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200201
Oliver Neukum884b6002005-04-21 21:28:02 +0200202}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100203/*
204 * attributes exported through sysfs
205 */
206static ssize_t show_caps
207(struct device *dev, struct device_attribute *attr, char *buf)
208{
209 struct usb_interface *intf = to_usb_interface(dev);
210 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200211
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100212 return sprintf(buf, "%d", acm->ctrl_caps);
213}
214static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
215
216static ssize_t show_country_codes
217(struct device *dev, struct device_attribute *attr, char *buf)
218{
219 struct usb_interface *intf = to_usb_interface(dev);
220 struct acm *acm = usb_get_intfdata(intf);
221
222 memcpy(buf, acm->country_codes, acm->country_code_size);
223 return acm->country_code_size;
224}
225
226static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
227
228static ssize_t show_country_rel_date
229(struct device *dev, struct device_attribute *attr, char *buf)
230{
231 struct usb_interface *intf = to_usb_interface(dev);
232 struct acm *acm = usb_get_intfdata(intf);
233
234 return sprintf(buf, "%d", acm->country_rel_date);
235}
236
237static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200238/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 * Interrupt handlers for various ACM device responses
240 */
241
242/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100243static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244{
245 struct acm *acm = urb->context;
246 struct usb_cdc_notification *dr = urb->transfer_buffer;
Alan Cox10077d42009-06-11 12:36:09 +0100247 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 unsigned char *data;
249 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700250 int retval;
251 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700253 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 case 0:
255 /* success */
256 break;
257 case -ECONNRESET:
258 case -ENOENT:
259 case -ESHUTDOWN:
260 /* this urb is terminated, clean up */
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100261 dev_dbg(&acm->control->dev,
262 "%s - urb shutting down with status: %d\n",
263 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 return;
265 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100266 dev_dbg(&acm->control->dev,
267 "%s - nonzero urb status received: %d\n",
268 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 goto exit;
270 }
271
272 if (!ACM_READY(acm))
273 goto exit;
274
Johan Hovold7e7797e2011-03-22 11:12:11 +0100275 usb_mark_last_busy(acm->dev);
276
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 data = (unsigned char *)(dr + 1);
278 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100279 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100280 dev_dbg(&acm->control->dev, "%s - network connection: %d\n",
281 __func__, dr->wValue);
Alan Cox6e47e062009-06-11 12:37:06 +0100282 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283
Alan Cox6e47e062009-06-11 12:37:06 +0100284 case USB_CDC_NOTIFY_SERIAL_STATE:
285 tty = tty_port_tty_get(&acm->port);
286 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287
Alan Cox6e47e062009-06-11 12:37:06 +0100288 if (tty) {
289 if (!acm->clocal &&
290 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100291 dev_dbg(&acm->control->dev,
292 "%s - calling hangup\n", __func__);
Alan Cox6e47e062009-06-11 12:37:06 +0100293 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 }
Alan Cox6e47e062009-06-11 12:37:06 +0100295 tty_kref_put(tty);
296 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297
Alan Cox6e47e062009-06-11 12:37:06 +0100298 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100300 dev_dbg(&acm->control->dev,
301 "%s - input control lines: dcd%c dsr%c break%c "
302 "ring%c framing%c parity%c overrun%c\n",
303 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100304 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
305 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
306 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
307 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
308 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
309 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
310 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 break;
312
Alan Cox6e47e062009-06-11 12:37:06 +0100313 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100314 dev_dbg(&acm->control->dev,
315 "%s - unknown notification %d received: index %d "
316 "len %d data0 %d data1 %d\n",
317 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100318 dr->bNotificationType, dr->wIndex,
319 dr->wLength, data[0], data[1]);
320 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 }
322exit:
Alan Cox6e47e062009-06-11 12:37:06 +0100323 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700324 if (retval)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100325 dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
326 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327}
328
329/* data interface returns incoming bytes, or we got unthrottled */
David Howells7d12e782006-10-05 14:55:46 +0100330static void acm_read_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331{
David Kubicek61a87ad2005-11-01 18:51:34 +0100332 struct acm_rb *buf;
333 struct acm_ru *rcv = urb->context;
334 struct acm *acm = rcv->instance;
Oliver Neukum86478942006-05-13 22:50:47 +0200335 int status = urb->status;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700336
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100337 dev_vdbg(&acm->data->dev, "%s - status %d\n", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
Oliver Neukum11ea8592008-06-20 11:25:57 +0200339 if (!ACM_READY(acm)) {
Johan Hovold1d9846e2011-03-22 11:12:14 +0100340 dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200342 }
343 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344
Oliver Neukum86478942006-05-13 22:50:47 +0200345 if (status)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100346 dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
347 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
David Kubicek61a87ad2005-11-01 18:51:34 +0100349 buf = rcv->buffer;
350 buf->size = urb->actual_length;
351
Oliver Neukum86478942006-05-13 22:50:47 +0200352 if (likely(status == 0)) {
353 spin_lock(&acm->read_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200354 acm->processing++;
Oliver Neukum86478942006-05-13 22:50:47 +0200355 list_add_tail(&rcv->list, &acm->spare_read_urbs);
356 list_add_tail(&buf->list, &acm->filled_read_bufs);
357 spin_unlock(&acm->read_lock);
358 } else {
359 /* we drop the buffer due to an error */
360 spin_lock(&acm->read_lock);
361 list_add_tail(&rcv->list, &acm->spare_read_urbs);
362 list_add(&buf->list, &acm->spare_read_bufs);
363 spin_unlock(&acm->read_lock);
364 /* nevertheless the tasklet must be kicked unconditionally
365 so the queue cannot dry up */
366 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200367 if (likely(!acm->susp_count))
368 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369}
370
371static void acm_rx_tasklet(unsigned long _acm)
372{
373 struct acm *acm = (void *)_acm;
David Kubicek61a87ad2005-11-01 18:51:34 +0100374 struct acm_rb *buf;
Alan Cox10077d42009-06-11 12:36:09 +0100375 struct tty_struct *tty;
David Kubicek61a87ad2005-11-01 18:51:34 +0100376 struct acm_ru *rcv;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200377 unsigned long flags;
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100378 unsigned char throttled;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200379
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100380 dev_vdbg(&acm->data->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381
Alan Cox10077d42009-06-11 12:36:09 +0100382 if (!ACM_READY(acm)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100383 dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100384 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200385 }
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100386
Oliver Neukum834dbca2007-03-06 10:47:04 +0100387 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100388 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100389 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Alan Cox10077d42009-06-11 12:36:09 +0100390 if (throttled) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100391 dev_dbg(&acm->data->dev, "%s - throttled\n", __func__);
David Kubicek61a87ad2005-11-01 18:51:34 +0100392 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200393 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100394
Alan Cox10077d42009-06-11 12:36:09 +0100395 tty = tty_port_tty_get(&acm->port);
396
David Kubicek61a87ad2005-11-01 18:51:34 +0100397next_buffer:
Jarek Poplawski762f0072006-10-06 07:23:11 +0200398 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100399 if (list_empty(&acm->filled_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200400 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100401 goto urbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100403 buf = list_entry(acm->filled_read_bufs.next,
404 struct acm_rb, list);
405 list_del(&buf->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200406 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100407
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100408 dev_vdbg(&acm->data->dev, "%s - processing buf 0x%p, size = %d\n",
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100409 __func__, buf, buf->size);
Alan Cox10077d42009-06-11 12:36:09 +0100410 if (tty) {
411 spin_lock_irqsave(&acm->throttle_lock, flags);
412 throttled = acm->throttle;
413 spin_unlock_irqrestore(&acm->throttle_lock, flags);
414 if (!throttled) {
Alan Cox10077d42009-06-11 12:36:09 +0100415 tty_insert_flip_string(tty, buf->base, buf->size);
416 tty_flip_buffer_push(tty);
417 } else {
418 tty_kref_put(tty);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100419 dev_dbg(&acm->data->dev, "%s - throttling noticed\n",
420 __func__);
Alan Cox10077d42009-06-11 12:36:09 +0100421 spin_lock_irqsave(&acm->read_lock, flags);
422 list_add(&buf->list, &acm->filled_read_bufs);
423 spin_unlock_irqrestore(&acm->read_lock, flags);
424 return;
425 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
Jarek Poplawski762f0072006-10-06 07:23:11 +0200428 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100429 list_add(&buf->list, &acm->spare_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200430 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100431 goto next_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432
David Kubicek61a87ad2005-11-01 18:51:34 +0100433urbs:
Alan Cox10077d42009-06-11 12:36:09 +0100434 tty_kref_put(tty);
435
David Kubicek61a87ad2005-11-01 18:51:34 +0100436 while (!list_empty(&acm->spare_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200437 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100438 if (list_empty(&acm->spare_read_urbs)) {
Oliver Neukum11ea8592008-06-20 11:25:57 +0200439 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200440 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100441 return;
442 }
443 rcv = list_entry(acm->spare_read_urbs.next,
444 struct acm_ru, list);
445 list_del(&rcv->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200446 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100447
448 buf = list_entry(acm->spare_read_bufs.next,
449 struct acm_rb, list);
450 list_del(&buf->list);
451
452 rcv->buffer = buf;
453
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200454 if (acm->is_int_ep)
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400455 usb_fill_int_urb(rcv->urb, acm->dev,
456 acm->rx_endpoint,
457 buf->base,
458 acm->readsize,
Oliver Neukumcf7fdd52009-08-04 23:52:09 +0200459 acm_read_bulk, rcv, acm->bInterval);
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +0400460 else
461 usb_fill_bulk_urb(rcv->urb, acm->dev,
462 acm->rx_endpoint,
463 buf->base,
464 acm->readsize,
465 acm_read_bulk, rcv);
David Kubicek61a87ad2005-11-01 18:51:34 +0100466 rcv->urb->transfer_dma = buf->dma;
467 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
468
Alan Cox6e47e062009-06-11 12:37:06 +0100469 /* This shouldn't kill the driver as unsuccessful URBs are
470 returned to the free-urbs-pool and resubmited ASAP */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200471 spin_lock_irqsave(&acm->read_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100472 if (acm->susp_count ||
473 usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100474 list_add(&buf->list, &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100475 list_add(&rcv->list, &acm->spare_read_urbs);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200476 acm->processing = 0;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200477 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100478 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200479 } else {
480 spin_unlock_irqrestore(&acm->read_lock, flags);
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100481 dev_vdbg(&acm->data->dev,
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100482 "%s - sending urb 0x%p, rcv 0x%p, buf 0x%p\n",
483 __func__, rcv->urb, rcv, buf);
David Kubicek61a87ad2005-11-01 18:51:34 +0100484 }
485 }
Oliver Neukum11ea8592008-06-20 11:25:57 +0200486 spin_lock_irqsave(&acm->read_lock, flags);
487 acm->processing = 0;
488 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489}
490
491/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100492static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493{
Ming Leicdc97792008-02-24 18:41:47 +0800494 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700495 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800496 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200497
Johan Hovold4fa46262011-03-22 11:12:16 +0100498 if (urb->status || (urb->actual_length != urb->transfer_buffer_length))
499 dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
Johan Hovold1d9846e2011-03-22 11:12:14 +0100500 __func__,
David Brownelle5fbab52008-08-06 18:46:10 -0700501 urb->actual_length,
502 urb->transfer_buffer_length,
503 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800505 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100506 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800507 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200508 if (ACM_READY(acm))
509 schedule_work(&acm->work);
David Brownelle5fbab52008-08-06 18:46:10 -0700510 else
511 wake_up_interruptible(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512}
513
David Howellsc4028952006-11-22 14:57:56 +0000514static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515{
David Howellsc4028952006-11-22 14:57:56 +0000516 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100517 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700518
Johan Hovold1d9846e2011-03-22 11:12:14 +0100519 dev_vdbg(&acm->data->dev, "%s\n", __func__);
520
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 if (!ACM_READY(acm))
522 return;
Alan Cox10077d42009-06-11 12:36:09 +0100523 tty = tty_port_tty_get(&acm->port);
Johan Hovold15e5bee2011-03-22 11:12:10 +0100524 if (!tty)
525 return;
Alan Cox10077d42009-06-11 12:36:09 +0100526 tty_wakeup(tty);
527 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528}
529
530/*
531 * TTY handlers
532 */
533
534static int acm_tty_open(struct tty_struct *tty, struct file *filp)
535{
536 struct acm *acm;
Thadeu Lima de Souza Cascardo42dd2aa2009-06-25 14:41:24 +0100537 int rv = -ENODEV;
David Kubicek61a87ad2005-11-01 18:51:34 +0100538 int i;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100539
540 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
542 acm = acm_table[tty->index];
543 if (!acm || !acm->dev)
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100544 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 else
546 rv = 0;
547
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100548 dev_dbg(&acm->control->dev, "%s\n", __func__);
549
David Engraf28d1dfa2008-03-20 10:53:52 +0100550 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
Alan Cox10077d42009-06-11 12:36:09 +0100551
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 tty->driver_data = acm;
Alan Cox10077d42009-06-11 12:36:09 +0100553 tty_port_tty_set(&acm->port, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
Oliver Neukum94409cc2008-02-11 15:22:29 +0100555 if (usb_autopm_get_interface(acm->control) < 0)
556 goto early_bail;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200557 else
558 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200559
560 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100561 if (acm->port.count++) {
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100562 mutex_unlock(&acm->mutex);
Oliver Neukum1365baf2007-10-12 17:24:28 +0200563 usb_autopm_put_interface(acm->control);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100564 goto out;
Alan Cox10077d42009-06-11 12:36:09 +0100565 }
Oliver Neukum1365baf2007-10-12 17:24:28 +0200566
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 acm->ctrlurb->dev = acm->dev;
568 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100569 dev_err(&acm->control->dev,
570 "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 goto bail_out;
572 }
573
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100574 if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
575 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 goto full_bailout;
Alan Cox10077d42009-06-11 12:36:09 +0100577
Oliver Neukum11ea8592008-06-20 11:25:57 +0200578 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
David Kubicek61a87ad2005-11-01 18:51:34 +0100580 INIT_LIST_HEAD(&acm->spare_read_urbs);
581 INIT_LIST_HEAD(&acm->spare_read_bufs);
582 INIT_LIST_HEAD(&acm->filled_read_bufs);
Alan Cox6e47e062009-06-11 12:37:06 +0100583
584 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100585 list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
Alan Cox6e47e062009-06-11 12:37:06 +0100586 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100587 list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
David Kubicek61a87ad2005-11-01 18:51:34 +0100588
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100589 acm->throttle = 0;
590
Oliver Neukum7af25b42009-09-08 23:51:28 +0200591 set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
Alan Cox10077d42009-06-11 12:36:09 +0100592 rv = tty_port_block_til_ready(&acm->port, tty, filp);
Henry Gebhardt18a77b52009-11-04 11:19:28 +0100593 tasklet_schedule(&acm->urb_task);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100594
Oliver Neukum1365baf2007-10-12 17:24:28 +0200595 mutex_unlock(&acm->mutex);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100596out:
Oliver Neukum94409cc2008-02-11 15:22:29 +0100597 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 return rv;
599
600full_bailout:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 usb_kill_urb(acm->ctrlurb);
602bail_out:
Alan Cox10077d42009-06-11 12:36:09 +0100603 acm->port.count--;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200604 mutex_unlock(&acm->mutex);
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100605 usb_autopm_put_interface(acm->control);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100606early_bail:
607 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +0100608 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 return -EIO;
610}
611
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700612static void acm_tty_unregister(struct acm *acm)
613{
Alan Cox10077d42009-06-11 12:36:09 +0100614 int i, nr;
David Kubicek61a87ad2005-11-01 18:51:34 +0100615
Oliver Neukum86478942006-05-13 22:50:47 +0200616 nr = acm->rx_buflimit;
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700617 tty_unregister_device(acm_tty_driver, acm->minor);
618 usb_put_intf(acm->control);
619 acm_table[acm->minor] = NULL;
620 usb_free_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100621 for (i = 0; i < ACM_NW; i++)
622 usb_free_urb(acm->wb[i].urb);
Oliver Neukum86478942006-05-13 22:50:47 +0200623 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100624 usb_free_urb(acm->ru[i].urb);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100625 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700626 kfree(acm);
627}
628
Arnd Bergmann4e608672010-06-01 22:53:04 +0200629static void acm_port_down(struct acm *acm)
Alan Cox10077d42009-06-11 12:36:09 +0100630{
631 int i, nr = acm->rx_buflimit;
632 mutex_lock(&open_mutex);
633 if (acm->dev) {
634 usb_autopm_get_interface(acm->control);
635 acm_set_control(acm, acm->ctrlout = 0);
Alan Cox10077d42009-06-11 12:36:09 +0100636 usb_kill_urb(acm->ctrlurb);
637 for (i = 0; i < ACM_NW; i++)
638 usb_kill_urb(acm->wb[i].urb);
Johan Hovold23b80552011-03-22 11:12:09 +0100639 tasklet_disable(&acm->urb_task);
Alan Cox10077d42009-06-11 12:36:09 +0100640 for (i = 0; i < nr; i++)
641 usb_kill_urb(acm->ru[i].urb);
Johan Hovold23b80552011-03-22 11:12:09 +0100642 tasklet_enable(&acm->urb_task);
Alan Cox10077d42009-06-11 12:36:09 +0100643 acm->control->needs_remote_wakeup = 0;
644 usb_autopm_put_interface(acm->control);
645 }
646 mutex_unlock(&open_mutex);
647}
648
649static void acm_tty_hangup(struct tty_struct *tty)
650{
651 struct acm *acm = tty->driver_data;
652 tty_port_hangup(&acm->port);
Arnd Bergmann4e608672010-06-01 22:53:04 +0200653 acm_port_down(acm);
Alan Cox10077d42009-06-11 12:36:09 +0100654}
655
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656static void acm_tty_close(struct tty_struct *tty, struct file *filp)
657{
658 struct acm *acm = tty->driver_data;
659
Alan Cox10077d42009-06-11 12:36:09 +0100660 /* Perform the closing process and see if we need to do the hardware
661 shutdown */
Francesco Lavra051522b2009-11-03 10:53:07 +0000662 if (!acm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 return;
Francesco Lavra051522b2009-11-03 10:53:07 +0000664 if (tty_port_close_start(&acm->port, tty, filp) == 0) {
665 mutex_lock(&open_mutex);
666 if (!acm->dev) {
667 tty_port_tty_set(&acm->port, NULL);
668 acm_tty_unregister(acm);
669 tty->driver_data = NULL;
670 }
671 mutex_unlock(&open_mutex);
672 return;
673 }
Arnd Bergmann4e608672010-06-01 22:53:04 +0200674 acm_port_down(acm);
Alan Cox10077d42009-06-11 12:36:09 +0100675 tty_port_close_end(&acm->port, tty);
Alan Cox10077d42009-06-11 12:36:09 +0100676 tty_port_tty_set(&acm->port, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677}
678
Alan Cox6e47e062009-06-11 12:37:06 +0100679static int acm_tty_write(struct tty_struct *tty,
680 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681{
682 struct acm *acm = tty->driver_data;
683 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200684 unsigned long flags;
685 int wbn;
686 struct acm_wb *wb;
687
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 if (!ACM_READY(acm))
689 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 if (!count)
691 return 0;
692
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100693 dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100694
Oliver Neukum884b6002005-04-21 21:28:02 +0200695 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100696 wbn = acm_wb_alloc(acm);
697 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200698 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200699 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200701 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702
Oliver Neukum884b6002005-04-21 21:28:02 +0200703 count = (count > acm->writesize) ? acm->writesize : count;
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100704 dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
Oliver Neukum884b6002005-04-21 21:28:02 +0200705 memcpy(wb->buf, buf, count);
706 wb->len = count;
707 spin_unlock_irqrestore(&acm->write_lock, flags);
708
Alan Cox6e47e062009-06-11 12:37:06 +0100709 stat = acm_write_start(acm, wbn);
710 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200711 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 return count;
713}
714
715static int acm_tty_write_room(struct tty_struct *tty)
716{
717 struct acm *acm = tty->driver_data;
718 if (!ACM_READY(acm))
719 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200720 /*
721 * Do not let the line discipline to know that we have a reserve,
722 * or it might get too enthusiastic.
723 */
David Brownell934da462008-08-06 18:44:12 -0700724 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725}
726
727static int acm_tty_chars_in_buffer(struct tty_struct *tty)
728{
729 struct acm *acm = tty->driver_data;
730 if (!ACM_READY(acm))
Alan Cox23198fd2009-07-20 16:05:27 +0100731 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200732 /*
733 * This is inaccurate (overcounts), but it works.
734 */
Oliver Neukum86478942006-05-13 22:50:47 +0200735 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736}
737
738static void acm_tty_throttle(struct tty_struct *tty)
739{
740 struct acm *acm = tty->driver_data;
741 if (!ACM_READY(acm))
742 return;
743 spin_lock_bh(&acm->throttle_lock);
744 acm->throttle = 1;
745 spin_unlock_bh(&acm->throttle_lock);
746}
747
748static void acm_tty_unthrottle(struct tty_struct *tty)
749{
750 struct acm *acm = tty->driver_data;
751 if (!ACM_READY(acm))
752 return;
753 spin_lock_bh(&acm->throttle_lock);
754 acm->throttle = 0;
755 spin_unlock_bh(&acm->throttle_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100756 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757}
758
Alan Cox9e989662008-07-22 11:18:03 +0100759static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760{
761 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100762 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 if (!ACM_READY(acm))
Alan Cox9e989662008-07-22 11:18:03 +0100764 return -EINVAL;
765 retval = acm_send_break(acm, state ? 0xffff : 0);
766 if (retval < 0)
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100767 dev_dbg(&acm->control->dev, "%s - send break failed\n",
768 __func__);
Alan Cox9e989662008-07-22 11:18:03 +0100769 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770}
771
Alan Cox60b33c12011-02-14 16:26:14 +0000772static int acm_tty_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773{
774 struct acm *acm = tty->driver_data;
775
776 if (!ACM_READY(acm))
777 return -EINVAL;
778
779 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
780 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
781 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
782 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
783 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
784 TIOCM_CTS;
785}
786
Alan Cox20b9d172011-02-14 16:26:50 +0000787static int acm_tty_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 unsigned int set, unsigned int clear)
789{
790 struct acm *acm = tty->driver_data;
791 unsigned int newctrl;
792
793 if (!ACM_READY(acm))
794 return -EINVAL;
795
796 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100797 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
798 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
799 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
800 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
802 newctrl = (newctrl & ~clear) | set;
803
804 if (acm->ctrlout == newctrl)
805 return 0;
806 return acm_set_control(acm, acm->ctrlout = newctrl);
807}
808
Alan Cox6caa76b2011-02-14 16:27:22 +0000809static int acm_tty_ioctl(struct tty_struct *tty,
Alan Cox6e47e062009-06-11 12:37:06 +0100810 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811{
812 struct acm *acm = tty->driver_data;
813
814 if (!ACM_READY(acm))
815 return -EINVAL;
816
817 return -ENOIOCTLCMD;
818}
819
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100820static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 0, 50, 75, 110, 134, 150, 200, 300, 600,
822 1200, 1800, 2400, 4800, 9600, 19200, 38400,
823 57600, 115200, 230400, 460800, 500000, 576000,
824 921600, 1000000, 1152000, 1500000, 2000000,
825 2500000, 3000000, 3500000, 4000000
826};
827
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100828static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 5, 6, 7, 8
830};
831
Alan Cox6e47e062009-06-11 12:37:06 +0100832static void acm_tty_set_termios(struct tty_struct *tty,
833 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834{
835 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800836 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 struct usb_cdc_line_coding newline;
838 int newctrl = acm->ctrlout;
839
840 if (!ACM_READY(acm))
841 return;
842
Alan Cox9b80fee2009-09-19 13:13:23 -0700843 newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
845 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100846 (termios->c_cflag & PARODD ? 1 : 2) +
847 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
Alan Cox6e47e062009-06-11 12:37:06 +0100849 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
851
852 if (!newline.dwDTERate) {
853 newline.dwDTERate = acm->line.dwDTERate;
854 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +0100855 } else
856 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
858 if (newctrl != acm->ctrlout)
859 acm_set_control(acm, acm->ctrlout = newctrl);
860
861 if (memcmp(&acm->line, &newline, sizeof newline)) {
862 memcpy(&acm->line, &newline, sizeof newline);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100863 dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
864 __func__,
865 le32_to_cpu(newline.dwDTERate),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 newline.bCharFormat, newline.bParityType,
867 newline.bDataBits);
868 acm_set_line(acm, &acm->line);
869 }
870}
871
872/*
873 * USB probe and disconnect routines.
874 */
875
Oliver Neukum830f4022008-06-25 14:17:16 +0200876/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +0200877static void acm_write_buffers_free(struct acm *acm)
878{
879 int i;
880 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +0200881 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200882
Alan Cox6e47e062009-06-11 12:37:06 +0100883 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Daniel Mack997ea582010-04-12 13:17:25 +0200884 usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +0200885}
886
Oliver Neukum830f4022008-06-25 14:17:16 +0200887static void acm_read_buffers_free(struct acm *acm)
888{
889 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
890 int i, n = acm->rx_buflimit;
891
892 for (i = 0; i < n; i++)
Daniel Mack997ea582010-04-12 13:17:25 +0200893 usb_free_coherent(usb_dev, acm->readsize,
894 acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +0200895}
896
Oliver Neukum884b6002005-04-21 21:28:02 +0200897/* Little helper: write buffers allocate */
898static int acm_write_buffers_alloc(struct acm *acm)
899{
900 int i;
901 struct acm_wb *wb;
902
Oliver Neukum86478942006-05-13 22:50:47 +0200903 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Daniel Mack997ea582010-04-12 13:17:25 +0200904 wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
Oliver Neukum884b6002005-04-21 21:28:02 +0200905 &wb->dmah);
906 if (!wb->buf) {
907 while (i != 0) {
908 --i;
909 --wb;
Daniel Mack997ea582010-04-12 13:17:25 +0200910 usb_free_coherent(acm->dev, acm->writesize,
Oliver Neukum884b6002005-04-21 21:28:02 +0200911 wb->buf, wb->dmah);
912 }
913 return -ENOMEM;
914 }
915 }
916 return 0;
917}
918
Alan Cox10077d42009-06-11 12:36:09 +0100919static int acm_probe(struct usb_interface *intf,
920 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921{
922 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100923 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -0700924 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 int buflen = intf->altsetting->extralen;
926 struct usb_interface *control_interface;
927 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200928 struct usb_endpoint_descriptor *epctrl = NULL;
929 struct usb_endpoint_descriptor *epread = NULL;
930 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 struct usb_device *usb_dev = interface_to_usbdev(intf);
932 struct acm *acm;
933 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +0100934 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 u8 *buf;
936 u8 ac_management_function = 0;
937 u8 call_management_function = 0;
938 int call_interface_num = -1;
939 int data_interface_num;
940 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200941 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100942 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200943 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
Oliver Neukum86478942006-05-13 22:50:47 +0200945 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200947 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
948
949 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 if (quirks == NO_UNION_NORMAL) {
951 data_interface = usb_ifnum_to_if(usb_dev, 1);
952 control_interface = usb_ifnum_to_if(usb_dev, 0);
953 goto skip_normal_probe;
954 }
Alan Cox6e47e062009-06-11 12:37:06 +0100955
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 /* normal probing*/
957 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700958 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 return -EINVAL;
960 }
961
962 if (!buflen) {
Toby Gray577045c2010-09-02 10:46:20 +0100963 if (intf->cur_altsetting->endpoint &&
964 intf->cur_altsetting->endpoint->extralen &&
Alan Cox6e47e062009-06-11 12:37:06 +0100965 intf->cur_altsetting->endpoint->extra) {
966 dev_dbg(&intf->dev,
967 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 buflen = intf->cur_altsetting->endpoint->extralen;
969 buffer = intf->cur_altsetting->endpoint->extra;
970 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700971 dev_err(&intf->dev,
972 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 return -EINVAL;
974 }
975 }
976
977 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +0100978 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700979 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 goto next_desc;
981 }
982
Alan Cox6e47e062009-06-11 12:37:06 +0100983 switch (buffer[2]) {
984 case USB_CDC_UNION_TYPE: /* we've found it */
985 if (union_header) {
986 dev_err(&intf->dev, "More than one "
987 "union descriptor, skipping ...\n");
988 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 }
Alan Cox6e47e062009-06-11 12:37:06 +0100990 union_header = (struct usb_cdc_union_desc *)buffer;
991 break;
992 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
993 cfd = (struct usb_cdc_country_functional_desc *)buffer;
994 break;
995 case USB_CDC_HEADER_TYPE: /* maybe check version */
996 break; /* for now we ignore it */
997 case USB_CDC_ACM_TYPE:
998 ac_management_function = buffer[3];
999 break;
1000 case USB_CDC_CALL_MANAGEMENT_TYPE:
1001 call_management_function = buffer[3];
1002 call_interface_num = buffer[4];
Julian Calabyce126642010-01-05 23:58:20 +11001003 if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
Alan Cox6e47e062009-06-11 12:37:06 +01001004 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1005 break;
1006 default:
1007 /* there are LOTS more CDC descriptors that
1008 * could legitimately be found here.
1009 */
1010 dev_dbg(&intf->dev, "Ignoring descriptor: "
1011 "type %02x, length %d\n",
1012 buffer[2], buffer[0]);
1013 break;
1014 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015next_desc:
1016 buflen -= buffer[0];
1017 buffer += buffer[0];
1018 }
1019
1020 if (!union_header) {
1021 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001022 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
1024 control_interface = intf;
1025 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001026 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1027 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1028 return -ENODEV;
1029 } else {
1030 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1031 combined_interfaces = 1;
1032 control_interface = data_interface = intf;
1033 goto look_for_collapsed_interface;
1034 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 }
1036 } else {
1037 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1038 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1039 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001040 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 return -ENODEV;
1042 }
1043 }
Alan Cox6e47e062009-06-11 12:37:06 +01001044
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001046 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001048 if (control_interface == data_interface) {
1049 /* some broken devices designed for windows work this way */
1050 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1051 combined_interfaces = 1;
1052 /* a popular other OS doesn't use it */
1053 quirks |= NO_CAP_LINE;
1054 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1055 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1056 return -EINVAL;
1057 }
1058look_for_collapsed_interface:
1059 for (i = 0; i < 3; i++) {
1060 struct usb_endpoint_descriptor *ep;
1061 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1062
1063 if (usb_endpoint_is_int_in(ep))
1064 epctrl = ep;
1065 else if (usb_endpoint_is_bulk_out(ep))
1066 epwrite = ep;
1067 else if (usb_endpoint_is_bulk_in(ep))
1068 epread = ep;
1069 else
1070 return -EINVAL;
1071 }
1072 if (!epctrl || !epread || !epwrite)
1073 return -ENODEV;
1074 else
1075 goto made_compressed_probe;
1076 }
1077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078skip_normal_probe:
1079
1080 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001081 if (data_interface->cur_altsetting->desc.bInterfaceClass
1082 != CDC_DATA_INTERFACE_TYPE) {
1083 if (control_interface->cur_altsetting->desc.bInterfaceClass
1084 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001086 dev_dbg(&intf->dev,
1087 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 t = control_interface;
1089 control_interface = data_interface;
1090 data_interface = t;
1091 } else {
1092 return -EINVAL;
1093 }
1094 }
Alan Stern74da5d62007-08-02 13:29:10 -04001095
1096 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001097 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001098 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001099
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001100 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1101 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001102 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 return -EBUSY;
1104 }
1105
1106
1107 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
1108 return -EINVAL;
1109
1110 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1111 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1112 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1113
1114
1115 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001116 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 /* descriptors are swapped */
1118 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001119 dev_dbg(&intf->dev,
1120 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 t = epread;
1122 epread = epwrite;
1123 epwrite = t;
1124 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001125made_compressed_probe:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001126 dev_dbg(&intf->dev, "interfaces are valid\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
1128
1129 if (minor == ACM_TTY_MINORS) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001130 dev_err(&intf->dev, "no more free acm devices\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 return -ENODEV;
1132 }
1133
Alan Cox6e47e062009-06-11 12:37:06 +01001134 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1135 if (acm == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001136 dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 goto alloc_fail;
1138 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139
1140 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Alan Cox6e47e062009-06-11 12:37:06 +01001141 readsize = le16_to_cpu(epread->wMaxPacketSize) *
1142 (quirks == SINGLE_RX_URB ? 1 : 2);
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001143 acm->combined_interfaces = combined_interfaces;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001144 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 acm->control = control_interface;
1146 acm->data = data_interface;
1147 acm->minor = minor;
1148 acm->dev = usb_dev;
1149 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001150 if (quirks & NO_CAP_LINE)
1151 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 acm->ctrlsize = ctrlsize;
1153 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001154 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001155 acm->urb_task.func = acm_rx_tasklet;
1156 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +00001157 INIT_WORK(&acm->work, acm_softint);
David Brownelle5fbab52008-08-06 18:46:10 -07001158 init_waitqueue_head(&acm->drain_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +02001160 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001161 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001162 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001163 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001164 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1165 if (acm->is_int_ep)
1166 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001167 tty_port_init(&acm->port);
1168 acm->port.ops = &acm_port_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169
Daniel Mack997ea582010-04-12 13:17:25 +02001170 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 if (!buf) {
Johan Hovold255ab562011-03-22 11:12:13 +01001172 dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 goto alloc_fail2;
1174 }
1175 acm->ctrl_buffer = buf;
1176
Oliver Neukum884b6002005-04-21 21:28:02 +02001177 if (acm_write_buffers_alloc(acm) < 0) {
Johan Hovold255ab562011-03-22 11:12:13 +01001178 dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 goto alloc_fail4;
1180 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181
1182 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1183 if (!acm->ctrlurb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001184 dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 goto alloc_fail5;
1186 }
Oliver Neukum86478942006-05-13 22:50:47 +02001187 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001188 struct acm_ru *rcv = &(acm->ru[i]);
1189
Alan Cox6e47e062009-06-11 12:37:06 +01001190 rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
1191 if (rcv->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001192 dev_err(&intf->dev,
Alan Cox6e47e062009-06-11 12:37:06 +01001193 "out of memory (read urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001194 goto alloc_fail6;
David Kubicek61a87ad2005-11-01 18:51:34 +01001195 }
1196
1197 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1198 rcv->instance = acm;
1199 }
Oliver Neukum86478942006-05-13 22:50:47 +02001200 for (i = 0; i < num_rx_buf; i++) {
David Brownell672c4e12008-08-06 18:41:12 -07001201 struct acm_rb *rb = &(acm->rb[i]);
David Kubicek61a87ad2005-11-01 18:51:34 +01001202
Daniel Mack997ea582010-04-12 13:17:25 +02001203 rb->base = usb_alloc_coherent(acm->dev, readsize,
David Brownell672c4e12008-08-06 18:41:12 -07001204 GFP_KERNEL, &rb->dma);
1205 if (!rb->base) {
Johan Hovold255ab562011-03-22 11:12:13 +01001206 dev_err(&intf->dev,
Daniel Mack997ea582010-04-12 13:17:25 +02001207 "out of memory (read bufs usb_alloc_coherent)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001208 goto alloc_fail7;
1209 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 }
Alan Cox6e47e062009-06-11 12:37:06 +01001211 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001212 struct acm_wb *snd = &(acm->wb[i]);
1213
Alan Cox6e47e062009-06-11 12:37:06 +01001214 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1215 if (snd->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001216 dev_err(&intf->dev,
Johan Hovold59d7fec2011-03-22 11:12:12 +01001217 "out of memory (write urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001218 goto alloc_fail8;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001219 }
1220
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001221 if (usb_endpoint_xfer_int(epwrite))
1222 usb_fill_int_urb(snd->urb, usb_dev,
1223 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1224 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1225 else
1226 usb_fill_bulk_urb(snd->urb, usb_dev,
1227 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1228 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001229 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1230 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 }
1232
Alan Cox6e47e062009-06-11 12:37:06 +01001233 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001234
1235 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1236 if (i < 0)
1237 goto alloc_fail8;
1238
1239 if (cfd) { /* export the country data */
1240 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1241 if (!acm->country_codes)
1242 goto skip_countries;
1243 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001244 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1245 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001246 acm->country_rel_date = cfd->iCountryCodeRelDate;
1247
1248 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1249 if (i < 0) {
1250 kfree(acm->country_codes);
1251 goto skip_countries;
1252 }
1253
Alan Cox6e47e062009-06-11 12:37:06 +01001254 i = device_create_file(&intf->dev,
1255 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001256 if (i < 0) {
Axel Linc2572b72010-05-31 08:04:47 +08001257 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001258 kfree(acm->country_codes);
1259 goto skip_countries;
1260 }
1261 }
1262
1263skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001264 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001265 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1266 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1267 /* works around buggy devices */
1268 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1270 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1271
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1273
1274 acm_set_control(acm, acm->ctrlout);
1275
1276 acm->line.dwDTERate = cpu_to_le32(9600);
1277 acm->line.bDataBits = 8;
1278 acm_set_line(acm, &acm->line);
1279
1280 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001281 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001283 usb_get_intf(control_interface);
1284 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285
1286 acm_table[minor] = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001288 return 0;
1289alloc_fail8:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001290 for (i = 0; i < ACM_NW; i++)
1291 usb_free_urb(acm->wb[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292alloc_fail7:
Oliver Neukum830f4022008-06-25 14:17:16 +02001293 acm_read_buffers_free(acm);
Axel Linc2572b72010-05-31 08:04:47 +08001294alloc_fail6:
Oliver Neukum86478942006-05-13 22:50:47 +02001295 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001296 usb_free_urb(acm->ru[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 usb_free_urb(acm->ctrlurb);
1298alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001299 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300alloc_fail4:
Daniel Mack997ea582010-04-12 13:17:25 +02001301 usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302alloc_fail2:
1303 kfree(acm);
1304alloc_fail:
1305 return -ENOMEM;
1306}
1307
Oliver Neukum1365baf2007-10-12 17:24:28 +02001308static void stop_data_traffic(struct acm *acm)
1309{
1310 int i;
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001311
1312 dev_dbg(&acm->control->dev, "%s\n", __func__);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001313
1314 tasklet_disable(&acm->urb_task);
1315
1316 usb_kill_urb(acm->ctrlurb);
Alan Cox6e47e062009-06-11 12:37:06 +01001317 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001318 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001319 for (i = 0; i < acm->rx_buflimit; i++)
1320 usb_kill_urb(acm->ru[i].urb);
1321
Oliver Neukum1365baf2007-10-12 17:24:28 +02001322 tasklet_enable(&acm->urb_task);
1323
1324 cancel_work_sync(&acm->work);
1325}
1326
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327static void acm_disconnect(struct usb_interface *intf)
1328{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001329 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001331 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332
David Brownell672c4e12008-08-06 18:41:12 -07001333 /* sibling interface is already cleaning up */
1334 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001335 return;
David Brownell672c4e12008-08-06 18:41:12 -07001336
1337 mutex_lock(&open_mutex);
Alan Cox6e47e062009-06-11 12:37:06 +01001338 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001339 device_remove_file(&acm->control->dev,
1340 &dev_attr_wCountryCodes);
1341 device_remove_file(&acm->control->dev,
1342 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001343 }
Alan Stern74da5d62007-08-02 13:29:10 -04001344 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 acm->dev = NULL;
Oliver Neukum86067eea2006-01-08 12:39:13 +01001346 usb_set_intfdata(acm->control, NULL);
1347 usb_set_intfdata(acm->data, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348
Oliver Neukum1365baf2007-10-12 17:24:28 +02001349 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350
Oliver Neukum884b6002005-04-21 21:28:02 +02001351 acm_write_buffers_free(acm);
Daniel Mack997ea582010-04-12 13:17:25 +02001352 usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
1353 acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001354 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001356 if (!acm->combined_interfaces)
1357 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001358 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359
Alan Cox10077d42009-06-11 12:36:09 +01001360 if (acm->port.count == 0) {
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001361 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001362 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 return;
1364 }
1365
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001366 mutex_unlock(&open_mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001367 tty = tty_port_tty_get(&acm->port);
1368 if (tty) {
1369 tty_hangup(tty);
1370 tty_kref_put(tty);
1371 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372}
1373
Oliver Neukum35758582008-07-01 19:10:08 +02001374#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001375static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1376{
1377 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001378 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001379
Alan Stern65bfd292008-11-25 16:39:18 -05001380 if (message.event & PM_EVENT_AUTO) {
Oliver Neukum11ea8592008-06-20 11:25:57 +02001381 int b;
1382
1383 spin_lock_irq(&acm->read_lock);
1384 spin_lock(&acm->write_lock);
1385 b = acm->processing + acm->transmitting;
1386 spin_unlock(&acm->write_lock);
1387 spin_unlock_irq(&acm->read_lock);
1388 if (b)
1389 return -EBUSY;
1390 }
1391
1392 spin_lock_irq(&acm->read_lock);
1393 spin_lock(&acm->write_lock);
1394 cnt = acm->susp_count++;
1395 spin_unlock(&acm->write_lock);
1396 spin_unlock_irq(&acm->read_lock);
1397
1398 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001399 return 0;
1400 /*
1401 we treat opened interfaces differently,
1402 we must guard against open
1403 */
1404 mutex_lock(&acm->mutex);
1405
Alan Cox10077d42009-06-11 12:36:09 +01001406 if (acm->port.count)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001407 stop_data_traffic(acm);
1408
1409 mutex_unlock(&acm->mutex);
1410 return 0;
1411}
1412
1413static int acm_resume(struct usb_interface *intf)
1414{
1415 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001416 struct acm_wb *wb;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001417 int rv = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001418 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001419
Oliver Neukum11ea8592008-06-20 11:25:57 +02001420 spin_lock_irq(&acm->read_lock);
1421 acm->susp_count -= 1;
1422 cnt = acm->susp_count;
1423 spin_unlock_irq(&acm->read_lock);
1424
1425 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001426 return 0;
1427
1428 mutex_lock(&acm->mutex);
Alan Cox10077d42009-06-11 12:36:09 +01001429 if (acm->port.count) {
Oliver Neukum1365baf2007-10-12 17:24:28 +02001430 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001431
1432 spin_lock_irq(&acm->write_lock);
1433 if (acm->delayed_wb) {
1434 wb = acm->delayed_wb;
1435 acm->delayed_wb = NULL;
1436 spin_unlock_irq(&acm->write_lock);
Oliver Neukumf0730922010-03-03 00:37:56 +01001437 acm_start_wb(acm, wb);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001438 } else {
1439 spin_unlock_irq(&acm->write_lock);
1440 }
1441
1442 /*
1443 * delayed error checking because we must
1444 * do the write path at all cost
1445 */
Oliver Neukum1365baf2007-10-12 17:24:28 +02001446 if (rv < 0)
Oliver Neukum11ea8592008-06-20 11:25:57 +02001447 goto err_out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001448
1449 tasklet_schedule(&acm->urb_task);
1450 }
1451
1452err_out:
1453 mutex_unlock(&acm->mutex);
1454 return rv;
1455}
Oliver Neukum35758582008-07-01 19:10:08 +02001456
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001457static int acm_reset_resume(struct usb_interface *intf)
1458{
1459 struct acm *acm = usb_get_intfdata(intf);
1460 struct tty_struct *tty;
1461
1462 mutex_lock(&acm->mutex);
1463 if (acm->port.count) {
1464 tty = tty_port_tty_get(&acm->port);
1465 if (tty) {
1466 tty_hangup(tty);
1467 tty_kref_put(tty);
1468 }
1469 }
1470 mutex_unlock(&acm->mutex);
1471 return acm_resume(intf);
1472}
1473
Oliver Neukum35758582008-07-01 19:10:08 +02001474#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001475
1476#define NOKIA_PCSUITE_ACM_INFO(x) \
1477 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1478 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1479 USB_CDC_ACM_PROTO_VENDOR)
1480
Toby Gray4035e452010-09-01 16:01:19 +01001481#define SAMSUNG_PCSUITE_ACM_INFO(x) \
1482 USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
1483 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1484 USB_CDC_ACM_PROTO_VENDOR)
1485
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486/*
1487 * USB driver structure.
1488 */
1489
Németh Márton6ef48522010-01-10 15:33:45 +01001490static const struct usb_device_id acm_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 /* quirky and broken devices */
1492 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1493 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1494 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001495 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1496 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1497 },
Andrew Lunn0f9c7b4a2008-12-23 17:31:23 +01001498 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1499 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1500 },
Masahito Omote8753e652005-07-29 12:17:25 -07001501 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1502 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1503 },
Chris Malley91a9c922006-10-03 10:08:28 +01001504 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1505 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1506 },
Alan Cox7abcf202009-04-06 17:35:01 +01001507 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1508 .driver_info = SINGLE_RX_URB,
1509 },
Oliver Neukum86478942006-05-13 22:50:47 +02001510 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1511 .driver_info = SINGLE_RX_URB, /* firmware bug */
1512 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001513 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1514 .driver_info = SINGLE_RX_URB, /* firmware bug */
1515 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001516 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1517 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1518 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001519 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1520 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1521 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001522 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1523 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1524 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001525 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1526 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1527 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001528 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1529 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1530 },
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001531 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1532 },
Adam Richterc332b4e2009-02-18 16:17:15 -08001533 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1534 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1535 data interface instead of
1536 communications interface.
1537 Maybe we should define a new
1538 quirk for this. */
1539 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001540 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1541 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1542 },
Russ Nelsonc3baa192010-04-21 23:07:03 -04001543 { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1544 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1545 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001546
Adrian Taylorc1479a92009-11-19 10:35:33 +00001547 /* Nokia S60 phones expose two ACM channels. The first is
1548 * a modem and is picked up by the standard AT-command
1549 * information below. The second is 'vendor-specific' but
1550 * is treated as a serial device at the S60 end, so we want
1551 * to expose it on Linux too. */
1552 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1553 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1554 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1555 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1556 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1557 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1558 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1559 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1560 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1561 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1562 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1563 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1564 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1565 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1566 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1567 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1568 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1569 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1570 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1571 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1572 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1573 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1574 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1575 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1576 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1577 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1578 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1579 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1580 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1581 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1582 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1583 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1584 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1585 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1586 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1587 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1588 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1589 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1590 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1591 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1592 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1593 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1594 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
Przemo Firszt83a4eae92010-06-28 21:29:34 +01001595 { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
Toby Gray4035e452010-09-01 16:01:19 +01001596 { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
1597 { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
1598 { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
1599 { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
1600 { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
1601 { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
1602 { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
1603 { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
1604 { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
1605 { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
Arvid Ephraim Picciani721d92f2011-01-25 15:58:40 +01001606 { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
Toby Gray4035e452010-09-01 16:01:19 +01001607 { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001608
1609 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1610
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001611 /* Support Lego NXT using pbLua firmware */
Julian Calabyce126642010-01-05 23:58:20 +11001612 { USB_DEVICE(0x0694, 0xff00),
1613 .driver_info = NOT_A_MODEM,
Otavio Salvador7893afc2010-09-26 23:35:05 -03001614 },
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001615
Philippe Corbes5b239f02010-08-31 19:31:32 +02001616 /* control interfaces without any protocol set */
1617 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1618 USB_CDC_PROTO_NONE) },
1619
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 /* control interfaces with various AT-command sets */
1621 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1622 USB_CDC_ACM_PROTO_AT_V25TER) },
1623 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1624 USB_CDC_ACM_PROTO_AT_PCCA101) },
1625 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1626 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1627 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1628 USB_CDC_ACM_PROTO_AT_GSM) },
1629 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001630 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1632 USB_CDC_ACM_PROTO_AT_CDMA) },
1633
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 { }
1635};
1636
Alan Cox6e47e062009-06-11 12:37:06 +01001637MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638
1639static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640 .name = "cdc_acm",
1641 .probe = acm_probe,
1642 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001643#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001644 .suspend = acm_suspend,
1645 .resume = acm_resume,
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001646 .reset_resume = acm_reset_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001647#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001649#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001650 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001651#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652};
1653
1654/*
1655 * TTY driver structures.
1656 */
1657
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001658static const struct tty_operations acm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659 .open = acm_tty_open,
1660 .close = acm_tty_close,
Alan Cox10077d42009-06-11 12:36:09 +01001661 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 .write = acm_tty_write,
1663 .write_room = acm_tty_write_room,
1664 .ioctl = acm_tty_ioctl,
1665 .throttle = acm_tty_throttle,
1666 .unthrottle = acm_tty_unthrottle,
1667 .chars_in_buffer = acm_tty_chars_in_buffer,
1668 .break_ctl = acm_tty_break_ctl,
1669 .set_termios = acm_tty_set_termios,
1670 .tiocmget = acm_tty_tiocmget,
1671 .tiocmset = acm_tty_tiocmset,
1672};
1673
1674/*
1675 * Init / exit.
1676 */
1677
1678static int __init acm_init(void)
1679{
1680 int retval;
1681 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1682 if (!acm_tty_driver)
1683 return -ENOMEM;
1684 acm_tty_driver->owner = THIS_MODULE,
1685 acm_tty_driver->driver_name = "acm",
1686 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 acm_tty_driver->major = ACM_TTY_MAJOR,
1688 acm_tty_driver->minor_start = 0,
1689 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1690 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001691 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001693 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1694 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 tty_set_operations(acm_tty_driver, &acm_ops);
1696
1697 retval = tty_register_driver(acm_tty_driver);
1698 if (retval) {
1699 put_tty_driver(acm_tty_driver);
1700 return retval;
1701 }
1702
1703 retval = usb_register(&acm_driver);
1704 if (retval) {
1705 tty_unregister_driver(acm_tty_driver);
1706 put_tty_driver(acm_tty_driver);
1707 return retval;
1708 }
1709
Johan Hovolda2c7b932011-03-22 11:12:18 +01001710 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711
1712 return 0;
1713}
1714
1715static void __exit acm_exit(void)
1716{
1717 usb_deregister(&acm_driver);
1718 tty_unregister_driver(acm_tty_driver);
1719 put_tty_driver(acm_tty_driver);
1720}
1721
1722module_init(acm_init);
1723module_exit(acm_exit);
1724
Alan Cox6e47e062009-06-11 12:37:06 +01001725MODULE_AUTHOR(DRIVER_AUTHOR);
1726MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01001728MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);