blob: 67c21d7e10eed7a7110c382e972892677785088a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * cdc-acm.c
3 *
4 * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de>
Pavel Macheka2531292010-07-18 14:27:13 +02005 * Copyright (c) 1999 Pavel Machek <pavel@ucw.cz>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * Copyright (c) 1999 Johannes Erdfelt <johannes@erdfelt.com>
7 * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
8 * Copyright (c) 2004 Oliver Neukum <oliver@neukum.name>
David Kubicek61a87ad2005-11-01 18:51:34 +01009 * Copyright (c) 2005 David Kubicek <dave@awk.cz>
Johan Hovold088c64f2011-03-25 11:06:02 +010010 * Copyright (c) 2011 Johan Hovold <jhovold@gmail.com>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 *
12 * USB Abstract Control Model driver for USB modems and ISDN adapters
13 *
14 * Sponsored by SuSE
15 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070016 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 */
30
31#undef DEBUG
David Brownelle5fbab52008-08-06 18:46:10 -070032#undef VERBOSE_DEBUG
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
34#include <linux/kernel.h>
35#include <linux/errno.h>
36#include <linux/init.h>
37#include <linux/slab.h>
38#include <linux/tty.h>
Oliver Neukum7af25b42009-09-08 23:51:28 +020039#include <linux/serial.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <linux/tty_driver.h>
41#include <linux/tty_flip.h>
Oliver Neukum18c75722012-02-17 17:21:24 -050042#include <linux/serial.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <linux/module.h>
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010044#include <linux/mutex.h>
Alan Cox10077d42009-06-11 12:36:09 +010045#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <linux/usb.h>
David Brownella8c28f22006-06-13 09:57:47 -070047#include <linux/usb/cdc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <asm/byteorder.h>
49#include <asm/unaligned.h>
David Kubicek61a87ad2005-11-01 18:51:34 +010050#include <linux/list.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52#include "cdc-acm.h"
53
David Brownelle5fbab52008-08-06 18:46:10 -070054
flintman5c2f9732014-08-11 21:52:25 -040055/*
56 * Version Information
57 */
58#define DRIVER_VERSION "v0.26-mbm_2"
59#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
61
62static struct usb_driver acm_driver;
63static struct tty_driver *acm_tty_driver;
64static struct acm *acm_table[ACM_TTY_MINORS];
65
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -080066static DEFINE_MUTEX(acm_table_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -080068/*
69 * acm_table accessors
70 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070071
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -080072/*
73 * Look up an ACM structure by index. If found and not disconnected, increment
74 * its refcount and return it with its mutex held.
75 */
76static struct acm *acm_get_by_index(unsigned index)
77{
78 struct acm *acm;
79
80 mutex_lock(&acm_table_lock);
81 acm = acm_table[index];
82 if (acm) {
83 mutex_lock(&acm->mutex);
84 if (acm->disconnected) {
85 mutex_unlock(&acm->mutex);
86 acm = NULL;
87 } else {
88 tty_port_get(&acm->port);
89 mutex_unlock(&acm->mutex);
90 }
91 }
92 mutex_unlock(&acm_table_lock);
93 return acm;
94}
95
96/*
97 * Try to find an available minor number and if found, associate it with 'acm'.
98 */
99static int acm_alloc_minor(struct acm *acm)
100{
101 int minor;
102
103 mutex_lock(&acm_table_lock);
104 for (minor = 0; minor < ACM_TTY_MINORS; minor++) {
105 if (!acm_table[minor]) {
106 acm_table[minor] = acm;
107 break;
108 }
109 }
110 mutex_unlock(&acm_table_lock);
111
112 return minor;
113}
114
115/* Release the minor number associated with 'acm'. */
116static void acm_release_minor(struct acm *acm)
117{
118 mutex_lock(&acm_table_lock);
119 acm_table[acm->minor] = NULL;
120 mutex_unlock(&acm_table_lock);
121}
Alan Cox739e0282009-06-11 12:27:50 +0100122
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123/*
124 * Functions for ACM control messages.
125 */
126
Alan Cox6e47e062009-06-11 12:37:06 +0100127static int acm_ctrl_msg(struct acm *acm, int request, int value,
128 void *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129{
Johan Hovold9f81ca82014-05-26 19:23:39 +0200130 int retval;
131
132 retval = usb_autopm_get_interface(acm->control);
133 if (retval)
134 return retval;
135
136 retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 request, USB_RT_ACM, value,
138 acm->control->altsetting[0].desc.bInterfaceNumber,
139 buf, len, 5000);
Johan Hovold9f81ca82014-05-26 19:23:39 +0200140
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100141 dev_dbg(&acm->control->dev,
142 "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
143 __func__, request, value, len, retval);
Johan Hovold9f81ca82014-05-26 19:23:39 +0200144
145 usb_autopm_put_interface(acm->control);
146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 return retval < 0 ? retval : 0;
148}
149
flintman5c2f9732014-08-11 21:52:25 -0400150/* MBM */
151#define USB_CDC_SET_COMM_FEATURE 0x02
152#define USB_CDC_GET_COMM_FEATURE 0x03
153#define USB_CDC_CLEAR_COMM_FEATURE 0x04
154
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155/* devices aren't required to support these requests.
156 * the cdc acm descriptor tells whether they do...
157 */
158#define acm_set_control(acm, control) \
159 acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
160#define acm_set_line(acm, line) \
161 acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
162#define acm_send_break(acm, ms) \
163 acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
flintman5c2f9732014-08-11 21:52:25 -0400164/* MBM */
165#define acm_set_comm_feature(acm, feature, state) \
166 acm_ctrl_msg(acm, USB_CDC_SET_COMM_FEATURE, feature, state, 2)
167#define acm_clear_comm_feature(acm, feature) \
168 acm_ctrl_msg(acm, USB_CDC_CLEAR_COMM_FEATURE, feature, NULL, 0)
169#define acm_send_encap_resp(acm, msg) \
170 acm_ctrl_msg(acm, USB_CDC_SEND_ENCAPSULATED_COMMAND, 0, msg, sizeof *(msg))
171
172
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
174/*
Oliver Neukum884b6002005-04-21 21:28:02 +0200175 * Write buffer management.
176 * All of these assume proper locks taken by the caller.
177 */
178
179static int acm_wb_alloc(struct acm *acm)
180{
181 int i, wbn;
182 struct acm_wb *wb;
183
David Engrafe4cf3aa2008-03-20 10:01:34 +0100184 wbn = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200185 i = 0;
186 for (;;) {
187 wb = &acm->wb[wbn];
188 if (!wb->use) {
189 wb->use = 1;
190 return wbn;
191 }
Oliver Neukum86478942006-05-13 22:50:47 +0200192 wbn = (wbn + 1) % ACM_NW;
193 if (++i >= ACM_NW)
Oliver Neukum884b6002005-04-21 21:28:02 +0200194 return -1;
195 }
196}
197
Oliver Neukum884b6002005-04-21 21:28:02 +0200198static int acm_wb_is_avail(struct acm *acm)
199{
200 int i, n;
David Brownelle5fbab52008-08-06 18:46:10 -0700201 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200202
Oliver Neukum86478942006-05-13 22:50:47 +0200203 n = ACM_NW;
David Brownelle5fbab52008-08-06 18:46:10 -0700204 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100205 for (i = 0; i < ACM_NW; i++)
Oliver Neukum86478942006-05-13 22:50:47 +0200206 n -= acm->wb[i].use;
David Brownelle5fbab52008-08-06 18:46:10 -0700207 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200208 return n;
209}
210
Oliver Neukum884b6002005-04-21 21:28:02 +0200211/*
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800212 * Finish write. Caller must hold acm->write_lock
Oliver Neukum884b6002005-04-21 21:28:02 +0200213 */
David Engrafe4cf3aa2008-03-20 10:01:34 +0100214static void acm_write_done(struct acm *acm, struct acm_wb *wb)
Oliver Neukum884b6002005-04-21 21:28:02 +0200215{
David Engrafe4cf3aa2008-03-20 10:01:34 +0100216 wb->use = 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200217 acm->transmitting--;
Oliver Neukum97d35f92009-12-16 17:05:57 +0100218 usb_autopm_put_interface_async(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +0200219}
220
221/*
222 * Poke write.
Oliver Neukum11ea8592008-06-20 11:25:57 +0200223 *
224 * the caller is responsible for locking
Oliver Neukum884b6002005-04-21 21:28:02 +0200225 */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200226
227static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
228{
229 int rc;
230
231 acm->transmitting++;
232
233 wb->urb->transfer_buffer = wb->buf;
234 wb->urb->transfer_dma = wb->dmah;
235 wb->urb->transfer_buffer_length = wb->len;
236 wb->urb->dev = acm->dev;
237
Alan Cox6e47e062009-06-11 12:37:06 +0100238 rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
239 if (rc < 0) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100240 dev_err(&acm->data->dev,
241 "%s - usb_submit_urb(write bulk) failed: %d\n",
242 __func__, rc);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200243 acm_write_done(acm, wb);
244 }
245 return rc;
246}
247
David Engrafe4cf3aa2008-03-20 10:01:34 +0100248static int acm_write_start(struct acm *acm, int wbn)
Oliver Neukum884b6002005-04-21 21:28:02 +0200249{
250 unsigned long flags;
David Brownell934da462008-08-06 18:44:12 -0700251 struct acm_wb *wb = &acm->wb[wbn];
Oliver Neukum884b6002005-04-21 21:28:02 +0200252 int rc;
253
254 spin_lock_irqsave(&acm->write_lock, flags);
255 if (!acm->dev) {
David Brownell934da462008-08-06 18:44:12 -0700256 wb->use = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200257 spin_unlock_irqrestore(&acm->write_lock, flags);
258 return -ENODEV;
259 }
260
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100261 dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100262 acm->susp_count);
Oliver Neukum97d35f92009-12-16 17:05:57 +0100263 usb_autopm_get_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200264 if (acm->susp_count) {
flintmand53cff62015-03-04 20:21:21 -0500265 if (!acm->delayed_wb)
266 acm->delayed_wb = wb;
267 else
268 usb_autopm_put_interface_async(acm->control);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200269 spin_unlock_irqrestore(&acm->write_lock, flags);
flintmand53cff62015-03-04 20:21:21 -0500270 return 0; /* A white lie */
Oliver Neukum11ea8592008-06-20 11:25:57 +0200271 }
272 usb_mark_last_busy(acm->dev);
273
Oliver Neukum11ea8592008-06-20 11:25:57 +0200274 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200275 spin_unlock_irqrestore(&acm->write_lock, flags);
276
Oliver Neukum884b6002005-04-21 21:28:02 +0200277 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200278
Oliver Neukum884b6002005-04-21 21:28:02 +0200279}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100280/*
281 * attributes exported through sysfs
282 */
283static ssize_t show_caps
284(struct device *dev, struct device_attribute *attr, char *buf)
285{
286 struct usb_interface *intf = to_usb_interface(dev);
287 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200288
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100289 return sprintf(buf, "%d", acm->ctrl_caps);
290}
291static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
292
293static ssize_t show_country_codes
294(struct device *dev, struct device_attribute *attr, char *buf)
295{
296 struct usb_interface *intf = to_usb_interface(dev);
297 struct acm *acm = usb_get_intfdata(intf);
298
299 memcpy(buf, acm->country_codes, acm->country_code_size);
300 return acm->country_code_size;
301}
302
303static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
304
305static ssize_t show_country_rel_date
306(struct device *dev, struct device_attribute *attr, char *buf)
307{
308 struct usb_interface *intf = to_usb_interface(dev);
309 struct acm *acm = usb_get_intfdata(intf);
310
311 return sprintf(buf, "%d", acm->country_rel_date);
312}
313
314static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200315/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 * Interrupt handlers for various ACM device responses
317 */
318
flintman5c2f9732014-08-11 21:52:25 -0400319/* MBM */
320static void acm_in_callback(struct urb *urb)
321{
322 struct acm *acm = urb->context;
323 int status = urb->status;
324
325 if (status) {
326 switch (status) {
327 case -ENOENT:
328 dev_dbg(&urb->dev->dev,
329 "nonzero urb status received: -ENOENT");
330 goto skip_error;
331 case -ECONNRESET:
332 dev_dbg(&urb->dev->dev,
333 "nonzero urb status received: -ECONNRESET");
334 goto skip_error;
335 case -ESHUTDOWN:
336 dev_dbg(&urb->dev->dev,
337 "nonzero urb status received: -ESHUTDOWN");
338 goto skip_error;
339 case -EPIPE:
340 dev_err(&urb->dev->dev,
341 "nonzero urb status received: -EPIPE\n");
342 break;
343 default:
344 dev_err(&urb->dev->dev,
345 "Unexpected error %d\n", status);
346 break;
347 }
348 }
349
350 dbg("unsupported encap: %s", (char *)acm->inbuf);
351skip_error:
352 dbg("");
353}
354
355
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100357static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358{
359 struct acm *acm = urb->context;
360 struct usb_cdc_notification *dr = urb->transfer_buffer;
flintman5c2f9732014-08-11 21:52:25 -0400361 /* MBM */
362 struct usb_ctrlrequest *req = acm->irq;
Alan Cox10077d42009-06-11 12:36:09 +0100363 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 unsigned char *data;
365 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700366 int retval;
367 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700369 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 case 0:
371 /* success */
372 break;
373 case -ECONNRESET:
374 case -ENOENT:
375 case -ESHUTDOWN:
376 /* this urb is terminated, clean up */
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100377 dev_dbg(&acm->control->dev,
378 "%s - urb shutting down with status: %d\n",
379 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 return;
381 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100382 dev_dbg(&acm->control->dev,
383 "%s - nonzero urb status received: %d\n",
384 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 goto exit;
386 }
387
Johan Hovold7e7797e2011-03-22 11:12:11 +0100388 usb_mark_last_busy(acm->dev);
389
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 data = (unsigned char *)(dr + 1);
391 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100392 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
flintman5c2f9732014-08-11 21:52:25 -0400393 /* MBM */
394 dev_info(&urb->dev->dev, "%s network", dr->wValue ?
395 "connected to" : "disconnected from");
Alan Cox6e47e062009-06-11 12:37:06 +0100396 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
Alan Cox6e47e062009-06-11 12:37:06 +0100398 case USB_CDC_NOTIFY_SERIAL_STATE:
399 tty = tty_port_tty_get(&acm->port);
400 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
Alan Cox6e47e062009-06-11 12:37:06 +0100402 if (tty) {
403 if (!acm->clocal &&
404 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100405 dev_dbg(&acm->control->dev,
406 "%s - calling hangup\n", __func__);
Alan Cox6e47e062009-06-11 12:37:06 +0100407 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 }
Alan Cox6e47e062009-06-11 12:37:06 +0100409 tty_kref_put(tty);
410 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
Alan Cox6e47e062009-06-11 12:37:06 +0100412 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100414 dev_dbg(&acm->control->dev,
415 "%s - input control lines: dcd%c dsr%c break%c "
416 "ring%c framing%c parity%c overrun%c\n",
417 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100418 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
419 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
420 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
421 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
422 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
423 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
424 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 break;
426
flintman5c2f9732014-08-11 21:52:25 -0400427 /* MBM */
428 case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
429 dev_err(&urb->dev->dev, "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
430 dr->wIndex, dr->wLength);
431 req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
432 req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
433 req->wValue = 0;
434 req->wIndex = cpu_to_le16(dr->wIndex);
435 req->wLength = cpu_to_le16(acm->bMaxPacketSize0);
436
437 usb_fill_control_urb(
438 acm->response,
439 acm->dev,
440 usb_sndctrlpipe(acm->dev, 0),
441 (unsigned char *)req,
442 acm->inbuf,
443 acm->bMaxPacketSize0,
444 acm_in_callback,
445 acm);
446
447 break;
448
Alan Cox6e47e062009-06-11 12:37:06 +0100449 default:
flintman5c2f9732014-08-11 21:52:25 -0400450 /* MBM */
451 dev_info(&urb->dev->dev,"unknown notification %d received: index %d len %d data0 %d data1 %d",
Alan Cox6e47e062009-06-11 12:37:06 +0100452 dr->bNotificationType, dr->wIndex,
453 dr->wLength, data[0], data[1]);
454 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 }
456exit:
Alan Cox6e47e062009-06-11 12:37:06 +0100457 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700458 if (retval)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100459 dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
460 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461}
462
Johan Hovold088c64f2011-03-25 11:06:02 +0100463static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464{
Johan Hovold088c64f2011-03-25 11:06:02 +0100465 int res;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700466
Johan Hovold088c64f2011-03-25 11:06:02 +0100467 if (!test_and_clear_bit(index, &acm->read_urbs_free))
468 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469
Johan Hovold088c64f2011-03-25 11:06:02 +0100470 dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
471
472 res = usb_submit_urb(acm->read_urbs[index], mem_flags);
473 if (res) {
474 if (res != -EPERM) {
475 dev_err(&acm->data->dev,
476 "%s - usb_submit_urb failed: %d\n",
477 __func__, res);
478 }
479 set_bit(index, &acm->read_urbs_free);
480 return res;
481 }
482
483 return 0;
484}
485
486static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
487{
488 int res;
489 int i;
490
491 for (i = 0; i < acm->rx_buflimit; ++i) {
492 res = acm_submit_read_urb(acm, i, mem_flags);
493 if (res)
494 return res;
495 }
496
497 return 0;
498}
499
500static void acm_process_read_urb(struct acm *acm, struct urb *urb)
501{
502 struct tty_struct *tty;
503
504 if (!urb->actual_length)
505 return;
506
507 tty = tty_port_tty_get(&acm->port);
508 if (!tty)
509 return;
510
511 tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
512 tty_flip_buffer_push(tty);
513
514 tty_kref_put(tty);
515}
516
517static void acm_read_bulk_callback(struct urb *urb)
518{
519 struct acm_rb *rb = urb->context;
520 struct acm *acm = rb->instance;
521 unsigned long flags;
522
523 dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
524 rb->index, urb->actual_length);
525 set_bit(rb->index, &acm->read_urbs_free);
526
527 if (!acm->dev) {
528 dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200530 }
531 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532
Johan Hovold088c64f2011-03-25 11:06:02 +0100533 if (urb->status) {
Johan Hovold1d9846e2011-03-22 11:12:14 +0100534 dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
Johan Hovold088c64f2011-03-25 11:06:02 +0100535 __func__, urb->status);
536 return;
537 }
538 acm_process_read_urb(acm, urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
Johan Hovold088c64f2011-03-25 11:06:02 +0100540 /* throttle device if requested by tty */
541 spin_lock_irqsave(&acm->read_lock, flags);
542 acm->throttled = acm->throttle_req;
543 if (!acm->throttled && !acm->susp_count) {
544 spin_unlock_irqrestore(&acm->read_lock, flags);
545 acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
Oliver Neukum86478942006-05-13 22:50:47 +0200546 } else {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200547 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549}
550
551/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100552static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553{
Ming Leicdc97792008-02-24 18:41:47 +0800554 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700555 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800556 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200557
Johan Hovold4fa46262011-03-22 11:12:16 +0100558 if (urb->status || (urb->actual_length != urb->transfer_buffer_length))
559 dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
Johan Hovold1d9846e2011-03-22 11:12:14 +0100560 __func__,
David Brownelle5fbab52008-08-06 18:46:10 -0700561 urb->actual_length,
562 urb->transfer_buffer_length,
563 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800565 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100566 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800567 spin_unlock_irqrestore(&acm->write_lock, flags);
Havard Skinnemoen99823f42011-12-09 16:51:54 -0800568 schedule_work(&acm->work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569}
570
David Howellsc4028952006-11-22 14:57:56 +0000571static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572{
David Howellsc4028952006-11-22 14:57:56 +0000573 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100574 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700575
Johan Hovold1d9846e2011-03-22 11:12:14 +0100576 dev_vdbg(&acm->data->dev, "%s\n", __func__);
577
Alan Cox10077d42009-06-11 12:36:09 +0100578 tty = tty_port_tty_get(&acm->port);
Johan Hovold15e5bee2011-03-22 11:12:10 +0100579 if (!tty)
580 return;
Alan Cox10077d42009-06-11 12:36:09 +0100581 tty_wakeup(tty);
582 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583}
584
585/*
586 * TTY handlers
587 */
588
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800589static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590{
591 struct acm *acm;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800592 int retval;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100593
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800594 dev_dbg(tty->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800596 acm = acm_get_by_index(tty->index);
597 if (!acm)
598 return -ENODEV;
599
Jiri Slabyf8a8c102012-03-05 14:51:48 +0100600 retval = tty_standard_install(driver, tty);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800601 if (retval)
602 goto error_init_termios;
603
604 tty->driver_data = acm;
605
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800606 return 0;
607
608error_init_termios:
609 tty_port_put(&acm->port);
610 return retval;
611}
612
613static int acm_tty_open(struct tty_struct *tty, struct file *filp)
614{
615 struct acm *acm = tty->driver_data;
616
617 dev_dbg(tty->dev, "%s\n", __func__);
618
619 return tty_port_open(&acm->port, tty, filp);
620}
621
622static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
623{
624 struct acm *acm = container_of(port, struct acm, port);
625 int retval = -ENODEV;
Johan Hovold8fd20d52014-05-26 19:23:44 +0200626 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100628 dev_dbg(&acm->control->dev, "%s\n", __func__);
629
Oliver Neukum1365baf2007-10-12 17:24:28 +0200630 mutex_lock(&acm->mutex);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800631 if (acm->disconnected)
632 goto disconnected;
633
634 retval = usb_autopm_get_interface(acm->control);
635 if (retval)
636 goto error_get_interface;
637
638 /*
639 * FIXME: Why do we need this? Allocating 64K of physically contiguous
640 * memory is really nasty...
641 */
642 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
643 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200644
flintman5c2f9732014-08-11 21:52:25 -0400645#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 acm->ctrlurb->dev = acm->dev;
647 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100648 dev_err(&acm->control->dev,
649 "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800650 goto error_submit_urb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 }
flintman5c2f9732014-08-11 21:52:25 -0400652#endif
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800653 acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS;
654 if (acm_set_control(acm, acm->ctrlout) < 0 &&
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100655 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800656 goto error_set_control;
Alan Cox10077d42009-06-11 12:36:09 +0100657
flintman5c2f9732014-08-11 21:52:25 -0400658 /* MBM */
659 acm->state &= ~ACM_ABS_IDLE;
660 if (0 > acm_set_comm_feature(acm, ACM_ABSTRACT_STATE, &acm->state) &&
661 (acm->ctrl_caps & USB_CDC_COMM_FEATURE))
662 goto error_set_control;
663
Oliver Neukum11ea8592008-06-20 11:25:57 +0200664 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
Otto Meta9cc2d462012-06-06 18:46:21 +0200666 /*
667 * Unthrottle device in case the TTY was closed while throttled.
668 */
669 spin_lock_irq(&acm->read_lock);
670 acm->throttled = 0;
671 acm->throttle_req = 0;
672 spin_unlock_irq(&acm->read_lock);
673
Johan Hovold088c64f2011-03-25 11:06:02 +0100674 if (acm_submit_read_urbs(acm, GFP_KERNEL))
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800675 goto error_submit_read_urbs;
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100676
Oliver Neukum1365baf2007-10-12 17:24:28 +0200677 mutex_unlock(&acm->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800679 return 0;
680
681error_submit_read_urbs:
Johan Hovold8fd20d52014-05-26 19:23:44 +0200682 for (i = 0; i < acm->rx_buflimit; i++)
683 usb_kill_urb(acm->read_urbs[i]);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800684 acm->ctrlout = 0;
685 acm_set_control(acm, acm->ctrlout);
686error_set_control:
687 usb_kill_urb(acm->ctrlurb);
flintman5c2f9732014-08-11 21:52:25 -0400688#ifndef CONFIG_MACH_TENDERLOIN
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800689error_submit_urb:
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100690 usb_autopm_put_interface(acm->control);
flintman5c2f9732014-08-11 21:52:25 -0400691#endif
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800692error_get_interface:
693disconnected:
694 mutex_unlock(&acm->mutex);
695 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696}
697
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800698static void acm_port_destruct(struct tty_port *port)
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700699{
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800700 struct acm *acm = container_of(port, struct acm, port);
701
702 dev_dbg(&acm->control->dev, "%s\n", __func__);
David Kubicek61a87ad2005-11-01 18:51:34 +0100703
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800704 acm_release_minor(acm);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700705 usb_put_intf(acm->control);
flintman5c2f9732014-08-11 21:52:25 -0400706 /* MBM */
707 usb_free_urb(acm->response);
708 kfree(acm->irq);
709
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100710 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700711 kfree(acm);
712}
713
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800714static void acm_port_shutdown(struct tty_port *port)
Alan Cox10077d42009-06-11 12:36:09 +0100715{
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800716 struct acm *acm = container_of(port, struct acm, port);
Johan Hovolddab54c92011-03-22 11:12:21 +0100717 int i;
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200718 int pm_err;
Johan Hovolddab54c92011-03-22 11:12:21 +0100719
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800720 dev_dbg(&acm->control->dev, "%s\n", __func__);
721
722 mutex_lock(&acm->mutex);
723 if (!acm->disconnected) {
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200724 pm_err = usb_autopm_get_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100725 acm_set_control(acm, acm->ctrlout = 0);
Alan Cox10077d42009-06-11 12:36:09 +0100726 usb_kill_urb(acm->ctrlurb);
727 for (i = 0; i < ACM_NW; i++)
728 usb_kill_urb(acm->wb[i].urb);
Johan Hovolddab54c92011-03-22 11:12:21 +0100729 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +0100730 usb_kill_urb(acm->read_urbs[i]);
Alan Cox10077d42009-06-11 12:36:09 +0100731 acm->control->needs_remote_wakeup = 0;
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200732 if (!pm_err)
733 usb_autopm_put_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100734 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800735 mutex_unlock(&acm->mutex);
736}
737
738static void acm_tty_cleanup(struct tty_struct *tty)
739{
740 struct acm *acm = tty->driver_data;
741 dev_dbg(&acm->control->dev, "%s\n", __func__);
742 tty_port_put(&acm->port);
Alan Cox10077d42009-06-11 12:36:09 +0100743}
744
745static void acm_tty_hangup(struct tty_struct *tty)
746{
747 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800748 dev_dbg(&acm->control->dev, "%s\n", __func__);
Alan Cox10077d42009-06-11 12:36:09 +0100749 tty_port_hangup(&acm->port);
Alan Cox10077d42009-06-11 12:36:09 +0100750}
751
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752static void acm_tty_close(struct tty_struct *tty, struct file *filp)
753{
754 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800755 dev_dbg(&acm->control->dev, "%s\n", __func__);
756 tty_port_close(&acm->port, tty, filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757}
758
Alan Cox6e47e062009-06-11 12:37:06 +0100759static int acm_tty_write(struct tty_struct *tty,
760 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761{
762 struct acm *acm = tty->driver_data;
763 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200764 unsigned long flags;
765 int wbn;
766 struct acm_wb *wb;
767
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 if (!count)
769 return 0;
770
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100771 dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100772
Oliver Neukum884b6002005-04-21 21:28:02 +0200773 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100774 wbn = acm_wb_alloc(acm);
775 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200776 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200777 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200779 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
Oliver Neukum884b6002005-04-21 21:28:02 +0200781 count = (count > acm->writesize) ? acm->writesize : count;
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100782 dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
Oliver Neukum884b6002005-04-21 21:28:02 +0200783 memcpy(wb->buf, buf, count);
784 wb->len = count;
785 spin_unlock_irqrestore(&acm->write_lock, flags);
786
Alan Cox6e47e062009-06-11 12:37:06 +0100787 stat = acm_write_start(acm, wbn);
788 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200789 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 return count;
791}
792
793static int acm_tty_write_room(struct tty_struct *tty)
794{
795 struct acm *acm = tty->driver_data;
Oliver Neukum884b6002005-04-21 21:28:02 +0200796 /*
797 * Do not let the line discipline to know that we have a reserve,
798 * or it might get too enthusiastic.
799 */
David Brownell934da462008-08-06 18:44:12 -0700800 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801}
802
803static int acm_tty_chars_in_buffer(struct tty_struct *tty)
804{
805 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800806 /*
807 * if the device was unplugged then any remaining characters fell out
808 * of the connector ;)
809 */
810 if (acm->disconnected)
Alan Cox23198fd2009-07-20 16:05:27 +0100811 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200812 /*
813 * This is inaccurate (overcounts), but it works.
814 */
Oliver Neukum86478942006-05-13 22:50:47 +0200815 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816}
817
818static void acm_tty_throttle(struct tty_struct *tty)
819{
820 struct acm *acm = tty->driver_data;
Johan Hovold088c64f2011-03-25 11:06:02 +0100821
Johan Hovold088c64f2011-03-25 11:06:02 +0100822 spin_lock_irq(&acm->read_lock);
823 acm->throttle_req = 1;
824 spin_unlock_irq(&acm->read_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825}
826
827static void acm_tty_unthrottle(struct tty_struct *tty)
828{
829 struct acm *acm = tty->driver_data;
Johan Hovold088c64f2011-03-25 11:06:02 +0100830 unsigned int was_throttled;
831
Johan Hovold088c64f2011-03-25 11:06:02 +0100832 spin_lock_irq(&acm->read_lock);
833 was_throttled = acm->throttled;
834 acm->throttled = 0;
835 acm->throttle_req = 0;
836 spin_unlock_irq(&acm->read_lock);
837
838 if (was_throttled)
839 acm_submit_read_urbs(acm, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840}
841
Alan Cox9e989662008-07-22 11:18:03 +0100842static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843{
844 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100845 int retval;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800846
Alan Cox9e989662008-07-22 11:18:03 +0100847 retval = acm_send_break(acm, state ? 0xffff : 0);
848 if (retval < 0)
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100849 dev_dbg(&acm->control->dev, "%s - send break failed\n",
850 __func__);
Alan Cox9e989662008-07-22 11:18:03 +0100851 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852}
853
Alan Cox60b33c12011-02-14 16:26:14 +0000854static int acm_tty_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855{
856 struct acm *acm = tty->driver_data;
857
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
859 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
860 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
861 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
862 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
863 TIOCM_CTS;
864}
865
Alan Cox20b9d172011-02-14 16:26:50 +0000866static int acm_tty_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 unsigned int set, unsigned int clear)
868{
869 struct acm *acm = tty->driver_data;
870 unsigned int newctrl;
871
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100873 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
874 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
875 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
876 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
878 newctrl = (newctrl & ~clear) | set;
879
880 if (acm->ctrlout == newctrl)
881 return 0;
882 return acm_set_control(acm, acm->ctrlout = newctrl);
883}
884
Oliver Neukum18c75722012-02-17 17:21:24 -0500885static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
886{
887 struct serial_struct tmp;
888
889 if (!info)
890 return -EINVAL;
891
892 memset(&tmp, 0, sizeof(tmp));
893 tmp.flags = ASYNC_LOW_LATENCY;
894 tmp.xmit_fifo_size = acm->writesize;
895 tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
Dan Williams1229a832012-11-08 12:47:41 -0600896 tmp.close_delay = acm->port.close_delay / 10;
897 tmp.closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
898 ASYNC_CLOSING_WAIT_NONE :
899 acm->port.closing_wait / 10;
Oliver Neukum18c75722012-02-17 17:21:24 -0500900
901 if (copy_to_user(info, &tmp, sizeof(tmp)))
902 return -EFAULT;
903 else
904 return 0;
905}
906
Dan Williams1229a832012-11-08 12:47:41 -0600907static int set_serial_info(struct acm *acm,
908 struct serial_struct __user *newinfo)
909{
910 struct serial_struct new_serial;
911 unsigned int closing_wait, close_delay;
912 int retval = 0;
913
914 if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
915 return -EFAULT;
916
917 close_delay = new_serial.close_delay * 10;
918 closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
919 ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
920
921 mutex_lock(&acm->port.mutex);
922
923 if (!capable(CAP_SYS_ADMIN)) {
924 if ((close_delay != acm->port.close_delay) ||
925 (closing_wait != acm->port.closing_wait))
926 retval = -EPERM;
927 else
928 retval = -EOPNOTSUPP;
929 } else {
930 acm->port.close_delay = close_delay;
931 acm->port.closing_wait = closing_wait;
932 }
933
934 mutex_unlock(&acm->port.mutex);
935 return retval;
936}
937
Alan Cox6caa76b2011-02-14 16:27:22 +0000938static int acm_tty_ioctl(struct tty_struct *tty,
Alan Cox6e47e062009-06-11 12:37:06 +0100939 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940{
Oliver Neukum18c75722012-02-17 17:21:24 -0500941 struct acm *acm = tty->driver_data;
942 int rv = -ENOIOCTLCMD;
943
944 switch (cmd) {
945 case TIOCGSERIAL: /* gets serial port data */
946 rv = get_serial_info(acm, (struct serial_struct __user *) arg);
947 break;
Dan Williams1229a832012-11-08 12:47:41 -0600948 case TIOCSSERIAL:
949 rv = set_serial_info(acm, (struct serial_struct __user *) arg);
950 break;
Oliver Neukum18c75722012-02-17 17:21:24 -0500951 }
952
953 return rv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954}
955
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100956static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 0, 50, 75, 110, 134, 150, 200, 300, 600,
958 1200, 1800, 2400, 4800, 9600, 19200, 38400,
959 57600, 115200, 230400, 460800, 500000, 576000,
960 921600, 1000000, 1152000, 1500000, 2000000,
961 2500000, 3000000, 3500000, 4000000
962};
963
Alan Cox6e47e062009-06-11 12:37:06 +0100964static void acm_tty_set_termios(struct tty_struct *tty,
965 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966{
967 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800968 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 struct usb_cdc_line_coding newline;
970 int newctrl = acm->ctrlout;
971
Alan Cox9b80fee2009-09-19 13:13:23 -0700972 newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
974 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100975 (termios->c_cflag & PARODD ? 1 : 2) +
976 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Nicolas Boullisa5204462012-10-16 00:06:23 +0200977 switch (termios->c_cflag & CSIZE) {
978 case CS5:
979 newline.bDataBits = 5;
980 break;
981 case CS6:
982 newline.bDataBits = 6;
983 break;
984 case CS7:
985 newline.bDataBits = 7;
986 break;
987 case CS8:
988 default:
989 newline.bDataBits = 8;
990 break;
991 }
Alan Cox6e47e062009-06-11 12:37:06 +0100992 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
994
Johan Hovolddc3e2ac2014-11-05 18:41:59 +0100995 if (C_BAUD(tty) == B0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 newline.dwDTERate = acm->line.dwDTERate;
997 newctrl &= ~ACM_CTRL_DTR;
Johan Hovolddc3e2ac2014-11-05 18:41:59 +0100998 } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) {
Alan Cox6e47e062009-06-11 12:37:06 +0100999 newctrl |= ACM_CTRL_DTR;
Johan Hovolddc3e2ac2014-11-05 18:41:59 +01001000 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001
1002 if (newctrl != acm->ctrlout)
1003 acm_set_control(acm, acm->ctrlout = newctrl);
1004
1005 if (memcmp(&acm->line, &newline, sizeof newline)) {
1006 memcpy(&acm->line, &newline, sizeof newline);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001007 dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
1008 __func__,
1009 le32_to_cpu(newline.dwDTERate),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 newline.bCharFormat, newline.bParityType,
1011 newline.bDataBits);
1012 acm_set_line(acm, &acm->line);
1013 }
1014}
1015
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001016static const struct tty_port_operations acm_port_ops = {
1017 .shutdown = acm_port_shutdown,
1018 .activate = acm_port_activate,
1019 .destruct = acm_port_destruct,
1020};
1021
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022/*
1023 * USB probe and disconnect routines.
1024 */
1025
Oliver Neukum830f4022008-06-25 14:17:16 +02001026/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +02001027static void acm_write_buffers_free(struct acm *acm)
1028{
1029 int i;
1030 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +02001031 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +02001032
Alan Cox6e47e062009-06-11 12:37:06 +01001033 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Daniel Mack997ea582010-04-12 13:17:25 +02001034 usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +02001035}
1036
Oliver Neukum830f4022008-06-25 14:17:16 +02001037static void acm_read_buffers_free(struct acm *acm)
1038{
1039 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Johan Hovolddab54c92011-03-22 11:12:21 +01001040 int i;
Oliver Neukum830f4022008-06-25 14:17:16 +02001041
Johan Hovolddab54c92011-03-22 11:12:21 +01001042 for (i = 0; i < acm->rx_buflimit; i++)
Daniel Mack997ea582010-04-12 13:17:25 +02001043 usb_free_coherent(usb_dev, acm->readsize,
Johan Hovold088c64f2011-03-25 11:06:02 +01001044 acm->read_buffers[i].base, acm->read_buffers[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001045}
1046
Oliver Neukum884b6002005-04-21 21:28:02 +02001047/* Little helper: write buffers allocate */
1048static int acm_write_buffers_alloc(struct acm *acm)
1049{
1050 int i;
1051 struct acm_wb *wb;
1052
Oliver Neukum86478942006-05-13 22:50:47 +02001053 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Daniel Mack997ea582010-04-12 13:17:25 +02001054 wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
Oliver Neukum884b6002005-04-21 21:28:02 +02001055 &wb->dmah);
1056 if (!wb->buf) {
1057 while (i != 0) {
1058 --i;
1059 --wb;
Daniel Mack997ea582010-04-12 13:17:25 +02001060 usb_free_coherent(acm->dev, acm->writesize,
Oliver Neukum884b6002005-04-21 21:28:02 +02001061 wb->buf, wb->dmah);
1062 }
1063 return -ENOMEM;
1064 }
1065 }
1066 return 0;
1067}
1068
Alan Cox10077d42009-06-11 12:36:09 +01001069static int acm_probe(struct usb_interface *intf,
1070 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071{
1072 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001073 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -07001074 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 int buflen = intf->altsetting->extralen;
1076 struct usb_interface *control_interface;
1077 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001078 struct usb_endpoint_descriptor *epctrl = NULL;
1079 struct usb_endpoint_descriptor *epread = NULL;
1080 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 struct usb_device *usb_dev = interface_to_usbdev(intf);
1082 struct acm *acm;
1083 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +01001084 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 u8 *buf;
1086 u8 ac_management_function = 0;
1087 u8 call_management_function = 0;
1088 int call_interface_num = -1;
Erik Slagterfd5054c2011-05-11 12:06:55 +02001089 int data_interface_num = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +02001091 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001092 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001093 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094
Oliver Neukum86478942006-05-13 22:50:47 +02001095 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +02001097 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
1098
flintman5c2f9732014-08-11 21:52:25 -04001099 /* MBM, Not a real CDC ACM device */
1100 if (quirks == NOT_REAL_ACM)
1101 return -ENODEV;
1102
Oliver Neukum86478942006-05-13 22:50:47 +02001103 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 if (quirks == NO_UNION_NORMAL) {
1105 data_interface = usb_ifnum_to_if(usb_dev, 1);
1106 control_interface = usb_ifnum_to_if(usb_dev, 0);
1107 goto skip_normal_probe;
1108 }
Alan Cox6e47e062009-06-11 12:37:06 +01001109
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 /* normal probing*/
1111 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001112 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 return -EINVAL;
1114 }
1115
1116 if (!buflen) {
flintman5c2f9732014-08-11 21:52:25 -04001117#ifndef CONFIG_MACH_TENDERLOIN
Toby Gray577045c2010-09-02 10:46:20 +01001118 if (intf->cur_altsetting->endpoint &&
1119 intf->cur_altsetting->endpoint->extralen &&
Alan Cox6e47e062009-06-11 12:37:06 +01001120 intf->cur_altsetting->endpoint->extra) {
flintman5c2f9732014-08-11 21:52:25 -04001121#else
1122 /* MBM */
1123 if (intf->cur_altsetting->endpoint->extralen &&
1124 intf->cur_altsetting->endpoint->extra) {
1125#endif
Alan Cox6e47e062009-06-11 12:37:06 +01001126 dev_dbg(&intf->dev,
1127 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 buflen = intf->cur_altsetting->endpoint->extralen;
1129 buffer = intf->cur_altsetting->endpoint->extra;
1130 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001131 dev_err(&intf->dev,
1132 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 return -EINVAL;
1134 }
1135 }
1136
1137 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001138 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001139 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 goto next_desc;
1141 }
1142
Alan Cox6e47e062009-06-11 12:37:06 +01001143 switch (buffer[2]) {
1144 case USB_CDC_UNION_TYPE: /* we've found it */
1145 if (union_header) {
1146 dev_err(&intf->dev, "More than one "
1147 "union descriptor, skipping ...\n");
1148 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 }
Alan Cox6e47e062009-06-11 12:37:06 +01001150 union_header = (struct usb_cdc_union_desc *)buffer;
1151 break;
1152 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
1153 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1154 break;
1155 case USB_CDC_HEADER_TYPE: /* maybe check version */
1156 break; /* for now we ignore it */
1157 case USB_CDC_ACM_TYPE:
1158 ac_management_function = buffer[3];
1159 break;
1160 case USB_CDC_CALL_MANAGEMENT_TYPE:
1161 call_management_function = buffer[3];
1162 call_interface_num = buffer[4];
Julian Calabyce126642010-01-05 23:58:20 +11001163 if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
Alan Cox6e47e062009-06-11 12:37:06 +01001164 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1165 break;
1166 default:
1167 /* there are LOTS more CDC descriptors that
1168 * could legitimately be found here.
1169 */
1170 dev_dbg(&intf->dev, "Ignoring descriptor: "
1171 "type %02x, length %d\n",
1172 buffer[2], buffer[0]);
1173 break;
1174 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175next_desc:
1176 buflen -= buffer[0];
1177 buffer += buffer[0];
1178 }
1179
1180 if (!union_header) {
1181 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001182 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Erik Slagterfd5054c2011-05-11 12:06:55 +02001183 /* quirks for Droids MuIn LCD */
1184 if (quirks & NO_DATA_INTERFACE)
1185 data_interface = usb_ifnum_to_if(usb_dev, 0);
1186 else
1187 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 control_interface = intf;
1189 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001190 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1191 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1192 return -ENODEV;
1193 } else {
1194 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1195 combined_interfaces = 1;
1196 control_interface = data_interface = intf;
1197 goto look_for_collapsed_interface;
1198 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 }
1200 } else {
1201 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1202 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
Greg Kroah-Hartmanfacb66e2014-11-07 08:48:15 -08001203 }
1204
1205 if (!control_interface || !data_interface) {
1206 dev_dbg(&intf->dev, "no interfaces\n");
1207 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 }
Alan Cox6e47e062009-06-11 12:37:06 +01001209
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001211 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001213 if (control_interface == data_interface) {
1214 /* some broken devices designed for windows work this way */
1215 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1216 combined_interfaces = 1;
1217 /* a popular other OS doesn't use it */
1218 quirks |= NO_CAP_LINE;
1219 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1220 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1221 return -EINVAL;
1222 }
1223look_for_collapsed_interface:
1224 for (i = 0; i < 3; i++) {
1225 struct usb_endpoint_descriptor *ep;
1226 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1227
1228 if (usb_endpoint_is_int_in(ep))
1229 epctrl = ep;
1230 else if (usb_endpoint_is_bulk_out(ep))
1231 epwrite = ep;
1232 else if (usb_endpoint_is_bulk_in(ep))
1233 epread = ep;
1234 else
1235 return -EINVAL;
1236 }
1237 if (!epctrl || !epread || !epwrite)
1238 return -ENODEV;
1239 else
1240 goto made_compressed_probe;
1241 }
1242
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243skip_normal_probe:
1244
1245 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001246 if (data_interface->cur_altsetting->desc.bInterfaceClass
1247 != CDC_DATA_INTERFACE_TYPE) {
1248 if (control_interface->cur_altsetting->desc.bInterfaceClass
1249 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001251 dev_dbg(&intf->dev,
1252 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 t = control_interface;
1254 control_interface = data_interface;
1255 data_interface = t;
1256 } else {
1257 return -EINVAL;
1258 }
1259 }
Alan Stern74da5d62007-08-02 13:29:10 -04001260
1261 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001262 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001263 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001264
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001265 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1266 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001267 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 return -EBUSY;
1269 }
1270
1271
Sven Schnellea9959bb2012-08-17 21:43:43 +02001272 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
1273 control_interface->cur_altsetting->desc.bNumEndpoints == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 return -EINVAL;
1275
1276 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1277 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1278 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1279
1280
1281 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001282 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 /* descriptors are swapped */
1284 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001285 dev_dbg(&intf->dev,
1286 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 t = epread;
1288 epread = epwrite;
1289 epwrite = t;
1290 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001291made_compressed_probe:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001292 dev_dbg(&intf->dev, "interfaces are valid\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293
Alan Cox6e47e062009-06-11 12:37:06 +01001294 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1295 if (acm == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001296 dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 goto alloc_fail;
1298 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001300 minor = acm_alloc_minor(acm);
1301 if (minor == ACM_TTY_MINORS) {
1302 dev_err(&intf->dev, "no more free acm devices\n");
1303 kfree(acm);
1304 return -ENODEV;
1305 }
1306
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001307 ctrlsize = usb_endpoint_maxp(epctrl);
1308 readsize = usb_endpoint_maxp(epread) *
flintman5c2f9732014-08-11 21:52:25 -04001309#ifndef CONFIG_MACH_TENDERLOIN
Alan Cox6e47e062009-06-11 12:37:06 +01001310 (quirks == SINGLE_RX_URB ? 1 : 2);
flintman5c2f9732014-08-11 21:52:25 -04001311#else
1312 /* MBM */
1313 (quirks == SINGLE_RX_URB ? 1 : 4);
1314#endif
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001315 acm->combined_interfaces = combined_interfaces;
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001316 acm->writesize = usb_endpoint_maxp(epwrite) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 acm->control = control_interface;
1318 acm->data = data_interface;
1319 acm->minor = minor;
1320 acm->dev = usb_dev;
1321 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001322 if (quirks & NO_CAP_LINE)
1323 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 acm->ctrlsize = ctrlsize;
1325 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001326 acm->rx_buflimit = num_rx_buf;
David Howellsc4028952006-11-22 14:57:56 +00001327 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum884b6002005-04-21 21:28:02 +02001328 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001329 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001330 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001331 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001332 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1333 if (acm->is_int_ep)
1334 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001335 tty_port_init(&acm->port);
1336 acm->port.ops = &acm_port_ops;
flintman5c2f9732014-08-11 21:52:25 -04001337#ifdef CONFIG_MACH_TENDERLOIN
1338 /* MBM */
1339 acm->response = usb_alloc_urb(0, GFP_KERNEL);
1340 if (!acm->response) {
1341 dev_dbg(&intf->dev, "out of memory (response kmalloc)\n");
1342 goto alloc_fail2;
1343 }
1344
1345 /* MBM */
1346 acm->bMaxPacketSize0 = usb_dev->descriptor.bMaxPacketSize0;
1347 acm->inbuf = usb_alloc_coherent(usb_dev,
1348 acm->bMaxPacketSize0,
1349 GFP_KERNEL,
1350 &acm->response->transfer_dma);
1351 /* MBM */
1352 if (!acm->inbuf) {
1353 dev_dbg(&intf->dev, "out of memory (inbuf kmalloc)\n");
1354 goto alloc_fail3;
1355 }
1356#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
Daniel Mack997ea582010-04-12 13:17:25 +02001358 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 if (!buf) {
Johan Hovold255ab562011-03-22 11:12:13 +01001360 dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
flintman5c2f9732014-08-11 21:52:25 -04001361#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 goto alloc_fail2;
flintman5c2f9732014-08-11 21:52:25 -04001363#else
1364 /* MBM */
1365 goto alloc_fail3_1;
1366#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 }
1368 acm->ctrl_buffer = buf;
1369
Oliver Neukum884b6002005-04-21 21:28:02 +02001370 if (acm_write_buffers_alloc(acm) < 0) {
Johan Hovold255ab562011-03-22 11:12:13 +01001371 dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 goto alloc_fail4;
1373 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374
1375 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1376 if (!acm->ctrlurb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001377 dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 goto alloc_fail5;
1379 }
Oliver Neukum86478942006-05-13 22:50:47 +02001380 for (i = 0; i < num_rx_buf; i++) {
Johan Hovold088c64f2011-03-25 11:06:02 +01001381 struct acm_rb *rb = &(acm->read_buffers[i]);
1382 struct urb *urb;
David Kubicek61a87ad2005-11-01 18:51:34 +01001383
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001384 rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
1385 &rb->dma);
1386 if (!rb->base) {
1387 dev_err(&intf->dev, "out of memory "
1388 "(read bufs usb_alloc_coherent)\n");
1389 goto alloc_fail6;
1390 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001391 rb->index = i;
1392 rb->instance = acm;
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001393
Johan Hovold088c64f2011-03-25 11:06:02 +01001394 urb = usb_alloc_urb(0, GFP_KERNEL);
1395 if (!urb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001396 dev_err(&intf->dev,
Alan Cox6e47e062009-06-11 12:37:06 +01001397 "out of memory (read urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001398 goto alloc_fail6;
David Kubicek61a87ad2005-11-01 18:51:34 +01001399 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001400 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1401 urb->transfer_dma = rb->dma;
1402 if (acm->is_int_ep) {
1403 usb_fill_int_urb(urb, acm->dev,
1404 acm->rx_endpoint,
1405 rb->base,
1406 acm->readsize,
1407 acm_read_bulk_callback, rb,
1408 acm->bInterval);
1409 } else {
1410 usb_fill_bulk_urb(urb, acm->dev,
1411 acm->rx_endpoint,
1412 rb->base,
1413 acm->readsize,
1414 acm_read_bulk_callback, rb);
1415 }
David Kubicek61a87ad2005-11-01 18:51:34 +01001416
Johan Hovold088c64f2011-03-25 11:06:02 +01001417 acm->read_urbs[i] = urb;
1418 __set_bit(i, &acm->read_urbs_free);
David Kubicek61a87ad2005-11-01 18:51:34 +01001419 }
Alan Cox6e47e062009-06-11 12:37:06 +01001420 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001421 struct acm_wb *snd = &(acm->wb[i]);
1422
Alan Cox6e47e062009-06-11 12:37:06 +01001423 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1424 if (snd->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001425 dev_err(&intf->dev,
Johan Hovold59d7fec2011-03-22 11:12:12 +01001426 "out of memory (write urbs usb_alloc_urb)\n");
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001427 goto alloc_fail7;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001428 }
1429
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001430 if (usb_endpoint_xfer_int(epwrite))
1431 usb_fill_int_urb(snd->urb, usb_dev,
Ming Leie83863d2012-10-16 21:21:21 +08001432 usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001433 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1434 else
1435 usb_fill_bulk_urb(snd->urb, usb_dev,
1436 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1437 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001438 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1439 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 }
1441
Alan Cox6e47e062009-06-11 12:37:06 +01001442 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001443
1444 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1445 if (i < 0)
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001446 goto alloc_fail7;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001447
1448 if (cfd) { /* export the country data */
1449 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1450 if (!acm->country_codes)
1451 goto skip_countries;
1452 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001453 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1454 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001455 acm->country_rel_date = cfd->iCountryCodeRelDate;
1456
1457 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1458 if (i < 0) {
1459 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001460 acm->country_codes = NULL;
1461 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001462 goto skip_countries;
1463 }
1464
Alan Cox6e47e062009-06-11 12:37:06 +01001465 i = device_create_file(&intf->dev,
1466 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001467 if (i < 0) {
Axel Linc2572b72010-05-31 08:04:47 +08001468 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001469 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001470 acm->country_codes = NULL;
1471 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001472 goto skip_countries;
1473 }
1474 }
1475
1476skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001477 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001478 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1479 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1480 /* works around buggy devices */
1481 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1483 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
flintman5c2f9732014-08-11 21:52:25 -04001484#ifdef CONFIG_MACH_TENDERLOIN
1485 /* MBM */
1486 acm->ctrlurb->dev = acm->dev;
1487 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
1488 dev_err(&intf->dev, "usb_submit_urb(ctrl irq) failed");
1489 goto kill_urb;
1490 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491
flintman5c2f9732014-08-11 21:52:25 -04001492 /* MBM */
1493 acm->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
1494 if (!acm->irq)
1495 goto kill_urb;
1496#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1498
1499 acm_set_control(acm, acm->ctrlout);
flintman5c2f9732014-08-11 21:52:25 -04001500#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 acm->line.dwDTERate = cpu_to_le32(9600);
flintman5c2f9732014-08-11 21:52:25 -04001502#else
1503 acm->line.dwDTERate = cpu_to_le32(115200);
1504#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 acm->line.bDataBits = 8;
1506 acm_set_line(acm, &acm->line);
flintman5c2f9732014-08-11 21:52:25 -04001507#ifdef CONFIG_MACH_TENDERLOIN
1508 /* MBM */
1509 acm->state |= ACM_ABS_IDLE;
1510 acm_set_comm_feature(acm, ACM_ABSTRACT_STATE, &acm->state);
1511#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512
1513 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001514 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001516 usb_get_intf(control_interface);
1517 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001519 return 0;
flintman5c2f9732014-08-11 21:52:25 -04001520#ifdef CONFIG_MACH_TENDERLOIN
1521 /* MBM */
1522kill_urb:
1523 usb_kill_urb(acm->ctrlurb);
1524#endif
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001525alloc_fail7:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001526 for (i = 0; i < ACM_NW; i++)
1527 usb_free_urb(acm->wb[i].urb);
Axel Linc2572b72010-05-31 08:04:47 +08001528alloc_fail6:
Oliver Neukum86478942006-05-13 22:50:47 +02001529 for (i = 0; i < num_rx_buf; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001530 usb_free_urb(acm->read_urbs[i]);
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001531 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 usb_free_urb(acm->ctrlurb);
1533alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001534 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535alloc_fail4:
Daniel Mack997ea582010-04-12 13:17:25 +02001536 usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
flintman5c2f9732014-08-11 21:52:25 -04001537#ifdef CONFIG_MACH_TENDERLOIN
1538 /* MBM */
1539alloc_fail3_1:
1540 usb_free_coherent(usb_dev,acm->bMaxPacketSize0, acm->inbuf, acm->response->transfer_dma);
1541alloc_fail3:
1542 usb_free_urb(acm->response);
1543#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544alloc_fail2:
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001545 acm_release_minor(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 kfree(acm);
1547alloc_fail:
1548 return -ENOMEM;
1549}
1550
Oliver Neukum1365baf2007-10-12 17:24:28 +02001551static void stop_data_traffic(struct acm *acm)
1552{
1553 int i;
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001554
1555 dev_dbg(&acm->control->dev, "%s\n", __func__);
flintman5c2f9732014-08-11 21:52:25 -04001556#ifndef CONFIG_MACH_TENDERLOIN
Oliver Neukum1365baf2007-10-12 17:24:28 +02001557 usb_kill_urb(acm->ctrlurb);
flintman5c2f9732014-08-11 21:52:25 -04001558#endif
Alan Cox6e47e062009-06-11 12:37:06 +01001559 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001560 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001561 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001562 usb_kill_urb(acm->read_urbs[i]);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001563
1564 cancel_work_sync(&acm->work);
1565}
1566
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567static void acm_disconnect(struct usb_interface *intf)
1568{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001569 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001571 struct tty_struct *tty;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001572 int i;
1573
1574 dev_dbg(&intf->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575
David Brownell672c4e12008-08-06 18:41:12 -07001576 /* sibling interface is already cleaning up */
1577 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001578 return;
David Brownell672c4e12008-08-06 18:41:12 -07001579
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001580 mutex_lock(&acm->mutex);
1581 acm->disconnected = true;
Alan Cox6e47e062009-06-11 12:37:06 +01001582 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001583 device_remove_file(&acm->control->dev,
1584 &dev_attr_wCountryCodes);
1585 device_remove_file(&acm->control->dev,
1586 &dev_attr_iCountryCodeRelDate);
Oliver Neukum65069bd2014-11-20 14:54:35 +01001587 kfree(acm->country_codes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001588 }
Alan Stern74da5d62007-08-02 13:29:10 -04001589 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001590 usb_set_intfdata(acm->control, NULL);
1591 usb_set_intfdata(acm->data, NULL);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001592 mutex_unlock(&acm->mutex);
1593
1594 tty = tty_port_tty_get(&acm->port);
1595 if (tty) {
1596 tty_vhangup(tty);
1597 tty_kref_put(tty);
1598 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599
Oliver Neukum1365baf2007-10-12 17:24:28 +02001600 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601
Johan Hovold10a00e32013-03-19 09:21:06 +01001602 tty_unregister_device(acm_tty_driver, acm->minor);
1603
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001604 usb_free_urb(acm->ctrlurb);
1605 for (i = 0; i < ACM_NW; i++)
1606 usb_free_urb(acm->wb[i].urb);
1607 for (i = 0; i < acm->rx_buflimit; i++)
1608 usb_free_urb(acm->read_urbs[i]);
Oliver Neukum884b6002005-04-21 21:28:02 +02001609 acm_write_buffers_free(acm);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001610 usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001611 acm_read_buffers_free(acm);
flintman5c2f9732014-08-11 21:52:25 -04001612#ifdef CONFIG_MACH_TENDERLOIN
1613 /* MBM */
1614 usb_free_coherent(usb_dev,acm->bMaxPacketSize0, acm->inbuf, acm->response->transfer_dma);
1615#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001617 if (!acm->combined_interfaces)
1618 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001619 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001621 tty_port_put(&acm->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622}
1623
Oliver Neukum35758582008-07-01 19:10:08 +02001624#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001625static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1626{
1627 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001628 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001629
Oliver Neukum11ea8592008-06-20 11:25:57 +02001630 spin_lock_irq(&acm->read_lock);
1631 spin_lock(&acm->write_lock);
Johan Hovoldbe7978b2014-05-26 19:23:36 +02001632 if (PMSG_IS_AUTO(message)) {
1633 if (acm->transmitting) {
1634 spin_unlock(&acm->write_lock);
1635 spin_unlock_irq(&acm->read_lock);
1636 return -EBUSY;
1637 }
1638 }
Oliver Neukum11ea8592008-06-20 11:25:57 +02001639 cnt = acm->susp_count++;
1640 spin_unlock(&acm->write_lock);
1641 spin_unlock_irq(&acm->read_lock);
1642
1643 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001644 return 0;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001645
flintman5c2f9732014-08-11 21:52:25 -04001646#ifdef CONFIG_MACH_TENDERLOIN
1647 /* MBM, Kill URB here! */
1648 usb_kill_urb(acm->ctrlurb);
1649#endif
1650
Johan Hovold181614f2014-05-26 19:23:40 +02001651 stop_data_traffic(acm);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001652
Oliver Neukum1365baf2007-10-12 17:24:28 +02001653 return 0;
1654}
1655
1656static int acm_resume(struct usb_interface *intf)
1657{
1658 struct acm *acm = usb_get_intfdata(intf);
flintmand53cff62015-03-04 20:21:21 -05001659 struct acm_wb *wb;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001660 int rv = 0;
1661
Oliver Neukum11ea8592008-06-20 11:25:57 +02001662 spin_lock_irq(&acm->read_lock);
Johan Hovold97e72c42014-05-26 19:23:37 +02001663 spin_lock(&acm->write_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001664
Johan Hovold97e72c42014-05-26 19:23:37 +02001665 if (--acm->susp_count)
1666 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001667
flintman5c2f9732014-08-11 21:52:25 -04001668#ifdef CONFIG_MACH_TENDERLOIN
1669 /* MBM, We have to resubmit the INT URB regardless of the port is open or not */
1670 if (usb_submit_urb(acm->ctrlurb, GFP_ATOMIC)) {
1671 dev_err(&intf->dev, "usb_submit_urb(ctrl irq) failed");
1672 }
1673#endif
1674
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001675 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
flintman5c2f9732014-08-11 21:52:25 -04001676#ifndef CONFIG_MACH_TENDERLOIN
Johan Hovold97e72c42014-05-26 19:23:37 +02001677 rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
flintman5c2f9732014-08-11 21:52:25 -04001678#endif
Oliver Neukum97d35f92009-12-16 17:05:57 +01001679
flintmand53cff62015-03-04 20:21:21 -05001680 if (acm->delayed_wb) {
1681 wb = acm->delayed_wb;
1682 acm->delayed_wb = NULL;
1683 acm_start_wb(acm, wb);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001684 }
1685
1686 /*
1687 * delayed error checking because we must
1688 * do the write path at all cost
1689 */
Oliver Neukum1365baf2007-10-12 17:24:28 +02001690 if (rv < 0)
Johan Hovold97e72c42014-05-26 19:23:37 +02001691 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001692
Johan Hovold97e72c42014-05-26 19:23:37 +02001693 rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001694 }
Johan Hovold97e72c42014-05-26 19:23:37 +02001695out:
1696 spin_unlock(&acm->write_lock);
1697 spin_unlock_irq(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001698
Oliver Neukum1365baf2007-10-12 17:24:28 +02001699 return rv;
1700}
Oliver Neukum35758582008-07-01 19:10:08 +02001701
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001702static int acm_reset_resume(struct usb_interface *intf)
1703{
1704 struct acm *acm = usb_get_intfdata(intf);
1705 struct tty_struct *tty;
1706
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001707 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001708 tty = tty_port_tty_get(&acm->port);
1709 if (tty) {
1710 tty_hangup(tty);
1711 tty_kref_put(tty);
1712 }
1713 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001714
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001715 return acm_resume(intf);
1716}
1717
Oliver Neukum35758582008-07-01 19:10:08 +02001718#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001719
1720#define NOKIA_PCSUITE_ACM_INFO(x) \
1721 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1722 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1723 USB_CDC_ACM_PROTO_VENDOR)
1724
Toby Gray4035e452010-09-01 16:01:19 +01001725#define SAMSUNG_PCSUITE_ACM_INFO(x) \
1726 USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
1727 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1728 USB_CDC_ACM_PROTO_VENDOR)
1729
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730/*
1731 * USB driver structure.
1732 */
1733
Németh Márton6ef48522010-01-10 15:33:45 +01001734static const struct usb_device_id acm_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735 /* quirky and broken devices */
David Cluytensbd73e382013-12-03 14:18:57 +01001736 { USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */
1737 .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1739 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1740 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001741 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1742 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1743 },
Andrew Lunn0f9c7b42008-12-23 17:31:23 +01001744 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1745 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1746 },
Masahito Omote8753e652005-07-29 12:17:25 -07001747 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1748 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1749 },
Chris Malley91a9c922006-10-03 10:08:28 +01001750 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1751 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1752 },
Alan Cox7abcf202009-04-06 17:35:01 +01001753 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1754 .driver_info = SINGLE_RX_URB,
1755 },
Oliver Neukum86478942006-05-13 22:50:47 +02001756 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1757 .driver_info = SINGLE_RX_URB, /* firmware bug */
1758 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001759 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1760 .driver_info = SINGLE_RX_URB, /* firmware bug */
1761 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001762 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1763 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1764 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001765 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1766 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1767 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001768 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1769 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1770 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001771 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1772 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1773 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001774 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1775 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1776 },
Johan Hovold1ff3bbb2014-10-27 18:34:33 +01001777 { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001778 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1779 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001780 /* Motorola H24 HSPA module: */
1781 { USB_DEVICE(0x22b8, 0x2d91) }, /* modem */
Michael Ulbrichtb08f08b2014-03-25 10:34:18 +01001782 { USB_DEVICE(0x22b8, 0x2d92), /* modem + diagnostics */
1783 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1784 },
1785 { USB_DEVICE(0x22b8, 0x2d93), /* modem + AT port */
1786 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1787 },
1788 { USB_DEVICE(0x22b8, 0x2d95), /* modem + AT port + diagnostics */
1789 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1790 },
1791 { USB_DEVICE(0x22b8, 0x2d96), /* modem + NMEA */
1792 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1793 },
1794 { USB_DEVICE(0x22b8, 0x2d97), /* modem + diagnostics + NMEA */
1795 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1796 },
1797 { USB_DEVICE(0x22b8, 0x2d99), /* modem + AT port + NMEA */
1798 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1799 },
1800 { USB_DEVICE(0x22b8, 0x2d9a), /* modem + AT port + diagnostics + NMEA */
1801 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1802 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001803
Adam Richterc332b4e2009-02-18 16:17:15 -08001804 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1805 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1806 data interface instead of
1807 communications interface.
1808 Maybe we should define a new
1809 quirk for this. */
1810 },
Jean-Christian de Rivaz52538352012-10-10 12:49:02 +00001811 { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */
1812 .driver_info = NO_UNION_NORMAL,
1813 },
Denis N Ladinb756d752012-12-26 18:29:44 +05001814 { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */
1815 .driver_info = NO_UNION_NORMAL,
1816 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001817 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1818 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1819 },
Russ Nelsonc3baa192010-04-21 23:07:03 -04001820 { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1821 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1822 },
flintman5c2f9732014-08-11 21:52:25 -04001823 /* MBM */
1824 { USB_DEVICE(0x1519, 0x0020), /* IMC_MAIN - XMM6260, XMM6262 */
1825 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1826 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001827
Adrian Taylorc1479a92009-11-19 10:35:33 +00001828 /* Nokia S60 phones expose two ACM channels. The first is
1829 * a modem and is picked up by the standard AT-command
1830 * information below. The second is 'vendor-specific' but
1831 * is treated as a serial device at the S60 end, so we want
1832 * to expose it on Linux too. */
1833 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1834 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1835 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1836 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1837 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1838 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1839 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1840 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1841 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1842 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1843 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1844 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1845 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1846 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1847 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1848 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1849 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1850 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1851 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1852 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1853 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1854 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1855 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1856 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1857 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1858 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1859 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1860 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1861 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1862 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1863 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1864 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1865 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1866 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1867 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1868 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1869 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1870 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1871 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1872 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1873 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1874 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1875 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
Przemo Firszt83a4eae2010-06-28 21:29:34 +01001876 { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
Toby Gray4035e452010-09-01 16:01:19 +01001877 { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
1878 { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
1879 { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
1880 { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
1881 { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
1882 { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
1883 { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
1884 { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
1885 { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
1886 { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
Arvid Ephraim Picciani721d92f2011-01-25 15:58:40 +01001887 { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
Toby Gray4061fde2011-06-06 14:52:48 +01001888 { NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */
1889 { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
Toby Gray4035e452010-09-01 16:01:19 +01001890 { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001891
Denis Pershin65e52f42011-09-04 17:37:21 +07001892 /* Support for Owen devices */
1893 { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */
1894
Adrian Taylorc1479a92009-11-19 10:35:33 +00001895 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1896
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001897 /* Support Lego NXT using pbLua firmware */
Julian Calabyce126642010-01-05 23:58:20 +11001898 { USB_DEVICE(0x0694, 0xff00),
1899 .driver_info = NOT_A_MODEM,
Otavio Salvador7893afc2010-09-26 23:35:05 -03001900 },
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001901
Erik Slagterfd5054c2011-05-11 12:06:55 +02001902 /* Support for Droids MuIn LCD */
1903 { USB_DEVICE(0x04d8, 0x000b),
1904 .driver_info = NO_DATA_INTERFACE,
1905 },
flintman5c2f9732014-08-11 21:52:25 -04001906#ifndef CONFIG_MACH_TENDERLOIN
Philippe Corbes5b239f02010-08-31 19:31:32 +02001907 /* control interfaces without any protocol set */
1908 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1909 USB_CDC_PROTO_NONE) },
flintman5c2f9732014-08-11 21:52:25 -04001910#else
1911 /* Exclude XMM6260 boot rom (not running modem software yet) */
1912 { USB_DEVICE(0x058b, 0x0041),
1913 .driver_info = NOT_REAL_ACM,
1914 },
1915#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916 /* control interfaces with various AT-command sets */
1917 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1918 USB_CDC_ACM_PROTO_AT_V25TER) },
1919 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1920 USB_CDC_ACM_PROTO_AT_PCCA101) },
1921 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1922 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1923 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1924 USB_CDC_ACM_PROTO_AT_GSM) },
1925 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001926 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1928 USB_CDC_ACM_PROTO_AT_CDMA) },
1929
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930 { }
1931};
1932
Alan Cox6e47e062009-06-11 12:37:06 +01001933MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934
1935static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 .name = "cdc_acm",
1937 .probe = acm_probe,
1938 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001939#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001940 .suspend = acm_suspend,
1941 .resume = acm_resume,
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001942 .reset_resume = acm_reset_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001943#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001945#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001946 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001947#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948};
1949
1950/*
1951 * TTY driver structures.
1952 */
1953
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001954static const struct tty_operations acm_ops = {
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001955 .install = acm_tty_install,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 .open = acm_tty_open,
1957 .close = acm_tty_close,
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001958 .cleanup = acm_tty_cleanup,
Alan Cox10077d42009-06-11 12:36:09 +01001959 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 .write = acm_tty_write,
1961 .write_room = acm_tty_write_room,
1962 .ioctl = acm_tty_ioctl,
1963 .throttle = acm_tty_throttle,
1964 .unthrottle = acm_tty_unthrottle,
1965 .chars_in_buffer = acm_tty_chars_in_buffer,
1966 .break_ctl = acm_tty_break_ctl,
1967 .set_termios = acm_tty_set_termios,
1968 .tiocmget = acm_tty_tiocmget,
1969 .tiocmset = acm_tty_tiocmset,
1970};
1971
1972/*
1973 * Init / exit.
1974 */
1975
1976static int __init acm_init(void)
1977{
1978 int retval;
1979 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1980 if (!acm_tty_driver)
1981 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 acm_tty_driver->driver_name = "acm",
1983 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 acm_tty_driver->major = ACM_TTY_MAJOR,
1985 acm_tty_driver->minor_start = 0,
1986 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1987 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001988 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001990 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1991 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 tty_set_operations(acm_tty_driver, &acm_ops);
1993
1994 retval = tty_register_driver(acm_tty_driver);
1995 if (retval) {
1996 put_tty_driver(acm_tty_driver);
1997 return retval;
1998 }
1999
2000 retval = usb_register(&acm_driver);
2001 if (retval) {
2002 tty_unregister_driver(acm_tty_driver);
2003 put_tty_driver(acm_tty_driver);
2004 return retval;
2005 }
2006
Johan Hovolda2c7b932011-03-22 11:12:18 +01002007 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008
2009 return 0;
2010}
2011
2012static void __exit acm_exit(void)
2013{
2014 usb_deregister(&acm_driver);
2015 tty_unregister_driver(acm_tty_driver);
2016 put_tty_driver(acm_tty_driver);
2017}
2018
2019module_init(acm_init);
2020module_exit(acm_exit);
2021
Alan Cox6e47e062009-06-11 12:37:06 +01002022MODULE_AUTHOR(DRIVER_AUTHOR);
2023MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01002025MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);