blob: 2f2540ff21f65190eedefa79ecf79b884f7471cf [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{
Johan Hovold9f81ca82014-05-26 19:23:39 +0200126 int retval;
127
128 retval = usb_autopm_get_interface(acm->control);
129 if (retval)
130 return retval;
131
132 retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 request, USB_RT_ACM, value,
134 acm->control->altsetting[0].desc.bInterfaceNumber,
135 buf, len, 5000);
Johan Hovold9f81ca82014-05-26 19:23:39 +0200136
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100137 dev_dbg(&acm->control->dev,
138 "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
139 __func__, request, value, len, retval);
Johan Hovold9f81ca82014-05-26 19:23:39 +0200140
141 usb_autopm_put_interface(acm->control);
142
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 return retval < 0 ? retval : 0;
144}
145
146/* devices aren't required to support these requests.
147 * the cdc acm descriptor tells whether they do...
148 */
149#define acm_set_control(acm, control) \
150 acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
151#define acm_set_line(acm, line) \
152 acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
153#define acm_send_break(acm, ms) \
154 acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
155
156/*
Oliver Neukum884b6002005-04-21 21:28:02 +0200157 * Write buffer management.
158 * All of these assume proper locks taken by the caller.
159 */
160
161static int acm_wb_alloc(struct acm *acm)
162{
163 int i, wbn;
164 struct acm_wb *wb;
165
David Engrafe4cf3aa2008-03-20 10:01:34 +0100166 wbn = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200167 i = 0;
168 for (;;) {
169 wb = &acm->wb[wbn];
170 if (!wb->use) {
171 wb->use = 1;
172 return wbn;
173 }
Oliver Neukum86478942006-05-13 22:50:47 +0200174 wbn = (wbn + 1) % ACM_NW;
175 if (++i >= ACM_NW)
Oliver Neukum884b6002005-04-21 21:28:02 +0200176 return -1;
177 }
178}
179
Oliver Neukum884b6002005-04-21 21:28:02 +0200180static int acm_wb_is_avail(struct acm *acm)
181{
182 int i, n;
David Brownelle5fbab52008-08-06 18:46:10 -0700183 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200184
Oliver Neukum86478942006-05-13 22:50:47 +0200185 n = ACM_NW;
David Brownelle5fbab52008-08-06 18:46:10 -0700186 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100187 for (i = 0; i < ACM_NW; i++)
Oliver Neukum86478942006-05-13 22:50:47 +0200188 n -= acm->wb[i].use;
David Brownelle5fbab52008-08-06 18:46:10 -0700189 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200190 return n;
191}
192
Oliver Neukum884b6002005-04-21 21:28:02 +0200193/*
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800194 * Finish write. Caller must hold acm->write_lock
Oliver Neukum884b6002005-04-21 21:28:02 +0200195 */
David Engrafe4cf3aa2008-03-20 10:01:34 +0100196static void acm_write_done(struct acm *acm, struct acm_wb *wb)
Oliver Neukum884b6002005-04-21 21:28:02 +0200197{
David Engrafe4cf3aa2008-03-20 10:01:34 +0100198 wb->use = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200199 acm->transmitting--;
Oliver Neukum97d35f92009-12-16 17:05:57 +0100200 usb_autopm_put_interface_async(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200201}
202
203/*
204 * Poke write.
Oliver Neukum11ea8592008-06-20 11:25:57 +0200205 *
206 * the caller is responsible for locking
Oliver Neukum884b6002005-04-21 21:28:02 +0200207 */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200208
209static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
210{
211 int rc;
212
213 acm->transmitting++;
214
215 wb->urb->transfer_buffer = wb->buf;
216 wb->urb->transfer_dma = wb->dmah;
217 wb->urb->transfer_buffer_length = wb->len;
218 wb->urb->dev = acm->dev;
219
Alan Cox6e47e062009-06-11 12:37:06 +0100220 rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
221 if (rc < 0) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100222 dev_err(&acm->data->dev,
223 "%s - usb_submit_urb(write bulk) failed: %d\n",
224 __func__, rc);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200225 acm_write_done(acm, wb);
226 }
227 return rc;
228}
229
David Engrafe4cf3aa2008-03-20 10:01:34 +0100230static int acm_write_start(struct acm *acm, int wbn)
Oliver Neukum884b6002005-04-21 21:28:02 +0200231{
232 unsigned long flags;
David Brownell934da462008-08-06 18:44:12 -0700233 struct acm_wb *wb = &acm->wb[wbn];
Oliver Neukum884b6002005-04-21 21:28:02 +0200234 int rc;
235
236 spin_lock_irqsave(&acm->write_lock, flags);
237 if (!acm->dev) {
David Brownell934da462008-08-06 18:44:12 -0700238 wb->use = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200239 spin_unlock_irqrestore(&acm->write_lock, flags);
240 return -ENODEV;
241 }
242
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100243 dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100244 acm->susp_count);
Oliver Neukum97d35f92009-12-16 17:05:57 +0100245 usb_autopm_get_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200246 if (acm->susp_count) {
Johan Hovold21ffba32014-05-26 19:23:38 +0200247 usb_anchor_urb(wb->urb, &acm->delayed);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200248 spin_unlock_irqrestore(&acm->write_lock, flags);
Johan Hovold21ffba32014-05-26 19:23:38 +0200249 return 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200250 }
251 usb_mark_last_busy(acm->dev);
252
Oliver Neukum11ea8592008-06-20 11:25:57 +0200253 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200254 spin_unlock_irqrestore(&acm->write_lock, flags);
255
Oliver Neukum884b6002005-04-21 21:28:02 +0200256 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200257
Oliver Neukum884b6002005-04-21 21:28:02 +0200258}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100259/*
260 * attributes exported through sysfs
261 */
262static ssize_t show_caps
263(struct device *dev, struct device_attribute *attr, char *buf)
264{
265 struct usb_interface *intf = to_usb_interface(dev);
266 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200267
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100268 return sprintf(buf, "%d", acm->ctrl_caps);
269}
270static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
271
272static ssize_t show_country_codes
273(struct device *dev, struct device_attribute *attr, char *buf)
274{
275 struct usb_interface *intf = to_usb_interface(dev);
276 struct acm *acm = usb_get_intfdata(intf);
277
278 memcpy(buf, acm->country_codes, acm->country_code_size);
279 return acm->country_code_size;
280}
281
282static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
283
284static ssize_t show_country_rel_date
285(struct device *dev, struct device_attribute *attr, char *buf)
286{
287 struct usb_interface *intf = to_usb_interface(dev);
288 struct acm *acm = usb_get_intfdata(intf);
289
290 return sprintf(buf, "%d", acm->country_rel_date);
291}
292
293static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200294/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 * Interrupt handlers for various ACM device responses
296 */
297
298/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100299static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300{
301 struct acm *acm = urb->context;
302 struct usb_cdc_notification *dr = urb->transfer_buffer;
Alan Cox10077d42009-06-11 12:36:09 +0100303 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 unsigned char *data;
305 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700306 int retval;
307 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700309 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 case 0:
311 /* success */
312 break;
313 case -ECONNRESET:
314 case -ENOENT:
315 case -ESHUTDOWN:
316 /* this urb is terminated, clean up */
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100317 dev_dbg(&acm->control->dev,
318 "%s - urb shutting down with status: %d\n",
319 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 return;
321 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100322 dev_dbg(&acm->control->dev,
323 "%s - nonzero urb status received: %d\n",
324 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 goto exit;
326 }
327
Johan Hovold7e7797e2011-03-22 11:12:11 +0100328 usb_mark_last_busy(acm->dev);
329
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 data = (unsigned char *)(dr + 1);
331 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100332 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100333 dev_dbg(&acm->control->dev, "%s - network connection: %d\n",
334 __func__, dr->wValue);
Alan Cox6e47e062009-06-11 12:37:06 +0100335 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
Alan Cox6e47e062009-06-11 12:37:06 +0100337 case USB_CDC_NOTIFY_SERIAL_STATE:
338 tty = tty_port_tty_get(&acm->port);
339 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
Alan Cox6e47e062009-06-11 12:37:06 +0100341 if (tty) {
342 if (!acm->clocal &&
343 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100344 dev_dbg(&acm->control->dev,
345 "%s - calling hangup\n", __func__);
Alan Cox6e47e062009-06-11 12:37:06 +0100346 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 }
Alan Cox6e47e062009-06-11 12:37:06 +0100348 tty_kref_put(tty);
349 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
Alan Cox6e47e062009-06-11 12:37:06 +0100351 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100353 dev_dbg(&acm->control->dev,
354 "%s - input control lines: dcd%c dsr%c break%c "
355 "ring%c framing%c parity%c overrun%c\n",
356 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100357 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
358 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
359 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
360 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
361 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
362 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
363 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 break;
365
Alan Cox6e47e062009-06-11 12:37:06 +0100366 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100367 dev_dbg(&acm->control->dev,
368 "%s - unknown notification %d received: index %d "
369 "len %d data0 %d data1 %d\n",
370 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100371 dr->bNotificationType, dr->wIndex,
372 dr->wLength, data[0], data[1]);
373 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 }
375exit:
Alan Cox6e47e062009-06-11 12:37:06 +0100376 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700377 if (retval)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100378 dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
379 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380}
381
Johan Hovold088c64f2011-03-25 11:06:02 +0100382static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383{
Johan Hovold088c64f2011-03-25 11:06:02 +0100384 int res;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700385
Johan Hovold088c64f2011-03-25 11:06:02 +0100386 if (!test_and_clear_bit(index, &acm->read_urbs_free))
387 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
Johan Hovold088c64f2011-03-25 11:06:02 +0100389 dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
390
391 res = usb_submit_urb(acm->read_urbs[index], mem_flags);
392 if (res) {
393 if (res != -EPERM) {
394 dev_err(&acm->data->dev,
395 "%s - usb_submit_urb failed: %d\n",
396 __func__, res);
397 }
398 set_bit(index, &acm->read_urbs_free);
399 return res;
400 }
401
402 return 0;
403}
404
405static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
406{
407 int res;
408 int i;
409
410 for (i = 0; i < acm->rx_buflimit; ++i) {
411 res = acm_submit_read_urb(acm, i, mem_flags);
412 if (res)
413 return res;
414 }
415
416 return 0;
417}
418
419static void acm_process_read_urb(struct acm *acm, struct urb *urb)
420{
421 struct tty_struct *tty;
422
423 if (!urb->actual_length)
424 return;
425
426 tty = tty_port_tty_get(&acm->port);
427 if (!tty)
428 return;
429
430 tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
431 tty_flip_buffer_push(tty);
432
433 tty_kref_put(tty);
434}
435
436static void acm_read_bulk_callback(struct urb *urb)
437{
438 struct acm_rb *rb = urb->context;
439 struct acm *acm = rb->instance;
440 unsigned long flags;
441
442 dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
443 rb->index, urb->actual_length);
444 set_bit(rb->index, &acm->read_urbs_free);
445
446 if (!acm->dev) {
447 dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200449 }
450 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451
Johan Hovold088c64f2011-03-25 11:06:02 +0100452 if (urb->status) {
Johan Hovold1d9846e2011-03-22 11:12:14 +0100453 dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
Johan Hovold088c64f2011-03-25 11:06:02 +0100454 __func__, urb->status);
455 return;
456 }
457 acm_process_read_urb(acm, urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
Johan Hovold088c64f2011-03-25 11:06:02 +0100459 /* throttle device if requested by tty */
460 spin_lock_irqsave(&acm->read_lock, flags);
461 acm->throttled = acm->throttle_req;
462 if (!acm->throttled && !acm->susp_count) {
463 spin_unlock_irqrestore(&acm->read_lock, flags);
464 acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
Oliver Neukum86478942006-05-13 22:50:47 +0200465 } else {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200466 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468}
469
470/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100471static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472{
Ming Leicdc97792008-02-24 18:41:47 +0800473 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700474 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800475 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200476
Johan Hovold4fa46262011-03-22 11:12:16 +0100477 if (urb->status || (urb->actual_length != urb->transfer_buffer_length))
478 dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
Johan Hovold1d9846e2011-03-22 11:12:14 +0100479 __func__,
David Brownelle5fbab52008-08-06 18:46:10 -0700480 urb->actual_length,
481 urb->transfer_buffer_length,
482 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800484 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100485 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800486 spin_unlock_irqrestore(&acm->write_lock, flags);
Havard Skinnemoen99823f42011-12-09 16:51:54 -0800487 schedule_work(&acm->work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488}
489
David Howellsc4028952006-11-22 14:57:56 +0000490static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491{
David Howellsc4028952006-11-22 14:57:56 +0000492 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100493 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700494
Johan Hovold1d9846e2011-03-22 11:12:14 +0100495 dev_vdbg(&acm->data->dev, "%s\n", __func__);
496
Alan Cox10077d42009-06-11 12:36:09 +0100497 tty = tty_port_tty_get(&acm->port);
Johan Hovold15e5bee2011-03-22 11:12:10 +0100498 if (!tty)
499 return;
Alan Cox10077d42009-06-11 12:36:09 +0100500 tty_wakeup(tty);
501 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502}
503
504/*
505 * TTY handlers
506 */
507
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800508static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509{
510 struct acm *acm;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800511 int retval;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100512
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800513 dev_dbg(tty->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800515 acm = acm_get_by_index(tty->index);
516 if (!acm)
517 return -ENODEV;
518
Jiri Slabyf8a8c102012-03-05 14:51:48 +0100519 retval = tty_standard_install(driver, tty);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800520 if (retval)
521 goto error_init_termios;
522
523 tty->driver_data = acm;
524
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800525 return 0;
526
527error_init_termios:
528 tty_port_put(&acm->port);
529 return retval;
530}
531
532static int acm_tty_open(struct tty_struct *tty, struct file *filp)
533{
534 struct acm *acm = tty->driver_data;
535
536 dev_dbg(tty->dev, "%s\n", __func__);
537
538 return tty_port_open(&acm->port, tty, filp);
539}
540
541static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
542{
543 struct acm *acm = container_of(port, struct acm, port);
544 int retval = -ENODEV;
Johan Hovold8fd20d52014-05-26 19:23:44 +0200545 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100547 dev_dbg(&acm->control->dev, "%s\n", __func__);
548
Oliver Neukum1365baf2007-10-12 17:24:28 +0200549 mutex_lock(&acm->mutex);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800550 if (acm->disconnected)
551 goto disconnected;
552
553 retval = usb_autopm_get_interface(acm->control);
554 if (retval)
555 goto error_get_interface;
556
557 /*
558 * FIXME: Why do we need this? Allocating 64K of physically contiguous
559 * memory is really nasty...
560 */
561 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
562 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200563
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 acm->ctrlurb->dev = acm->dev;
565 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100566 dev_err(&acm->control->dev,
567 "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800568 goto error_submit_urb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 }
570
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800571 acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS;
572 if (acm_set_control(acm, acm->ctrlout) < 0 &&
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100573 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800574 goto error_set_control;
Alan Cox10077d42009-06-11 12:36:09 +0100575
Oliver Neukum11ea8592008-06-20 11:25:57 +0200576 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Otto Meta9cc2d462012-06-06 18:46:21 +0200578 /*
579 * Unthrottle device in case the TTY was closed while throttled.
580 */
581 spin_lock_irq(&acm->read_lock);
582 acm->throttled = 0;
583 acm->throttle_req = 0;
584 spin_unlock_irq(&acm->read_lock);
585
Johan Hovold088c64f2011-03-25 11:06:02 +0100586 if (acm_submit_read_urbs(acm, GFP_KERNEL))
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800587 goto error_submit_read_urbs;
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100588
Oliver Neukum1365baf2007-10-12 17:24:28 +0200589 mutex_unlock(&acm->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800591 return 0;
592
593error_submit_read_urbs:
Johan Hovold8fd20d52014-05-26 19:23:44 +0200594 for (i = 0; i < acm->rx_buflimit; i++)
595 usb_kill_urb(acm->read_urbs[i]);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800596 acm->ctrlout = 0;
597 acm_set_control(acm, acm->ctrlout);
598error_set_control:
599 usb_kill_urb(acm->ctrlurb);
600error_submit_urb:
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100601 usb_autopm_put_interface(acm->control);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800602error_get_interface:
603disconnected:
604 mutex_unlock(&acm->mutex);
605 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606}
607
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800608static void acm_port_destruct(struct tty_port *port)
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700609{
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800610 struct acm *acm = container_of(port, struct acm, port);
611
612 dev_dbg(&acm->control->dev, "%s\n", __func__);
David Kubicek61a87ad2005-11-01 18:51:34 +0100613
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800614 acm_release_minor(acm);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700615 usb_put_intf(acm->control);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100616 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700617 kfree(acm);
618}
619
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800620static void acm_port_shutdown(struct tty_port *port)
Alan Cox10077d42009-06-11 12:36:09 +0100621{
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800622 struct acm *acm = container_of(port, struct acm, port);
Johan Hovold21ffba32014-05-26 19:23:38 +0200623 struct urb *urb;
624 struct acm_wb *wb;
Johan Hovolddab54c92011-03-22 11:12:21 +0100625 int i;
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200626 int pm_err;
Johan Hovolddab54c92011-03-22 11:12:21 +0100627
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800628 dev_dbg(&acm->control->dev, "%s\n", __func__);
629
630 mutex_lock(&acm->mutex);
631 if (!acm->disconnected) {
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200632 pm_err = usb_autopm_get_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100633 acm_set_control(acm, acm->ctrlout = 0);
Johan Hovold21ffba32014-05-26 19:23:38 +0200634
635 for (;;) {
636 urb = usb_get_from_anchor(&acm->delayed);
637 if (!urb)
638 break;
639 wb = urb->context;
640 wb->use = 0;
641 usb_autopm_put_interface_async(acm->control);
642 }
643
Alan Cox10077d42009-06-11 12:36:09 +0100644 usb_kill_urb(acm->ctrlurb);
645 for (i = 0; i < ACM_NW; i++)
646 usb_kill_urb(acm->wb[i].urb);
Johan Hovolddab54c92011-03-22 11:12:21 +0100647 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +0100648 usb_kill_urb(acm->read_urbs[i]);
Alan Cox10077d42009-06-11 12:36:09 +0100649 acm->control->needs_remote_wakeup = 0;
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200650 if (!pm_err)
651 usb_autopm_put_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100652 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800653 mutex_unlock(&acm->mutex);
654}
655
656static void acm_tty_cleanup(struct tty_struct *tty)
657{
658 struct acm *acm = tty->driver_data;
659 dev_dbg(&acm->control->dev, "%s\n", __func__);
660 tty_port_put(&acm->port);
Alan Cox10077d42009-06-11 12:36:09 +0100661}
662
663static void acm_tty_hangup(struct tty_struct *tty)
664{
665 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800666 dev_dbg(&acm->control->dev, "%s\n", __func__);
Alan Cox10077d42009-06-11 12:36:09 +0100667 tty_port_hangup(&acm->port);
Alan Cox10077d42009-06-11 12:36:09 +0100668}
669
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670static void acm_tty_close(struct tty_struct *tty, struct file *filp)
671{
672 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800673 dev_dbg(&acm->control->dev, "%s\n", __func__);
674 tty_port_close(&acm->port, tty, filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675}
676
Alan Cox6e47e062009-06-11 12:37:06 +0100677static int acm_tty_write(struct tty_struct *tty,
678 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679{
680 struct acm *acm = tty->driver_data;
681 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200682 unsigned long flags;
683 int wbn;
684 struct acm_wb *wb;
685
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 if (!count)
687 return 0;
688
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100689 dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100690
Oliver Neukum884b6002005-04-21 21:28:02 +0200691 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100692 wbn = acm_wb_alloc(acm);
693 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200694 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200695 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200697 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Oliver Neukum884b6002005-04-21 21:28:02 +0200699 count = (count > acm->writesize) ? acm->writesize : count;
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100700 dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
Oliver Neukum884b6002005-04-21 21:28:02 +0200701 memcpy(wb->buf, buf, count);
702 wb->len = count;
703 spin_unlock_irqrestore(&acm->write_lock, flags);
704
Alan Cox6e47e062009-06-11 12:37:06 +0100705 stat = acm_write_start(acm, wbn);
706 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200707 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 return count;
709}
710
711static int acm_tty_write_room(struct tty_struct *tty)
712{
713 struct acm *acm = tty->driver_data;
Oliver Neukum884b6002005-04-21 21:28:02 +0200714 /*
715 * Do not let the line discipline to know that we have a reserve,
716 * or it might get too enthusiastic.
717 */
David Brownell934da462008-08-06 18:44:12 -0700718 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719}
720
721static int acm_tty_chars_in_buffer(struct tty_struct *tty)
722{
723 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800724 /*
725 * if the device was unplugged then any remaining characters fell out
726 * of the connector ;)
727 */
728 if (acm->disconnected)
Alan Cox23198fd2009-07-20 16:05:27 +0100729 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200730 /*
731 * This is inaccurate (overcounts), but it works.
732 */
Oliver Neukum86478942006-05-13 22:50:47 +0200733 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734}
735
736static void acm_tty_throttle(struct tty_struct *tty)
737{
738 struct acm *acm = tty->driver_data;
Johan Hovold088c64f2011-03-25 11:06:02 +0100739
Johan Hovold088c64f2011-03-25 11:06:02 +0100740 spin_lock_irq(&acm->read_lock);
741 acm->throttle_req = 1;
742 spin_unlock_irq(&acm->read_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743}
744
745static void acm_tty_unthrottle(struct tty_struct *tty)
746{
747 struct acm *acm = tty->driver_data;
Johan Hovold088c64f2011-03-25 11:06:02 +0100748 unsigned int was_throttled;
749
Johan Hovold088c64f2011-03-25 11:06:02 +0100750 spin_lock_irq(&acm->read_lock);
751 was_throttled = acm->throttled;
752 acm->throttled = 0;
753 acm->throttle_req = 0;
754 spin_unlock_irq(&acm->read_lock);
755
756 if (was_throttled)
757 acm_submit_read_urbs(acm, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758}
759
Alan Cox9e989662008-07-22 11:18:03 +0100760static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761{
762 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100763 int retval;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800764
Alan Cox9e989662008-07-22 11:18:03 +0100765 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
777 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
778 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
779 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
780 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
781 TIOCM_CTS;
782}
783
Alan Cox20b9d172011-02-14 16:26:50 +0000784static int acm_tty_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 unsigned int set, unsigned int clear)
786{
787 struct acm *acm = tty->driver_data;
788 unsigned int newctrl;
789
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100791 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
792 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
793 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
794 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
796 newctrl = (newctrl & ~clear) | set;
797
798 if (acm->ctrlout == newctrl)
799 return 0;
800 return acm_set_control(acm, acm->ctrlout = newctrl);
801}
802
Oliver Neukum18c75722012-02-17 17:21:24 -0500803static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
804{
805 struct serial_struct tmp;
806
807 if (!info)
808 return -EINVAL;
809
810 memset(&tmp, 0, sizeof(tmp));
811 tmp.flags = ASYNC_LOW_LATENCY;
812 tmp.xmit_fifo_size = acm->writesize;
813 tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
Dan Williams1229a832012-11-08 12:47:41 -0600814 tmp.close_delay = acm->port.close_delay / 10;
815 tmp.closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
816 ASYNC_CLOSING_WAIT_NONE :
817 acm->port.closing_wait / 10;
Oliver Neukum18c75722012-02-17 17:21:24 -0500818
819 if (copy_to_user(info, &tmp, sizeof(tmp)))
820 return -EFAULT;
821 else
822 return 0;
823}
824
Dan Williams1229a832012-11-08 12:47:41 -0600825static int set_serial_info(struct acm *acm,
826 struct serial_struct __user *newinfo)
827{
828 struct serial_struct new_serial;
829 unsigned int closing_wait, close_delay;
830 int retval = 0;
831
832 if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
833 return -EFAULT;
834
835 close_delay = new_serial.close_delay * 10;
836 closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
837 ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
838
839 mutex_lock(&acm->port.mutex);
840
841 if (!capable(CAP_SYS_ADMIN)) {
842 if ((close_delay != acm->port.close_delay) ||
843 (closing_wait != acm->port.closing_wait))
844 retval = -EPERM;
845 else
846 retval = -EOPNOTSUPP;
847 } else {
848 acm->port.close_delay = close_delay;
849 acm->port.closing_wait = closing_wait;
850 }
851
852 mutex_unlock(&acm->port.mutex);
853 return retval;
854}
855
Alan Cox6caa76b2011-02-14 16:27:22 +0000856static int acm_tty_ioctl(struct tty_struct *tty,
Alan Cox6e47e062009-06-11 12:37:06 +0100857 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858{
Oliver Neukum18c75722012-02-17 17:21:24 -0500859 struct acm *acm = tty->driver_data;
860 int rv = -ENOIOCTLCMD;
861
862 switch (cmd) {
863 case TIOCGSERIAL: /* gets serial port data */
864 rv = get_serial_info(acm, (struct serial_struct __user *) arg);
865 break;
Dan Williams1229a832012-11-08 12:47:41 -0600866 case TIOCSSERIAL:
867 rv = set_serial_info(acm, (struct serial_struct __user *) arg);
868 break;
Oliver Neukum18c75722012-02-17 17:21:24 -0500869 }
870
871 return rv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872}
873
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100874static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 0, 50, 75, 110, 134, 150, 200, 300, 600,
876 1200, 1800, 2400, 4800, 9600, 19200, 38400,
877 57600, 115200, 230400, 460800, 500000, 576000,
878 921600, 1000000, 1152000, 1500000, 2000000,
879 2500000, 3000000, 3500000, 4000000
880};
881
Alan Cox6e47e062009-06-11 12:37:06 +0100882static void acm_tty_set_termios(struct tty_struct *tty,
883 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884{
885 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800886 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 struct usb_cdc_line_coding newline;
888 int newctrl = acm->ctrlout;
889
Alan Cox9b80fee2009-09-19 13:13:23 -0700890 newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
892 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100893 (termios->c_cflag & PARODD ? 1 : 2) +
894 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Nicolas Boullisa5204462012-10-16 00:06:23 +0200895 switch (termios->c_cflag & CSIZE) {
896 case CS5:
897 newline.bDataBits = 5;
898 break;
899 case CS6:
900 newline.bDataBits = 6;
901 break;
902 case CS7:
903 newline.bDataBits = 7;
904 break;
905 case CS8:
906 default:
907 newline.bDataBits = 8;
908 break;
909 }
Alan Cox6e47e062009-06-11 12:37:06 +0100910 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
912
913 if (!newline.dwDTERate) {
914 newline.dwDTERate = acm->line.dwDTERate;
915 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +0100916 } else
917 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
919 if (newctrl != acm->ctrlout)
920 acm_set_control(acm, acm->ctrlout = newctrl);
921
922 if (memcmp(&acm->line, &newline, sizeof newline)) {
923 memcpy(&acm->line, &newline, sizeof newline);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100924 dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
925 __func__,
926 le32_to_cpu(newline.dwDTERate),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 newline.bCharFormat, newline.bParityType,
928 newline.bDataBits);
929 acm_set_line(acm, &acm->line);
930 }
931}
932
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800933static const struct tty_port_operations acm_port_ops = {
934 .shutdown = acm_port_shutdown,
935 .activate = acm_port_activate,
936 .destruct = acm_port_destruct,
937};
938
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939/*
940 * USB probe and disconnect routines.
941 */
942
Oliver Neukum830f4022008-06-25 14:17:16 +0200943/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +0200944static void acm_write_buffers_free(struct acm *acm)
945{
946 int i;
947 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +0200948 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200949
Alan Cox6e47e062009-06-11 12:37:06 +0100950 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Daniel Mack997ea582010-04-12 13:17:25 +0200951 usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +0200952}
953
Oliver Neukum830f4022008-06-25 14:17:16 +0200954static void acm_read_buffers_free(struct acm *acm)
955{
956 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Johan Hovolddab54c92011-03-22 11:12:21 +0100957 int i;
Oliver Neukum830f4022008-06-25 14:17:16 +0200958
Johan Hovolddab54c92011-03-22 11:12:21 +0100959 for (i = 0; i < acm->rx_buflimit; i++)
Daniel Mack997ea582010-04-12 13:17:25 +0200960 usb_free_coherent(usb_dev, acm->readsize,
Johan Hovold088c64f2011-03-25 11:06:02 +0100961 acm->read_buffers[i].base, acm->read_buffers[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +0200962}
963
Oliver Neukum884b6002005-04-21 21:28:02 +0200964/* Little helper: write buffers allocate */
965static int acm_write_buffers_alloc(struct acm *acm)
966{
967 int i;
968 struct acm_wb *wb;
969
Oliver Neukum86478942006-05-13 22:50:47 +0200970 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Daniel Mack997ea582010-04-12 13:17:25 +0200971 wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
Oliver Neukum884b6002005-04-21 21:28:02 +0200972 &wb->dmah);
973 if (!wb->buf) {
974 while (i != 0) {
975 --i;
976 --wb;
Daniel Mack997ea582010-04-12 13:17:25 +0200977 usb_free_coherent(acm->dev, acm->writesize,
Oliver Neukum884b6002005-04-21 21:28:02 +0200978 wb->buf, wb->dmah);
979 }
980 return -ENOMEM;
981 }
982 }
983 return 0;
984}
985
Alan Cox10077d42009-06-11 12:36:09 +0100986static int acm_probe(struct usb_interface *intf,
987 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988{
989 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100990 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -0700991 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 int buflen = intf->altsetting->extralen;
993 struct usb_interface *control_interface;
994 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +0200995 struct usb_endpoint_descriptor *epctrl = NULL;
996 struct usb_endpoint_descriptor *epread = NULL;
997 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 struct usb_device *usb_dev = interface_to_usbdev(intf);
999 struct acm *acm;
1000 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +01001001 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 u8 *buf;
1003 u8 ac_management_function = 0;
1004 u8 call_management_function = 0;
1005 int call_interface_num = -1;
Erik Slagterfd5054c2011-05-11 12:06:55 +02001006 int data_interface_num = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +02001008 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001009 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001010 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011
Oliver Neukum86478942006-05-13 22:50:47 +02001012 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +02001014 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
1015
1016 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 if (quirks == NO_UNION_NORMAL) {
1018 data_interface = usb_ifnum_to_if(usb_dev, 1);
1019 control_interface = usb_ifnum_to_if(usb_dev, 0);
1020 goto skip_normal_probe;
1021 }
Alan Cox6e47e062009-06-11 12:37:06 +01001022
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 /* normal probing*/
1024 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001025 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 return -EINVAL;
1027 }
1028
1029 if (!buflen) {
Toby Gray577045c2010-09-02 10:46:20 +01001030 if (intf->cur_altsetting->endpoint &&
1031 intf->cur_altsetting->endpoint->extralen &&
Alan Cox6e47e062009-06-11 12:37:06 +01001032 intf->cur_altsetting->endpoint->extra) {
1033 dev_dbg(&intf->dev,
1034 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 buflen = intf->cur_altsetting->endpoint->extralen;
1036 buffer = intf->cur_altsetting->endpoint->extra;
1037 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001038 dev_err(&intf->dev,
1039 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 return -EINVAL;
1041 }
1042 }
1043
1044 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001045 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001046 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 goto next_desc;
1048 }
1049
Alan Cox6e47e062009-06-11 12:37:06 +01001050 switch (buffer[2]) {
1051 case USB_CDC_UNION_TYPE: /* we've found it */
1052 if (union_header) {
1053 dev_err(&intf->dev, "More than one "
1054 "union descriptor, skipping ...\n");
1055 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 }
Alan Cox6e47e062009-06-11 12:37:06 +01001057 union_header = (struct usb_cdc_union_desc *)buffer;
1058 break;
1059 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
1060 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1061 break;
1062 case USB_CDC_HEADER_TYPE: /* maybe check version */
1063 break; /* for now we ignore it */
1064 case USB_CDC_ACM_TYPE:
1065 ac_management_function = buffer[3];
1066 break;
1067 case USB_CDC_CALL_MANAGEMENT_TYPE:
1068 call_management_function = buffer[3];
1069 call_interface_num = buffer[4];
Julian Calabyce126642010-01-05 23:58:20 +11001070 if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
Alan Cox6e47e062009-06-11 12:37:06 +01001071 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1072 break;
1073 default:
1074 /* there are LOTS more CDC descriptors that
1075 * could legitimately be found here.
1076 */
1077 dev_dbg(&intf->dev, "Ignoring descriptor: "
1078 "type %02x, length %d\n",
1079 buffer[2], buffer[0]);
1080 break;
1081 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082next_desc:
1083 buflen -= buffer[0];
1084 buffer += buffer[0];
1085 }
1086
1087 if (!union_header) {
1088 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001089 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Erik Slagterfd5054c2011-05-11 12:06:55 +02001090 /* quirks for Droids MuIn LCD */
1091 if (quirks & NO_DATA_INTERFACE)
1092 data_interface = usb_ifnum_to_if(usb_dev, 0);
1093 else
1094 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 control_interface = intf;
1096 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001097 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1098 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1099 return -ENODEV;
1100 } else {
1101 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1102 combined_interfaces = 1;
1103 control_interface = data_interface = intf;
1104 goto look_for_collapsed_interface;
1105 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 }
1107 } else {
1108 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1109 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1110 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001111 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 return -ENODEV;
1113 }
1114 }
Alan Cox6e47e062009-06-11 12:37:06 +01001115
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001117 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001119 if (control_interface == data_interface) {
1120 /* some broken devices designed for windows work this way */
1121 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1122 combined_interfaces = 1;
1123 /* a popular other OS doesn't use it */
1124 quirks |= NO_CAP_LINE;
1125 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1126 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1127 return -EINVAL;
1128 }
1129look_for_collapsed_interface:
1130 for (i = 0; i < 3; i++) {
1131 struct usb_endpoint_descriptor *ep;
1132 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1133
1134 if (usb_endpoint_is_int_in(ep))
1135 epctrl = ep;
1136 else if (usb_endpoint_is_bulk_out(ep))
1137 epwrite = ep;
1138 else if (usb_endpoint_is_bulk_in(ep))
1139 epread = ep;
1140 else
1141 return -EINVAL;
1142 }
1143 if (!epctrl || !epread || !epwrite)
1144 return -ENODEV;
1145 else
1146 goto made_compressed_probe;
1147 }
1148
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149skip_normal_probe:
1150
1151 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001152 if (data_interface->cur_altsetting->desc.bInterfaceClass
1153 != CDC_DATA_INTERFACE_TYPE) {
1154 if (control_interface->cur_altsetting->desc.bInterfaceClass
1155 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001157 dev_dbg(&intf->dev,
1158 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 t = control_interface;
1160 control_interface = data_interface;
1161 data_interface = t;
1162 } else {
1163 return -EINVAL;
1164 }
1165 }
Alan Stern74da5d62007-08-02 13:29:10 -04001166
1167 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001168 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001169 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001170
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001171 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1172 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001173 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 return -EBUSY;
1175 }
1176
1177
Sven Schnellea9959bb2012-08-17 21:43:43 +02001178 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
1179 control_interface->cur_altsetting->desc.bNumEndpoints == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 return -EINVAL;
1181
1182 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1183 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1184 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1185
1186
1187 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001188 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 /* descriptors are swapped */
1190 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001191 dev_dbg(&intf->dev,
1192 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 t = epread;
1194 epread = epwrite;
1195 epwrite = t;
1196 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001197made_compressed_probe:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001198 dev_dbg(&intf->dev, "interfaces are valid\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199
Alan Cox6e47e062009-06-11 12:37:06 +01001200 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1201 if (acm == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001202 dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 goto alloc_fail;
1204 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001206 minor = acm_alloc_minor(acm);
1207 if (minor == ACM_TTY_MINORS) {
1208 dev_err(&intf->dev, "no more free acm devices\n");
1209 kfree(acm);
1210 return -ENODEV;
1211 }
1212
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001213 ctrlsize = usb_endpoint_maxp(epctrl);
1214 readsize = usb_endpoint_maxp(epread) *
Alan Cox6e47e062009-06-11 12:37:06 +01001215 (quirks == SINGLE_RX_URB ? 1 : 2);
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001216 acm->combined_interfaces = combined_interfaces;
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001217 acm->writesize = usb_endpoint_maxp(epwrite) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 acm->control = control_interface;
1219 acm->data = data_interface;
1220 acm->minor = minor;
1221 acm->dev = usb_dev;
1222 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001223 if (quirks & NO_CAP_LINE)
1224 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 acm->ctrlsize = ctrlsize;
1226 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001227 acm->rx_buflimit = num_rx_buf;
David Howellsc4028952006-11-22 14:57:56 +00001228 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum884b6002005-04-21 21:28:02 +02001229 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001230 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001231 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001232 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001233 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1234 if (acm->is_int_ep)
1235 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001236 tty_port_init(&acm->port);
1237 acm->port.ops = &acm_port_ops;
Johan Hovold21ffba32014-05-26 19:23:38 +02001238 init_usb_anchor(&acm->delayed);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239
Daniel Mack997ea582010-04-12 13:17:25 +02001240 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 if (!buf) {
Johan Hovold255ab562011-03-22 11:12:13 +01001242 dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 goto alloc_fail2;
1244 }
1245 acm->ctrl_buffer = buf;
1246
Oliver Neukum884b6002005-04-21 21:28:02 +02001247 if (acm_write_buffers_alloc(acm) < 0) {
Johan Hovold255ab562011-03-22 11:12:13 +01001248 dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 goto alloc_fail4;
1250 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251
1252 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1253 if (!acm->ctrlurb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001254 dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 goto alloc_fail5;
1256 }
Oliver Neukum86478942006-05-13 22:50:47 +02001257 for (i = 0; i < num_rx_buf; i++) {
Johan Hovold088c64f2011-03-25 11:06:02 +01001258 struct acm_rb *rb = &(acm->read_buffers[i]);
1259 struct urb *urb;
David Kubicek61a87ad2005-11-01 18:51:34 +01001260
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001261 rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
1262 &rb->dma);
1263 if (!rb->base) {
1264 dev_err(&intf->dev, "out of memory "
1265 "(read bufs usb_alloc_coherent)\n");
1266 goto alloc_fail6;
1267 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001268 rb->index = i;
1269 rb->instance = acm;
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001270
Johan Hovold088c64f2011-03-25 11:06:02 +01001271 urb = usb_alloc_urb(0, GFP_KERNEL);
1272 if (!urb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001273 dev_err(&intf->dev,
Alan Cox6e47e062009-06-11 12:37:06 +01001274 "out of memory (read urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001275 goto alloc_fail6;
David Kubicek61a87ad2005-11-01 18:51:34 +01001276 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001277 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1278 urb->transfer_dma = rb->dma;
1279 if (acm->is_int_ep) {
1280 usb_fill_int_urb(urb, acm->dev,
1281 acm->rx_endpoint,
1282 rb->base,
1283 acm->readsize,
1284 acm_read_bulk_callback, rb,
1285 acm->bInterval);
1286 } else {
1287 usb_fill_bulk_urb(urb, acm->dev,
1288 acm->rx_endpoint,
1289 rb->base,
1290 acm->readsize,
1291 acm_read_bulk_callback, rb);
1292 }
David Kubicek61a87ad2005-11-01 18:51:34 +01001293
Johan Hovold088c64f2011-03-25 11:06:02 +01001294 acm->read_urbs[i] = urb;
1295 __set_bit(i, &acm->read_urbs_free);
David Kubicek61a87ad2005-11-01 18:51:34 +01001296 }
Alan Cox6e47e062009-06-11 12:37:06 +01001297 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001298 struct acm_wb *snd = &(acm->wb[i]);
1299
Alan Cox6e47e062009-06-11 12:37:06 +01001300 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1301 if (snd->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001302 dev_err(&intf->dev,
Johan Hovold59d7fec2011-03-22 11:12:12 +01001303 "out of memory (write urbs usb_alloc_urb)\n");
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001304 goto alloc_fail7;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001305 }
1306
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001307 if (usb_endpoint_xfer_int(epwrite))
1308 usb_fill_int_urb(snd->urb, usb_dev,
Ming Leie83863d2012-10-16 21:21:21 +08001309 usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001310 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1311 else
1312 usb_fill_bulk_urb(snd->urb, usb_dev,
1313 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1314 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001315 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1316 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 }
1318
Alan Cox6e47e062009-06-11 12:37:06 +01001319 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001320
1321 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1322 if (i < 0)
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001323 goto alloc_fail7;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001324
1325 if (cfd) { /* export the country data */
1326 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1327 if (!acm->country_codes)
1328 goto skip_countries;
1329 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001330 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1331 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001332 acm->country_rel_date = cfd->iCountryCodeRelDate;
1333
1334 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1335 if (i < 0) {
1336 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001337 acm->country_codes = NULL;
1338 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001339 goto skip_countries;
1340 }
1341
Alan Cox6e47e062009-06-11 12:37:06 +01001342 i = device_create_file(&intf->dev,
1343 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001344 if (i < 0) {
Axel Linc2572b72010-05-31 08:04:47 +08001345 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001346 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001347 acm->country_codes = NULL;
1348 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001349 goto skip_countries;
1350 }
1351 }
1352
1353skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001354 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001355 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1356 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1357 /* works around buggy devices */
1358 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1360 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1361
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1363
1364 acm_set_control(acm, acm->ctrlout);
1365
1366 acm->line.dwDTERate = cpu_to_le32(9600);
1367 acm->line.bDataBits = 8;
1368 acm_set_line(acm, &acm->line);
1369
1370 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001371 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001373 usb_get_intf(control_interface);
1374 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001376 return 0;
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001377alloc_fail7:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001378 for (i = 0; i < ACM_NW; i++)
1379 usb_free_urb(acm->wb[i].urb);
Axel Linc2572b72010-05-31 08:04:47 +08001380alloc_fail6:
Oliver Neukum86478942006-05-13 22:50:47 +02001381 for (i = 0; i < num_rx_buf; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001382 usb_free_urb(acm->read_urbs[i]);
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001383 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 usb_free_urb(acm->ctrlurb);
1385alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001386 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387alloc_fail4:
Daniel Mack997ea582010-04-12 13:17:25 +02001388 usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389alloc_fail2:
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001390 acm_release_minor(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 kfree(acm);
1392alloc_fail:
1393 return -ENOMEM;
1394}
1395
Oliver Neukum1365baf2007-10-12 17:24:28 +02001396static void stop_data_traffic(struct acm *acm)
1397{
1398 int i;
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001399
1400 dev_dbg(&acm->control->dev, "%s\n", __func__);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001401
Oliver Neukum1365baf2007-10-12 17:24:28 +02001402 usb_kill_urb(acm->ctrlurb);
Alan Cox6e47e062009-06-11 12:37:06 +01001403 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001404 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001405 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001406 usb_kill_urb(acm->read_urbs[i]);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001407
1408 cancel_work_sync(&acm->work);
1409}
1410
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411static void acm_disconnect(struct usb_interface *intf)
1412{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001413 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001415 struct tty_struct *tty;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001416 int i;
1417
1418 dev_dbg(&intf->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419
David Brownell672c4e12008-08-06 18:41:12 -07001420 /* sibling interface is already cleaning up */
1421 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001422 return;
David Brownell672c4e12008-08-06 18:41:12 -07001423
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001424 mutex_lock(&acm->mutex);
1425 acm->disconnected = true;
Alan Cox6e47e062009-06-11 12:37:06 +01001426 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001427 device_remove_file(&acm->control->dev,
1428 &dev_attr_wCountryCodes);
1429 device_remove_file(&acm->control->dev,
1430 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001431 }
Alan Stern74da5d62007-08-02 13:29:10 -04001432 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001433 usb_set_intfdata(acm->control, NULL);
1434 usb_set_intfdata(acm->data, NULL);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001435 mutex_unlock(&acm->mutex);
1436
1437 tty = tty_port_tty_get(&acm->port);
1438 if (tty) {
1439 tty_vhangup(tty);
1440 tty_kref_put(tty);
1441 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442
Oliver Neukum1365baf2007-10-12 17:24:28 +02001443 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444
Johan Hovold10a00e32013-03-19 09:21:06 +01001445 tty_unregister_device(acm_tty_driver, acm->minor);
1446
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001447 usb_free_urb(acm->ctrlurb);
1448 for (i = 0; i < ACM_NW; i++)
1449 usb_free_urb(acm->wb[i].urb);
1450 for (i = 0; i < acm->rx_buflimit; i++)
1451 usb_free_urb(acm->read_urbs[i]);
Oliver Neukum884b6002005-04-21 21:28:02 +02001452 acm_write_buffers_free(acm);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001453 usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001454 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001456 if (!acm->combined_interfaces)
1457 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001458 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001460 tty_port_put(&acm->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461}
1462
Oliver Neukum35758582008-07-01 19:10:08 +02001463#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001464static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1465{
1466 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001467 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001468
Oliver Neukum11ea8592008-06-20 11:25:57 +02001469 spin_lock_irq(&acm->read_lock);
1470 spin_lock(&acm->write_lock);
Johan Hovoldbe7978b2014-05-26 19:23:36 +02001471 if (PMSG_IS_AUTO(message)) {
1472 if (acm->transmitting) {
1473 spin_unlock(&acm->write_lock);
1474 spin_unlock_irq(&acm->read_lock);
1475 return -EBUSY;
1476 }
1477 }
Oliver Neukum11ea8592008-06-20 11:25:57 +02001478 cnt = acm->susp_count++;
1479 spin_unlock(&acm->write_lock);
1480 spin_unlock_irq(&acm->read_lock);
1481
1482 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001483 return 0;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001484
Johan Hovold181614f2014-05-26 19:23:40 +02001485 stop_data_traffic(acm);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001486
Oliver Neukum1365baf2007-10-12 17:24:28 +02001487 return 0;
1488}
1489
1490static int acm_resume(struct usb_interface *intf)
1491{
1492 struct acm *acm = usb_get_intfdata(intf);
Johan Hovold21ffba32014-05-26 19:23:38 +02001493 struct urb *urb;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001494 int rv = 0;
1495
Oliver Neukum11ea8592008-06-20 11:25:57 +02001496 spin_lock_irq(&acm->read_lock);
Johan Hovold97e72c42014-05-26 19:23:37 +02001497 spin_lock(&acm->write_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001498
Johan Hovold97e72c42014-05-26 19:23:37 +02001499 if (--acm->susp_count)
1500 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001501
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001502 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
Johan Hovold97e72c42014-05-26 19:23:37 +02001503 rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001504
Johan Hovold21ffba32014-05-26 19:23:38 +02001505 for (;;) {
1506 urb = usb_get_from_anchor(&acm->delayed);
1507 if (!urb)
1508 break;
1509
1510 acm_start_wb(acm, urb->context);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001511 }
1512
1513 /*
1514 * delayed error checking because we must
1515 * do the write path at all cost
1516 */
Oliver Neukum1365baf2007-10-12 17:24:28 +02001517 if (rv < 0)
Johan Hovold97e72c42014-05-26 19:23:37 +02001518 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001519
Johan Hovold97e72c42014-05-26 19:23:37 +02001520 rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001521 }
Johan Hovold97e72c42014-05-26 19:23:37 +02001522out:
1523 spin_unlock(&acm->write_lock);
1524 spin_unlock_irq(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001525
Oliver Neukum1365baf2007-10-12 17:24:28 +02001526 return rv;
1527}
Oliver Neukum35758582008-07-01 19:10:08 +02001528
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001529static int acm_reset_resume(struct usb_interface *intf)
1530{
1531 struct acm *acm = usb_get_intfdata(intf);
1532 struct tty_struct *tty;
1533
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001534 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001535 tty = tty_port_tty_get(&acm->port);
1536 if (tty) {
1537 tty_hangup(tty);
1538 tty_kref_put(tty);
1539 }
1540 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001541
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001542 return acm_resume(intf);
1543}
1544
Oliver Neukum35758582008-07-01 19:10:08 +02001545#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001546
1547#define NOKIA_PCSUITE_ACM_INFO(x) \
1548 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1549 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1550 USB_CDC_ACM_PROTO_VENDOR)
1551
Toby Gray4035e452010-09-01 16:01:19 +01001552#define SAMSUNG_PCSUITE_ACM_INFO(x) \
1553 USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
1554 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1555 USB_CDC_ACM_PROTO_VENDOR)
1556
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557/*
1558 * USB driver structure.
1559 */
1560
Németh Márton6ef48522010-01-10 15:33:45 +01001561static const struct usb_device_id acm_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 /* quirky and broken devices */
David Cluytensbd73e382013-12-03 14:18:57 +01001563 { USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */
1564 .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1566 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1567 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001568 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1569 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1570 },
Andrew Lunn0f9c7b42008-12-23 17:31:23 +01001571 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1572 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1573 },
Masahito Omote8753e652005-07-29 12:17:25 -07001574 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1575 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1576 },
Chris Malley91a9c922006-10-03 10:08:28 +01001577 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1578 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1579 },
Alan Cox7abcf202009-04-06 17:35:01 +01001580 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1581 .driver_info = SINGLE_RX_URB,
1582 },
Oliver Neukum86478942006-05-13 22:50:47 +02001583 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1584 .driver_info = SINGLE_RX_URB, /* firmware bug */
1585 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001586 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1587 .driver_info = SINGLE_RX_URB, /* firmware bug */
1588 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001589 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1590 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1591 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001592 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1593 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1594 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001595 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1596 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1597 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001598 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1599 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1600 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001601 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1602 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1603 },
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001604 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1605 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001606 /* Motorola H24 HSPA module: */
1607 { USB_DEVICE(0x22b8, 0x2d91) }, /* modem */
Michael Ulbrichtb08f08b2014-03-25 10:34:18 +01001608 { USB_DEVICE(0x22b8, 0x2d92), /* modem + diagnostics */
1609 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1610 },
1611 { USB_DEVICE(0x22b8, 0x2d93), /* modem + AT port */
1612 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1613 },
1614 { USB_DEVICE(0x22b8, 0x2d95), /* modem + AT port + diagnostics */
1615 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1616 },
1617 { USB_DEVICE(0x22b8, 0x2d96), /* modem + NMEA */
1618 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1619 },
1620 { USB_DEVICE(0x22b8, 0x2d97), /* modem + diagnostics + NMEA */
1621 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1622 },
1623 { USB_DEVICE(0x22b8, 0x2d99), /* modem + AT port + NMEA */
1624 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1625 },
1626 { USB_DEVICE(0x22b8, 0x2d9a), /* modem + AT port + diagnostics + NMEA */
1627 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1628 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001629
Adam Richterc332b4e2009-02-18 16:17:15 -08001630 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1631 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1632 data interface instead of
1633 communications interface.
1634 Maybe we should define a new
1635 quirk for this. */
1636 },
Jean-Christian de Rivaz52538352012-10-10 12:49:02 +00001637 { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */
1638 .driver_info = NO_UNION_NORMAL,
1639 },
Denis N Ladinb756d752012-12-26 18:29:44 +05001640 { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */
1641 .driver_info = NO_UNION_NORMAL,
1642 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001643 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1644 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1645 },
Russ Nelsonc3baa192010-04-21 23:07:03 -04001646 { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1647 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1648 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001649
Adrian Taylorc1479a92009-11-19 10:35:33 +00001650 /* Nokia S60 phones expose two ACM channels. The first is
1651 * a modem and is picked up by the standard AT-command
1652 * information below. The second is 'vendor-specific' but
1653 * is treated as a serial device at the S60 end, so we want
1654 * to expose it on Linux too. */
1655 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1656 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1657 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1658 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1659 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1660 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1661 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1662 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1663 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1664 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1665 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1666 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1667 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1668 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1669 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1670 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1671 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1672 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1673 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1674 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1675 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1676 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1677 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1678 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1679 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1680 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1681 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1682 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1683 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1684 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1685 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1686 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1687 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1688 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1689 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1690 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1691 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1692 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1693 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1694 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1695 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1696 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1697 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
Przemo Firszt83a4eae2010-06-28 21:29:34 +01001698 { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
Toby Gray4035e452010-09-01 16:01:19 +01001699 { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
1700 { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
1701 { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
1702 { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
1703 { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
1704 { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
1705 { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
1706 { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
1707 { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
1708 { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
Arvid Ephraim Picciani721d92f2011-01-25 15:58:40 +01001709 { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
Toby Gray4061fde2011-06-06 14:52:48 +01001710 { NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */
1711 { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
Toby Gray4035e452010-09-01 16:01:19 +01001712 { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001713
Denis Pershin65e52f42011-09-04 17:37:21 +07001714 /* Support for Owen devices */
1715 { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */
1716
Adrian Taylorc1479a92009-11-19 10:35:33 +00001717 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1718
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001719 /* Support Lego NXT using pbLua firmware */
Julian Calabyce126642010-01-05 23:58:20 +11001720 { USB_DEVICE(0x0694, 0xff00),
1721 .driver_info = NOT_A_MODEM,
Otavio Salvador7893afc2010-09-26 23:35:05 -03001722 },
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001723
Erik Slagterfd5054c2011-05-11 12:06:55 +02001724 /* Support for Droids MuIn LCD */
1725 { USB_DEVICE(0x04d8, 0x000b),
1726 .driver_info = NO_DATA_INTERFACE,
1727 },
1728
Philippe Corbes5b239f02010-08-31 19:31:32 +02001729 /* control interfaces without any protocol set */
1730 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1731 USB_CDC_PROTO_NONE) },
1732
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 /* control interfaces with various AT-command sets */
1734 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1735 USB_CDC_ACM_PROTO_AT_V25TER) },
1736 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1737 USB_CDC_ACM_PROTO_AT_PCCA101) },
1738 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1739 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1740 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1741 USB_CDC_ACM_PROTO_AT_GSM) },
1742 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001743 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1745 USB_CDC_ACM_PROTO_AT_CDMA) },
1746
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 { }
1748};
1749
Alan Cox6e47e062009-06-11 12:37:06 +01001750MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751
1752static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 .name = "cdc_acm",
1754 .probe = acm_probe,
1755 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001756#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001757 .suspend = acm_suspend,
1758 .resume = acm_resume,
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001759 .reset_resume = acm_reset_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001760#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001762#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001763 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001764#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765};
1766
1767/*
1768 * TTY driver structures.
1769 */
1770
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001771static const struct tty_operations acm_ops = {
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001772 .install = acm_tty_install,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 .open = acm_tty_open,
1774 .close = acm_tty_close,
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001775 .cleanup = acm_tty_cleanup,
Alan Cox10077d42009-06-11 12:36:09 +01001776 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777 .write = acm_tty_write,
1778 .write_room = acm_tty_write_room,
1779 .ioctl = acm_tty_ioctl,
1780 .throttle = acm_tty_throttle,
1781 .unthrottle = acm_tty_unthrottle,
1782 .chars_in_buffer = acm_tty_chars_in_buffer,
1783 .break_ctl = acm_tty_break_ctl,
1784 .set_termios = acm_tty_set_termios,
1785 .tiocmget = acm_tty_tiocmget,
1786 .tiocmset = acm_tty_tiocmset,
1787};
1788
1789/*
1790 * Init / exit.
1791 */
1792
1793static int __init acm_init(void)
1794{
1795 int retval;
1796 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1797 if (!acm_tty_driver)
1798 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799 acm_tty_driver->driver_name = "acm",
1800 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801 acm_tty_driver->major = ACM_TTY_MAJOR,
1802 acm_tty_driver->minor_start = 0,
1803 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1804 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001805 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001807 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1808 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 tty_set_operations(acm_tty_driver, &acm_ops);
1810
1811 retval = tty_register_driver(acm_tty_driver);
1812 if (retval) {
1813 put_tty_driver(acm_tty_driver);
1814 return retval;
1815 }
1816
1817 retval = usb_register(&acm_driver);
1818 if (retval) {
1819 tty_unregister_driver(acm_tty_driver);
1820 put_tty_driver(acm_tty_driver);
1821 return retval;
1822 }
1823
Johan Hovolda2c7b932011-03-22 11:12:18 +01001824 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825
1826 return 0;
1827}
1828
1829static void __exit acm_exit(void)
1830{
1831 usb_deregister(&acm_driver);
1832 tty_unregister_driver(acm_tty_driver);
1833 put_tty_driver(acm_tty_driver);
1834}
1835
1836module_init(acm_init);
1837module_exit(acm_exit);
1838
Alan Cox6e47e062009-06-11 12:37:06 +01001839MODULE_AUTHOR(DRIVER_AUTHOR);
1840MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01001842MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);