blob: b32ccb46101960b62e2e81bf4f0b655797a45668 [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>
Johan Hovold088c64f2011-03-25 11:06:02 +010010 * Copyright (c) 2011 Johan Hovold <jhovold@gmail.com>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 *
12 * USB Abstract Control Model driver for USB modems and ISDN adapters
13 *
14 * Sponsored by SuSE
15 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070016 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 */
30
31#undef DEBUG
David Brownelle5fbab52008-08-06 18:46:10 -070032#undef VERBOSE_DEBUG
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
34#include <linux/kernel.h>
35#include <linux/errno.h>
36#include <linux/init.h>
37#include <linux/slab.h>
38#include <linux/tty.h>
Oliver Neukum7af25b42009-09-08 23:51:28 +020039#include <linux/serial.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <linux/tty_driver.h>
41#include <linux/tty_flip.h>
Oliver Neukum18c75722012-02-17 17:21:24 -050042#include <linux/serial.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <linux/module.h>
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010044#include <linux/mutex.h>
Alan Cox10077d42009-06-11 12:36:09 +010045#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <linux/usb.h>
David Brownella8c28f22006-06-13 09:57:47 -070047#include <linux/usb/cdc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <asm/byteorder.h>
49#include <asm/unaligned.h>
David Kubicek61a87ad2005-11-01 18:51:34 +010050#include <linux/list.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52#include "cdc-acm.h"
53
David Brownelle5fbab52008-08-06 18:46:10 -070054
Johan Hovold088c64f2011-03-25 11:06:02 +010055#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek, Johan Hovold"
Linus Torvalds1da177e2005-04-16 15:20:36 -070056#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
57
58static struct usb_driver acm_driver;
59static struct tty_driver *acm_tty_driver;
60static struct acm *acm_table[ACM_TTY_MINORS];
61
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -080062static DEFINE_MUTEX(acm_table_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070063
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -080064/*
65 * acm_table accessors
66 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -080068/*
69 * Look up an ACM structure by index. If found and not disconnected, increment
70 * its refcount and return it with its mutex held.
71 */
72static struct acm *acm_get_by_index(unsigned index)
73{
74 struct acm *acm;
75
76 mutex_lock(&acm_table_lock);
77 acm = acm_table[index];
78 if (acm) {
79 mutex_lock(&acm->mutex);
80 if (acm->disconnected) {
81 mutex_unlock(&acm->mutex);
82 acm = NULL;
83 } else {
84 tty_port_get(&acm->port);
85 mutex_unlock(&acm->mutex);
86 }
87 }
88 mutex_unlock(&acm_table_lock);
89 return acm;
90}
91
92/*
93 * Try to find an available minor number and if found, associate it with 'acm'.
94 */
95static int acm_alloc_minor(struct acm *acm)
96{
97 int minor;
98
99 mutex_lock(&acm_table_lock);
100 for (minor = 0; minor < ACM_TTY_MINORS; minor++) {
101 if (!acm_table[minor]) {
102 acm_table[minor] = acm;
103 break;
104 }
105 }
106 mutex_unlock(&acm_table_lock);
107
108 return minor;
109}
110
111/* Release the minor number associated with 'acm'. */
112static void acm_release_minor(struct acm *acm)
113{
114 mutex_lock(&acm_table_lock);
115 acm_table[acm->minor] = NULL;
116 mutex_unlock(&acm_table_lock);
117}
Alan Cox739e0282009-06-11 12:27:50 +0100118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119/*
120 * Functions for ACM control messages.
121 */
122
Alan Cox6e47e062009-06-11 12:37:06 +0100123static int acm_ctrl_msg(struct acm *acm, int request, int value,
124 void *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125{
126 int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
127 request, USB_RT_ACM, value,
128 acm->control->altsetting[0].desc.bInterfaceNumber,
129 buf, len, 5000);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100130 dev_dbg(&acm->control->dev,
131 "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
132 __func__, request, value, len, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 return retval < 0 ? retval : 0;
134}
135
136/* devices aren't required to support these requests.
137 * the cdc acm descriptor tells whether they do...
138 */
139#define acm_set_control(acm, control) \
140 acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
141#define acm_set_line(acm, line) \
142 acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
143#define acm_send_break(acm, ms) \
144 acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
145
146/*
Oliver Neukum884b6002005-04-21 21:28:02 +0200147 * Write buffer management.
148 * All of these assume proper locks taken by the caller.
149 */
150
151static int acm_wb_alloc(struct acm *acm)
152{
153 int i, wbn;
154 struct acm_wb *wb;
155
David Engrafe4cf3aa2008-03-20 10:01:34 +0100156 wbn = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200157 i = 0;
158 for (;;) {
159 wb = &acm->wb[wbn];
160 if (!wb->use) {
161 wb->use = 1;
162 return wbn;
163 }
Oliver Neukum86478942006-05-13 22:50:47 +0200164 wbn = (wbn + 1) % ACM_NW;
165 if (++i >= ACM_NW)
Oliver Neukum884b6002005-04-21 21:28:02 +0200166 return -1;
167 }
168}
169
Oliver Neukum884b6002005-04-21 21:28:02 +0200170static int acm_wb_is_avail(struct acm *acm)
171{
172 int i, n;
David Brownelle5fbab52008-08-06 18:46:10 -0700173 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200174
Oliver Neukum86478942006-05-13 22:50:47 +0200175 n = ACM_NW;
David Brownelle5fbab52008-08-06 18:46:10 -0700176 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100177 for (i = 0; i < ACM_NW; i++)
Oliver Neukum86478942006-05-13 22:50:47 +0200178 n -= acm->wb[i].use;
David Brownelle5fbab52008-08-06 18:46:10 -0700179 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200180 return n;
181}
182
Oliver Neukum884b6002005-04-21 21:28:02 +0200183/*
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800184 * Finish write. Caller must hold acm->write_lock
Oliver Neukum884b6002005-04-21 21:28:02 +0200185 */
David Engrafe4cf3aa2008-03-20 10:01:34 +0100186static void acm_write_done(struct acm *acm, struct acm_wb *wb)
Oliver Neukum884b6002005-04-21 21:28:02 +0200187{
David Engrafe4cf3aa2008-03-20 10:01:34 +0100188 wb->use = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200189 acm->transmitting--;
Oliver Neukum97d35f92009-12-16 17:05:57 +0100190 usb_autopm_put_interface_async(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200191}
192
193/*
194 * Poke write.
Oliver Neukum11ea8592008-06-20 11:25:57 +0200195 *
196 * the caller is responsible for locking
Oliver Neukum884b6002005-04-21 21:28:02 +0200197 */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200198
199static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
200{
201 int rc;
202
203 acm->transmitting++;
204
205 wb->urb->transfer_buffer = wb->buf;
206 wb->urb->transfer_dma = wb->dmah;
207 wb->urb->transfer_buffer_length = wb->len;
208 wb->urb->dev = acm->dev;
209
Alan Cox6e47e062009-06-11 12:37:06 +0100210 rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
211 if (rc < 0) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100212 dev_err(&acm->data->dev,
213 "%s - usb_submit_urb(write bulk) failed: %d\n",
214 __func__, rc);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200215 acm_write_done(acm, wb);
216 }
217 return rc;
218}
219
David Engrafe4cf3aa2008-03-20 10:01:34 +0100220static int acm_write_start(struct acm *acm, int wbn)
Oliver Neukum884b6002005-04-21 21:28:02 +0200221{
222 unsigned long flags;
David Brownell934da462008-08-06 18:44:12 -0700223 struct acm_wb *wb = &acm->wb[wbn];
Oliver Neukum884b6002005-04-21 21:28:02 +0200224 int rc;
225
226 spin_lock_irqsave(&acm->write_lock, flags);
227 if (!acm->dev) {
David Brownell934da462008-08-06 18:44:12 -0700228 wb->use = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200229 spin_unlock_irqrestore(&acm->write_lock, flags);
230 return -ENODEV;
231 }
232
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100233 dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100234 acm->susp_count);
Oliver Neukum97d35f92009-12-16 17:05:57 +0100235 usb_autopm_get_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200236 if (acm->susp_count) {
Oliver Neukum97d35f92009-12-16 17:05:57 +0100237 if (!acm->delayed_wb)
238 acm->delayed_wb = wb;
239 else
240 usb_autopm_put_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200241 spin_unlock_irqrestore(&acm->write_lock, flags);
242 return 0; /* A white lie */
243 }
244 usb_mark_last_busy(acm->dev);
245
Oliver Neukum11ea8592008-06-20 11:25:57 +0200246 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200247 spin_unlock_irqrestore(&acm->write_lock, flags);
248
Oliver Neukum884b6002005-04-21 21:28:02 +0200249 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200250
Oliver Neukum884b6002005-04-21 21:28:02 +0200251}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100252/*
253 * attributes exported through sysfs
254 */
255static ssize_t show_caps
256(struct device *dev, struct device_attribute *attr, char *buf)
257{
258 struct usb_interface *intf = to_usb_interface(dev);
259 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200260
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100261 return sprintf(buf, "%d", acm->ctrl_caps);
262}
263static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
264
265static ssize_t show_country_codes
266(struct device *dev, struct device_attribute *attr, char *buf)
267{
268 struct usb_interface *intf = to_usb_interface(dev);
269 struct acm *acm = usb_get_intfdata(intf);
270
271 memcpy(buf, acm->country_codes, acm->country_code_size);
272 return acm->country_code_size;
273}
274
275static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
276
277static ssize_t show_country_rel_date
278(struct device *dev, struct device_attribute *attr, char *buf)
279{
280 struct usb_interface *intf = to_usb_interface(dev);
281 struct acm *acm = usb_get_intfdata(intf);
282
283 return sprintf(buf, "%d", acm->country_rel_date);
284}
285
286static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200287/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 * Interrupt handlers for various ACM device responses
289 */
290
291/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100292static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293{
294 struct acm *acm = urb->context;
295 struct usb_cdc_notification *dr = urb->transfer_buffer;
Alan Cox10077d42009-06-11 12:36:09 +0100296 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 unsigned char *data;
298 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700299 int retval;
300 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700302 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 case 0:
304 /* success */
305 break;
306 case -ECONNRESET:
307 case -ENOENT:
308 case -ESHUTDOWN:
309 /* this urb is terminated, clean up */
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100310 dev_dbg(&acm->control->dev,
311 "%s - urb shutting down with status: %d\n",
312 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 return;
314 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100315 dev_dbg(&acm->control->dev,
316 "%s - nonzero urb status received: %d\n",
317 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 goto exit;
319 }
320
Johan Hovold7e7797e2011-03-22 11:12:11 +0100321 usb_mark_last_busy(acm->dev);
322
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 data = (unsigned char *)(dr + 1);
324 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100325 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100326 dev_dbg(&acm->control->dev, "%s - network connection: %d\n",
327 __func__, dr->wValue);
Alan Cox6e47e062009-06-11 12:37:06 +0100328 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
Alan Cox6e47e062009-06-11 12:37:06 +0100330 case USB_CDC_NOTIFY_SERIAL_STATE:
331 tty = tty_port_tty_get(&acm->port);
332 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
Alan Cox6e47e062009-06-11 12:37:06 +0100334 if (tty) {
335 if (!acm->clocal &&
336 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100337 dev_dbg(&acm->control->dev,
338 "%s - calling hangup\n", __func__);
Alan Cox6e47e062009-06-11 12:37:06 +0100339 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 }
Alan Cox6e47e062009-06-11 12:37:06 +0100341 tty_kref_put(tty);
342 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343
Alan Cox6e47e062009-06-11 12:37:06 +0100344 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100346 dev_dbg(&acm->control->dev,
347 "%s - input control lines: dcd%c dsr%c break%c "
348 "ring%c framing%c parity%c overrun%c\n",
349 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100350 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
351 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
352 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
353 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
354 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
355 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
356 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 break;
358
Alan Cox6e47e062009-06-11 12:37:06 +0100359 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100360 dev_dbg(&acm->control->dev,
361 "%s - unknown notification %d received: index %d "
362 "len %d data0 %d data1 %d\n",
363 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100364 dr->bNotificationType, dr->wIndex,
365 dr->wLength, data[0], data[1]);
366 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 }
368exit:
Alan Cox6e47e062009-06-11 12:37:06 +0100369 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700370 if (retval)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100371 dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
372 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373}
374
Johan Hovold088c64f2011-03-25 11:06:02 +0100375static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376{
Johan Hovold088c64f2011-03-25 11:06:02 +0100377 int res;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700378
Johan Hovold088c64f2011-03-25 11:06:02 +0100379 if (!test_and_clear_bit(index, &acm->read_urbs_free))
380 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381
Johan Hovold088c64f2011-03-25 11:06:02 +0100382 dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
383
384 res = usb_submit_urb(acm->read_urbs[index], mem_flags);
385 if (res) {
386 if (res != -EPERM) {
387 dev_err(&acm->data->dev,
388 "%s - usb_submit_urb failed: %d\n",
389 __func__, res);
390 }
391 set_bit(index, &acm->read_urbs_free);
392 return res;
393 }
394
395 return 0;
396}
397
398static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
399{
400 int res;
401 int i;
402
403 for (i = 0; i < acm->rx_buflimit; ++i) {
404 res = acm_submit_read_urb(acm, i, mem_flags);
405 if (res)
406 return res;
407 }
408
409 return 0;
410}
411
412static void acm_process_read_urb(struct acm *acm, struct urb *urb)
413{
414 struct tty_struct *tty;
415
416 if (!urb->actual_length)
417 return;
418
419 tty = tty_port_tty_get(&acm->port);
420 if (!tty)
421 return;
422
423 tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
424 tty_flip_buffer_push(tty);
425
426 tty_kref_put(tty);
427}
428
429static void acm_read_bulk_callback(struct urb *urb)
430{
431 struct acm_rb *rb = urb->context;
432 struct acm *acm = rb->instance;
433 unsigned long flags;
434
435 dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
436 rb->index, urb->actual_length);
437 set_bit(rb->index, &acm->read_urbs_free);
438
439 if (!acm->dev) {
440 dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200442 }
443 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
Johan Hovold088c64f2011-03-25 11:06:02 +0100445 if (urb->status) {
Johan Hovold1d9846e2011-03-22 11:12:14 +0100446 dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
Johan Hovold088c64f2011-03-25 11:06:02 +0100447 __func__, urb->status);
448 return;
449 }
450 acm_process_read_urb(acm, urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451
Johan Hovold088c64f2011-03-25 11:06:02 +0100452 /* throttle device if requested by tty */
453 spin_lock_irqsave(&acm->read_lock, flags);
454 acm->throttled = acm->throttle_req;
455 if (!acm->throttled && !acm->susp_count) {
456 spin_unlock_irqrestore(&acm->read_lock, flags);
457 acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
Oliver Neukum86478942006-05-13 22:50:47 +0200458 } else {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200459 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461}
462
463/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100464static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465{
Ming Leicdc97792008-02-24 18:41:47 +0800466 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700467 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800468 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200469
Johan Hovold4fa46262011-03-22 11:12:16 +0100470 if (urb->status || (urb->actual_length != urb->transfer_buffer_length))
471 dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
Johan Hovold1d9846e2011-03-22 11:12:14 +0100472 __func__,
David Brownelle5fbab52008-08-06 18:46:10 -0700473 urb->actual_length,
474 urb->transfer_buffer_length,
475 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800477 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100478 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800479 spin_unlock_irqrestore(&acm->write_lock, flags);
Havard Skinnemoen99823f42011-12-09 16:51:54 -0800480 schedule_work(&acm->work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481}
482
David Howellsc4028952006-11-22 14:57:56 +0000483static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484{
David Howellsc4028952006-11-22 14:57:56 +0000485 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100486 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700487
Johan Hovold1d9846e2011-03-22 11:12:14 +0100488 dev_vdbg(&acm->data->dev, "%s\n", __func__);
489
Alan Cox10077d42009-06-11 12:36:09 +0100490 tty = tty_port_tty_get(&acm->port);
Johan Hovold15e5bee2011-03-22 11:12:10 +0100491 if (!tty)
492 return;
Alan Cox10077d42009-06-11 12:36:09 +0100493 tty_wakeup(tty);
494 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495}
496
497/*
498 * TTY handlers
499 */
500
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800501static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502{
503 struct acm *acm;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800504 int retval;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100505
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800506 dev_dbg(tty->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800508 acm = acm_get_by_index(tty->index);
509 if (!acm)
510 return -ENODEV;
511
Jiri Slabyf8a8c102012-03-05 14:51:48 +0100512 retval = tty_standard_install(driver, tty);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800513 if (retval)
514 goto error_init_termios;
515
516 tty->driver_data = acm;
517
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800518 return 0;
519
520error_init_termios:
521 tty_port_put(&acm->port);
522 return retval;
523}
524
525static int acm_tty_open(struct tty_struct *tty, struct file *filp)
526{
527 struct acm *acm = tty->driver_data;
528
529 dev_dbg(tty->dev, "%s\n", __func__);
530
531 return tty_port_open(&acm->port, tty, filp);
532}
533
534static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
535{
536 struct acm *acm = container_of(port, struct acm, port);
537 int retval = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100539 dev_dbg(&acm->control->dev, "%s\n", __func__);
540
Oliver Neukum1365baf2007-10-12 17:24:28 +0200541 mutex_lock(&acm->mutex);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800542 if (acm->disconnected)
543 goto disconnected;
544
545 retval = usb_autopm_get_interface(acm->control);
546 if (retval)
547 goto error_get_interface;
548
549 /*
550 * FIXME: Why do we need this? Allocating 64K of physically contiguous
551 * memory is really nasty...
552 */
553 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
554 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200555
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 acm->ctrlurb->dev = acm->dev;
557 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100558 dev_err(&acm->control->dev,
559 "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800560 goto error_submit_urb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 }
562
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800563 acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS;
564 if (acm_set_control(acm, acm->ctrlout) < 0 &&
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100565 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800566 goto error_set_control;
Alan Cox10077d42009-06-11 12:36:09 +0100567
Oliver Neukum11ea8592008-06-20 11:25:57 +0200568 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
Johan Hovold088c64f2011-03-25 11:06:02 +0100570 if (acm_submit_read_urbs(acm, GFP_KERNEL))
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800571 goto error_submit_read_urbs;
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100572
Oliver Neukum1365baf2007-10-12 17:24:28 +0200573 mutex_unlock(&acm->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800575 return 0;
576
577error_submit_read_urbs:
578 acm->ctrlout = 0;
579 acm_set_control(acm, acm->ctrlout);
580error_set_control:
581 usb_kill_urb(acm->ctrlurb);
582error_submit_urb:
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100583 usb_autopm_put_interface(acm->control);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800584error_get_interface:
585disconnected:
586 mutex_unlock(&acm->mutex);
587 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588}
589
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800590static void acm_port_destruct(struct tty_port *port)
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700591{
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800592 struct acm *acm = container_of(port, struct acm, port);
593
594 dev_dbg(&acm->control->dev, "%s\n", __func__);
David Kubicek61a87ad2005-11-01 18:51:34 +0100595
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700596 tty_unregister_device(acm_tty_driver, acm->minor);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800597 acm_release_minor(acm);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700598 usb_put_intf(acm->control);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100599 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700600 kfree(acm);
601}
602
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800603static void acm_port_shutdown(struct tty_port *port)
Alan Cox10077d42009-06-11 12:36:09 +0100604{
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800605 struct acm *acm = container_of(port, struct acm, port);
Johan Hovolddab54c92011-03-22 11:12:21 +0100606 int i;
607
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800608 dev_dbg(&acm->control->dev, "%s\n", __func__);
609
610 mutex_lock(&acm->mutex);
611 if (!acm->disconnected) {
Alan Cox10077d42009-06-11 12:36:09 +0100612 usb_autopm_get_interface(acm->control);
613 acm_set_control(acm, acm->ctrlout = 0);
Alan Cox10077d42009-06-11 12:36:09 +0100614 usb_kill_urb(acm->ctrlurb);
615 for (i = 0; i < ACM_NW; i++)
616 usb_kill_urb(acm->wb[i].urb);
Johan Hovolddab54c92011-03-22 11:12:21 +0100617 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +0100618 usb_kill_urb(acm->read_urbs[i]);
Alan Cox10077d42009-06-11 12:36:09 +0100619 acm->control->needs_remote_wakeup = 0;
620 usb_autopm_put_interface(acm->control);
621 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800622 mutex_unlock(&acm->mutex);
623}
624
625static void acm_tty_cleanup(struct tty_struct *tty)
626{
627 struct acm *acm = tty->driver_data;
628 dev_dbg(&acm->control->dev, "%s\n", __func__);
629 tty_port_put(&acm->port);
Alan Cox10077d42009-06-11 12:36:09 +0100630}
631
632static void acm_tty_hangup(struct tty_struct *tty)
633{
634 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800635 dev_dbg(&acm->control->dev, "%s\n", __func__);
Alan Cox10077d42009-06-11 12:36:09 +0100636 tty_port_hangup(&acm->port);
Alan Cox10077d42009-06-11 12:36:09 +0100637}
638
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639static void acm_tty_close(struct tty_struct *tty, struct file *filp)
640{
641 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800642 dev_dbg(&acm->control->dev, "%s\n", __func__);
643 tty_port_close(&acm->port, tty, filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644}
645
Alan Cox6e47e062009-06-11 12:37:06 +0100646static int acm_tty_write(struct tty_struct *tty,
647 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648{
649 struct acm *acm = tty->driver_data;
650 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200651 unsigned long flags;
652 int wbn;
653 struct acm_wb *wb;
654
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 if (!count)
656 return 0;
657
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100658 dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100659
Oliver Neukum884b6002005-04-21 21:28:02 +0200660 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100661 wbn = acm_wb_alloc(acm);
662 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200663 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200664 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200666 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667
Oliver Neukum884b6002005-04-21 21:28:02 +0200668 count = (count > acm->writesize) ? acm->writesize : count;
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100669 dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
Oliver Neukum884b6002005-04-21 21:28:02 +0200670 memcpy(wb->buf, buf, count);
671 wb->len = count;
672 spin_unlock_irqrestore(&acm->write_lock, flags);
673
Alan Cox6e47e062009-06-11 12:37:06 +0100674 stat = acm_write_start(acm, wbn);
675 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200676 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 return count;
678}
679
680static int acm_tty_write_room(struct tty_struct *tty)
681{
682 struct acm *acm = tty->driver_data;
Oliver Neukum884b6002005-04-21 21:28:02 +0200683 /*
684 * Do not let the line discipline to know that we have a reserve,
685 * or it might get too enthusiastic.
686 */
David Brownell934da462008-08-06 18:44:12 -0700687 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688}
689
690static int acm_tty_chars_in_buffer(struct tty_struct *tty)
691{
692 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800693 /*
694 * if the device was unplugged then any remaining characters fell out
695 * of the connector ;)
696 */
697 if (acm->disconnected)
Alan Cox23198fd2009-07-20 16:05:27 +0100698 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200699 /*
700 * This is inaccurate (overcounts), but it works.
701 */
Oliver Neukum86478942006-05-13 22:50:47 +0200702 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703}
704
705static void acm_tty_throttle(struct tty_struct *tty)
706{
707 struct acm *acm = tty->driver_data;
Johan Hovold088c64f2011-03-25 11:06:02 +0100708
Johan Hovold088c64f2011-03-25 11:06:02 +0100709 spin_lock_irq(&acm->read_lock);
710 acm->throttle_req = 1;
711 spin_unlock_irq(&acm->read_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712}
713
714static void acm_tty_unthrottle(struct tty_struct *tty)
715{
716 struct acm *acm = tty->driver_data;
Johan Hovold088c64f2011-03-25 11:06:02 +0100717 unsigned int was_throttled;
718
Johan Hovold088c64f2011-03-25 11:06:02 +0100719 spin_lock_irq(&acm->read_lock);
720 was_throttled = acm->throttled;
721 acm->throttled = 0;
722 acm->throttle_req = 0;
723 spin_unlock_irq(&acm->read_lock);
724
725 if (was_throttled)
726 acm_submit_read_urbs(acm, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727}
728
Alan Cox9e989662008-07-22 11:18:03 +0100729static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730{
731 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100732 int retval;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800733
Alan Cox9e989662008-07-22 11:18:03 +0100734 retval = acm_send_break(acm, state ? 0xffff : 0);
735 if (retval < 0)
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100736 dev_dbg(&acm->control->dev, "%s - send break failed\n",
737 __func__);
Alan Cox9e989662008-07-22 11:18:03 +0100738 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739}
740
Alan Cox60b33c12011-02-14 16:26:14 +0000741static int acm_tty_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742{
743 struct acm *acm = tty->driver_data;
744
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
746 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
747 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
748 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
749 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
750 TIOCM_CTS;
751}
752
Alan Cox20b9d172011-02-14 16:26:50 +0000753static int acm_tty_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 unsigned int set, unsigned int clear)
755{
756 struct acm *acm = tty->driver_data;
757 unsigned int newctrl;
758
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100760 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
761 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
762 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
763 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
765 newctrl = (newctrl & ~clear) | set;
766
767 if (acm->ctrlout == newctrl)
768 return 0;
769 return acm_set_control(acm, acm->ctrlout = newctrl);
770}
771
Oliver Neukum18c75722012-02-17 17:21:24 -0500772static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
773{
774 struct serial_struct tmp;
775
776 if (!info)
777 return -EINVAL;
778
779 memset(&tmp, 0, sizeof(tmp));
780 tmp.flags = ASYNC_LOW_LATENCY;
781 tmp.xmit_fifo_size = acm->writesize;
782 tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
783
784 if (copy_to_user(info, &tmp, sizeof(tmp)))
785 return -EFAULT;
786 else
787 return 0;
788}
789
Alan Cox6caa76b2011-02-14 16:27:22 +0000790static int acm_tty_ioctl(struct tty_struct *tty,
Alan Cox6e47e062009-06-11 12:37:06 +0100791 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792{
Oliver Neukum18c75722012-02-17 17:21:24 -0500793 struct acm *acm = tty->driver_data;
794 int rv = -ENOIOCTLCMD;
795
796 switch (cmd) {
797 case TIOCGSERIAL: /* gets serial port data */
798 rv = get_serial_info(acm, (struct serial_struct __user *) arg);
799 break;
800 }
801
802 return rv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803}
804
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100805static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 0, 50, 75, 110, 134, 150, 200, 300, 600,
807 1200, 1800, 2400, 4800, 9600, 19200, 38400,
808 57600, 115200, 230400, 460800, 500000, 576000,
809 921600, 1000000, 1152000, 1500000, 2000000,
810 2500000, 3000000, 3500000, 4000000
811};
812
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100813static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 5, 6, 7, 8
815};
816
Alan Cox6e47e062009-06-11 12:37:06 +0100817static void acm_tty_set_termios(struct tty_struct *tty,
818 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819{
820 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800821 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 struct usb_cdc_line_coding newline;
823 int newctrl = acm->ctrlout;
824
Alan Cox9b80fee2009-09-19 13:13:23 -0700825 newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
827 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100828 (termios->c_cflag & PARODD ? 1 : 2) +
829 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
Alan Cox6e47e062009-06-11 12:37:06 +0100831 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
833
834 if (!newline.dwDTERate) {
835 newline.dwDTERate = acm->line.dwDTERate;
836 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +0100837 } else
838 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
840 if (newctrl != acm->ctrlout)
841 acm_set_control(acm, acm->ctrlout = newctrl);
842
843 if (memcmp(&acm->line, &newline, sizeof newline)) {
844 memcpy(&acm->line, &newline, sizeof newline);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100845 dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
846 __func__,
847 le32_to_cpu(newline.dwDTERate),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 newline.bCharFormat, newline.bParityType,
849 newline.bDataBits);
850 acm_set_line(acm, &acm->line);
851 }
852}
853
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800854static const struct tty_port_operations acm_port_ops = {
855 .shutdown = acm_port_shutdown,
856 .activate = acm_port_activate,
857 .destruct = acm_port_destruct,
858};
859
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860/*
861 * USB probe and disconnect routines.
862 */
863
Oliver Neukum830f4022008-06-25 14:17:16 +0200864/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +0200865static void acm_write_buffers_free(struct acm *acm)
866{
867 int i;
868 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +0200869 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200870
Alan Cox6e47e062009-06-11 12:37:06 +0100871 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Daniel Mack997ea582010-04-12 13:17:25 +0200872 usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +0200873}
874
Oliver Neukum830f4022008-06-25 14:17:16 +0200875static void acm_read_buffers_free(struct acm *acm)
876{
877 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Johan Hovolddab54c92011-03-22 11:12:21 +0100878 int i;
Oliver Neukum830f4022008-06-25 14:17:16 +0200879
Johan Hovolddab54c92011-03-22 11:12:21 +0100880 for (i = 0; i < acm->rx_buflimit; i++)
Daniel Mack997ea582010-04-12 13:17:25 +0200881 usb_free_coherent(usb_dev, acm->readsize,
Johan Hovold088c64f2011-03-25 11:06:02 +0100882 acm->read_buffers[i].base, acm->read_buffers[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +0200883}
884
Oliver Neukum884b6002005-04-21 21:28:02 +0200885/* Little helper: write buffers allocate */
886static int acm_write_buffers_alloc(struct acm *acm)
887{
888 int i;
889 struct acm_wb *wb;
890
Oliver Neukum86478942006-05-13 22:50:47 +0200891 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Daniel Mack997ea582010-04-12 13:17:25 +0200892 wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
Oliver Neukum884b6002005-04-21 21:28:02 +0200893 &wb->dmah);
894 if (!wb->buf) {
895 while (i != 0) {
896 --i;
897 --wb;
Daniel Mack997ea582010-04-12 13:17:25 +0200898 usb_free_coherent(acm->dev, acm->writesize,
Oliver Neukum884b6002005-04-21 21:28:02 +0200899 wb->buf, wb->dmah);
900 }
901 return -ENOMEM;
902 }
903 }
904 return 0;
905}
906
Alan Cox10077d42009-06-11 12:36:09 +0100907static int acm_probe(struct usb_interface *intf,
908 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909{
910 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100911 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -0700912 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 int buflen = intf->altsetting->extralen;
914 struct usb_interface *control_interface;
915 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200916 struct usb_endpoint_descriptor *epctrl = NULL;
917 struct usb_endpoint_descriptor *epread = NULL;
918 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 struct usb_device *usb_dev = interface_to_usbdev(intf);
920 struct acm *acm;
921 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +0100922 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 u8 *buf;
924 u8 ac_management_function = 0;
925 u8 call_management_function = 0;
926 int call_interface_num = -1;
Erik Slagterfd5054c2011-05-11 12:06:55 +0200927 int data_interface_num = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200929 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100930 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200931 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
Oliver Neukum86478942006-05-13 22:50:47 +0200933 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200935 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
936
937 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 if (quirks == NO_UNION_NORMAL) {
939 data_interface = usb_ifnum_to_if(usb_dev, 1);
940 control_interface = usb_ifnum_to_if(usb_dev, 0);
941 goto skip_normal_probe;
942 }
Alan Cox6e47e062009-06-11 12:37:06 +0100943
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 /* normal probing*/
945 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700946 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 return -EINVAL;
948 }
949
950 if (!buflen) {
Toby Gray577045c2010-09-02 10:46:20 +0100951 if (intf->cur_altsetting->endpoint &&
952 intf->cur_altsetting->endpoint->extralen &&
Alan Cox6e47e062009-06-11 12:37:06 +0100953 intf->cur_altsetting->endpoint->extra) {
954 dev_dbg(&intf->dev,
955 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 buflen = intf->cur_altsetting->endpoint->extralen;
957 buffer = intf->cur_altsetting->endpoint->extra;
958 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700959 dev_err(&intf->dev,
960 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 return -EINVAL;
962 }
963 }
964
965 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +0100966 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700967 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 goto next_desc;
969 }
970
Alan Cox6e47e062009-06-11 12:37:06 +0100971 switch (buffer[2]) {
972 case USB_CDC_UNION_TYPE: /* we've found it */
973 if (union_header) {
974 dev_err(&intf->dev, "More than one "
975 "union descriptor, skipping ...\n");
976 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 }
Alan Cox6e47e062009-06-11 12:37:06 +0100978 union_header = (struct usb_cdc_union_desc *)buffer;
979 break;
980 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
981 cfd = (struct usb_cdc_country_functional_desc *)buffer;
982 break;
983 case USB_CDC_HEADER_TYPE: /* maybe check version */
984 break; /* for now we ignore it */
985 case USB_CDC_ACM_TYPE:
986 ac_management_function = buffer[3];
987 break;
988 case USB_CDC_CALL_MANAGEMENT_TYPE:
989 call_management_function = buffer[3];
990 call_interface_num = buffer[4];
Julian Calabyce126642010-01-05 23:58:20 +1100991 if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
Alan Cox6e47e062009-06-11 12:37:06 +0100992 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
993 break;
994 default:
995 /* there are LOTS more CDC descriptors that
996 * could legitimately be found here.
997 */
998 dev_dbg(&intf->dev, "Ignoring descriptor: "
999 "type %02x, length %d\n",
1000 buffer[2], buffer[0]);
1001 break;
1002 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003next_desc:
1004 buflen -= buffer[0];
1005 buffer += buffer[0];
1006 }
1007
1008 if (!union_header) {
1009 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001010 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Erik Slagterfd5054c2011-05-11 12:06:55 +02001011 /* quirks for Droids MuIn LCD */
1012 if (quirks & NO_DATA_INTERFACE)
1013 data_interface = usb_ifnum_to_if(usb_dev, 0);
1014 else
1015 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 control_interface = intf;
1017 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001018 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1019 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1020 return -ENODEV;
1021 } else {
1022 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1023 combined_interfaces = 1;
1024 control_interface = data_interface = intf;
1025 goto look_for_collapsed_interface;
1026 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 }
1028 } else {
1029 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1030 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1031 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001032 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 return -ENODEV;
1034 }
1035 }
Alan Cox6e47e062009-06-11 12:37:06 +01001036
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001038 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001040 if (control_interface == data_interface) {
1041 /* some broken devices designed for windows work this way */
1042 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1043 combined_interfaces = 1;
1044 /* a popular other OS doesn't use it */
1045 quirks |= NO_CAP_LINE;
1046 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1047 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1048 return -EINVAL;
1049 }
1050look_for_collapsed_interface:
1051 for (i = 0; i < 3; i++) {
1052 struct usb_endpoint_descriptor *ep;
1053 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1054
1055 if (usb_endpoint_is_int_in(ep))
1056 epctrl = ep;
1057 else if (usb_endpoint_is_bulk_out(ep))
1058 epwrite = ep;
1059 else if (usb_endpoint_is_bulk_in(ep))
1060 epread = ep;
1061 else
1062 return -EINVAL;
1063 }
1064 if (!epctrl || !epread || !epwrite)
1065 return -ENODEV;
1066 else
1067 goto made_compressed_probe;
1068 }
1069
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070skip_normal_probe:
1071
1072 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001073 if (data_interface->cur_altsetting->desc.bInterfaceClass
1074 != CDC_DATA_INTERFACE_TYPE) {
1075 if (control_interface->cur_altsetting->desc.bInterfaceClass
1076 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001078 dev_dbg(&intf->dev,
1079 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 t = control_interface;
1081 control_interface = data_interface;
1082 data_interface = t;
1083 } else {
1084 return -EINVAL;
1085 }
1086 }
Alan Stern74da5d62007-08-02 13:29:10 -04001087
1088 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001089 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001090 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001091
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001092 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1093 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001094 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 return -EBUSY;
1096 }
1097
1098
1099 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
1100 return -EINVAL;
1101
1102 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1103 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1104 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1105
1106
1107 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001108 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 /* descriptors are swapped */
1110 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001111 dev_dbg(&intf->dev,
1112 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 t = epread;
1114 epread = epwrite;
1115 epwrite = t;
1116 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001117made_compressed_probe:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001118 dev_dbg(&intf->dev, "interfaces are valid\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119
Alan Cox6e47e062009-06-11 12:37:06 +01001120 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1121 if (acm == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001122 dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 goto alloc_fail;
1124 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001126 minor = acm_alloc_minor(acm);
1127 if (minor == ACM_TTY_MINORS) {
1128 dev_err(&intf->dev, "no more free acm devices\n");
1129 kfree(acm);
1130 return -ENODEV;
1131 }
1132
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001133 ctrlsize = usb_endpoint_maxp(epctrl);
1134 readsize = usb_endpoint_maxp(epread) *
Alan Cox6e47e062009-06-11 12:37:06 +01001135 (quirks == SINGLE_RX_URB ? 1 : 2);
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001136 acm->combined_interfaces = combined_interfaces;
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001137 acm->writesize = usb_endpoint_maxp(epwrite) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 acm->control = control_interface;
1139 acm->data = data_interface;
1140 acm->minor = minor;
1141 acm->dev = usb_dev;
1142 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001143 if (quirks & NO_CAP_LINE)
1144 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 acm->ctrlsize = ctrlsize;
1146 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001147 acm->rx_buflimit = num_rx_buf;
David Howellsc4028952006-11-22 14:57:56 +00001148 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum884b6002005-04-21 21:28:02 +02001149 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001150 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001151 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001152 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001153 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1154 if (acm->is_int_ep)
1155 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001156 tty_port_init(&acm->port);
1157 acm->port.ops = &acm_port_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158
Daniel Mack997ea582010-04-12 13:17:25 +02001159 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 if (!buf) {
Johan Hovold255ab562011-03-22 11:12:13 +01001161 dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 goto alloc_fail2;
1163 }
1164 acm->ctrl_buffer = buf;
1165
Oliver Neukum884b6002005-04-21 21:28:02 +02001166 if (acm_write_buffers_alloc(acm) < 0) {
Johan Hovold255ab562011-03-22 11:12:13 +01001167 dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 goto alloc_fail4;
1169 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170
1171 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1172 if (!acm->ctrlurb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001173 dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 goto alloc_fail5;
1175 }
Oliver Neukum86478942006-05-13 22:50:47 +02001176 for (i = 0; i < num_rx_buf; i++) {
Johan Hovold088c64f2011-03-25 11:06:02 +01001177 struct acm_rb *rb = &(acm->read_buffers[i]);
1178 struct urb *urb;
David Kubicek61a87ad2005-11-01 18:51:34 +01001179
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001180 rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
1181 &rb->dma);
1182 if (!rb->base) {
1183 dev_err(&intf->dev, "out of memory "
1184 "(read bufs usb_alloc_coherent)\n");
1185 goto alloc_fail6;
1186 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001187 rb->index = i;
1188 rb->instance = acm;
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001189
Johan Hovold088c64f2011-03-25 11:06:02 +01001190 urb = usb_alloc_urb(0, GFP_KERNEL);
1191 if (!urb) {
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 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001196 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1197 urb->transfer_dma = rb->dma;
1198 if (acm->is_int_ep) {
1199 usb_fill_int_urb(urb, acm->dev,
1200 acm->rx_endpoint,
1201 rb->base,
1202 acm->readsize,
1203 acm_read_bulk_callback, rb,
1204 acm->bInterval);
1205 } else {
1206 usb_fill_bulk_urb(urb, acm->dev,
1207 acm->rx_endpoint,
1208 rb->base,
1209 acm->readsize,
1210 acm_read_bulk_callback, rb);
1211 }
David Kubicek61a87ad2005-11-01 18:51:34 +01001212
Johan Hovold088c64f2011-03-25 11:06:02 +01001213 acm->read_urbs[i] = urb;
1214 __set_bit(i, &acm->read_urbs_free);
David Kubicek61a87ad2005-11-01 18:51:34 +01001215 }
Alan Cox6e47e062009-06-11 12:37:06 +01001216 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001217 struct acm_wb *snd = &(acm->wb[i]);
1218
Alan Cox6e47e062009-06-11 12:37:06 +01001219 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1220 if (snd->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001221 dev_err(&intf->dev,
Johan Hovold59d7fec2011-03-22 11:12:12 +01001222 "out of memory (write urbs usb_alloc_urb)\n");
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001223 goto alloc_fail7;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001224 }
1225
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001226 if (usb_endpoint_xfer_int(epwrite))
1227 usb_fill_int_urb(snd->urb, usb_dev,
1228 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1229 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1230 else
1231 usb_fill_bulk_urb(snd->urb, usb_dev,
1232 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1233 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001234 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1235 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 }
1237
Alan Cox6e47e062009-06-11 12:37:06 +01001238 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001239
1240 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1241 if (i < 0)
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001242 goto alloc_fail7;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001243
1244 if (cfd) { /* export the country data */
1245 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1246 if (!acm->country_codes)
1247 goto skip_countries;
1248 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001249 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1250 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001251 acm->country_rel_date = cfd->iCountryCodeRelDate;
1252
1253 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1254 if (i < 0) {
1255 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001256 acm->country_codes = NULL;
1257 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001258 goto skip_countries;
1259 }
1260
Alan Cox6e47e062009-06-11 12:37:06 +01001261 i = device_create_file(&intf->dev,
1262 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001263 if (i < 0) {
Axel Linc2572b72010-05-31 08:04:47 +08001264 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001265 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001266 acm->country_codes = NULL;
1267 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001268 goto skip_countries;
1269 }
1270 }
1271
1272skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001273 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001274 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1275 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1276 /* works around buggy devices */
1277 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1279 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1280
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1282
1283 acm_set_control(acm, acm->ctrlout);
1284
1285 acm->line.dwDTERate = cpu_to_le32(9600);
1286 acm->line.bDataBits = 8;
1287 acm_set_line(acm, &acm->line);
1288
1289 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001290 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001292 usb_get_intf(control_interface);
1293 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001295 return 0;
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001296alloc_fail7:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001297 for (i = 0; i < ACM_NW; i++)
1298 usb_free_urb(acm->wb[i].urb);
Axel Linc2572b72010-05-31 08:04:47 +08001299alloc_fail6:
Oliver Neukum86478942006-05-13 22:50:47 +02001300 for (i = 0; i < num_rx_buf; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001301 usb_free_urb(acm->read_urbs[i]);
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001302 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 usb_free_urb(acm->ctrlurb);
1304alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001305 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306alloc_fail4:
Daniel Mack997ea582010-04-12 13:17:25 +02001307 usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308alloc_fail2:
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001309 acm_release_minor(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 kfree(acm);
1311alloc_fail:
1312 return -ENOMEM;
1313}
1314
Oliver Neukum1365baf2007-10-12 17:24:28 +02001315static void stop_data_traffic(struct acm *acm)
1316{
1317 int i;
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001318
1319 dev_dbg(&acm->control->dev, "%s\n", __func__);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001320
Oliver Neukum1365baf2007-10-12 17:24:28 +02001321 usb_kill_urb(acm->ctrlurb);
Alan Cox6e47e062009-06-11 12:37:06 +01001322 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001323 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001324 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001325 usb_kill_urb(acm->read_urbs[i]);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001326
1327 cancel_work_sync(&acm->work);
1328}
1329
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330static void acm_disconnect(struct usb_interface *intf)
1331{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001332 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001334 struct tty_struct *tty;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001335 int i;
1336
1337 dev_dbg(&intf->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338
David Brownell672c4e12008-08-06 18:41:12 -07001339 /* sibling interface is already cleaning up */
1340 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001341 return;
David Brownell672c4e12008-08-06 18:41:12 -07001342
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001343 mutex_lock(&acm->mutex);
1344 acm->disconnected = true;
Alan Cox6e47e062009-06-11 12:37:06 +01001345 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001346 device_remove_file(&acm->control->dev,
1347 &dev_attr_wCountryCodes);
1348 device_remove_file(&acm->control->dev,
1349 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001350 }
Alan Stern74da5d62007-08-02 13:29:10 -04001351 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001352 usb_set_intfdata(acm->control, NULL);
1353 usb_set_intfdata(acm->data, NULL);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001354 mutex_unlock(&acm->mutex);
1355
1356 tty = tty_port_tty_get(&acm->port);
1357 if (tty) {
1358 tty_vhangup(tty);
1359 tty_kref_put(tty);
1360 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
Oliver Neukum1365baf2007-10-12 17:24:28 +02001362 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001364 usb_free_urb(acm->ctrlurb);
1365 for (i = 0; i < ACM_NW; i++)
1366 usb_free_urb(acm->wb[i].urb);
1367 for (i = 0; i < acm->rx_buflimit; i++)
1368 usb_free_urb(acm->read_urbs[i]);
Oliver Neukum884b6002005-04-21 21:28:02 +02001369 acm_write_buffers_free(acm);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001370 usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001371 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001373 if (!acm->combined_interfaces)
1374 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001375 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001377 tty_port_put(&acm->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378}
1379
Oliver Neukum35758582008-07-01 19:10:08 +02001380#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001381static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1382{
1383 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001384 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001385
Alan Stern5b1b0b82011-08-19 23:49:48 +02001386 if (PMSG_IS_AUTO(message)) {
Oliver Neukum11ea8592008-06-20 11:25:57 +02001387 int b;
1388
Johan Hovold088c64f2011-03-25 11:06:02 +01001389 spin_lock_irq(&acm->write_lock);
1390 b = acm->transmitting;
1391 spin_unlock_irq(&acm->write_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001392 if (b)
1393 return -EBUSY;
1394 }
1395
1396 spin_lock_irq(&acm->read_lock);
1397 spin_lock(&acm->write_lock);
1398 cnt = acm->susp_count++;
1399 spin_unlock(&acm->write_lock);
1400 spin_unlock_irq(&acm->read_lock);
1401
1402 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001403 return 0;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001404
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001405 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags))
Oliver Neukum1365baf2007-10-12 17:24:28 +02001406 stop_data_traffic(acm);
1407
Oliver Neukum1365baf2007-10-12 17:24:28 +02001408 return 0;
1409}
1410
1411static int acm_resume(struct usb_interface *intf)
1412{
1413 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001414 struct acm_wb *wb;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001415 int rv = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +02001416 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001417
Oliver Neukum11ea8592008-06-20 11:25:57 +02001418 spin_lock_irq(&acm->read_lock);
1419 acm->susp_count -= 1;
1420 cnt = acm->susp_count;
1421 spin_unlock_irq(&acm->read_lock);
1422
1423 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001424 return 0;
1425
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001426 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
Oliver Neukum1365baf2007-10-12 17:24:28 +02001427 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001428
1429 spin_lock_irq(&acm->write_lock);
1430 if (acm->delayed_wb) {
1431 wb = acm->delayed_wb;
1432 acm->delayed_wb = NULL;
1433 spin_unlock_irq(&acm->write_lock);
Oliver Neukumf0730922010-03-03 00:37:56 +01001434 acm_start_wb(acm, wb);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001435 } else {
1436 spin_unlock_irq(&acm->write_lock);
1437 }
1438
1439 /*
1440 * delayed error checking because we must
1441 * do the write path at all cost
1442 */
Oliver Neukum1365baf2007-10-12 17:24:28 +02001443 if (rv < 0)
Oliver Neukum11ea8592008-06-20 11:25:57 +02001444 goto err_out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001445
Johan Hovold088c64f2011-03-25 11:06:02 +01001446 rv = acm_submit_read_urbs(acm, GFP_NOIO);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001447 }
1448
1449err_out:
Oliver Neukum1365baf2007-10-12 17:24:28 +02001450 return rv;
1451}
Oliver Neukum35758582008-07-01 19:10:08 +02001452
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001453static int acm_reset_resume(struct usb_interface *intf)
1454{
1455 struct acm *acm = usb_get_intfdata(intf);
1456 struct tty_struct *tty;
1457
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001458 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001459 tty = tty_port_tty_get(&acm->port);
1460 if (tty) {
1461 tty_hangup(tty);
1462 tty_kref_put(tty);
1463 }
1464 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001465
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001466 return acm_resume(intf);
1467}
1468
Oliver Neukum35758582008-07-01 19:10:08 +02001469#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001470
1471#define NOKIA_PCSUITE_ACM_INFO(x) \
1472 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1473 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1474 USB_CDC_ACM_PROTO_VENDOR)
1475
Toby Gray4035e452010-09-01 16:01:19 +01001476#define SAMSUNG_PCSUITE_ACM_INFO(x) \
1477 USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
1478 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1479 USB_CDC_ACM_PROTO_VENDOR)
1480
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481/*
1482 * USB driver structure.
1483 */
1484
Németh Márton6ef48522010-01-10 15:33:45 +01001485static const struct usb_device_id acm_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 /* quirky and broken devices */
1487 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1488 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1489 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001490 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1491 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1492 },
Andrew Lunn0f9c7b42008-12-23 17:31:23 +01001493 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1494 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1495 },
Masahito Omote8753e652005-07-29 12:17:25 -07001496 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1497 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1498 },
Chris Malley91a9c922006-10-03 10:08:28 +01001499 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1500 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1501 },
Alan Cox7abcf202009-04-06 17:35:01 +01001502 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1503 .driver_info = SINGLE_RX_URB,
1504 },
Oliver Neukum86478942006-05-13 22:50:47 +02001505 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1506 .driver_info = SINGLE_RX_URB, /* firmware bug */
1507 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001508 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1509 .driver_info = SINGLE_RX_URB, /* firmware bug */
1510 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001511 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1512 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1513 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001514 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1515 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1516 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001517 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1518 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1519 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001520 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1521 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1522 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001523 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1524 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1525 },
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001526 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1527 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001528 /* Motorola H24 HSPA module: */
1529 { USB_DEVICE(0x22b8, 0x2d91) }, /* modem */
1530 { USB_DEVICE(0x22b8, 0x2d92) }, /* modem + diagnostics */
1531 { USB_DEVICE(0x22b8, 0x2d93) }, /* modem + AT port */
1532 { USB_DEVICE(0x22b8, 0x2d95) }, /* modem + AT port + diagnostics */
1533 { USB_DEVICE(0x22b8, 0x2d96) }, /* modem + NMEA */
1534 { USB_DEVICE(0x22b8, 0x2d97) }, /* modem + diagnostics + NMEA */
1535 { USB_DEVICE(0x22b8, 0x2d99) }, /* modem + AT port + NMEA */
1536 { USB_DEVICE(0x22b8, 0x2d9a) }, /* modem + AT port + diagnostics + NMEA */
1537
Adam Richterc332b4e2009-02-18 16:17:15 -08001538 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1539 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1540 data interface instead of
1541 communications interface.
1542 Maybe we should define a new
1543 quirk for this. */
1544 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001545 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1546 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1547 },
Russ Nelsonc3baa192010-04-21 23:07:03 -04001548 { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1549 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1550 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001551
Adrian Taylorc1479a92009-11-19 10:35:33 +00001552 /* Nokia S60 phones expose two ACM channels. The first is
1553 * a modem and is picked up by the standard AT-command
1554 * information below. The second is 'vendor-specific' but
1555 * is treated as a serial device at the S60 end, so we want
1556 * to expose it on Linux too. */
1557 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1558 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1559 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1560 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1561 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1562 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1563 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1564 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1565 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1566 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1567 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1568 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1569 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1570 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1571 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1572 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1573 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1574 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1575 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1576 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1577 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1578 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1579 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1580 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1581 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1582 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1583 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1584 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1585 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1586 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1587 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1588 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1589 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1590 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1591 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1592 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1593 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1594 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1595 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1596 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1597 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1598 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1599 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
Przemo Firszt83a4eae2010-06-28 21:29:34 +01001600 { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
Toby Gray4035e452010-09-01 16:01:19 +01001601 { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
1602 { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
1603 { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
1604 { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
1605 { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
1606 { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
1607 { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
1608 { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
1609 { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
1610 { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
Arvid Ephraim Picciani721d92f2011-01-25 15:58:40 +01001611 { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
Toby Gray4061fde2011-06-06 14:52:48 +01001612 { NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */
1613 { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
Toby Gray4035e452010-09-01 16:01:19 +01001614 { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001615
Denis Pershin65e52f42011-09-04 17:37:21 +07001616 /* Support for Owen devices */
1617 { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */
1618
Adrian Taylorc1479a92009-11-19 10:35:33 +00001619 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1620
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001621 /* Support Lego NXT using pbLua firmware */
Julian Calabyce126642010-01-05 23:58:20 +11001622 { USB_DEVICE(0x0694, 0xff00),
1623 .driver_info = NOT_A_MODEM,
Otavio Salvador7893afc2010-09-26 23:35:05 -03001624 },
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001625
Erik Slagterfd5054c2011-05-11 12:06:55 +02001626 /* Support for Droids MuIn LCD */
1627 { USB_DEVICE(0x04d8, 0x000b),
1628 .driver_info = NO_DATA_INTERFACE,
1629 },
1630
Philippe Corbes5b239f02010-08-31 19:31:32 +02001631 /* control interfaces without any protocol set */
1632 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1633 USB_CDC_PROTO_NONE) },
1634
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635 /* control interfaces with various AT-command sets */
1636 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1637 USB_CDC_ACM_PROTO_AT_V25TER) },
1638 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1639 USB_CDC_ACM_PROTO_AT_PCCA101) },
1640 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1641 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1642 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1643 USB_CDC_ACM_PROTO_AT_GSM) },
1644 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001645 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1647 USB_CDC_ACM_PROTO_AT_CDMA) },
1648
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649 { }
1650};
1651
Alan Cox6e47e062009-06-11 12:37:06 +01001652MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
1654static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 .name = "cdc_acm",
1656 .probe = acm_probe,
1657 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001658#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001659 .suspend = acm_suspend,
1660 .resume = acm_resume,
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001661 .reset_resume = acm_reset_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001662#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001664#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001665 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001666#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667};
1668
1669/*
1670 * TTY driver structures.
1671 */
1672
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001673static const struct tty_operations acm_ops = {
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001674 .install = acm_tty_install,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675 .open = acm_tty_open,
1676 .close = acm_tty_close,
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001677 .cleanup = acm_tty_cleanup,
Alan Cox10077d42009-06-11 12:36:09 +01001678 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679 .write = acm_tty_write,
1680 .write_room = acm_tty_write_room,
1681 .ioctl = acm_tty_ioctl,
1682 .throttle = acm_tty_throttle,
1683 .unthrottle = acm_tty_unthrottle,
1684 .chars_in_buffer = acm_tty_chars_in_buffer,
1685 .break_ctl = acm_tty_break_ctl,
1686 .set_termios = acm_tty_set_termios,
1687 .tiocmget = acm_tty_tiocmget,
1688 .tiocmset = acm_tty_tiocmset,
1689};
1690
1691/*
1692 * Init / exit.
1693 */
1694
1695static int __init acm_init(void)
1696{
1697 int retval;
1698 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1699 if (!acm_tty_driver)
1700 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 acm_tty_driver->driver_name = "acm",
1702 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703 acm_tty_driver->major = ACM_TTY_MAJOR,
1704 acm_tty_driver->minor_start = 0,
1705 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1706 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001707 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001709 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1710 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 tty_set_operations(acm_tty_driver, &acm_ops);
1712
1713 retval = tty_register_driver(acm_tty_driver);
1714 if (retval) {
1715 put_tty_driver(acm_tty_driver);
1716 return retval;
1717 }
1718
1719 retval = usb_register(&acm_driver);
1720 if (retval) {
1721 tty_unregister_driver(acm_tty_driver);
1722 put_tty_driver(acm_tty_driver);
1723 return retval;
1724 }
1725
Johan Hovolda2c7b932011-03-22 11:12:18 +01001726 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727
1728 return 0;
1729}
1730
1731static void __exit acm_exit(void)
1732{
1733 usb_deregister(&acm_driver);
1734 tty_unregister_driver(acm_tty_driver);
1735 put_tty_driver(acm_tty_driver);
1736}
1737
1738module_init(acm_init);
1739module_exit(acm_exit);
1740
Alan Cox6e47e062009-06-11 12:37:06 +01001741MODULE_AUTHOR(DRIVER_AUTHOR);
1742MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01001744MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);