blob: 09d0d8e1d8701eaef11f73bd163aef01c9a757d0 [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
flintman5c2f9732014-08-11 21:52:25 -040055/*
56 * Version Information
57 */
58#define DRIVER_VERSION "v0.26-mbm_2"
59#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
61
62static struct usb_driver acm_driver;
63static struct tty_driver *acm_tty_driver;
64static struct acm *acm_table[ACM_TTY_MINORS];
65
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -080066static DEFINE_MUTEX(acm_table_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -080068/*
69 * acm_table accessors
70 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070071
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -080072/*
73 * Look up an ACM structure by index. If found and not disconnected, increment
74 * its refcount and return it with its mutex held.
75 */
76static struct acm *acm_get_by_index(unsigned index)
77{
78 struct acm *acm;
79
80 mutex_lock(&acm_table_lock);
81 acm = acm_table[index];
82 if (acm) {
83 mutex_lock(&acm->mutex);
84 if (acm->disconnected) {
85 mutex_unlock(&acm->mutex);
86 acm = NULL;
87 } else {
88 tty_port_get(&acm->port);
89 mutex_unlock(&acm->mutex);
90 }
91 }
92 mutex_unlock(&acm_table_lock);
93 return acm;
94}
95
96/*
97 * Try to find an available minor number and if found, associate it with 'acm'.
98 */
99static int acm_alloc_minor(struct acm *acm)
100{
101 int minor;
102
103 mutex_lock(&acm_table_lock);
104 for (minor = 0; minor < ACM_TTY_MINORS; minor++) {
105 if (!acm_table[minor]) {
106 acm_table[minor] = acm;
107 break;
108 }
109 }
110 mutex_unlock(&acm_table_lock);
111
112 return minor;
113}
114
115/* Release the minor number associated with 'acm'. */
116static void acm_release_minor(struct acm *acm)
117{
118 mutex_lock(&acm_table_lock);
119 acm_table[acm->minor] = NULL;
120 mutex_unlock(&acm_table_lock);
121}
Alan Cox739e0282009-06-11 12:27:50 +0100122
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123/*
124 * Functions for ACM control messages.
125 */
126
Alan Cox6e47e062009-06-11 12:37:06 +0100127static int acm_ctrl_msg(struct acm *acm, int request, int value,
128 void *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129{
Johan Hovold9f81ca82014-05-26 19:23:39 +0200130 int retval;
131
132 retval = usb_autopm_get_interface(acm->control);
133 if (retval)
134 return retval;
135
136 retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 request, USB_RT_ACM, value,
138 acm->control->altsetting[0].desc.bInterfaceNumber,
139 buf, len, 5000);
Johan Hovold9f81ca82014-05-26 19:23:39 +0200140
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100141 dev_dbg(&acm->control->dev,
142 "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
143 __func__, request, value, len, retval);
Johan Hovold9f81ca82014-05-26 19:23:39 +0200144
145 usb_autopm_put_interface(acm->control);
146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 return retval < 0 ? retval : 0;
148}
149
flintman5c2f9732014-08-11 21:52:25 -0400150/* MBM */
151#define USB_CDC_SET_COMM_FEATURE 0x02
152#define USB_CDC_GET_COMM_FEATURE 0x03
153#define USB_CDC_CLEAR_COMM_FEATURE 0x04
154
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155/* devices aren't required to support these requests.
156 * the cdc acm descriptor tells whether they do...
157 */
158#define acm_set_control(acm, control) \
159 acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
160#define acm_set_line(acm, line) \
161 acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
162#define acm_send_break(acm, ms) \
163 acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
flintman5c2f9732014-08-11 21:52:25 -0400164/* MBM */
165#define acm_set_comm_feature(acm, feature, state) \
166 acm_ctrl_msg(acm, USB_CDC_SET_COMM_FEATURE, feature, state, 2)
167#define acm_clear_comm_feature(acm, feature) \
168 acm_ctrl_msg(acm, USB_CDC_CLEAR_COMM_FEATURE, feature, NULL, 0)
169#define acm_send_encap_resp(acm, msg) \
170 acm_ctrl_msg(acm, USB_CDC_SEND_ENCAPSULATED_COMMAND, 0, msg, sizeof *(msg))
171
172
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
174/*
Oliver Neukum884b6002005-04-21 21:28:02 +0200175 * Write buffer management.
176 * All of these assume proper locks taken by the caller.
177 */
178
179static int acm_wb_alloc(struct acm *acm)
180{
181 int i, wbn;
182 struct acm_wb *wb;
183
David Engrafe4cf3aa2008-03-20 10:01:34 +0100184 wbn = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200185 i = 0;
186 for (;;) {
187 wb = &acm->wb[wbn];
188 if (!wb->use) {
189 wb->use = 1;
190 return wbn;
191 }
Oliver Neukum86478942006-05-13 22:50:47 +0200192 wbn = (wbn + 1) % ACM_NW;
193 if (++i >= ACM_NW)
Oliver Neukum884b6002005-04-21 21:28:02 +0200194 return -1;
195 }
196}
197
Oliver Neukum884b6002005-04-21 21:28:02 +0200198static int acm_wb_is_avail(struct acm *acm)
199{
200 int i, n;
David Brownelle5fbab52008-08-06 18:46:10 -0700201 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200202
Oliver Neukum86478942006-05-13 22:50:47 +0200203 n = ACM_NW;
David Brownelle5fbab52008-08-06 18:46:10 -0700204 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100205 for (i = 0; i < ACM_NW; i++)
Oliver Neukum86478942006-05-13 22:50:47 +0200206 n -= acm->wb[i].use;
David Brownelle5fbab52008-08-06 18:46:10 -0700207 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200208 return n;
209}
210
Oliver Neukum884b6002005-04-21 21:28:02 +0200211/*
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800212 * Finish write. Caller must hold acm->write_lock
Oliver Neukum884b6002005-04-21 21:28:02 +0200213 */
David Engrafe4cf3aa2008-03-20 10:01:34 +0100214static void acm_write_done(struct acm *acm, struct acm_wb *wb)
Oliver Neukum884b6002005-04-21 21:28:02 +0200215{
David Engrafe4cf3aa2008-03-20 10:01:34 +0100216 wb->use = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200217 acm->transmitting--;
Oliver Neukum97d35f92009-12-16 17:05:57 +0100218 usb_autopm_put_interface_async(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200219}
220
221/*
222 * Poke write.
Oliver Neukum11ea8592008-06-20 11:25:57 +0200223 *
224 * the caller is responsible for locking
Oliver Neukum884b6002005-04-21 21:28:02 +0200225 */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200226
227static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
228{
229 int rc;
230
231 acm->transmitting++;
232
233 wb->urb->transfer_buffer = wb->buf;
234 wb->urb->transfer_dma = wb->dmah;
235 wb->urb->transfer_buffer_length = wb->len;
236 wb->urb->dev = acm->dev;
237
Alan Cox6e47e062009-06-11 12:37:06 +0100238 rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
239 if (rc < 0) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100240 dev_err(&acm->data->dev,
241 "%s - usb_submit_urb(write bulk) failed: %d\n",
242 __func__, rc);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200243 acm_write_done(acm, wb);
244 }
245 return rc;
246}
247
David Engrafe4cf3aa2008-03-20 10:01:34 +0100248static int acm_write_start(struct acm *acm, int wbn)
Oliver Neukum884b6002005-04-21 21:28:02 +0200249{
250 unsigned long flags;
David Brownell934da462008-08-06 18:44:12 -0700251 struct acm_wb *wb = &acm->wb[wbn];
Oliver Neukum884b6002005-04-21 21:28:02 +0200252 int rc;
253
254 spin_lock_irqsave(&acm->write_lock, flags);
255 if (!acm->dev) {
David Brownell934da462008-08-06 18:44:12 -0700256 wb->use = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200257 spin_unlock_irqrestore(&acm->write_lock, flags);
258 return -ENODEV;
259 }
260
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100261 dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100262 acm->susp_count);
Oliver Neukum97d35f92009-12-16 17:05:57 +0100263 usb_autopm_get_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200264 if (acm->susp_count) {
flintmand53cff62015-03-04 20:21:21 -0500265 if (!acm->delayed_wb)
266 acm->delayed_wb = wb;
267 else
268 usb_autopm_put_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200269 spin_unlock_irqrestore(&acm->write_lock, flags);
flintmand53cff62015-03-04 20:21:21 -0500270 return 0; /* A white lie */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200271 }
272 usb_mark_last_busy(acm->dev);
273
Oliver Neukum11ea8592008-06-20 11:25:57 +0200274 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200275 spin_unlock_irqrestore(&acm->write_lock, flags);
276
Oliver Neukum884b6002005-04-21 21:28:02 +0200277 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200278
Oliver Neukum884b6002005-04-21 21:28:02 +0200279}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100280/*
281 * attributes exported through sysfs
282 */
283static ssize_t show_caps
284(struct device *dev, struct device_attribute *attr, char *buf)
285{
286 struct usb_interface *intf = to_usb_interface(dev);
287 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200288
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100289 return sprintf(buf, "%d", acm->ctrl_caps);
290}
291static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
292
293static ssize_t show_country_codes
294(struct device *dev, struct device_attribute *attr, char *buf)
295{
296 struct usb_interface *intf = to_usb_interface(dev);
297 struct acm *acm = usb_get_intfdata(intf);
298
299 memcpy(buf, acm->country_codes, acm->country_code_size);
300 return acm->country_code_size;
301}
302
303static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
304
305static ssize_t show_country_rel_date
306(struct device *dev, struct device_attribute *attr, char *buf)
307{
308 struct usb_interface *intf = to_usb_interface(dev);
309 struct acm *acm = usb_get_intfdata(intf);
310
311 return sprintf(buf, "%d", acm->country_rel_date);
312}
313
314static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200315/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 * Interrupt handlers for various ACM device responses
317 */
318
flintman5c2f9732014-08-11 21:52:25 -0400319/* MBM */
320static void acm_in_callback(struct urb *urb)
321{
322 struct acm *acm = urb->context;
323 int status = urb->status;
324
325 if (status) {
326 switch (status) {
327 case -ENOENT:
328 dev_dbg(&urb->dev->dev,
329 "nonzero urb status received: -ENOENT");
330 goto skip_error;
331 case -ECONNRESET:
332 dev_dbg(&urb->dev->dev,
333 "nonzero urb status received: -ECONNRESET");
334 goto skip_error;
335 case -ESHUTDOWN:
336 dev_dbg(&urb->dev->dev,
337 "nonzero urb status received: -ESHUTDOWN");
338 goto skip_error;
339 case -EPIPE:
340 dev_err(&urb->dev->dev,
341 "nonzero urb status received: -EPIPE\n");
342 break;
343 default:
344 dev_err(&urb->dev->dev,
345 "Unexpected error %d\n", status);
346 break;
347 }
348 }
349
350 dbg("unsupported encap: %s", (char *)acm->inbuf);
351skip_error:
352 dbg("");
353}
354
355
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100357static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358{
359 struct acm *acm = urb->context;
360 struct usb_cdc_notification *dr = urb->transfer_buffer;
flintman5c2f9732014-08-11 21:52:25 -0400361 /* MBM */
362 struct usb_ctrlrequest *req = acm->irq;
Alan Cox10077d42009-06-11 12:36:09 +0100363 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 unsigned char *data;
365 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700366 int retval;
367 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700369 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 case 0:
371 /* success */
372 break;
373 case -ECONNRESET:
374 case -ENOENT:
375 case -ESHUTDOWN:
376 /* this urb is terminated, clean up */
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100377 dev_dbg(&acm->control->dev,
378 "%s - urb shutting down with status: %d\n",
379 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 return;
381 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100382 dev_dbg(&acm->control->dev,
383 "%s - nonzero urb status received: %d\n",
384 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 goto exit;
386 }
387
Johan Hovold7e7797e2011-03-22 11:12:11 +0100388 usb_mark_last_busy(acm->dev);
389
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 data = (unsigned char *)(dr + 1);
391 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100392 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
flintman5c2f9732014-08-11 21:52:25 -0400393 /* MBM */
394 dev_info(&urb->dev->dev, "%s network", dr->wValue ?
395 "connected to" : "disconnected from");
Alan Cox6e47e062009-06-11 12:37:06 +0100396 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
Alan Cox6e47e062009-06-11 12:37:06 +0100398 case USB_CDC_NOTIFY_SERIAL_STATE:
399 tty = tty_port_tty_get(&acm->port);
400 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
Alan Cox6e47e062009-06-11 12:37:06 +0100402 if (tty) {
403 if (!acm->clocal &&
404 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100405 dev_dbg(&acm->control->dev,
406 "%s - calling hangup\n", __func__);
Alan Cox6e47e062009-06-11 12:37:06 +0100407 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 }
Alan Cox6e47e062009-06-11 12:37:06 +0100409 tty_kref_put(tty);
410 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
Alan Cox6e47e062009-06-11 12:37:06 +0100412 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100414 dev_dbg(&acm->control->dev,
415 "%s - input control lines: dcd%c dsr%c break%c "
416 "ring%c framing%c parity%c overrun%c\n",
417 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100418 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
419 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
420 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
421 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
422 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
423 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
424 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 break;
426
flintman5c2f9732014-08-11 21:52:25 -0400427 /* MBM */
428 case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
429 dev_err(&urb->dev->dev, "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
430 dr->wIndex, dr->wLength);
431 req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
432 req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
433 req->wValue = 0;
434 req->wIndex = cpu_to_le16(dr->wIndex);
435 req->wLength = cpu_to_le16(acm->bMaxPacketSize0);
436
437 usb_fill_control_urb(
438 acm->response,
439 acm->dev,
440 usb_sndctrlpipe(acm->dev, 0),
441 (unsigned char *)req,
442 acm->inbuf,
443 acm->bMaxPacketSize0,
444 acm_in_callback,
445 acm);
446
447 break;
448
Alan Cox6e47e062009-06-11 12:37:06 +0100449 default:
flintman5c2f9732014-08-11 21:52:25 -0400450 /* MBM */
451 dev_info(&urb->dev->dev,"unknown notification %d received: index %d len %d data0 %d data1 %d",
Alan Cox6e47e062009-06-11 12:37:06 +0100452 dr->bNotificationType, dr->wIndex,
453 dr->wLength, data[0], data[1]);
454 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 }
456exit:
Alan Cox6e47e062009-06-11 12:37:06 +0100457 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700458 if (retval)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100459 dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
460 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461}
462
Johan Hovold088c64f2011-03-25 11:06:02 +0100463static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464{
Johan Hovold088c64f2011-03-25 11:06:02 +0100465 int res;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700466
Johan Hovold088c64f2011-03-25 11:06:02 +0100467 if (!test_and_clear_bit(index, &acm->read_urbs_free))
468 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469
Johan Hovold088c64f2011-03-25 11:06:02 +0100470 dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
471
472 res = usb_submit_urb(acm->read_urbs[index], mem_flags);
473 if (res) {
474 if (res != -EPERM) {
475 dev_err(&acm->data->dev,
476 "%s - usb_submit_urb failed: %d\n",
477 __func__, res);
478 }
479 set_bit(index, &acm->read_urbs_free);
480 return res;
481 }
482
483 return 0;
484}
485
486static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
487{
488 int res;
489 int i;
490
491 for (i = 0; i < acm->rx_buflimit; ++i) {
492 res = acm_submit_read_urb(acm, i, mem_flags);
493 if (res)
494 return res;
495 }
496
497 return 0;
498}
499
500static void acm_process_read_urb(struct acm *acm, struct urb *urb)
501{
502 struct tty_struct *tty;
503
504 if (!urb->actual_length)
505 return;
506
507 tty = tty_port_tty_get(&acm->port);
508 if (!tty)
509 return;
510
511 tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
512 tty_flip_buffer_push(tty);
513
514 tty_kref_put(tty);
515}
516
517static void acm_read_bulk_callback(struct urb *urb)
518{
519 struct acm_rb *rb = urb->context;
520 struct acm *acm = rb->instance;
521 unsigned long flags;
522
523 dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
524 rb->index, urb->actual_length);
525 set_bit(rb->index, &acm->read_urbs_free);
526
527 if (!acm->dev) {
528 dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200530 }
531 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532
Johan Hovold088c64f2011-03-25 11:06:02 +0100533 if (urb->status) {
Johan Hovold1d9846e2011-03-22 11:12:14 +0100534 dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
Johan Hovold088c64f2011-03-25 11:06:02 +0100535 __func__, urb->status);
536 return;
537 }
538 acm_process_read_urb(acm, urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
Johan Hovold088c64f2011-03-25 11:06:02 +0100540 /* throttle device if requested by tty */
541 spin_lock_irqsave(&acm->read_lock, flags);
542 acm->throttled = acm->throttle_req;
543 if (!acm->throttled && !acm->susp_count) {
544 spin_unlock_irqrestore(&acm->read_lock, flags);
545 acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
Oliver Neukum86478942006-05-13 22:50:47 +0200546 } else {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200547 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549}
550
551/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100552static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553{
Ming Leicdc97792008-02-24 18:41:47 +0800554 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700555 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800556 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200557
Johan Hovold4fa46262011-03-22 11:12:16 +0100558 if (urb->status || (urb->actual_length != urb->transfer_buffer_length))
559 dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
Johan Hovold1d9846e2011-03-22 11:12:14 +0100560 __func__,
David Brownelle5fbab52008-08-06 18:46:10 -0700561 urb->actual_length,
562 urb->transfer_buffer_length,
563 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800565 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100566 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800567 spin_unlock_irqrestore(&acm->write_lock, flags);
Havard Skinnemoen99823f42011-12-09 16:51:54 -0800568 schedule_work(&acm->work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569}
570
David Howellsc4028952006-11-22 14:57:56 +0000571static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572{
David Howellsc4028952006-11-22 14:57:56 +0000573 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100574 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700575
Johan Hovold1d9846e2011-03-22 11:12:14 +0100576 dev_vdbg(&acm->data->dev, "%s\n", __func__);
577
Alan Cox10077d42009-06-11 12:36:09 +0100578 tty = tty_port_tty_get(&acm->port);
Johan Hovold15e5bee2011-03-22 11:12:10 +0100579 if (!tty)
580 return;
Alan Cox10077d42009-06-11 12:36:09 +0100581 tty_wakeup(tty);
582 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583}
584
585/*
586 * TTY handlers
587 */
588
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800589static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590{
591 struct acm *acm;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800592 int retval;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100593
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800594 dev_dbg(tty->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800596 acm = acm_get_by_index(tty->index);
597 if (!acm)
598 return -ENODEV;
599
Jiri Slabyf8a8c102012-03-05 14:51:48 +0100600 retval = tty_standard_install(driver, tty);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800601 if (retval)
602 goto error_init_termios;
603
604 tty->driver_data = acm;
605
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800606 return 0;
607
608error_init_termios:
609 tty_port_put(&acm->port);
610 return retval;
611}
612
613static int acm_tty_open(struct tty_struct *tty, struct file *filp)
614{
615 struct acm *acm = tty->driver_data;
616
617 dev_dbg(tty->dev, "%s\n", __func__);
618
619 return tty_port_open(&acm->port, tty, filp);
620}
621
622static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
623{
624 struct acm *acm = container_of(port, struct acm, port);
625 int retval = -ENODEV;
Johan Hovold8fd20d52014-05-26 19:23:44 +0200626 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100628 dev_dbg(&acm->control->dev, "%s\n", __func__);
629
Oliver Neukum1365baf2007-10-12 17:24:28 +0200630 mutex_lock(&acm->mutex);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800631 if (acm->disconnected)
632 goto disconnected;
633
634 retval = usb_autopm_get_interface(acm->control);
635 if (retval)
636 goto error_get_interface;
637
638 /*
639 * FIXME: Why do we need this? Allocating 64K of physically contiguous
640 * memory is really nasty...
641 */
642 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
643 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200644
flintman5c2f9732014-08-11 21:52:25 -0400645#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 acm->ctrlurb->dev = acm->dev;
647 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100648 dev_err(&acm->control->dev,
649 "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800650 goto error_submit_urb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 }
flintman5c2f9732014-08-11 21:52:25 -0400652#endif
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800653 acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS;
654 if (acm_set_control(acm, acm->ctrlout) < 0 &&
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100655 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800656 goto error_set_control;
Alan Cox10077d42009-06-11 12:36:09 +0100657
flintman5c2f9732014-08-11 21:52:25 -0400658 /* MBM */
659 acm->state &= ~ACM_ABS_IDLE;
660 if (0 > acm_set_comm_feature(acm, ACM_ABSTRACT_STATE, &acm->state) &&
661 (acm->ctrl_caps & USB_CDC_COMM_FEATURE))
662 goto error_set_control;
663
Oliver Neukum11ea8592008-06-20 11:25:57 +0200664 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
Otto Meta9cc2d462012-06-06 18:46:21 +0200666 /*
667 * Unthrottle device in case the TTY was closed while throttled.
668 */
669 spin_lock_irq(&acm->read_lock);
670 acm->throttled = 0;
671 acm->throttle_req = 0;
672 spin_unlock_irq(&acm->read_lock);
673
Johan Hovold088c64f2011-03-25 11:06:02 +0100674 if (acm_submit_read_urbs(acm, GFP_KERNEL))
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800675 goto error_submit_read_urbs;
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100676
Oliver Neukum1365baf2007-10-12 17:24:28 +0200677 mutex_unlock(&acm->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800679 return 0;
680
681error_submit_read_urbs:
Johan Hovold8fd20d52014-05-26 19:23:44 +0200682 for (i = 0; i < acm->rx_buflimit; i++)
683 usb_kill_urb(acm->read_urbs[i]);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800684 acm->ctrlout = 0;
685 acm_set_control(acm, acm->ctrlout);
686error_set_control:
687 usb_kill_urb(acm->ctrlurb);
flintman5c2f9732014-08-11 21:52:25 -0400688#ifndef CONFIG_MACH_TENDERLOIN
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800689error_submit_urb:
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100690 usb_autopm_put_interface(acm->control);
flintman5c2f9732014-08-11 21:52:25 -0400691#endif
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800692error_get_interface:
693disconnected:
694 mutex_unlock(&acm->mutex);
695 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696}
697
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800698static void acm_port_destruct(struct tty_port *port)
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700699{
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800700 struct acm *acm = container_of(port, struct acm, port);
701
702 dev_dbg(&acm->control->dev, "%s\n", __func__);
David Kubicek61a87ad2005-11-01 18:51:34 +0100703
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800704 acm_release_minor(acm);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700705 usb_put_intf(acm->control);
flintman5c2f9732014-08-11 21:52:25 -0400706 /* MBM */
707 usb_free_urb(acm->response);
708 kfree(acm->irq);
709
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100710 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700711 kfree(acm);
712}
713
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800714static void acm_port_shutdown(struct tty_port *port)
Alan Cox10077d42009-06-11 12:36:09 +0100715{
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800716 struct acm *acm = container_of(port, struct acm, port);
Johan Hovolddab54c92011-03-22 11:12:21 +0100717 int i;
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200718 int pm_err;
Johan Hovolddab54c92011-03-22 11:12:21 +0100719
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800720 dev_dbg(&acm->control->dev, "%s\n", __func__);
721
722 mutex_lock(&acm->mutex);
723 if (!acm->disconnected) {
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200724 pm_err = usb_autopm_get_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100725 acm_set_control(acm, acm->ctrlout = 0);
Alan Cox10077d42009-06-11 12:36:09 +0100726 usb_kill_urb(acm->ctrlurb);
727 for (i = 0; i < ACM_NW; i++)
728 usb_kill_urb(acm->wb[i].urb);
Johan Hovolddab54c92011-03-22 11:12:21 +0100729 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +0100730 usb_kill_urb(acm->read_urbs[i]);
Alan Cox10077d42009-06-11 12:36:09 +0100731 acm->control->needs_remote_wakeup = 0;
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200732 if (!pm_err)
733 usb_autopm_put_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100734 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800735 mutex_unlock(&acm->mutex);
736}
737
738static void acm_tty_cleanup(struct tty_struct *tty)
739{
740 struct acm *acm = tty->driver_data;
741 dev_dbg(&acm->control->dev, "%s\n", __func__);
742 tty_port_put(&acm->port);
Alan Cox10077d42009-06-11 12:36:09 +0100743}
744
745static void acm_tty_hangup(struct tty_struct *tty)
746{
747 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800748 dev_dbg(&acm->control->dev, "%s\n", __func__);
Alan Cox10077d42009-06-11 12:36:09 +0100749 tty_port_hangup(&acm->port);
Alan Cox10077d42009-06-11 12:36:09 +0100750}
751
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752static void acm_tty_close(struct tty_struct *tty, struct file *filp)
753{
754 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800755 dev_dbg(&acm->control->dev, "%s\n", __func__);
756 tty_port_close(&acm->port, tty, filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757}
758
Alan Cox6e47e062009-06-11 12:37:06 +0100759static int acm_tty_write(struct tty_struct *tty,
760 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761{
762 struct acm *acm = tty->driver_data;
763 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200764 unsigned long flags;
765 int wbn;
766 struct acm_wb *wb;
767
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 if (!count)
769 return 0;
770
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100771 dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100772
Oliver Neukum884b6002005-04-21 21:28:02 +0200773 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100774 wbn = acm_wb_alloc(acm);
775 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200776 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200777 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200779 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
Oliver Neukum884b6002005-04-21 21:28:02 +0200781 count = (count > acm->writesize) ? acm->writesize : count;
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100782 dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
Oliver Neukum884b6002005-04-21 21:28:02 +0200783 memcpy(wb->buf, buf, count);
784 wb->len = count;
785 spin_unlock_irqrestore(&acm->write_lock, flags);
786
Alan Cox6e47e062009-06-11 12:37:06 +0100787 stat = acm_write_start(acm, wbn);
788 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200789 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 return count;
791}
792
793static int acm_tty_write_room(struct tty_struct *tty)
794{
795 struct acm *acm = tty->driver_data;
Oliver Neukum884b6002005-04-21 21:28:02 +0200796 /*
797 * Do not let the line discipline to know that we have a reserve,
798 * or it might get too enthusiastic.
799 */
David Brownell934da462008-08-06 18:44:12 -0700800 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801}
802
803static int acm_tty_chars_in_buffer(struct tty_struct *tty)
804{
805 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800806 /*
807 * if the device was unplugged then any remaining characters fell out
808 * of the connector ;)
809 */
810 if (acm->disconnected)
Alan Cox23198fd2009-07-20 16:05:27 +0100811 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200812 /*
813 * This is inaccurate (overcounts), but it works.
814 */
Oliver Neukum86478942006-05-13 22:50:47 +0200815 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816}
817
818static void acm_tty_throttle(struct tty_struct *tty)
819{
820 struct acm *acm = tty->driver_data;
Johan Hovold088c64f2011-03-25 11:06:02 +0100821
Johan Hovold088c64f2011-03-25 11:06:02 +0100822 spin_lock_irq(&acm->read_lock);
823 acm->throttle_req = 1;
824 spin_unlock_irq(&acm->read_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825}
826
827static void acm_tty_unthrottle(struct tty_struct *tty)
828{
829 struct acm *acm = tty->driver_data;
Johan Hovold088c64f2011-03-25 11:06:02 +0100830 unsigned int was_throttled;
831
Johan Hovold088c64f2011-03-25 11:06:02 +0100832 spin_lock_irq(&acm->read_lock);
833 was_throttled = acm->throttled;
834 acm->throttled = 0;
835 acm->throttle_req = 0;
836 spin_unlock_irq(&acm->read_lock);
837
838 if (was_throttled)
839 acm_submit_read_urbs(acm, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840}
841
Alan Cox9e989662008-07-22 11:18:03 +0100842static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843{
844 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100845 int retval;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800846
Alan Cox9e989662008-07-22 11:18:03 +0100847 retval = acm_send_break(acm, state ? 0xffff : 0);
848 if (retval < 0)
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100849 dev_dbg(&acm->control->dev, "%s - send break failed\n",
850 __func__);
Alan Cox9e989662008-07-22 11:18:03 +0100851 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852}
853
Alan Cox60b33c12011-02-14 16:26:14 +0000854static int acm_tty_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855{
856 struct acm *acm = tty->driver_data;
857
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
859 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
860 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
861 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
862 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
863 TIOCM_CTS;
864}
865
Alan Cox20b9d172011-02-14 16:26:50 +0000866static int acm_tty_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 unsigned int set, unsigned int clear)
868{
869 struct acm *acm = tty->driver_data;
870 unsigned int newctrl;
871
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100873 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
874 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
875 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
876 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
878 newctrl = (newctrl & ~clear) | set;
879
880 if (acm->ctrlout == newctrl)
881 return 0;
882 return acm_set_control(acm, acm->ctrlout = newctrl);
883}
884
Oliver Neukum18c75722012-02-17 17:21:24 -0500885static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
886{
887 struct serial_struct tmp;
888
889 if (!info)
890 return -EINVAL;
891
892 memset(&tmp, 0, sizeof(tmp));
893 tmp.flags = ASYNC_LOW_LATENCY;
894 tmp.xmit_fifo_size = acm->writesize;
895 tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
Dan Williams1229a832012-11-08 12:47:41 -0600896 tmp.close_delay = acm->port.close_delay / 10;
897 tmp.closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
898 ASYNC_CLOSING_WAIT_NONE :
899 acm->port.closing_wait / 10;
Oliver Neukum18c75722012-02-17 17:21:24 -0500900
901 if (copy_to_user(info, &tmp, sizeof(tmp)))
902 return -EFAULT;
903 else
904 return 0;
905}
906
Dan Williams1229a832012-11-08 12:47:41 -0600907static int set_serial_info(struct acm *acm,
908 struct serial_struct __user *newinfo)
909{
910 struct serial_struct new_serial;
911 unsigned int closing_wait, close_delay;
912 int retval = 0;
913
914 if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
915 return -EFAULT;
916
917 close_delay = new_serial.close_delay * 10;
918 closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
919 ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
920
921 mutex_lock(&acm->port.mutex);
922
923 if (!capable(CAP_SYS_ADMIN)) {
924 if ((close_delay != acm->port.close_delay) ||
925 (closing_wait != acm->port.closing_wait))
926 retval = -EPERM;
927 else
928 retval = -EOPNOTSUPP;
929 } else {
930 acm->port.close_delay = close_delay;
931 acm->port.closing_wait = closing_wait;
932 }
933
934 mutex_unlock(&acm->port.mutex);
935 return retval;
936}
937
Alan Cox6caa76b2011-02-14 16:27:22 +0000938static int acm_tty_ioctl(struct tty_struct *tty,
Alan Cox6e47e062009-06-11 12:37:06 +0100939 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940{
Oliver Neukum18c75722012-02-17 17:21:24 -0500941 struct acm *acm = tty->driver_data;
942 int rv = -ENOIOCTLCMD;
943
944 switch (cmd) {
945 case TIOCGSERIAL: /* gets serial port data */
946 rv = get_serial_info(acm, (struct serial_struct __user *) arg);
947 break;
Dan Williams1229a832012-11-08 12:47:41 -0600948 case TIOCSSERIAL:
949 rv = set_serial_info(acm, (struct serial_struct __user *) arg);
950 break;
Oliver Neukum18c75722012-02-17 17:21:24 -0500951 }
952
953 return rv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954}
955
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100956static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 0, 50, 75, 110, 134, 150, 200, 300, 600,
958 1200, 1800, 2400, 4800, 9600, 19200, 38400,
959 57600, 115200, 230400, 460800, 500000, 576000,
960 921600, 1000000, 1152000, 1500000, 2000000,
961 2500000, 3000000, 3500000, 4000000
962};
963
Alan Cox6e47e062009-06-11 12:37:06 +0100964static void acm_tty_set_termios(struct tty_struct *tty,
965 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966{
967 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800968 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 struct usb_cdc_line_coding newline;
970 int newctrl = acm->ctrlout;
971
Alan Cox9b80fee2009-09-19 13:13:23 -0700972 newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
974 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100975 (termios->c_cflag & PARODD ? 1 : 2) +
976 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Nicolas Boullisa5204462012-10-16 00:06:23 +0200977 switch (termios->c_cflag & CSIZE) {
978 case CS5:
979 newline.bDataBits = 5;
980 break;
981 case CS6:
982 newline.bDataBits = 6;
983 break;
984 case CS7:
985 newline.bDataBits = 7;
986 break;
987 case CS8:
988 default:
989 newline.bDataBits = 8;
990 break;
991 }
Alan Cox6e47e062009-06-11 12:37:06 +0100992 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
994
Johan Hovolddc3e2ac2014-11-05 18:41:59 +0100995 if (C_BAUD(tty) == B0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 newline.dwDTERate = acm->line.dwDTERate;
997 newctrl &= ~ACM_CTRL_DTR;
Johan Hovolddc3e2ac2014-11-05 18:41:59 +0100998 } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) {
Alan Cox6e47e062009-06-11 12:37:06 +0100999 newctrl |= ACM_CTRL_DTR;
Johan Hovolddc3e2ac2014-11-05 18:41:59 +01001000 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001
1002 if (newctrl != acm->ctrlout)
1003 acm_set_control(acm, acm->ctrlout = newctrl);
1004
1005 if (memcmp(&acm->line, &newline, sizeof newline)) {
1006 memcpy(&acm->line, &newline, sizeof newline);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001007 dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
1008 __func__,
1009 le32_to_cpu(newline.dwDTERate),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 newline.bCharFormat, newline.bParityType,
1011 newline.bDataBits);
1012 acm_set_line(acm, &acm->line);
1013 }
1014}
1015
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001016static const struct tty_port_operations acm_port_ops = {
1017 .shutdown = acm_port_shutdown,
1018 .activate = acm_port_activate,
1019 .destruct = acm_port_destruct,
1020};
1021
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022/*
1023 * USB probe and disconnect routines.
1024 */
1025
Oliver Neukum830f4022008-06-25 14:17:16 +02001026/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +02001027static void acm_write_buffers_free(struct acm *acm)
1028{
1029 int i;
1030 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +02001031 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +02001032
Alan Cox6e47e062009-06-11 12:37:06 +01001033 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Daniel Mack997ea582010-04-12 13:17:25 +02001034 usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +02001035}
1036
Oliver Neukum830f4022008-06-25 14:17:16 +02001037static void acm_read_buffers_free(struct acm *acm)
1038{
1039 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Johan Hovolddab54c92011-03-22 11:12:21 +01001040 int i;
Oliver Neukum830f4022008-06-25 14:17:16 +02001041
Johan Hovolddab54c92011-03-22 11:12:21 +01001042 for (i = 0; i < acm->rx_buflimit; i++)
Daniel Mack997ea582010-04-12 13:17:25 +02001043 usb_free_coherent(usb_dev, acm->readsize,
Johan Hovold088c64f2011-03-25 11:06:02 +01001044 acm->read_buffers[i].base, acm->read_buffers[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001045}
1046
Oliver Neukum884b6002005-04-21 21:28:02 +02001047/* Little helper: write buffers allocate */
1048static int acm_write_buffers_alloc(struct acm *acm)
1049{
1050 int i;
1051 struct acm_wb *wb;
1052
Oliver Neukum86478942006-05-13 22:50:47 +02001053 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Daniel Mack997ea582010-04-12 13:17:25 +02001054 wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
Oliver Neukum884b6002005-04-21 21:28:02 +02001055 &wb->dmah);
1056 if (!wb->buf) {
1057 while (i != 0) {
1058 --i;
1059 --wb;
Daniel Mack997ea582010-04-12 13:17:25 +02001060 usb_free_coherent(acm->dev, acm->writesize,
Oliver Neukum884b6002005-04-21 21:28:02 +02001061 wb->buf, wb->dmah);
1062 }
1063 return -ENOMEM;
1064 }
1065 }
1066 return 0;
1067}
1068
Alan Cox10077d42009-06-11 12:36:09 +01001069static int acm_probe(struct usb_interface *intf,
1070 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071{
1072 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001073 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -07001074 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 int buflen = intf->altsetting->extralen;
1076 struct usb_interface *control_interface;
1077 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001078 struct usb_endpoint_descriptor *epctrl = NULL;
1079 struct usb_endpoint_descriptor *epread = NULL;
1080 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 struct usb_device *usb_dev = interface_to_usbdev(intf);
1082 struct acm *acm;
1083 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +01001084 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 u8 *buf;
1086 u8 ac_management_function = 0;
1087 u8 call_management_function = 0;
1088 int call_interface_num = -1;
Erik Slagterfd5054c2011-05-11 12:06:55 +02001089 int data_interface_num = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +02001091 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001092 int i;
José Adolfo Galdámez900469d2015-06-20 23:45:36 -06001093 unsigned int elength = 0;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001094 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095
Oliver Neukum86478942006-05-13 22:50:47 +02001096 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +02001098 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
1099
flintman5c2f9732014-08-11 21:52:25 -04001100 /* MBM, Not a real CDC ACM device */
1101 if (quirks == NOT_REAL_ACM)
1102 return -ENODEV;
1103
Oliver Neukum86478942006-05-13 22:50:47 +02001104 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 if (quirks == NO_UNION_NORMAL) {
1106 data_interface = usb_ifnum_to_if(usb_dev, 1);
1107 control_interface = usb_ifnum_to_if(usb_dev, 0);
1108 goto skip_normal_probe;
1109 }
Alan Cox6e47e062009-06-11 12:37:06 +01001110
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 /* normal probing*/
1112 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001113 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 return -EINVAL;
1115 }
1116
1117 if (!buflen) {
flintman5c2f9732014-08-11 21:52:25 -04001118#ifndef CONFIG_MACH_TENDERLOIN
Toby Gray577045c2010-09-02 10:46:20 +01001119 if (intf->cur_altsetting->endpoint &&
1120 intf->cur_altsetting->endpoint->extralen &&
Alan Cox6e47e062009-06-11 12:37:06 +01001121 intf->cur_altsetting->endpoint->extra) {
flintman5c2f9732014-08-11 21:52:25 -04001122#else
1123 /* MBM */
1124 if (intf->cur_altsetting->endpoint->extralen &&
1125 intf->cur_altsetting->endpoint->extra) {
1126#endif
Alan Cox6e47e062009-06-11 12:37:06 +01001127 dev_dbg(&intf->dev,
1128 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 buflen = intf->cur_altsetting->endpoint->extralen;
1130 buffer = intf->cur_altsetting->endpoint->extra;
1131 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001132 dev_err(&intf->dev,
1133 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 return -EINVAL;
1135 }
1136 }
1137
1138 while (buflen > 0) {
José Adolfo Galdámez900469d2015-06-20 23:45:36 -06001139 elength = buffer[0];
1140 if (!elength) {
1141 dev_err(&intf->dev, "skipping garbage byte\n");
1142 elength = 1;
1143 goto next_desc;
1144 }
Alan Cox6e47e062009-06-11 12:37:06 +01001145 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001146 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 goto next_desc;
1148 }
1149
Alan Cox6e47e062009-06-11 12:37:06 +01001150 switch (buffer[2]) {
1151 case USB_CDC_UNION_TYPE: /* we've found it */
José Adolfo Galdámez900469d2015-06-20 23:45:36 -06001152 if (elength < sizeof(struct usb_cdc_union_desc))
1153 goto next_desc;
Alan Cox6e47e062009-06-11 12:37:06 +01001154 if (union_header) {
1155 dev_err(&intf->dev, "More than one "
1156 "union descriptor, skipping ...\n");
1157 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 }
Alan Cox6e47e062009-06-11 12:37:06 +01001159 union_header = (struct usb_cdc_union_desc *)buffer;
1160 break;
1161 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
José Adolfo Galdámez900469d2015-06-20 23:45:36 -06001162 if (elength < sizeof(struct usb_cdc_country_functional_desc))
1163 goto next_desc;
Alan Cox6e47e062009-06-11 12:37:06 +01001164 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1165 break;
1166 case USB_CDC_HEADER_TYPE: /* maybe check version */
1167 break; /* for now we ignore it */
1168 case USB_CDC_ACM_TYPE:
José Adolfo Galdámez900469d2015-06-20 23:45:36 -06001169 if (elength < 4)
1170 goto next_desc;
Alan Cox6e47e062009-06-11 12:37:06 +01001171 ac_management_function = buffer[3];
1172 break;
1173 case USB_CDC_CALL_MANAGEMENT_TYPE:
José Adolfo Galdámez900469d2015-06-20 23:45:36 -06001174 if (elength < 5)
1175 goto next_desc;
Alan Cox6e47e062009-06-11 12:37:06 +01001176 call_management_function = buffer[3];
1177 call_interface_num = buffer[4];
Julian Calabyce126642010-01-05 23:58:20 +11001178 if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
Alan Cox6e47e062009-06-11 12:37:06 +01001179 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1180 break;
1181 default:
José Adolfo Galdámez900469d2015-06-20 23:45:36 -06001182 /*
1183 * there are LOTS more CDC descriptors that
Alan Cox6e47e062009-06-11 12:37:06 +01001184 * could legitimately be found here.
1185 */
1186 dev_dbg(&intf->dev, "Ignoring descriptor: "
José Adolfo Galdámez900469d2015-06-20 23:45:36 -06001187 "type %02x, length %ud\n",
1188 buffer[2], elength);
Alan Cox6e47e062009-06-11 12:37:06 +01001189 break;
1190 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191next_desc:
José Adolfo Galdámez900469d2015-06-20 23:45:36 -06001192 buflen -= elength;
1193 buffer += elength;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 }
1195
1196 if (!union_header) {
1197 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001198 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Erik Slagterfd5054c2011-05-11 12:06:55 +02001199 /* quirks for Droids MuIn LCD */
1200 if (quirks & NO_DATA_INTERFACE)
1201 data_interface = usb_ifnum_to_if(usb_dev, 0);
1202 else
1203 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 control_interface = intf;
1205 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001206 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1207 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1208 return -ENODEV;
1209 } else {
1210 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1211 combined_interfaces = 1;
1212 control_interface = data_interface = intf;
1213 goto look_for_collapsed_interface;
1214 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 }
1216 } else {
1217 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1218 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
Greg Kroah-Hartmanfacb66e2014-11-07 08:48:15 -08001219 }
1220
1221 if (!control_interface || !data_interface) {
1222 dev_dbg(&intf->dev, "no interfaces\n");
1223 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 }
Alan Cox6e47e062009-06-11 12:37:06 +01001225
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001227 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001229 if (control_interface == data_interface) {
1230 /* some broken devices designed for windows work this way */
1231 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1232 combined_interfaces = 1;
1233 /* a popular other OS doesn't use it */
1234 quirks |= NO_CAP_LINE;
1235 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1236 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1237 return -EINVAL;
1238 }
1239look_for_collapsed_interface:
1240 for (i = 0; i < 3; i++) {
1241 struct usb_endpoint_descriptor *ep;
1242 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1243
1244 if (usb_endpoint_is_int_in(ep))
1245 epctrl = ep;
1246 else if (usb_endpoint_is_bulk_out(ep))
1247 epwrite = ep;
1248 else if (usb_endpoint_is_bulk_in(ep))
1249 epread = ep;
1250 else
1251 return -EINVAL;
1252 }
1253 if (!epctrl || !epread || !epwrite)
1254 return -ENODEV;
1255 else
1256 goto made_compressed_probe;
1257 }
1258
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259skip_normal_probe:
1260
1261 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001262 if (data_interface->cur_altsetting->desc.bInterfaceClass
1263 != CDC_DATA_INTERFACE_TYPE) {
1264 if (control_interface->cur_altsetting->desc.bInterfaceClass
1265 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001267 dev_dbg(&intf->dev,
1268 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 t = control_interface;
1270 control_interface = data_interface;
1271 data_interface = t;
1272 } else {
1273 return -EINVAL;
1274 }
1275 }
Alan Stern74da5d62007-08-02 13:29:10 -04001276
1277 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001278 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001279 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001280
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001281 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1282 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001283 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 return -EBUSY;
1285 }
1286
1287
Sven Schnellea9959bb2012-08-17 21:43:43 +02001288 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
1289 control_interface->cur_altsetting->desc.bNumEndpoints == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 return -EINVAL;
1291
1292 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1293 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1294 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1295
1296
1297 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001298 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 /* descriptors are swapped */
1300 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001301 dev_dbg(&intf->dev,
1302 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 t = epread;
1304 epread = epwrite;
1305 epwrite = t;
1306 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001307made_compressed_probe:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001308 dev_dbg(&intf->dev, "interfaces are valid\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309
Alan Cox6e47e062009-06-11 12:37:06 +01001310 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1311 if (acm == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001312 dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 goto alloc_fail;
1314 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001316 minor = acm_alloc_minor(acm);
1317 if (minor == ACM_TTY_MINORS) {
1318 dev_err(&intf->dev, "no more free acm devices\n");
1319 kfree(acm);
1320 return -ENODEV;
1321 }
1322
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001323 ctrlsize = usb_endpoint_maxp(epctrl);
1324 readsize = usb_endpoint_maxp(epread) *
flintman5c2f9732014-08-11 21:52:25 -04001325#ifndef CONFIG_MACH_TENDERLOIN
Alan Cox6e47e062009-06-11 12:37:06 +01001326 (quirks == SINGLE_RX_URB ? 1 : 2);
flintman5c2f9732014-08-11 21:52:25 -04001327#else
1328 /* MBM */
1329 (quirks == SINGLE_RX_URB ? 1 : 4);
1330#endif
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001331 acm->combined_interfaces = combined_interfaces;
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001332 acm->writesize = usb_endpoint_maxp(epwrite) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 acm->control = control_interface;
1334 acm->data = data_interface;
1335 acm->minor = minor;
1336 acm->dev = usb_dev;
1337 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001338 if (quirks & NO_CAP_LINE)
1339 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 acm->ctrlsize = ctrlsize;
1341 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001342 acm->rx_buflimit = num_rx_buf;
David Howellsc4028952006-11-22 14:57:56 +00001343 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum884b6002005-04-21 21:28:02 +02001344 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001345 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001346 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001347 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001348 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1349 if (acm->is_int_ep)
1350 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001351 tty_port_init(&acm->port);
1352 acm->port.ops = &acm_port_ops;
flintman5c2f9732014-08-11 21:52:25 -04001353#ifdef CONFIG_MACH_TENDERLOIN
1354 /* MBM */
1355 acm->response = usb_alloc_urb(0, GFP_KERNEL);
1356 if (!acm->response) {
1357 dev_dbg(&intf->dev, "out of memory (response kmalloc)\n");
1358 goto alloc_fail2;
1359 }
1360
1361 /* MBM */
1362 acm->bMaxPacketSize0 = usb_dev->descriptor.bMaxPacketSize0;
1363 acm->inbuf = usb_alloc_coherent(usb_dev,
1364 acm->bMaxPacketSize0,
1365 GFP_KERNEL,
1366 &acm->response->transfer_dma);
1367 /* MBM */
1368 if (!acm->inbuf) {
1369 dev_dbg(&intf->dev, "out of memory (inbuf kmalloc)\n");
1370 goto alloc_fail3;
1371 }
1372#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
Daniel Mack997ea582010-04-12 13:17:25 +02001374 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 if (!buf) {
Johan Hovold255ab562011-03-22 11:12:13 +01001376 dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
flintman5c2f9732014-08-11 21:52:25 -04001377#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 goto alloc_fail2;
flintman5c2f9732014-08-11 21:52:25 -04001379#else
1380 /* MBM */
1381 goto alloc_fail3_1;
1382#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 }
1384 acm->ctrl_buffer = buf;
1385
Oliver Neukum884b6002005-04-21 21:28:02 +02001386 if (acm_write_buffers_alloc(acm) < 0) {
Johan Hovold255ab562011-03-22 11:12:13 +01001387 dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 goto alloc_fail4;
1389 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390
1391 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1392 if (!acm->ctrlurb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001393 dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 goto alloc_fail5;
1395 }
Oliver Neukum86478942006-05-13 22:50:47 +02001396 for (i = 0; i < num_rx_buf; i++) {
Johan Hovold088c64f2011-03-25 11:06:02 +01001397 struct acm_rb *rb = &(acm->read_buffers[i]);
1398 struct urb *urb;
David Kubicek61a87ad2005-11-01 18:51:34 +01001399
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001400 rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
1401 &rb->dma);
1402 if (!rb->base) {
1403 dev_err(&intf->dev, "out of memory "
1404 "(read bufs usb_alloc_coherent)\n");
1405 goto alloc_fail6;
1406 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001407 rb->index = i;
1408 rb->instance = acm;
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001409
Johan Hovold088c64f2011-03-25 11:06:02 +01001410 urb = usb_alloc_urb(0, GFP_KERNEL);
1411 if (!urb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001412 dev_err(&intf->dev,
Alan Cox6e47e062009-06-11 12:37:06 +01001413 "out of memory (read urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001414 goto alloc_fail6;
David Kubicek61a87ad2005-11-01 18:51:34 +01001415 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001416 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1417 urb->transfer_dma = rb->dma;
1418 if (acm->is_int_ep) {
1419 usb_fill_int_urb(urb, acm->dev,
1420 acm->rx_endpoint,
1421 rb->base,
1422 acm->readsize,
1423 acm_read_bulk_callback, rb,
1424 acm->bInterval);
1425 } else {
1426 usb_fill_bulk_urb(urb, acm->dev,
1427 acm->rx_endpoint,
1428 rb->base,
1429 acm->readsize,
1430 acm_read_bulk_callback, rb);
1431 }
David Kubicek61a87ad2005-11-01 18:51:34 +01001432
Johan Hovold088c64f2011-03-25 11:06:02 +01001433 acm->read_urbs[i] = urb;
1434 __set_bit(i, &acm->read_urbs_free);
David Kubicek61a87ad2005-11-01 18:51:34 +01001435 }
Alan Cox6e47e062009-06-11 12:37:06 +01001436 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001437 struct acm_wb *snd = &(acm->wb[i]);
1438
Alan Cox6e47e062009-06-11 12:37:06 +01001439 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1440 if (snd->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001441 dev_err(&intf->dev,
Johan Hovold59d7fec2011-03-22 11:12:12 +01001442 "out of memory (write urbs usb_alloc_urb)\n");
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001443 goto alloc_fail7;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001444 }
1445
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001446 if (usb_endpoint_xfer_int(epwrite))
1447 usb_fill_int_urb(snd->urb, usb_dev,
Ming Leie83863d2012-10-16 21:21:21 +08001448 usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001449 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1450 else
1451 usb_fill_bulk_urb(snd->urb, usb_dev,
1452 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1453 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001454 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1455 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 }
1457
Alan Cox6e47e062009-06-11 12:37:06 +01001458 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001459
1460 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1461 if (i < 0)
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001462 goto alloc_fail7;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001463
1464 if (cfd) { /* export the country data */
1465 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1466 if (!acm->country_codes)
1467 goto skip_countries;
1468 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001469 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1470 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001471 acm->country_rel_date = cfd->iCountryCodeRelDate;
1472
1473 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1474 if (i < 0) {
1475 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001476 acm->country_codes = NULL;
1477 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001478 goto skip_countries;
1479 }
1480
Alan Cox6e47e062009-06-11 12:37:06 +01001481 i = device_create_file(&intf->dev,
1482 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001483 if (i < 0) {
Axel Linc2572b72010-05-31 08:04:47 +08001484 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001485 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001486 acm->country_codes = NULL;
1487 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001488 goto skip_countries;
1489 }
1490 }
1491
1492skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001493 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001494 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1495 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1496 /* works around buggy devices */
1497 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1499 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
flintman5c2f9732014-08-11 21:52:25 -04001500#ifdef CONFIG_MACH_TENDERLOIN
1501 /* MBM */
1502 acm->ctrlurb->dev = acm->dev;
1503 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
1504 dev_err(&intf->dev, "usb_submit_urb(ctrl irq) failed");
1505 goto kill_urb;
1506 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507
flintman5c2f9732014-08-11 21:52:25 -04001508 /* MBM */
1509 acm->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
1510 if (!acm->irq)
1511 goto kill_urb;
1512#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1514
1515 acm_set_control(acm, acm->ctrlout);
flintman5c2f9732014-08-11 21:52:25 -04001516#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 acm->line.dwDTERate = cpu_to_le32(9600);
flintman5c2f9732014-08-11 21:52:25 -04001518#else
1519 acm->line.dwDTERate = cpu_to_le32(115200);
1520#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 acm->line.bDataBits = 8;
1522 acm_set_line(acm, &acm->line);
flintman5c2f9732014-08-11 21:52:25 -04001523#ifdef CONFIG_MACH_TENDERLOIN
1524 /* MBM */
1525 acm->state |= ACM_ABS_IDLE;
1526 acm_set_comm_feature(acm, ACM_ABSTRACT_STATE, &acm->state);
1527#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528
1529 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001530 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001532 usb_get_intf(control_interface);
1533 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001535 return 0;
flintman5c2f9732014-08-11 21:52:25 -04001536#ifdef CONFIG_MACH_TENDERLOIN
1537 /* MBM */
1538kill_urb:
1539 usb_kill_urb(acm->ctrlurb);
1540#endif
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001541alloc_fail7:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001542 for (i = 0; i < ACM_NW; i++)
1543 usb_free_urb(acm->wb[i].urb);
Axel Linc2572b72010-05-31 08:04:47 +08001544alloc_fail6:
Oliver Neukum86478942006-05-13 22:50:47 +02001545 for (i = 0; i < num_rx_buf; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001546 usb_free_urb(acm->read_urbs[i]);
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001547 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 usb_free_urb(acm->ctrlurb);
1549alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001550 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551alloc_fail4:
Daniel Mack997ea582010-04-12 13:17:25 +02001552 usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
flintman5c2f9732014-08-11 21:52:25 -04001553#ifdef CONFIG_MACH_TENDERLOIN
1554 /* MBM */
1555alloc_fail3_1:
1556 usb_free_coherent(usb_dev,acm->bMaxPacketSize0, acm->inbuf, acm->response->transfer_dma);
1557alloc_fail3:
1558 usb_free_urb(acm->response);
1559#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560alloc_fail2:
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001561 acm_release_minor(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 kfree(acm);
1563alloc_fail:
1564 return -ENOMEM;
1565}
1566
Oliver Neukum1365baf2007-10-12 17:24:28 +02001567static void stop_data_traffic(struct acm *acm)
1568{
1569 int i;
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001570
1571 dev_dbg(&acm->control->dev, "%s\n", __func__);
flintman5c2f9732014-08-11 21:52:25 -04001572#ifndef CONFIG_MACH_TENDERLOIN
Oliver Neukum1365baf2007-10-12 17:24:28 +02001573 usb_kill_urb(acm->ctrlurb);
flintman5c2f9732014-08-11 21:52:25 -04001574#endif
Alan Cox6e47e062009-06-11 12:37:06 +01001575 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001576 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001577 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001578 usb_kill_urb(acm->read_urbs[i]);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001579
1580 cancel_work_sync(&acm->work);
1581}
1582
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583static void acm_disconnect(struct usb_interface *intf)
1584{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001585 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001587 struct tty_struct *tty;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001588 int i;
1589
1590 dev_dbg(&intf->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591
David Brownell672c4e12008-08-06 18:41:12 -07001592 /* sibling interface is already cleaning up */
1593 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001594 return;
David Brownell672c4e12008-08-06 18:41:12 -07001595
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001596 mutex_lock(&acm->mutex);
1597 acm->disconnected = true;
Alan Cox6e47e062009-06-11 12:37:06 +01001598 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001599 device_remove_file(&acm->control->dev,
1600 &dev_attr_wCountryCodes);
1601 device_remove_file(&acm->control->dev,
1602 &dev_attr_iCountryCodeRelDate);
Oliver Neukum65069bd2014-11-20 14:54:35 +01001603 kfree(acm->country_codes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001604 }
Alan Stern74da5d62007-08-02 13:29:10 -04001605 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001606 usb_set_intfdata(acm->control, NULL);
1607 usb_set_intfdata(acm->data, NULL);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001608 mutex_unlock(&acm->mutex);
1609
1610 tty = tty_port_tty_get(&acm->port);
1611 if (tty) {
1612 tty_vhangup(tty);
1613 tty_kref_put(tty);
1614 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615
Oliver Neukum1365baf2007-10-12 17:24:28 +02001616 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617
Johan Hovold10a00e32013-03-19 09:21:06 +01001618 tty_unregister_device(acm_tty_driver, acm->minor);
1619
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001620 usb_free_urb(acm->ctrlurb);
1621 for (i = 0; i < ACM_NW; i++)
1622 usb_free_urb(acm->wb[i].urb);
1623 for (i = 0; i < acm->rx_buflimit; i++)
1624 usb_free_urb(acm->read_urbs[i]);
Oliver Neukum884b6002005-04-21 21:28:02 +02001625 acm_write_buffers_free(acm);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001626 usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001627 acm_read_buffers_free(acm);
flintman5c2f9732014-08-11 21:52:25 -04001628#ifdef CONFIG_MACH_TENDERLOIN
1629 /* MBM */
1630 usb_free_coherent(usb_dev,acm->bMaxPacketSize0, acm->inbuf, acm->response->transfer_dma);
1631#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001633 if (!acm->combined_interfaces)
1634 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001635 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001637 tty_port_put(&acm->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638}
1639
Oliver Neukum35758582008-07-01 19:10:08 +02001640#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001641static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1642{
1643 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001644 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001645
Oliver Neukum11ea8592008-06-20 11:25:57 +02001646 spin_lock_irq(&acm->read_lock);
1647 spin_lock(&acm->write_lock);
Johan Hovoldbe7978b2014-05-26 19:23:36 +02001648 if (PMSG_IS_AUTO(message)) {
1649 if (acm->transmitting) {
1650 spin_unlock(&acm->write_lock);
1651 spin_unlock_irq(&acm->read_lock);
1652 return -EBUSY;
1653 }
1654 }
Oliver Neukum11ea8592008-06-20 11:25:57 +02001655 cnt = acm->susp_count++;
1656 spin_unlock(&acm->write_lock);
1657 spin_unlock_irq(&acm->read_lock);
1658
1659 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001660 return 0;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001661
flintman5c2f9732014-08-11 21:52:25 -04001662#ifdef CONFIG_MACH_TENDERLOIN
1663 /* MBM, Kill URB here! */
1664 usb_kill_urb(acm->ctrlurb);
1665#endif
1666
Johan Hovold181614f2014-05-26 19:23:40 +02001667 stop_data_traffic(acm);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001668
Oliver Neukum1365baf2007-10-12 17:24:28 +02001669 return 0;
1670}
1671
1672static int acm_resume(struct usb_interface *intf)
1673{
1674 struct acm *acm = usb_get_intfdata(intf);
flintmand53cff62015-03-04 20:21:21 -05001675 struct acm_wb *wb;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001676 int rv = 0;
1677
Oliver Neukum11ea8592008-06-20 11:25:57 +02001678 spin_lock_irq(&acm->read_lock);
Johan Hovold97e72c42014-05-26 19:23:37 +02001679 spin_lock(&acm->write_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001680
Johan Hovold97e72c42014-05-26 19:23:37 +02001681 if (--acm->susp_count)
1682 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001683
flintman5c2f9732014-08-11 21:52:25 -04001684#ifdef CONFIG_MACH_TENDERLOIN
1685 /* MBM, We have to resubmit the INT URB regardless of the port is open or not */
1686 if (usb_submit_urb(acm->ctrlurb, GFP_ATOMIC)) {
1687 dev_err(&intf->dev, "usb_submit_urb(ctrl irq) failed");
1688 }
1689#endif
1690
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001691 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
flintman5c2f9732014-08-11 21:52:25 -04001692#ifndef CONFIG_MACH_TENDERLOIN
Johan Hovold97e72c42014-05-26 19:23:37 +02001693 rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
flintman5c2f9732014-08-11 21:52:25 -04001694#endif
Oliver Neukum97d35f92009-12-16 17:05:57 +01001695
flintmand53cff62015-03-04 20:21:21 -05001696 if (acm->delayed_wb) {
1697 wb = acm->delayed_wb;
1698 acm->delayed_wb = NULL;
1699 acm_start_wb(acm, wb);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001700 }
1701
1702 /*
1703 * delayed error checking because we must
1704 * do the write path at all cost
1705 */
Oliver Neukum1365baf2007-10-12 17:24:28 +02001706 if (rv < 0)
Johan Hovold97e72c42014-05-26 19:23:37 +02001707 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001708
Johan Hovold97e72c42014-05-26 19:23:37 +02001709 rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001710 }
Johan Hovold97e72c42014-05-26 19:23:37 +02001711out:
1712 spin_unlock(&acm->write_lock);
1713 spin_unlock_irq(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001714
Oliver Neukum1365baf2007-10-12 17:24:28 +02001715 return rv;
1716}
Oliver Neukum35758582008-07-01 19:10:08 +02001717
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001718static int acm_reset_resume(struct usb_interface *intf)
1719{
1720 struct acm *acm = usb_get_intfdata(intf);
1721 struct tty_struct *tty;
1722
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001723 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001724 tty = tty_port_tty_get(&acm->port);
1725 if (tty) {
1726 tty_hangup(tty);
1727 tty_kref_put(tty);
1728 }
1729 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001730
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001731 return acm_resume(intf);
1732}
1733
Oliver Neukum35758582008-07-01 19:10:08 +02001734#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001735
1736#define NOKIA_PCSUITE_ACM_INFO(x) \
1737 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1738 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1739 USB_CDC_ACM_PROTO_VENDOR)
1740
Toby Gray4035e452010-09-01 16:01:19 +01001741#define SAMSUNG_PCSUITE_ACM_INFO(x) \
1742 USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
1743 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1744 USB_CDC_ACM_PROTO_VENDOR)
1745
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746/*
1747 * USB driver structure.
1748 */
1749
Németh Márton6ef48522010-01-10 15:33:45 +01001750static const struct usb_device_id acm_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751 /* quirky and broken devices */
David Cluytensbd73e382013-12-03 14:18:57 +01001752 { USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */
1753 .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1755 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1756 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001757 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1758 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1759 },
Andrew Lunn0f9c7b42008-12-23 17:31:23 +01001760 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1761 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1762 },
Masahito Omote8753e652005-07-29 12:17:25 -07001763 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1764 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1765 },
Chris Malley91a9c922006-10-03 10:08:28 +01001766 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1767 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1768 },
Alan Cox7abcf202009-04-06 17:35:01 +01001769 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1770 .driver_info = SINGLE_RX_URB,
1771 },
Oliver Neukum86478942006-05-13 22:50:47 +02001772 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1773 .driver_info = SINGLE_RX_URB, /* firmware bug */
1774 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001775 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1776 .driver_info = SINGLE_RX_URB, /* firmware bug */
1777 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001778 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1779 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1780 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001781 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1782 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1783 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001784 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1785 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1786 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001787 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1788 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1789 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001790 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1791 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1792 },
Johan Hovold1ff3bbb2014-10-27 18:34:33 +01001793 { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001794 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1795 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001796 /* Motorola H24 HSPA module: */
1797 { USB_DEVICE(0x22b8, 0x2d91) }, /* modem */
Michael Ulbrichtb08f08b2014-03-25 10:34:18 +01001798 { USB_DEVICE(0x22b8, 0x2d92), /* modem + diagnostics */
1799 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1800 },
1801 { USB_DEVICE(0x22b8, 0x2d93), /* modem + AT port */
1802 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1803 },
1804 { USB_DEVICE(0x22b8, 0x2d95), /* modem + AT port + diagnostics */
1805 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1806 },
1807 { USB_DEVICE(0x22b8, 0x2d96), /* modem + NMEA */
1808 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1809 },
1810 { USB_DEVICE(0x22b8, 0x2d97), /* modem + diagnostics + NMEA */
1811 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1812 },
1813 { USB_DEVICE(0x22b8, 0x2d99), /* modem + AT port + NMEA */
1814 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1815 },
1816 { USB_DEVICE(0x22b8, 0x2d9a), /* modem + AT port + diagnostics + NMEA */
1817 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1818 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001819
Adam Richterc332b4e2009-02-18 16:17:15 -08001820 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1821 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1822 data interface instead of
1823 communications interface.
1824 Maybe we should define a new
1825 quirk for this. */
1826 },
Jean-Christian de Rivaz52538352012-10-10 12:49:02 +00001827 { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */
1828 .driver_info = NO_UNION_NORMAL,
1829 },
Denis N Ladinb756d752012-12-26 18:29:44 +05001830 { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */
1831 .driver_info = NO_UNION_NORMAL,
1832 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001833 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1834 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1835 },
Russ Nelsonc3baa192010-04-21 23:07:03 -04001836 { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1837 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1838 },
flintman5c2f9732014-08-11 21:52:25 -04001839 /* MBM */
1840 { USB_DEVICE(0x1519, 0x0020), /* IMC_MAIN - XMM6260, XMM6262 */
1841 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1842 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001843
Adrian Taylorc1479a92009-11-19 10:35:33 +00001844 /* Nokia S60 phones expose two ACM channels. The first is
1845 * a modem and is picked up by the standard AT-command
1846 * information below. The second is 'vendor-specific' but
1847 * is treated as a serial device at the S60 end, so we want
1848 * to expose it on Linux too. */
1849 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1850 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1851 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1852 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1853 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1854 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1855 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1856 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1857 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1858 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1859 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1860 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1861 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1862 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1863 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1864 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1865 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1866 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1867 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1868 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1869 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1870 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1871 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1872 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1873 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1874 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1875 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1876 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1877 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1878 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1879 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1880 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1881 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1882 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1883 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1884 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1885 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1886 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1887 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1888 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1889 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1890 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1891 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
Przemo Firszt83a4eae2010-06-28 21:29:34 +01001892 { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
Toby Gray4035e452010-09-01 16:01:19 +01001893 { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
1894 { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
1895 { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
1896 { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
1897 { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
1898 { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
1899 { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
1900 { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
1901 { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
1902 { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
Arvid Ephraim Picciani721d92f2011-01-25 15:58:40 +01001903 { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
Toby Gray4061fde2011-06-06 14:52:48 +01001904 { NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */
1905 { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
Toby Gray4035e452010-09-01 16:01:19 +01001906 { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001907
Denis Pershin65e52f42011-09-04 17:37:21 +07001908 /* Support for Owen devices */
1909 { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */
1910
Adrian Taylorc1479a92009-11-19 10:35:33 +00001911 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1912
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001913 /* Support Lego NXT using pbLua firmware */
Julian Calabyce126642010-01-05 23:58:20 +11001914 { USB_DEVICE(0x0694, 0xff00),
1915 .driver_info = NOT_A_MODEM,
Otavio Salvador7893afc2010-09-26 23:35:05 -03001916 },
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001917
Erik Slagterfd5054c2011-05-11 12:06:55 +02001918 /* Support for Droids MuIn LCD */
1919 { USB_DEVICE(0x04d8, 0x000b),
1920 .driver_info = NO_DATA_INTERFACE,
1921 },
flintman5c2f9732014-08-11 21:52:25 -04001922#ifndef CONFIG_MACH_TENDERLOIN
Philippe Corbes5b239f02010-08-31 19:31:32 +02001923 /* control interfaces without any protocol set */
1924 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1925 USB_CDC_PROTO_NONE) },
flintman5c2f9732014-08-11 21:52:25 -04001926#else
1927 /* Exclude XMM6260 boot rom (not running modem software yet) */
1928 { USB_DEVICE(0x058b, 0x0041),
1929 .driver_info = NOT_REAL_ACM,
1930 },
1931#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 /* control interfaces with various AT-command sets */
1933 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1934 USB_CDC_ACM_PROTO_AT_V25TER) },
1935 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1936 USB_CDC_ACM_PROTO_AT_PCCA101) },
1937 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1938 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1939 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1940 USB_CDC_ACM_PROTO_AT_GSM) },
1941 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001942 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1944 USB_CDC_ACM_PROTO_AT_CDMA) },
1945
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 { }
1947};
1948
Alan Cox6e47e062009-06-11 12:37:06 +01001949MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950
1951static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 .name = "cdc_acm",
1953 .probe = acm_probe,
1954 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001955#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001956 .suspend = acm_suspend,
1957 .resume = acm_resume,
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001958 .reset_resume = acm_reset_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001959#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001961#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001962 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001963#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964};
1965
1966/*
1967 * TTY driver structures.
1968 */
1969
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001970static const struct tty_operations acm_ops = {
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001971 .install = acm_tty_install,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972 .open = acm_tty_open,
1973 .close = acm_tty_close,
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001974 .cleanup = acm_tty_cleanup,
Alan Cox10077d42009-06-11 12:36:09 +01001975 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976 .write = acm_tty_write,
1977 .write_room = acm_tty_write_room,
1978 .ioctl = acm_tty_ioctl,
1979 .throttle = acm_tty_throttle,
1980 .unthrottle = acm_tty_unthrottle,
1981 .chars_in_buffer = acm_tty_chars_in_buffer,
1982 .break_ctl = acm_tty_break_ctl,
1983 .set_termios = acm_tty_set_termios,
1984 .tiocmget = acm_tty_tiocmget,
1985 .tiocmset = acm_tty_tiocmset,
1986};
1987
1988/*
1989 * Init / exit.
1990 */
1991
1992static int __init acm_init(void)
1993{
1994 int retval;
1995 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1996 if (!acm_tty_driver)
1997 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998 acm_tty_driver->driver_name = "acm",
1999 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 acm_tty_driver->major = ACM_TTY_MAJOR,
2001 acm_tty_driver->minor_start = 0,
2002 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
2003 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07002004 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01002006 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
2007 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 tty_set_operations(acm_tty_driver, &acm_ops);
2009
2010 retval = tty_register_driver(acm_tty_driver);
2011 if (retval) {
2012 put_tty_driver(acm_tty_driver);
2013 return retval;
2014 }
2015
2016 retval = usb_register(&acm_driver);
2017 if (retval) {
2018 tty_unregister_driver(acm_tty_driver);
2019 put_tty_driver(acm_tty_driver);
2020 return retval;
2021 }
2022
Johan Hovolda2c7b932011-03-22 11:12:18 +01002023 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024
2025 return 0;
2026}
2027
2028static void __exit acm_exit(void)
2029{
2030 usb_deregister(&acm_driver);
2031 tty_unregister_driver(acm_tty_driver);
2032 put_tty_driver(acm_tty_driver);
2033}
2034
2035module_init(acm_init);
2036module_exit(acm_exit);
2037
Alan Cox6e47e062009-06-11 12:37:06 +01002038MODULE_AUTHOR(DRIVER_AUTHOR);
2039MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01002041MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);