blob: cb1dec34a85daa47d123858ea5e682d5f8a0834b [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) {
flintman5c2f9732014-08-11 21:52:25 -0400265 if (!acm->delayed_wb)
266 acm->delayed_wb = wb;
267 else {
268 if (acm->delayed_wb->len + wb->len <= acm->writesize ) {
269 memcpy(acm->delayed_wb->buf + acm->delayed_wb->len, wb->buf, wb->len);
270 acm->delayed_wb->len += wb->len;
271 }
272 wb->use = 0;
273 usb_autopm_put_interface_async(acm->control);
274 }
275
Oliver Neukum11ea8592008-06-20 11:25:57 +0200276 spin_unlock_irqrestore(&acm->write_lock, flags);
flintman5c2f9732014-08-11 21:52:25 -0400277 return 0; /* A white lie */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200278 }
279 usb_mark_last_busy(acm->dev);
280
Oliver Neukum11ea8592008-06-20 11:25:57 +0200281 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200282 spin_unlock_irqrestore(&acm->write_lock, flags);
283
Oliver Neukum884b6002005-04-21 21:28:02 +0200284 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200285
Oliver Neukum884b6002005-04-21 21:28:02 +0200286}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100287/*
288 * attributes exported through sysfs
289 */
290static ssize_t show_caps
291(struct device *dev, struct device_attribute *attr, char *buf)
292{
293 struct usb_interface *intf = to_usb_interface(dev);
294 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200295
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100296 return sprintf(buf, "%d", acm->ctrl_caps);
297}
298static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
299
300static ssize_t show_country_codes
301(struct device *dev, struct device_attribute *attr, char *buf)
302{
303 struct usb_interface *intf = to_usb_interface(dev);
304 struct acm *acm = usb_get_intfdata(intf);
305
306 memcpy(buf, acm->country_codes, acm->country_code_size);
307 return acm->country_code_size;
308}
309
310static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
311
312static ssize_t show_country_rel_date
313(struct device *dev, struct device_attribute *attr, char *buf)
314{
315 struct usb_interface *intf = to_usb_interface(dev);
316 struct acm *acm = usb_get_intfdata(intf);
317
318 return sprintf(buf, "%d", acm->country_rel_date);
319}
320
321static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200322/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 * Interrupt handlers for various ACM device responses
324 */
325
flintman5c2f9732014-08-11 21:52:25 -0400326/* MBM */
327static void acm_in_callback(struct urb *urb)
328{
329 struct acm *acm = urb->context;
330 int status = urb->status;
331
332 if (status) {
333 switch (status) {
334 case -ENOENT:
335 dev_dbg(&urb->dev->dev,
336 "nonzero urb status received: -ENOENT");
337 goto skip_error;
338 case -ECONNRESET:
339 dev_dbg(&urb->dev->dev,
340 "nonzero urb status received: -ECONNRESET");
341 goto skip_error;
342 case -ESHUTDOWN:
343 dev_dbg(&urb->dev->dev,
344 "nonzero urb status received: -ESHUTDOWN");
345 goto skip_error;
346 case -EPIPE:
347 dev_err(&urb->dev->dev,
348 "nonzero urb status received: -EPIPE\n");
349 break;
350 default:
351 dev_err(&urb->dev->dev,
352 "Unexpected error %d\n", status);
353 break;
354 }
355 }
356
357 dbg("unsupported encap: %s", (char *)acm->inbuf);
358skip_error:
359 dbg("");
360}
361
362
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100364static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365{
366 struct acm *acm = urb->context;
367 struct usb_cdc_notification *dr = urb->transfer_buffer;
flintman5c2f9732014-08-11 21:52:25 -0400368 /* MBM */
369 struct usb_ctrlrequest *req = acm->irq;
Alan Cox10077d42009-06-11 12:36:09 +0100370 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 unsigned char *data;
372 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700373 int retval;
374 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700376 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 case 0:
378 /* success */
379 break;
380 case -ECONNRESET:
381 case -ENOENT:
382 case -ESHUTDOWN:
383 /* this urb is terminated, clean up */
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100384 dev_dbg(&acm->control->dev,
385 "%s - urb shutting down with status: %d\n",
386 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 return;
388 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100389 dev_dbg(&acm->control->dev,
390 "%s - nonzero urb status received: %d\n",
391 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 goto exit;
393 }
394
Johan Hovold7e7797e2011-03-22 11:12:11 +0100395 usb_mark_last_busy(acm->dev);
396
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 data = (unsigned char *)(dr + 1);
398 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100399 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
flintman5c2f9732014-08-11 21:52:25 -0400400 /* MBM */
401 dev_info(&urb->dev->dev, "%s network", dr->wValue ?
402 "connected to" : "disconnected from");
Alan Cox6e47e062009-06-11 12:37:06 +0100403 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
Alan Cox6e47e062009-06-11 12:37:06 +0100405 case USB_CDC_NOTIFY_SERIAL_STATE:
406 tty = tty_port_tty_get(&acm->port);
407 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
Alan Cox6e47e062009-06-11 12:37:06 +0100409 if (tty) {
410 if (!acm->clocal &&
411 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100412 dev_dbg(&acm->control->dev,
413 "%s - calling hangup\n", __func__);
Alan Cox6e47e062009-06-11 12:37:06 +0100414 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 }
Alan Cox6e47e062009-06-11 12:37:06 +0100416 tty_kref_put(tty);
417 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
Alan Cox6e47e062009-06-11 12:37:06 +0100419 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100421 dev_dbg(&acm->control->dev,
422 "%s - input control lines: dcd%c dsr%c break%c "
423 "ring%c framing%c parity%c overrun%c\n",
424 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100425 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
426 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
427 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
428 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
429 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
430 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
431 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 break;
433
flintman5c2f9732014-08-11 21:52:25 -0400434 /* MBM */
435 case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
436 dev_err(&urb->dev->dev, "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
437 dr->wIndex, dr->wLength);
438 req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
439 req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
440 req->wValue = 0;
441 req->wIndex = cpu_to_le16(dr->wIndex);
442 req->wLength = cpu_to_le16(acm->bMaxPacketSize0);
443
444 usb_fill_control_urb(
445 acm->response,
446 acm->dev,
447 usb_sndctrlpipe(acm->dev, 0),
448 (unsigned char *)req,
449 acm->inbuf,
450 acm->bMaxPacketSize0,
451 acm_in_callback,
452 acm);
453
454 break;
455
Alan Cox6e47e062009-06-11 12:37:06 +0100456 default:
flintman5c2f9732014-08-11 21:52:25 -0400457 /* MBM */
458 dev_info(&urb->dev->dev,"unknown notification %d received: index %d len %d data0 %d data1 %d",
Alan Cox6e47e062009-06-11 12:37:06 +0100459 dr->bNotificationType, dr->wIndex,
460 dr->wLength, data[0], data[1]);
461 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 }
463exit:
Alan Cox6e47e062009-06-11 12:37:06 +0100464 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700465 if (retval)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100466 dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
467 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468}
469
Johan Hovold088c64f2011-03-25 11:06:02 +0100470static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471{
Johan Hovold088c64f2011-03-25 11:06:02 +0100472 int res;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700473
Johan Hovold088c64f2011-03-25 11:06:02 +0100474 if (!test_and_clear_bit(index, &acm->read_urbs_free))
475 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
Johan Hovold088c64f2011-03-25 11:06:02 +0100477 dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
478
479 res = usb_submit_urb(acm->read_urbs[index], mem_flags);
480 if (res) {
481 if (res != -EPERM) {
482 dev_err(&acm->data->dev,
483 "%s - usb_submit_urb failed: %d\n",
484 __func__, res);
485 }
486 set_bit(index, &acm->read_urbs_free);
487 return res;
488 }
489
490 return 0;
491}
492
493static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
494{
495 int res;
496 int i;
497
498 for (i = 0; i < acm->rx_buflimit; ++i) {
499 res = acm_submit_read_urb(acm, i, mem_flags);
500 if (res)
501 return res;
502 }
503
504 return 0;
505}
506
507static void acm_process_read_urb(struct acm *acm, struct urb *urb)
508{
509 struct tty_struct *tty;
510
511 if (!urb->actual_length)
512 return;
513
514 tty = tty_port_tty_get(&acm->port);
515 if (!tty)
516 return;
517
518 tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
519 tty_flip_buffer_push(tty);
520
521 tty_kref_put(tty);
522}
523
524static void acm_read_bulk_callback(struct urb *urb)
525{
526 struct acm_rb *rb = urb->context;
527 struct acm *acm = rb->instance;
528 unsigned long flags;
529
530 dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
531 rb->index, urb->actual_length);
532 set_bit(rb->index, &acm->read_urbs_free);
533
534 if (!acm->dev) {
535 dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200537 }
538 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
Johan Hovold088c64f2011-03-25 11:06:02 +0100540 if (urb->status) {
Johan Hovold1d9846e2011-03-22 11:12:14 +0100541 dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
Johan Hovold088c64f2011-03-25 11:06:02 +0100542 __func__, urb->status);
543 return;
544 }
545 acm_process_read_urb(acm, urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
Johan Hovold088c64f2011-03-25 11:06:02 +0100547 /* throttle device if requested by tty */
548 spin_lock_irqsave(&acm->read_lock, flags);
549 acm->throttled = acm->throttle_req;
550 if (!acm->throttled && !acm->susp_count) {
551 spin_unlock_irqrestore(&acm->read_lock, flags);
552 acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
Oliver Neukum86478942006-05-13 22:50:47 +0200553 } else {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200554 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556}
557
558/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100559static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560{
Ming Leicdc97792008-02-24 18:41:47 +0800561 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700562 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800563 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200564
Johan Hovold4fa46262011-03-22 11:12:16 +0100565 if (urb->status || (urb->actual_length != urb->transfer_buffer_length))
566 dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
Johan Hovold1d9846e2011-03-22 11:12:14 +0100567 __func__,
David Brownelle5fbab52008-08-06 18:46:10 -0700568 urb->actual_length,
569 urb->transfer_buffer_length,
570 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800572 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100573 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800574 spin_unlock_irqrestore(&acm->write_lock, flags);
Havard Skinnemoen99823f42011-12-09 16:51:54 -0800575 schedule_work(&acm->work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576}
577
David Howellsc4028952006-11-22 14:57:56 +0000578static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579{
David Howellsc4028952006-11-22 14:57:56 +0000580 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100581 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700582
Johan Hovold1d9846e2011-03-22 11:12:14 +0100583 dev_vdbg(&acm->data->dev, "%s\n", __func__);
584
Alan Cox10077d42009-06-11 12:36:09 +0100585 tty = tty_port_tty_get(&acm->port);
Johan Hovold15e5bee2011-03-22 11:12:10 +0100586 if (!tty)
587 return;
Alan Cox10077d42009-06-11 12:36:09 +0100588 tty_wakeup(tty);
589 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590}
591
592/*
593 * TTY handlers
594 */
595
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800596static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597{
598 struct acm *acm;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800599 int retval;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100600
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800601 dev_dbg(tty->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800603 acm = acm_get_by_index(tty->index);
604 if (!acm)
605 return -ENODEV;
606
Jiri Slabyf8a8c102012-03-05 14:51:48 +0100607 retval = tty_standard_install(driver, tty);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800608 if (retval)
609 goto error_init_termios;
610
611 tty->driver_data = acm;
612
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800613 return 0;
614
615error_init_termios:
616 tty_port_put(&acm->port);
617 return retval;
618}
619
620static int acm_tty_open(struct tty_struct *tty, struct file *filp)
621{
622 struct acm *acm = tty->driver_data;
623
624 dev_dbg(tty->dev, "%s\n", __func__);
625
626 return tty_port_open(&acm->port, tty, filp);
627}
628
629static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
630{
631 struct acm *acm = container_of(port, struct acm, port);
632 int retval = -ENODEV;
Johan Hovold8fd20d52014-05-26 19:23:44 +0200633 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100635 dev_dbg(&acm->control->dev, "%s\n", __func__);
636
Oliver Neukum1365baf2007-10-12 17:24:28 +0200637 mutex_lock(&acm->mutex);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800638 if (acm->disconnected)
639 goto disconnected;
640
641 retval = usb_autopm_get_interface(acm->control);
642 if (retval)
643 goto error_get_interface;
644
645 /*
646 * FIXME: Why do we need this? Allocating 64K of physically contiguous
647 * memory is really nasty...
648 */
649 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
650 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200651
flintman5c2f9732014-08-11 21:52:25 -0400652#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 acm->ctrlurb->dev = acm->dev;
654 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100655 dev_err(&acm->control->dev,
656 "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800657 goto error_submit_urb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 }
flintman5c2f9732014-08-11 21:52:25 -0400659#endif
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800660 acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS;
661 if (acm_set_control(acm, acm->ctrlout) < 0 &&
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100662 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800663 goto error_set_control;
Alan Cox10077d42009-06-11 12:36:09 +0100664
flintman5c2f9732014-08-11 21:52:25 -0400665 /* MBM */
666 acm->state &= ~ACM_ABS_IDLE;
667 if (0 > acm_set_comm_feature(acm, ACM_ABSTRACT_STATE, &acm->state) &&
668 (acm->ctrl_caps & USB_CDC_COMM_FEATURE))
669 goto error_set_control;
670
Oliver Neukum11ea8592008-06-20 11:25:57 +0200671 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
Otto Meta9cc2d462012-06-06 18:46:21 +0200673 /*
674 * Unthrottle device in case the TTY was closed while throttled.
675 */
676 spin_lock_irq(&acm->read_lock);
677 acm->throttled = 0;
678 acm->throttle_req = 0;
679 spin_unlock_irq(&acm->read_lock);
680
Johan Hovold088c64f2011-03-25 11:06:02 +0100681 if (acm_submit_read_urbs(acm, GFP_KERNEL))
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800682 goto error_submit_read_urbs;
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100683
Oliver Neukum1365baf2007-10-12 17:24:28 +0200684 mutex_unlock(&acm->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800686 return 0;
687
688error_submit_read_urbs:
Johan Hovold8fd20d52014-05-26 19:23:44 +0200689 for (i = 0; i < acm->rx_buflimit; i++)
690 usb_kill_urb(acm->read_urbs[i]);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800691 acm->ctrlout = 0;
692 acm_set_control(acm, acm->ctrlout);
693error_set_control:
694 usb_kill_urb(acm->ctrlurb);
flintman5c2f9732014-08-11 21:52:25 -0400695#ifndef CONFIG_MACH_TENDERLOIN
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800696error_submit_urb:
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100697 usb_autopm_put_interface(acm->control);
flintman5c2f9732014-08-11 21:52:25 -0400698#endif
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800699error_get_interface:
700disconnected:
701 mutex_unlock(&acm->mutex);
702 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703}
704
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800705static void acm_port_destruct(struct tty_port *port)
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700706{
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800707 struct acm *acm = container_of(port, struct acm, port);
708
709 dev_dbg(&acm->control->dev, "%s\n", __func__);
David Kubicek61a87ad2005-11-01 18:51:34 +0100710
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800711 acm_release_minor(acm);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700712 usb_put_intf(acm->control);
flintman5c2f9732014-08-11 21:52:25 -0400713 /* MBM */
714 usb_free_urb(acm->response);
715 kfree(acm->irq);
716
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100717 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700718 kfree(acm);
719}
720
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800721static void acm_port_shutdown(struct tty_port *port)
Alan Cox10077d42009-06-11 12:36:09 +0100722{
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800723 struct acm *acm = container_of(port, struct acm, port);
Johan Hovold21ffba32014-05-26 19:23:38 +0200724 struct urb *urb;
725 struct acm_wb *wb;
Johan Hovolddab54c92011-03-22 11:12:21 +0100726 int i;
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200727 int pm_err;
Johan Hovolddab54c92011-03-22 11:12:21 +0100728
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800729 dev_dbg(&acm->control->dev, "%s\n", __func__);
730
731 mutex_lock(&acm->mutex);
732 if (!acm->disconnected) {
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200733 pm_err = usb_autopm_get_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100734 acm_set_control(acm, acm->ctrlout = 0);
Johan Hovold21ffba32014-05-26 19:23:38 +0200735
736 for (;;) {
737 urb = usb_get_from_anchor(&acm->delayed);
738 if (!urb)
739 break;
740 wb = urb->context;
741 wb->use = 0;
742 usb_autopm_put_interface_async(acm->control);
743 }
744
Alan Cox10077d42009-06-11 12:36:09 +0100745 usb_kill_urb(acm->ctrlurb);
746 for (i = 0; i < ACM_NW; i++)
747 usb_kill_urb(acm->wb[i].urb);
Johan Hovolddab54c92011-03-22 11:12:21 +0100748 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +0100749 usb_kill_urb(acm->read_urbs[i]);
Alan Cox10077d42009-06-11 12:36:09 +0100750 acm->control->needs_remote_wakeup = 0;
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200751 if (!pm_err)
752 usb_autopm_put_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100753 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800754 mutex_unlock(&acm->mutex);
755}
756
757static void acm_tty_cleanup(struct tty_struct *tty)
758{
759 struct acm *acm = tty->driver_data;
760 dev_dbg(&acm->control->dev, "%s\n", __func__);
761 tty_port_put(&acm->port);
Alan Cox10077d42009-06-11 12:36:09 +0100762}
763
764static void acm_tty_hangup(struct tty_struct *tty)
765{
766 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800767 dev_dbg(&acm->control->dev, "%s\n", __func__);
Alan Cox10077d42009-06-11 12:36:09 +0100768 tty_port_hangup(&acm->port);
Alan Cox10077d42009-06-11 12:36:09 +0100769}
770
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771static void acm_tty_close(struct tty_struct *tty, struct file *filp)
772{
773 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800774 dev_dbg(&acm->control->dev, "%s\n", __func__);
775 tty_port_close(&acm->port, tty, filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776}
777
Alan Cox6e47e062009-06-11 12:37:06 +0100778static int acm_tty_write(struct tty_struct *tty,
779 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780{
781 struct acm *acm = tty->driver_data;
782 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200783 unsigned long flags;
784 int wbn;
785 struct acm_wb *wb;
786
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 if (!count)
788 return 0;
789
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100790 dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100791
Oliver Neukum884b6002005-04-21 21:28:02 +0200792 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100793 wbn = acm_wb_alloc(acm);
794 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200795 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200796 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200798 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
Oliver Neukum884b6002005-04-21 21:28:02 +0200800 count = (count > acm->writesize) ? acm->writesize : count;
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100801 dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
Oliver Neukum884b6002005-04-21 21:28:02 +0200802 memcpy(wb->buf, buf, count);
803 wb->len = count;
804 spin_unlock_irqrestore(&acm->write_lock, flags);
805
Alan Cox6e47e062009-06-11 12:37:06 +0100806 stat = acm_write_start(acm, wbn);
807 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200808 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 return count;
810}
811
812static int acm_tty_write_room(struct tty_struct *tty)
813{
814 struct acm *acm = tty->driver_data;
Oliver Neukum884b6002005-04-21 21:28:02 +0200815 /*
816 * Do not let the line discipline to know that we have a reserve,
817 * or it might get too enthusiastic.
818 */
David Brownell934da462008-08-06 18:44:12 -0700819 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820}
821
822static int acm_tty_chars_in_buffer(struct tty_struct *tty)
823{
824 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800825 /*
826 * if the device was unplugged then any remaining characters fell out
827 * of the connector ;)
828 */
829 if (acm->disconnected)
Alan Cox23198fd2009-07-20 16:05:27 +0100830 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200831 /*
832 * This is inaccurate (overcounts), but it works.
833 */
Oliver Neukum86478942006-05-13 22:50:47 +0200834 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835}
836
837static void acm_tty_throttle(struct tty_struct *tty)
838{
839 struct acm *acm = tty->driver_data;
Johan Hovold088c64f2011-03-25 11:06:02 +0100840
Johan Hovold088c64f2011-03-25 11:06:02 +0100841 spin_lock_irq(&acm->read_lock);
842 acm->throttle_req = 1;
843 spin_unlock_irq(&acm->read_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844}
845
846static void acm_tty_unthrottle(struct tty_struct *tty)
847{
848 struct acm *acm = tty->driver_data;
Johan Hovold088c64f2011-03-25 11:06:02 +0100849 unsigned int was_throttled;
850
Johan Hovold088c64f2011-03-25 11:06:02 +0100851 spin_lock_irq(&acm->read_lock);
852 was_throttled = acm->throttled;
853 acm->throttled = 0;
854 acm->throttle_req = 0;
855 spin_unlock_irq(&acm->read_lock);
856
857 if (was_throttled)
858 acm_submit_read_urbs(acm, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859}
860
Alan Cox9e989662008-07-22 11:18:03 +0100861static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862{
863 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100864 int retval;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800865
Alan Cox9e989662008-07-22 11:18:03 +0100866 retval = acm_send_break(acm, state ? 0xffff : 0);
867 if (retval < 0)
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100868 dev_dbg(&acm->control->dev, "%s - send break failed\n",
869 __func__);
Alan Cox9e989662008-07-22 11:18:03 +0100870 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871}
872
Alan Cox60b33c12011-02-14 16:26:14 +0000873static int acm_tty_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874{
875 struct acm *acm = tty->driver_data;
876
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
878 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
879 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
880 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
881 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
882 TIOCM_CTS;
883}
884
Alan Cox20b9d172011-02-14 16:26:50 +0000885static int acm_tty_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 unsigned int set, unsigned int clear)
887{
888 struct acm *acm = tty->driver_data;
889 unsigned int newctrl;
890
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100892 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
893 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
894 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
895 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896
897 newctrl = (newctrl & ~clear) | set;
898
899 if (acm->ctrlout == newctrl)
900 return 0;
901 return acm_set_control(acm, acm->ctrlout = newctrl);
902}
903
Oliver Neukum18c75722012-02-17 17:21:24 -0500904static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
905{
906 struct serial_struct tmp;
907
908 if (!info)
909 return -EINVAL;
910
911 memset(&tmp, 0, sizeof(tmp));
912 tmp.flags = ASYNC_LOW_LATENCY;
913 tmp.xmit_fifo_size = acm->writesize;
914 tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
Dan Williams1229a832012-11-08 12:47:41 -0600915 tmp.close_delay = acm->port.close_delay / 10;
916 tmp.closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
917 ASYNC_CLOSING_WAIT_NONE :
918 acm->port.closing_wait / 10;
Oliver Neukum18c75722012-02-17 17:21:24 -0500919
920 if (copy_to_user(info, &tmp, sizeof(tmp)))
921 return -EFAULT;
922 else
923 return 0;
924}
925
Dan Williams1229a832012-11-08 12:47:41 -0600926static int set_serial_info(struct acm *acm,
927 struct serial_struct __user *newinfo)
928{
929 struct serial_struct new_serial;
930 unsigned int closing_wait, close_delay;
931 int retval = 0;
932
933 if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
934 return -EFAULT;
935
936 close_delay = new_serial.close_delay * 10;
937 closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
938 ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
939
940 mutex_lock(&acm->port.mutex);
941
942 if (!capable(CAP_SYS_ADMIN)) {
943 if ((close_delay != acm->port.close_delay) ||
944 (closing_wait != acm->port.closing_wait))
945 retval = -EPERM;
946 else
947 retval = -EOPNOTSUPP;
948 } else {
949 acm->port.close_delay = close_delay;
950 acm->port.closing_wait = closing_wait;
951 }
952
953 mutex_unlock(&acm->port.mutex);
954 return retval;
955}
956
Alan Cox6caa76b2011-02-14 16:27:22 +0000957static int acm_tty_ioctl(struct tty_struct *tty,
Alan Cox6e47e062009-06-11 12:37:06 +0100958 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959{
Oliver Neukum18c75722012-02-17 17:21:24 -0500960 struct acm *acm = tty->driver_data;
961 int rv = -ENOIOCTLCMD;
962
963 switch (cmd) {
964 case TIOCGSERIAL: /* gets serial port data */
965 rv = get_serial_info(acm, (struct serial_struct __user *) arg);
966 break;
Dan Williams1229a832012-11-08 12:47:41 -0600967 case TIOCSSERIAL:
968 rv = set_serial_info(acm, (struct serial_struct __user *) arg);
969 break;
Oliver Neukum18c75722012-02-17 17:21:24 -0500970 }
971
972 return rv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973}
974
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100975static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 0, 50, 75, 110, 134, 150, 200, 300, 600,
977 1200, 1800, 2400, 4800, 9600, 19200, 38400,
978 57600, 115200, 230400, 460800, 500000, 576000,
979 921600, 1000000, 1152000, 1500000, 2000000,
980 2500000, 3000000, 3500000, 4000000
981};
982
Alan Cox6e47e062009-06-11 12:37:06 +0100983static void acm_tty_set_termios(struct tty_struct *tty,
984 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985{
986 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800987 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 struct usb_cdc_line_coding newline;
989 int newctrl = acm->ctrlout;
990
Alan Cox9b80fee2009-09-19 13:13:23 -0700991 newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
993 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100994 (termios->c_cflag & PARODD ? 1 : 2) +
995 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Nicolas Boullisa5204462012-10-16 00:06:23 +0200996 switch (termios->c_cflag & CSIZE) {
997 case CS5:
998 newline.bDataBits = 5;
999 break;
1000 case CS6:
1001 newline.bDataBits = 6;
1002 break;
1003 case CS7:
1004 newline.bDataBits = 7;
1005 break;
1006 case CS8:
1007 default:
1008 newline.bDataBits = 8;
1009 break;
1010 }
Alan Cox6e47e062009-06-11 12:37:06 +01001011 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
1013
1014 if (!newline.dwDTERate) {
1015 newline.dwDTERate = acm->line.dwDTERate;
1016 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +01001017 } else
1018 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019
1020 if (newctrl != acm->ctrlout)
1021 acm_set_control(acm, acm->ctrlout = newctrl);
1022
1023 if (memcmp(&acm->line, &newline, sizeof newline)) {
1024 memcpy(&acm->line, &newline, sizeof newline);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001025 dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
1026 __func__,
1027 le32_to_cpu(newline.dwDTERate),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 newline.bCharFormat, newline.bParityType,
1029 newline.bDataBits);
1030 acm_set_line(acm, &acm->line);
1031 }
1032}
1033
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001034static const struct tty_port_operations acm_port_ops = {
1035 .shutdown = acm_port_shutdown,
1036 .activate = acm_port_activate,
1037 .destruct = acm_port_destruct,
1038};
1039
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040/*
1041 * USB probe and disconnect routines.
1042 */
1043
Oliver Neukum830f4022008-06-25 14:17:16 +02001044/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +02001045static void acm_write_buffers_free(struct acm *acm)
1046{
1047 int i;
1048 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +02001049 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +02001050
Alan Cox6e47e062009-06-11 12:37:06 +01001051 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Daniel Mack997ea582010-04-12 13:17:25 +02001052 usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +02001053}
1054
Oliver Neukum830f4022008-06-25 14:17:16 +02001055static void acm_read_buffers_free(struct acm *acm)
1056{
1057 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Johan Hovolddab54c92011-03-22 11:12:21 +01001058 int i;
Oliver Neukum830f4022008-06-25 14:17:16 +02001059
Johan Hovolddab54c92011-03-22 11:12:21 +01001060 for (i = 0; i < acm->rx_buflimit; i++)
Daniel Mack997ea582010-04-12 13:17:25 +02001061 usb_free_coherent(usb_dev, acm->readsize,
Johan Hovold088c64f2011-03-25 11:06:02 +01001062 acm->read_buffers[i].base, acm->read_buffers[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001063}
1064
Oliver Neukum884b6002005-04-21 21:28:02 +02001065/* Little helper: write buffers allocate */
1066static int acm_write_buffers_alloc(struct acm *acm)
1067{
1068 int i;
1069 struct acm_wb *wb;
1070
Oliver Neukum86478942006-05-13 22:50:47 +02001071 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Daniel Mack997ea582010-04-12 13:17:25 +02001072 wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
Oliver Neukum884b6002005-04-21 21:28:02 +02001073 &wb->dmah);
1074 if (!wb->buf) {
1075 while (i != 0) {
1076 --i;
1077 --wb;
Daniel Mack997ea582010-04-12 13:17:25 +02001078 usb_free_coherent(acm->dev, acm->writesize,
Oliver Neukum884b6002005-04-21 21:28:02 +02001079 wb->buf, wb->dmah);
1080 }
1081 return -ENOMEM;
1082 }
1083 }
1084 return 0;
1085}
1086
Alan Cox10077d42009-06-11 12:36:09 +01001087static int acm_probe(struct usb_interface *intf,
1088 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089{
1090 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001091 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -07001092 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 int buflen = intf->altsetting->extralen;
1094 struct usb_interface *control_interface;
1095 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001096 struct usb_endpoint_descriptor *epctrl = NULL;
1097 struct usb_endpoint_descriptor *epread = NULL;
1098 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 struct usb_device *usb_dev = interface_to_usbdev(intf);
1100 struct acm *acm;
1101 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +01001102 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 u8 *buf;
1104 u8 ac_management_function = 0;
1105 u8 call_management_function = 0;
1106 int call_interface_num = -1;
Erik Slagterfd5054c2011-05-11 12:06:55 +02001107 int data_interface_num = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +02001109 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001110 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001111 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112
Oliver Neukum86478942006-05-13 22:50:47 +02001113 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +02001115 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
1116
flintman5c2f9732014-08-11 21:52:25 -04001117 /* MBM, Not a real CDC ACM device */
1118 if (quirks == NOT_REAL_ACM)
1119 return -ENODEV;
1120
Oliver Neukum86478942006-05-13 22:50:47 +02001121 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 if (quirks == NO_UNION_NORMAL) {
1123 data_interface = usb_ifnum_to_if(usb_dev, 1);
1124 control_interface = usb_ifnum_to_if(usb_dev, 0);
1125 goto skip_normal_probe;
1126 }
Alan Cox6e47e062009-06-11 12:37:06 +01001127
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 /* normal probing*/
1129 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001130 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 return -EINVAL;
1132 }
1133
1134 if (!buflen) {
flintman5c2f9732014-08-11 21:52:25 -04001135#ifndef CONFIG_MACH_TENDERLOIN
Toby Gray577045c2010-09-02 10:46:20 +01001136 if (intf->cur_altsetting->endpoint &&
1137 intf->cur_altsetting->endpoint->extralen &&
Alan Cox6e47e062009-06-11 12:37:06 +01001138 intf->cur_altsetting->endpoint->extra) {
flintman5c2f9732014-08-11 21:52:25 -04001139#else
1140 /* MBM */
1141 if (intf->cur_altsetting->endpoint->extralen &&
1142 intf->cur_altsetting->endpoint->extra) {
1143#endif
Alan Cox6e47e062009-06-11 12:37:06 +01001144 dev_dbg(&intf->dev,
1145 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 buflen = intf->cur_altsetting->endpoint->extralen;
1147 buffer = intf->cur_altsetting->endpoint->extra;
1148 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001149 dev_err(&intf->dev,
1150 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 return -EINVAL;
1152 }
1153 }
1154
1155 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001156 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001157 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 goto next_desc;
1159 }
1160
Alan Cox6e47e062009-06-11 12:37:06 +01001161 switch (buffer[2]) {
1162 case USB_CDC_UNION_TYPE: /* we've found it */
1163 if (union_header) {
1164 dev_err(&intf->dev, "More than one "
1165 "union descriptor, skipping ...\n");
1166 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 }
Alan Cox6e47e062009-06-11 12:37:06 +01001168 union_header = (struct usb_cdc_union_desc *)buffer;
1169 break;
1170 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
1171 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1172 break;
1173 case USB_CDC_HEADER_TYPE: /* maybe check version */
1174 break; /* for now we ignore it */
1175 case USB_CDC_ACM_TYPE:
1176 ac_management_function = buffer[3];
1177 break;
1178 case USB_CDC_CALL_MANAGEMENT_TYPE:
1179 call_management_function = buffer[3];
1180 call_interface_num = buffer[4];
Julian Calabyce126642010-01-05 23:58:20 +11001181 if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
Alan Cox6e47e062009-06-11 12:37:06 +01001182 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1183 break;
1184 default:
1185 /* there are LOTS more CDC descriptors that
1186 * could legitimately be found here.
1187 */
1188 dev_dbg(&intf->dev, "Ignoring descriptor: "
1189 "type %02x, length %d\n",
1190 buffer[2], buffer[0]);
1191 break;
1192 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193next_desc:
1194 buflen -= buffer[0];
1195 buffer += buffer[0];
1196 }
1197
1198 if (!union_header) {
1199 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001200 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Erik Slagterfd5054c2011-05-11 12:06:55 +02001201 /* quirks for Droids MuIn LCD */
1202 if (quirks & NO_DATA_INTERFACE)
1203 data_interface = usb_ifnum_to_if(usb_dev, 0);
1204 else
1205 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 control_interface = intf;
1207 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001208 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1209 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1210 return -ENODEV;
1211 } else {
1212 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1213 combined_interfaces = 1;
1214 control_interface = data_interface = intf;
1215 goto look_for_collapsed_interface;
1216 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 }
1218 } else {
1219 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1220 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1221 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001222 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 return -ENODEV;
1224 }
1225 }
Alan Cox6e47e062009-06-11 12:37:06 +01001226
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001228 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001230 if (control_interface == data_interface) {
1231 /* some broken devices designed for windows work this way */
1232 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1233 combined_interfaces = 1;
1234 /* a popular other OS doesn't use it */
1235 quirks |= NO_CAP_LINE;
1236 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1237 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1238 return -EINVAL;
1239 }
1240look_for_collapsed_interface:
1241 for (i = 0; i < 3; i++) {
1242 struct usb_endpoint_descriptor *ep;
1243 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1244
1245 if (usb_endpoint_is_int_in(ep))
1246 epctrl = ep;
1247 else if (usb_endpoint_is_bulk_out(ep))
1248 epwrite = ep;
1249 else if (usb_endpoint_is_bulk_in(ep))
1250 epread = ep;
1251 else
1252 return -EINVAL;
1253 }
1254 if (!epctrl || !epread || !epwrite)
1255 return -ENODEV;
1256 else
1257 goto made_compressed_probe;
1258 }
1259
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260skip_normal_probe:
1261
1262 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001263 if (data_interface->cur_altsetting->desc.bInterfaceClass
1264 != CDC_DATA_INTERFACE_TYPE) {
1265 if (control_interface->cur_altsetting->desc.bInterfaceClass
1266 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001268 dev_dbg(&intf->dev,
1269 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 t = control_interface;
1271 control_interface = data_interface;
1272 data_interface = t;
1273 } else {
1274 return -EINVAL;
1275 }
1276 }
Alan Stern74da5d62007-08-02 13:29:10 -04001277
1278 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001279 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001280 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001281
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001282 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1283 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001284 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 return -EBUSY;
1286 }
1287
1288
Sven Schnellea9959bb2012-08-17 21:43:43 +02001289 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
1290 control_interface->cur_altsetting->desc.bNumEndpoints == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 return -EINVAL;
1292
1293 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1294 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1295 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1296
1297
1298 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001299 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 /* descriptors are swapped */
1301 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001302 dev_dbg(&intf->dev,
1303 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 t = epread;
1305 epread = epwrite;
1306 epwrite = t;
1307 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001308made_compressed_probe:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001309 dev_dbg(&intf->dev, "interfaces are valid\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
Alan Cox6e47e062009-06-11 12:37:06 +01001311 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1312 if (acm == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001313 dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 goto alloc_fail;
1315 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001317 minor = acm_alloc_minor(acm);
1318 if (minor == ACM_TTY_MINORS) {
1319 dev_err(&intf->dev, "no more free acm devices\n");
1320 kfree(acm);
1321 return -ENODEV;
1322 }
1323
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001324 ctrlsize = usb_endpoint_maxp(epctrl);
1325 readsize = usb_endpoint_maxp(epread) *
flintman5c2f9732014-08-11 21:52:25 -04001326#ifndef CONFIG_MACH_TENDERLOIN
Alan Cox6e47e062009-06-11 12:37:06 +01001327 (quirks == SINGLE_RX_URB ? 1 : 2);
flintman5c2f9732014-08-11 21:52:25 -04001328#else
1329 /* MBM */
1330 (quirks == SINGLE_RX_URB ? 1 : 4);
1331#endif
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001332 acm->combined_interfaces = combined_interfaces;
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001333 acm->writesize = usb_endpoint_maxp(epwrite) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 acm->control = control_interface;
1335 acm->data = data_interface;
1336 acm->minor = minor;
1337 acm->dev = usb_dev;
1338 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001339 if (quirks & NO_CAP_LINE)
1340 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341 acm->ctrlsize = ctrlsize;
1342 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001343 acm->rx_buflimit = num_rx_buf;
David Howellsc4028952006-11-22 14:57:56 +00001344 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum884b6002005-04-21 21:28:02 +02001345 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001346 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001347 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001348 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001349 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1350 if (acm->is_int_ep)
1351 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001352 tty_port_init(&acm->port);
1353 acm->port.ops = &acm_port_ops;
flintman5c2f9732014-08-11 21:52:25 -04001354#ifdef CONFIG_MACH_TENDERLOIN
1355 /* MBM */
1356 acm->response = usb_alloc_urb(0, GFP_KERNEL);
1357 if (!acm->response) {
1358 dev_dbg(&intf->dev, "out of memory (response kmalloc)\n");
1359 goto alloc_fail2;
1360 }
1361
1362 /* MBM */
1363 acm->bMaxPacketSize0 = usb_dev->descriptor.bMaxPacketSize0;
1364 acm->inbuf = usb_alloc_coherent(usb_dev,
1365 acm->bMaxPacketSize0,
1366 GFP_KERNEL,
1367 &acm->response->transfer_dma);
1368 /* MBM */
1369 if (!acm->inbuf) {
1370 dev_dbg(&intf->dev, "out of memory (inbuf kmalloc)\n");
1371 goto alloc_fail3;
1372 }
1373#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374
Daniel Mack997ea582010-04-12 13:17:25 +02001375 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 if (!buf) {
Johan Hovold255ab562011-03-22 11:12:13 +01001377 dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
flintman5c2f9732014-08-11 21:52:25 -04001378#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 goto alloc_fail2;
flintman5c2f9732014-08-11 21:52:25 -04001380#else
1381 /* MBM */
1382 goto alloc_fail3_1;
1383#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 }
1385 acm->ctrl_buffer = buf;
1386
Oliver Neukum884b6002005-04-21 21:28:02 +02001387 if (acm_write_buffers_alloc(acm) < 0) {
Johan Hovold255ab562011-03-22 11:12:13 +01001388 dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 goto alloc_fail4;
1390 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391
1392 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1393 if (!acm->ctrlurb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001394 dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 goto alloc_fail5;
1396 }
Oliver Neukum86478942006-05-13 22:50:47 +02001397 for (i = 0; i < num_rx_buf; i++) {
Johan Hovold088c64f2011-03-25 11:06:02 +01001398 struct acm_rb *rb = &(acm->read_buffers[i]);
1399 struct urb *urb;
David Kubicek61a87ad2005-11-01 18:51:34 +01001400
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001401 rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
1402 &rb->dma);
1403 if (!rb->base) {
1404 dev_err(&intf->dev, "out of memory "
1405 "(read bufs usb_alloc_coherent)\n");
1406 goto alloc_fail6;
1407 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001408 rb->index = i;
1409 rb->instance = acm;
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001410
Johan Hovold088c64f2011-03-25 11:06:02 +01001411 urb = usb_alloc_urb(0, GFP_KERNEL);
1412 if (!urb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001413 dev_err(&intf->dev,
Alan Cox6e47e062009-06-11 12:37:06 +01001414 "out of memory (read urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001415 goto alloc_fail6;
David Kubicek61a87ad2005-11-01 18:51:34 +01001416 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001417 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1418 urb->transfer_dma = rb->dma;
1419 if (acm->is_int_ep) {
1420 usb_fill_int_urb(urb, acm->dev,
1421 acm->rx_endpoint,
1422 rb->base,
1423 acm->readsize,
1424 acm_read_bulk_callback, rb,
1425 acm->bInterval);
1426 } else {
1427 usb_fill_bulk_urb(urb, acm->dev,
1428 acm->rx_endpoint,
1429 rb->base,
1430 acm->readsize,
1431 acm_read_bulk_callback, rb);
1432 }
David Kubicek61a87ad2005-11-01 18:51:34 +01001433
Johan Hovold088c64f2011-03-25 11:06:02 +01001434 acm->read_urbs[i] = urb;
1435 __set_bit(i, &acm->read_urbs_free);
David Kubicek61a87ad2005-11-01 18:51:34 +01001436 }
Alan Cox6e47e062009-06-11 12:37:06 +01001437 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001438 struct acm_wb *snd = &(acm->wb[i]);
1439
Alan Cox6e47e062009-06-11 12:37:06 +01001440 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1441 if (snd->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001442 dev_err(&intf->dev,
Johan Hovold59d7fec2011-03-22 11:12:12 +01001443 "out of memory (write urbs usb_alloc_urb)\n");
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001444 goto alloc_fail7;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001445 }
1446
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001447 if (usb_endpoint_xfer_int(epwrite))
1448 usb_fill_int_urb(snd->urb, usb_dev,
Ming Leie83863d2012-10-16 21:21:21 +08001449 usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001450 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1451 else
1452 usb_fill_bulk_urb(snd->urb, usb_dev,
1453 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1454 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001455 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1456 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 }
1458
Alan Cox6e47e062009-06-11 12:37:06 +01001459 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001460
1461 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1462 if (i < 0)
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001463 goto alloc_fail7;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001464
1465 if (cfd) { /* export the country data */
1466 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1467 if (!acm->country_codes)
1468 goto skip_countries;
1469 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001470 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1471 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001472 acm->country_rel_date = cfd->iCountryCodeRelDate;
1473
1474 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1475 if (i < 0) {
1476 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001477 acm->country_codes = NULL;
1478 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001479 goto skip_countries;
1480 }
1481
Alan Cox6e47e062009-06-11 12:37:06 +01001482 i = device_create_file(&intf->dev,
1483 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001484 if (i < 0) {
Axel Linc2572b72010-05-31 08:04:47 +08001485 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001486 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001487 acm->country_codes = NULL;
1488 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001489 goto skip_countries;
1490 }
1491 }
1492
1493skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001494 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001495 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1496 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1497 /* works around buggy devices */
1498 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1500 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
flintman5c2f9732014-08-11 21:52:25 -04001501#ifdef CONFIG_MACH_TENDERLOIN
1502 /* MBM */
1503 acm->ctrlurb->dev = acm->dev;
1504 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
1505 dev_err(&intf->dev, "usb_submit_urb(ctrl irq) failed");
1506 goto kill_urb;
1507 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508
flintman5c2f9732014-08-11 21:52:25 -04001509 /* MBM */
1510 acm->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
1511 if (!acm->irq)
1512 goto kill_urb;
1513#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1515
1516 acm_set_control(acm, acm->ctrlout);
flintman5c2f9732014-08-11 21:52:25 -04001517#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 acm->line.dwDTERate = cpu_to_le32(9600);
flintman5c2f9732014-08-11 21:52:25 -04001519#else
1520 acm->line.dwDTERate = cpu_to_le32(115200);
1521#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 acm->line.bDataBits = 8;
1523 acm_set_line(acm, &acm->line);
flintman5c2f9732014-08-11 21:52:25 -04001524#ifdef CONFIG_MACH_TENDERLOIN
1525 /* MBM */
1526 acm->state |= ACM_ABS_IDLE;
1527 acm_set_comm_feature(acm, ACM_ABSTRACT_STATE, &acm->state);
1528#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
1530 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001531 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001533 usb_get_intf(control_interface);
1534 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001536 return 0;
flintman5c2f9732014-08-11 21:52:25 -04001537#ifdef CONFIG_MACH_TENDERLOIN
1538 /* MBM */
1539kill_urb:
1540 usb_kill_urb(acm->ctrlurb);
1541#endif
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001542alloc_fail7:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001543 for (i = 0; i < ACM_NW; i++)
1544 usb_free_urb(acm->wb[i].urb);
Axel Linc2572b72010-05-31 08:04:47 +08001545alloc_fail6:
Oliver Neukum86478942006-05-13 22:50:47 +02001546 for (i = 0; i < num_rx_buf; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001547 usb_free_urb(acm->read_urbs[i]);
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001548 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 usb_free_urb(acm->ctrlurb);
1550alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001551 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552alloc_fail4:
Daniel Mack997ea582010-04-12 13:17:25 +02001553 usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
flintman5c2f9732014-08-11 21:52:25 -04001554#ifdef CONFIG_MACH_TENDERLOIN
1555 /* MBM */
1556alloc_fail3_1:
1557 usb_free_coherent(usb_dev,acm->bMaxPacketSize0, acm->inbuf, acm->response->transfer_dma);
1558alloc_fail3:
1559 usb_free_urb(acm->response);
1560#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561alloc_fail2:
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001562 acm_release_minor(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 kfree(acm);
1564alloc_fail:
1565 return -ENOMEM;
1566}
1567
Oliver Neukum1365baf2007-10-12 17:24:28 +02001568static void stop_data_traffic(struct acm *acm)
1569{
1570 int i;
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001571
1572 dev_dbg(&acm->control->dev, "%s\n", __func__);
flintman5c2f9732014-08-11 21:52:25 -04001573#ifndef CONFIG_MACH_TENDERLOIN
Oliver Neukum1365baf2007-10-12 17:24:28 +02001574 usb_kill_urb(acm->ctrlurb);
flintman5c2f9732014-08-11 21:52:25 -04001575#endif
Alan Cox6e47e062009-06-11 12:37:06 +01001576 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001577 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001578 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001579 usb_kill_urb(acm->read_urbs[i]);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001580
1581 cancel_work_sync(&acm->work);
1582}
1583
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584static void acm_disconnect(struct usb_interface *intf)
1585{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001586 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001588 struct tty_struct *tty;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001589 int i;
1590
1591 dev_dbg(&intf->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
David Brownell672c4e12008-08-06 18:41:12 -07001593 /* sibling interface is already cleaning up */
1594 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001595 return;
David Brownell672c4e12008-08-06 18:41:12 -07001596
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001597 mutex_lock(&acm->mutex);
1598 acm->disconnected = true;
Alan Cox6e47e062009-06-11 12:37:06 +01001599 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001600 device_remove_file(&acm->control->dev,
1601 &dev_attr_wCountryCodes);
1602 device_remove_file(&acm->control->dev,
1603 &dev_attr_iCountryCodeRelDate);
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);
Johan Hovold21ffba32014-05-26 19:23:38 +02001675 struct urb *urb;
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
Johan Hovold21ffba32014-05-26 19:23:38 +02001696 for (;;) {
1697 urb = usb_get_from_anchor(&acm->delayed);
1698 if (!urb)
1699 break;
1700
1701 acm_start_wb(acm, urb->context);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001702 }
1703
1704 /*
1705 * delayed error checking because we must
1706 * do the write path at all cost
1707 */
Oliver Neukum1365baf2007-10-12 17:24:28 +02001708 if (rv < 0)
Johan Hovold97e72c42014-05-26 19:23:37 +02001709 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001710
Johan Hovold97e72c42014-05-26 19:23:37 +02001711 rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001712 }
Johan Hovold97e72c42014-05-26 19:23:37 +02001713out:
1714 spin_unlock(&acm->write_lock);
1715 spin_unlock_irq(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001716
Oliver Neukum1365baf2007-10-12 17:24:28 +02001717 return rv;
1718}
Oliver Neukum35758582008-07-01 19:10:08 +02001719
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001720static int acm_reset_resume(struct usb_interface *intf)
1721{
1722 struct acm *acm = usb_get_intfdata(intf);
1723 struct tty_struct *tty;
1724
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001725 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001726 tty = tty_port_tty_get(&acm->port);
1727 if (tty) {
1728 tty_hangup(tty);
1729 tty_kref_put(tty);
1730 }
1731 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001732
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001733 return acm_resume(intf);
1734}
1735
Oliver Neukum35758582008-07-01 19:10:08 +02001736#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001737
1738#define NOKIA_PCSUITE_ACM_INFO(x) \
1739 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1740 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1741 USB_CDC_ACM_PROTO_VENDOR)
1742
Toby Gray4035e452010-09-01 16:01:19 +01001743#define SAMSUNG_PCSUITE_ACM_INFO(x) \
1744 USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
1745 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1746 USB_CDC_ACM_PROTO_VENDOR)
1747
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748/*
1749 * USB driver structure.
1750 */
1751
Németh Márton6ef48522010-01-10 15:33:45 +01001752static const struct usb_device_id acm_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 /* quirky and broken devices */
David Cluytensbd73e382013-12-03 14:18:57 +01001754 { USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */
1755 .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1757 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1758 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001759 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1760 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1761 },
Andrew Lunn0f9c7b42008-12-23 17:31:23 +01001762 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1763 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1764 },
Masahito Omote8753e652005-07-29 12:17:25 -07001765 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1766 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1767 },
Chris Malley91a9c922006-10-03 10:08:28 +01001768 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1769 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1770 },
Alan Cox7abcf202009-04-06 17:35:01 +01001771 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1772 .driver_info = SINGLE_RX_URB,
1773 },
Oliver Neukum86478942006-05-13 22:50:47 +02001774 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1775 .driver_info = SINGLE_RX_URB, /* firmware bug */
1776 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001777 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1778 .driver_info = SINGLE_RX_URB, /* firmware bug */
1779 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001780 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1781 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1782 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001783 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1784 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1785 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001786 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1787 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1788 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001789 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1790 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1791 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001792 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1793 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1794 },
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001795 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1796 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001797 /* Motorola H24 HSPA module: */
1798 { USB_DEVICE(0x22b8, 0x2d91) }, /* modem */
Michael Ulbrichtb08f08b2014-03-25 10:34:18 +01001799 { USB_DEVICE(0x22b8, 0x2d92), /* modem + diagnostics */
1800 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1801 },
1802 { USB_DEVICE(0x22b8, 0x2d93), /* modem + AT port */
1803 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1804 },
1805 { USB_DEVICE(0x22b8, 0x2d95), /* modem + AT port + diagnostics */
1806 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1807 },
1808 { USB_DEVICE(0x22b8, 0x2d96), /* modem + NMEA */
1809 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1810 },
1811 { USB_DEVICE(0x22b8, 0x2d97), /* modem + diagnostics + NMEA */
1812 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1813 },
1814 { USB_DEVICE(0x22b8, 0x2d99), /* modem + AT port + NMEA */
1815 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1816 },
1817 { USB_DEVICE(0x22b8, 0x2d9a), /* modem + AT port + diagnostics + NMEA */
1818 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1819 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001820
Adam Richterc332b4e2009-02-18 16:17:15 -08001821 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1822 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1823 data interface instead of
1824 communications interface.
1825 Maybe we should define a new
1826 quirk for this. */
1827 },
Jean-Christian de Rivaz52538352012-10-10 12:49:02 +00001828 { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */
1829 .driver_info = NO_UNION_NORMAL,
1830 },
Denis N Ladinb756d752012-12-26 18:29:44 +05001831 { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */
1832 .driver_info = NO_UNION_NORMAL,
1833 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001834 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1835 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1836 },
Russ Nelsonc3baa192010-04-21 23:07:03 -04001837 { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1838 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1839 },
flintman5c2f9732014-08-11 21:52:25 -04001840 /* MBM */
1841 { USB_DEVICE(0x1519, 0x0020), /* IMC_MAIN - XMM6260, XMM6262 */
1842 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1843 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001844
Adrian Taylorc1479a92009-11-19 10:35:33 +00001845 /* Nokia S60 phones expose two ACM channels. The first is
1846 * a modem and is picked up by the standard AT-command
1847 * information below. The second is 'vendor-specific' but
1848 * is treated as a serial device at the S60 end, so we want
1849 * to expose it on Linux too. */
1850 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1851 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1852 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1853 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1854 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1855 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1856 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1857 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1858 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1859 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1860 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1861 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1862 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1863 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1864 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1865 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1866 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1867 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1868 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1869 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1870 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1871 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1872 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1873 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1874 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1875 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1876 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1877 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1878 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1879 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1880 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1881 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1882 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1883 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1884 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1885 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1886 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1887 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1888 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1889 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1890 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1891 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1892 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
Przemo Firszt83a4eae2010-06-28 21:29:34 +01001893 { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
Toby Gray4035e452010-09-01 16:01:19 +01001894 { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
1895 { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
1896 { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
1897 { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
1898 { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
1899 { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
1900 { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
1901 { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
1902 { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
1903 { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
Arvid Ephraim Picciani721d92f2011-01-25 15:58:40 +01001904 { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
Toby Gray4061fde2011-06-06 14:52:48 +01001905 { NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */
1906 { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
Toby Gray4035e452010-09-01 16:01:19 +01001907 { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001908
Denis Pershin65e52f42011-09-04 17:37:21 +07001909 /* Support for Owen devices */
1910 { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */
1911
Adrian Taylorc1479a92009-11-19 10:35:33 +00001912 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1913
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001914 /* Support Lego NXT using pbLua firmware */
Julian Calabyce126642010-01-05 23:58:20 +11001915 { USB_DEVICE(0x0694, 0xff00),
1916 .driver_info = NOT_A_MODEM,
Otavio Salvador7893afc2010-09-26 23:35:05 -03001917 },
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001918
Erik Slagterfd5054c2011-05-11 12:06:55 +02001919 /* Support for Droids MuIn LCD */
1920 { USB_DEVICE(0x04d8, 0x000b),
1921 .driver_info = NO_DATA_INTERFACE,
1922 },
flintman5c2f9732014-08-11 21:52:25 -04001923#ifndef CONFIG_MACH_TENDERLOIN
Philippe Corbes5b239f02010-08-31 19:31:32 +02001924 /* control interfaces without any protocol set */
1925 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1926 USB_CDC_PROTO_NONE) },
flintman5c2f9732014-08-11 21:52:25 -04001927#else
1928 /* Exclude XMM6260 boot rom (not running modem software yet) */
1929 { USB_DEVICE(0x058b, 0x0041),
1930 .driver_info = NOT_REAL_ACM,
1931 },
1932#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933 /* control interfaces with various AT-command sets */
1934 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1935 USB_CDC_ACM_PROTO_AT_V25TER) },
1936 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1937 USB_CDC_ACM_PROTO_AT_PCCA101) },
1938 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1939 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1940 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1941 USB_CDC_ACM_PROTO_AT_GSM) },
1942 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001943 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1945 USB_CDC_ACM_PROTO_AT_CDMA) },
1946
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 { }
1948};
1949
Alan Cox6e47e062009-06-11 12:37:06 +01001950MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951
1952static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953 .name = "cdc_acm",
1954 .probe = acm_probe,
1955 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001956#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001957 .suspend = acm_suspend,
1958 .resume = acm_resume,
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001959 .reset_resume = acm_reset_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001960#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001962#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001963 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001964#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965};
1966
1967/*
1968 * TTY driver structures.
1969 */
1970
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001971static const struct tty_operations acm_ops = {
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001972 .install = acm_tty_install,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973 .open = acm_tty_open,
1974 .close = acm_tty_close,
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001975 .cleanup = acm_tty_cleanup,
Alan Cox10077d42009-06-11 12:36:09 +01001976 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 .write = acm_tty_write,
1978 .write_room = acm_tty_write_room,
1979 .ioctl = acm_tty_ioctl,
1980 .throttle = acm_tty_throttle,
1981 .unthrottle = acm_tty_unthrottle,
1982 .chars_in_buffer = acm_tty_chars_in_buffer,
1983 .break_ctl = acm_tty_break_ctl,
1984 .set_termios = acm_tty_set_termios,
1985 .tiocmget = acm_tty_tiocmget,
1986 .tiocmset = acm_tty_tiocmset,
1987};
1988
1989/*
1990 * Init / exit.
1991 */
1992
1993static int __init acm_init(void)
1994{
1995 int retval;
1996 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1997 if (!acm_tty_driver)
1998 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 acm_tty_driver->driver_name = "acm",
2000 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 acm_tty_driver->major = ACM_TTY_MAJOR,
2002 acm_tty_driver->minor_start = 0,
2003 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
2004 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07002005 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01002007 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
2008 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 tty_set_operations(acm_tty_driver, &acm_ops);
2010
2011 retval = tty_register_driver(acm_tty_driver);
2012 if (retval) {
2013 put_tty_driver(acm_tty_driver);
2014 return retval;
2015 }
2016
2017 retval = usb_register(&acm_driver);
2018 if (retval) {
2019 tty_unregister_driver(acm_tty_driver);
2020 put_tty_driver(acm_tty_driver);
2021 return retval;
2022 }
2023
Johan Hovolda2c7b932011-03-22 11:12:18 +01002024 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025
2026 return 0;
2027}
2028
2029static void __exit acm_exit(void)
2030{
2031 usb_deregister(&acm_driver);
2032 tty_unregister_driver(acm_tty_driver);
2033 put_tty_driver(acm_tty_driver);
2034}
2035
2036module_init(acm_init);
2037module_exit(acm_exit);
2038
Alan Cox6e47e062009-06-11 12:37:06 +01002039MODULE_AUTHOR(DRIVER_AUTHOR);
2040MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01002042MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);