blob: 5c7103e6449703a3e344e7199fe32fa3650e1f79 [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));
1203 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001204 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 return -ENODEV;
1206 }
1207 }
Alan Cox6e47e062009-06-11 12:37:06 +01001208
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001210 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001212 if (control_interface == data_interface) {
1213 /* some broken devices designed for windows work this way */
1214 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1215 combined_interfaces = 1;
1216 /* a popular other OS doesn't use it */
1217 quirks |= NO_CAP_LINE;
1218 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1219 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1220 return -EINVAL;
1221 }
1222look_for_collapsed_interface:
1223 for (i = 0; i < 3; i++) {
1224 struct usb_endpoint_descriptor *ep;
1225 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1226
1227 if (usb_endpoint_is_int_in(ep))
1228 epctrl = ep;
1229 else if (usb_endpoint_is_bulk_out(ep))
1230 epwrite = ep;
1231 else if (usb_endpoint_is_bulk_in(ep))
1232 epread = ep;
1233 else
1234 return -EINVAL;
1235 }
1236 if (!epctrl || !epread || !epwrite)
1237 return -ENODEV;
1238 else
1239 goto made_compressed_probe;
1240 }
1241
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242skip_normal_probe:
1243
1244 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001245 if (data_interface->cur_altsetting->desc.bInterfaceClass
1246 != CDC_DATA_INTERFACE_TYPE) {
1247 if (control_interface->cur_altsetting->desc.bInterfaceClass
1248 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001250 dev_dbg(&intf->dev,
1251 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 t = control_interface;
1253 control_interface = data_interface;
1254 data_interface = t;
1255 } else {
1256 return -EINVAL;
1257 }
1258 }
Alan Stern74da5d62007-08-02 13:29:10 -04001259
1260 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001261 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001262 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001263
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001264 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1265 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001266 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 return -EBUSY;
1268 }
1269
1270
Sven Schnellea9959bb2012-08-17 21:43:43 +02001271 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
1272 control_interface->cur_altsetting->desc.bNumEndpoints == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 return -EINVAL;
1274
1275 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1276 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1277 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1278
1279
1280 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001281 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 /* descriptors are swapped */
1283 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001284 dev_dbg(&intf->dev,
1285 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 t = epread;
1287 epread = epwrite;
1288 epwrite = t;
1289 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001290made_compressed_probe:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001291 dev_dbg(&intf->dev, "interfaces are valid\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292
Alan Cox6e47e062009-06-11 12:37:06 +01001293 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1294 if (acm == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001295 dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 goto alloc_fail;
1297 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001299 minor = acm_alloc_minor(acm);
1300 if (minor == ACM_TTY_MINORS) {
1301 dev_err(&intf->dev, "no more free acm devices\n");
1302 kfree(acm);
1303 return -ENODEV;
1304 }
1305
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001306 ctrlsize = usb_endpoint_maxp(epctrl);
1307 readsize = usb_endpoint_maxp(epread) *
flintman5c2f9732014-08-11 21:52:25 -04001308#ifndef CONFIG_MACH_TENDERLOIN
Alan Cox6e47e062009-06-11 12:37:06 +01001309 (quirks == SINGLE_RX_URB ? 1 : 2);
flintman5c2f9732014-08-11 21:52:25 -04001310#else
1311 /* MBM */
1312 (quirks == SINGLE_RX_URB ? 1 : 4);
1313#endif
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001314 acm->combined_interfaces = combined_interfaces;
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001315 acm->writesize = usb_endpoint_maxp(epwrite) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 acm->control = control_interface;
1317 acm->data = data_interface;
1318 acm->minor = minor;
1319 acm->dev = usb_dev;
1320 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001321 if (quirks & NO_CAP_LINE)
1322 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 acm->ctrlsize = ctrlsize;
1324 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001325 acm->rx_buflimit = num_rx_buf;
David Howellsc4028952006-11-22 14:57:56 +00001326 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum884b6002005-04-21 21:28:02 +02001327 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001328 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001329 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001330 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001331 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1332 if (acm->is_int_ep)
1333 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001334 tty_port_init(&acm->port);
1335 acm->port.ops = &acm_port_ops;
flintman5c2f9732014-08-11 21:52:25 -04001336#ifdef CONFIG_MACH_TENDERLOIN
1337 /* MBM */
1338 acm->response = usb_alloc_urb(0, GFP_KERNEL);
1339 if (!acm->response) {
1340 dev_dbg(&intf->dev, "out of memory (response kmalloc)\n");
1341 goto alloc_fail2;
1342 }
1343
1344 /* MBM */
1345 acm->bMaxPacketSize0 = usb_dev->descriptor.bMaxPacketSize0;
1346 acm->inbuf = usb_alloc_coherent(usb_dev,
1347 acm->bMaxPacketSize0,
1348 GFP_KERNEL,
1349 &acm->response->transfer_dma);
1350 /* MBM */
1351 if (!acm->inbuf) {
1352 dev_dbg(&intf->dev, "out of memory (inbuf kmalloc)\n");
1353 goto alloc_fail3;
1354 }
1355#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356
Daniel Mack997ea582010-04-12 13:17:25 +02001357 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 if (!buf) {
Johan Hovold255ab562011-03-22 11:12:13 +01001359 dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
flintman5c2f9732014-08-11 21:52:25 -04001360#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 goto alloc_fail2;
flintman5c2f9732014-08-11 21:52:25 -04001362#else
1363 /* MBM */
1364 goto alloc_fail3_1;
1365#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 }
1367 acm->ctrl_buffer = buf;
1368
Oliver Neukum884b6002005-04-21 21:28:02 +02001369 if (acm_write_buffers_alloc(acm) < 0) {
Johan Hovold255ab562011-03-22 11:12:13 +01001370 dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 goto alloc_fail4;
1372 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
1374 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1375 if (!acm->ctrlurb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001376 dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 goto alloc_fail5;
1378 }
Oliver Neukum86478942006-05-13 22:50:47 +02001379 for (i = 0; i < num_rx_buf; i++) {
Johan Hovold088c64f2011-03-25 11:06:02 +01001380 struct acm_rb *rb = &(acm->read_buffers[i]);
1381 struct urb *urb;
David Kubicek61a87ad2005-11-01 18:51:34 +01001382
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001383 rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
1384 &rb->dma);
1385 if (!rb->base) {
1386 dev_err(&intf->dev, "out of memory "
1387 "(read bufs usb_alloc_coherent)\n");
1388 goto alloc_fail6;
1389 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001390 rb->index = i;
1391 rb->instance = acm;
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001392
Johan Hovold088c64f2011-03-25 11:06:02 +01001393 urb = usb_alloc_urb(0, GFP_KERNEL);
1394 if (!urb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001395 dev_err(&intf->dev,
Alan Cox6e47e062009-06-11 12:37:06 +01001396 "out of memory (read urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001397 goto alloc_fail6;
David Kubicek61a87ad2005-11-01 18:51:34 +01001398 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001399 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1400 urb->transfer_dma = rb->dma;
1401 if (acm->is_int_ep) {
1402 usb_fill_int_urb(urb, acm->dev,
1403 acm->rx_endpoint,
1404 rb->base,
1405 acm->readsize,
1406 acm_read_bulk_callback, rb,
1407 acm->bInterval);
1408 } else {
1409 usb_fill_bulk_urb(urb, acm->dev,
1410 acm->rx_endpoint,
1411 rb->base,
1412 acm->readsize,
1413 acm_read_bulk_callback, rb);
1414 }
David Kubicek61a87ad2005-11-01 18:51:34 +01001415
Johan Hovold088c64f2011-03-25 11:06:02 +01001416 acm->read_urbs[i] = urb;
1417 __set_bit(i, &acm->read_urbs_free);
David Kubicek61a87ad2005-11-01 18:51:34 +01001418 }
Alan Cox6e47e062009-06-11 12:37:06 +01001419 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001420 struct acm_wb *snd = &(acm->wb[i]);
1421
Alan Cox6e47e062009-06-11 12:37:06 +01001422 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1423 if (snd->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001424 dev_err(&intf->dev,
Johan Hovold59d7fec2011-03-22 11:12:12 +01001425 "out of memory (write urbs usb_alloc_urb)\n");
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001426 goto alloc_fail7;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001427 }
1428
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001429 if (usb_endpoint_xfer_int(epwrite))
1430 usb_fill_int_urb(snd->urb, usb_dev,
Ming Leie83863d2012-10-16 21:21:21 +08001431 usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001432 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1433 else
1434 usb_fill_bulk_urb(snd->urb, usb_dev,
1435 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1436 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001437 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1438 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 }
1440
Alan Cox6e47e062009-06-11 12:37:06 +01001441 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001442
1443 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1444 if (i < 0)
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001445 goto alloc_fail7;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001446
1447 if (cfd) { /* export the country data */
1448 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1449 if (!acm->country_codes)
1450 goto skip_countries;
1451 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001452 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1453 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001454 acm->country_rel_date = cfd->iCountryCodeRelDate;
1455
1456 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1457 if (i < 0) {
1458 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001459 acm->country_codes = NULL;
1460 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001461 goto skip_countries;
1462 }
1463
Alan Cox6e47e062009-06-11 12:37:06 +01001464 i = device_create_file(&intf->dev,
1465 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001466 if (i < 0) {
Axel Linc2572b72010-05-31 08:04:47 +08001467 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001468 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001469 acm->country_codes = NULL;
1470 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001471 goto skip_countries;
1472 }
1473 }
1474
1475skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001476 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001477 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1478 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1479 /* works around buggy devices */
1480 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1482 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
flintman5c2f9732014-08-11 21:52:25 -04001483#ifdef CONFIG_MACH_TENDERLOIN
1484 /* MBM */
1485 acm->ctrlurb->dev = acm->dev;
1486 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
1487 dev_err(&intf->dev, "usb_submit_urb(ctrl irq) failed");
1488 goto kill_urb;
1489 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490
flintman5c2f9732014-08-11 21:52:25 -04001491 /* MBM */
1492 acm->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
1493 if (!acm->irq)
1494 goto kill_urb;
1495#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1497
1498 acm_set_control(acm, acm->ctrlout);
flintman5c2f9732014-08-11 21:52:25 -04001499#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 acm->line.dwDTERate = cpu_to_le32(9600);
flintman5c2f9732014-08-11 21:52:25 -04001501#else
1502 acm->line.dwDTERate = cpu_to_le32(115200);
1503#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504 acm->line.bDataBits = 8;
1505 acm_set_line(acm, &acm->line);
flintman5c2f9732014-08-11 21:52:25 -04001506#ifdef CONFIG_MACH_TENDERLOIN
1507 /* MBM */
1508 acm->state |= ACM_ABS_IDLE;
1509 acm_set_comm_feature(acm, ACM_ABSTRACT_STATE, &acm->state);
1510#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511
1512 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001513 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001515 usb_get_intf(control_interface);
1516 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001518 return 0;
flintman5c2f9732014-08-11 21:52:25 -04001519#ifdef CONFIG_MACH_TENDERLOIN
1520 /* MBM */
1521kill_urb:
1522 usb_kill_urb(acm->ctrlurb);
1523#endif
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001524alloc_fail7:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001525 for (i = 0; i < ACM_NW; i++)
1526 usb_free_urb(acm->wb[i].urb);
Axel Linc2572b72010-05-31 08:04:47 +08001527alloc_fail6:
Oliver Neukum86478942006-05-13 22:50:47 +02001528 for (i = 0; i < num_rx_buf; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001529 usb_free_urb(acm->read_urbs[i]);
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001530 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 usb_free_urb(acm->ctrlurb);
1532alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001533 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534alloc_fail4:
Daniel Mack997ea582010-04-12 13:17:25 +02001535 usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
flintman5c2f9732014-08-11 21:52:25 -04001536#ifdef CONFIG_MACH_TENDERLOIN
1537 /* MBM */
1538alloc_fail3_1:
1539 usb_free_coherent(usb_dev,acm->bMaxPacketSize0, acm->inbuf, acm->response->transfer_dma);
1540alloc_fail3:
1541 usb_free_urb(acm->response);
1542#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543alloc_fail2:
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001544 acm_release_minor(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 kfree(acm);
1546alloc_fail:
1547 return -ENOMEM;
1548}
1549
Oliver Neukum1365baf2007-10-12 17:24:28 +02001550static void stop_data_traffic(struct acm *acm)
1551{
1552 int i;
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001553
1554 dev_dbg(&acm->control->dev, "%s\n", __func__);
flintman5c2f9732014-08-11 21:52:25 -04001555#ifndef CONFIG_MACH_TENDERLOIN
Oliver Neukum1365baf2007-10-12 17:24:28 +02001556 usb_kill_urb(acm->ctrlurb);
flintman5c2f9732014-08-11 21:52:25 -04001557#endif
Alan Cox6e47e062009-06-11 12:37:06 +01001558 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001559 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001560 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001561 usb_kill_urb(acm->read_urbs[i]);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001562
1563 cancel_work_sync(&acm->work);
1564}
1565
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566static void acm_disconnect(struct usb_interface *intf)
1567{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001568 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001570 struct tty_struct *tty;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001571 int i;
1572
1573 dev_dbg(&intf->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574
David Brownell672c4e12008-08-06 18:41:12 -07001575 /* sibling interface is already cleaning up */
1576 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001577 return;
David Brownell672c4e12008-08-06 18:41:12 -07001578
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001579 mutex_lock(&acm->mutex);
1580 acm->disconnected = true;
Alan Cox6e47e062009-06-11 12:37:06 +01001581 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001582 device_remove_file(&acm->control->dev,
1583 &dev_attr_wCountryCodes);
1584 device_remove_file(&acm->control->dev,
1585 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001586 }
Alan Stern74da5d62007-08-02 13:29:10 -04001587 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001588 usb_set_intfdata(acm->control, NULL);
1589 usb_set_intfdata(acm->data, NULL);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001590 mutex_unlock(&acm->mutex);
1591
1592 tty = tty_port_tty_get(&acm->port);
1593 if (tty) {
1594 tty_vhangup(tty);
1595 tty_kref_put(tty);
1596 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597
Oliver Neukum1365baf2007-10-12 17:24:28 +02001598 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599
Johan Hovold10a00e32013-03-19 09:21:06 +01001600 tty_unregister_device(acm_tty_driver, acm->minor);
1601
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001602 usb_free_urb(acm->ctrlurb);
1603 for (i = 0; i < ACM_NW; i++)
1604 usb_free_urb(acm->wb[i].urb);
1605 for (i = 0; i < acm->rx_buflimit; i++)
1606 usb_free_urb(acm->read_urbs[i]);
Oliver Neukum884b6002005-04-21 21:28:02 +02001607 acm_write_buffers_free(acm);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001608 usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001609 acm_read_buffers_free(acm);
flintman5c2f9732014-08-11 21:52:25 -04001610#ifdef CONFIG_MACH_TENDERLOIN
1611 /* MBM */
1612 usb_free_coherent(usb_dev,acm->bMaxPacketSize0, acm->inbuf, acm->response->transfer_dma);
1613#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001615 if (!acm->combined_interfaces)
1616 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001617 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001619 tty_port_put(&acm->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620}
1621
Oliver Neukum35758582008-07-01 19:10:08 +02001622#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001623static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1624{
1625 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001626 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001627
Oliver Neukum11ea8592008-06-20 11:25:57 +02001628 spin_lock_irq(&acm->read_lock);
1629 spin_lock(&acm->write_lock);
Johan Hovoldbe7978b2014-05-26 19:23:36 +02001630 if (PMSG_IS_AUTO(message)) {
1631 if (acm->transmitting) {
1632 spin_unlock(&acm->write_lock);
1633 spin_unlock_irq(&acm->read_lock);
1634 return -EBUSY;
1635 }
1636 }
Oliver Neukum11ea8592008-06-20 11:25:57 +02001637 cnt = acm->susp_count++;
1638 spin_unlock(&acm->write_lock);
1639 spin_unlock_irq(&acm->read_lock);
1640
1641 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001642 return 0;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001643
flintman5c2f9732014-08-11 21:52:25 -04001644#ifdef CONFIG_MACH_TENDERLOIN
1645 /* MBM, Kill URB here! */
1646 usb_kill_urb(acm->ctrlurb);
1647#endif
1648
Johan Hovold181614f2014-05-26 19:23:40 +02001649 stop_data_traffic(acm);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001650
Oliver Neukum1365baf2007-10-12 17:24:28 +02001651 return 0;
1652}
1653
1654static int acm_resume(struct usb_interface *intf)
1655{
1656 struct acm *acm = usb_get_intfdata(intf);
flintmand53cff62015-03-04 20:21:21 -05001657 struct acm_wb *wb;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001658 int rv = 0;
1659
Oliver Neukum11ea8592008-06-20 11:25:57 +02001660 spin_lock_irq(&acm->read_lock);
Johan Hovold97e72c42014-05-26 19:23:37 +02001661 spin_lock(&acm->write_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001662
Johan Hovold97e72c42014-05-26 19:23:37 +02001663 if (--acm->susp_count)
1664 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001665
flintman5c2f9732014-08-11 21:52:25 -04001666#ifdef CONFIG_MACH_TENDERLOIN
1667 /* MBM, We have to resubmit the INT URB regardless of the port is open or not */
1668 if (usb_submit_urb(acm->ctrlurb, GFP_ATOMIC)) {
1669 dev_err(&intf->dev, "usb_submit_urb(ctrl irq) failed");
1670 }
1671#endif
1672
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001673 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
flintman5c2f9732014-08-11 21:52:25 -04001674#ifndef CONFIG_MACH_TENDERLOIN
Johan Hovold97e72c42014-05-26 19:23:37 +02001675 rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
flintman5c2f9732014-08-11 21:52:25 -04001676#endif
Oliver Neukum97d35f92009-12-16 17:05:57 +01001677
flintmand53cff62015-03-04 20:21:21 -05001678 if (acm->delayed_wb) {
1679 wb = acm->delayed_wb;
1680 acm->delayed_wb = NULL;
1681 acm_start_wb(acm, wb);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001682 }
1683
1684 /*
1685 * delayed error checking because we must
1686 * do the write path at all cost
1687 */
Oliver Neukum1365baf2007-10-12 17:24:28 +02001688 if (rv < 0)
Johan Hovold97e72c42014-05-26 19:23:37 +02001689 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001690
Johan Hovold97e72c42014-05-26 19:23:37 +02001691 rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001692 }
Johan Hovold97e72c42014-05-26 19:23:37 +02001693out:
1694 spin_unlock(&acm->write_lock);
1695 spin_unlock_irq(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001696
Oliver Neukum1365baf2007-10-12 17:24:28 +02001697 return rv;
1698}
Oliver Neukum35758582008-07-01 19:10:08 +02001699
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001700static int acm_reset_resume(struct usb_interface *intf)
1701{
1702 struct acm *acm = usb_get_intfdata(intf);
1703 struct tty_struct *tty;
1704
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001705 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001706 tty = tty_port_tty_get(&acm->port);
1707 if (tty) {
1708 tty_hangup(tty);
1709 tty_kref_put(tty);
1710 }
1711 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001712
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001713 return acm_resume(intf);
1714}
1715
Oliver Neukum35758582008-07-01 19:10:08 +02001716#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001717
1718#define NOKIA_PCSUITE_ACM_INFO(x) \
1719 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1720 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1721 USB_CDC_ACM_PROTO_VENDOR)
1722
Toby Gray4035e452010-09-01 16:01:19 +01001723#define SAMSUNG_PCSUITE_ACM_INFO(x) \
1724 USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
1725 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1726 USB_CDC_ACM_PROTO_VENDOR)
1727
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728/*
1729 * USB driver structure.
1730 */
1731
Németh Márton6ef48522010-01-10 15:33:45 +01001732static const struct usb_device_id acm_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 /* quirky and broken devices */
David Cluytensbd73e382013-12-03 14:18:57 +01001734 { USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */
1735 .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1737 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1738 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001739 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1740 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1741 },
Andrew Lunn0f9c7b42008-12-23 17:31:23 +01001742 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1743 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1744 },
Masahito Omote8753e652005-07-29 12:17:25 -07001745 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1746 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1747 },
Chris Malley91a9c922006-10-03 10:08:28 +01001748 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1749 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1750 },
Alan Cox7abcf202009-04-06 17:35:01 +01001751 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1752 .driver_info = SINGLE_RX_URB,
1753 },
Oliver Neukum86478942006-05-13 22:50:47 +02001754 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1755 .driver_info = SINGLE_RX_URB, /* firmware bug */
1756 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001757 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1758 .driver_info = SINGLE_RX_URB, /* firmware bug */
1759 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001760 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1761 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1762 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001763 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1764 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1765 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001766 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1767 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1768 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001769 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1770 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1771 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001772 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1773 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1774 },
Johan Hovold1ff3bbb2014-10-27 18:34:33 +01001775 { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001776 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1777 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001778 /* Motorola H24 HSPA module: */
1779 { USB_DEVICE(0x22b8, 0x2d91) }, /* modem */
Michael Ulbrichtb08f08b2014-03-25 10:34:18 +01001780 { USB_DEVICE(0x22b8, 0x2d92), /* modem + diagnostics */
1781 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1782 },
1783 { USB_DEVICE(0x22b8, 0x2d93), /* modem + AT port */
1784 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1785 },
1786 { USB_DEVICE(0x22b8, 0x2d95), /* modem + AT port + diagnostics */
1787 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1788 },
1789 { USB_DEVICE(0x22b8, 0x2d96), /* modem + NMEA */
1790 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1791 },
1792 { USB_DEVICE(0x22b8, 0x2d97), /* modem + diagnostics + NMEA */
1793 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1794 },
1795 { USB_DEVICE(0x22b8, 0x2d99), /* modem + AT port + NMEA */
1796 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1797 },
1798 { USB_DEVICE(0x22b8, 0x2d9a), /* modem + AT port + diagnostics + NMEA */
1799 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1800 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001801
Adam Richterc332b4e2009-02-18 16:17:15 -08001802 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1803 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1804 data interface instead of
1805 communications interface.
1806 Maybe we should define a new
1807 quirk for this. */
1808 },
Jean-Christian de Rivaz52538352012-10-10 12:49:02 +00001809 { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */
1810 .driver_info = NO_UNION_NORMAL,
1811 },
Denis N Ladinb756d752012-12-26 18:29:44 +05001812 { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */
1813 .driver_info = NO_UNION_NORMAL,
1814 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001815 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1816 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1817 },
Russ Nelsonc3baa192010-04-21 23:07:03 -04001818 { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1819 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1820 },
flintman5c2f9732014-08-11 21:52:25 -04001821 /* MBM */
1822 { USB_DEVICE(0x1519, 0x0020), /* IMC_MAIN - XMM6260, XMM6262 */
1823 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1824 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001825
Adrian Taylorc1479a92009-11-19 10:35:33 +00001826 /* Nokia S60 phones expose two ACM channels. The first is
1827 * a modem and is picked up by the standard AT-command
1828 * information below. The second is 'vendor-specific' but
1829 * is treated as a serial device at the S60 end, so we want
1830 * to expose it on Linux too. */
1831 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1832 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1833 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1834 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1835 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1836 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1837 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1838 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1839 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1840 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1841 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1842 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1843 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1844 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1845 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1846 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1847 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1848 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1849 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1850 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1851 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1852 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1853 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1854 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1855 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1856 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1857 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1858 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1859 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1860 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1861 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1862 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1863 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1864 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1865 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1866 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1867 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1868 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1869 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1870 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1871 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1872 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1873 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
Przemo Firszt83a4eae2010-06-28 21:29:34 +01001874 { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
Toby Gray4035e452010-09-01 16:01:19 +01001875 { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
1876 { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
1877 { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
1878 { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
1879 { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
1880 { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
1881 { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
1882 { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
1883 { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
1884 { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
Arvid Ephraim Picciani721d92f2011-01-25 15:58:40 +01001885 { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
Toby Gray4061fde2011-06-06 14:52:48 +01001886 { NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */
1887 { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
Toby Gray4035e452010-09-01 16:01:19 +01001888 { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001889
Denis Pershin65e52f42011-09-04 17:37:21 +07001890 /* Support for Owen devices */
1891 { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */
1892
Adrian Taylorc1479a92009-11-19 10:35:33 +00001893 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1894
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001895 /* Support Lego NXT using pbLua firmware */
Julian Calabyce126642010-01-05 23:58:20 +11001896 { USB_DEVICE(0x0694, 0xff00),
1897 .driver_info = NOT_A_MODEM,
Otavio Salvador7893afc2010-09-26 23:35:05 -03001898 },
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001899
Erik Slagterfd5054c2011-05-11 12:06:55 +02001900 /* Support for Droids MuIn LCD */
1901 { USB_DEVICE(0x04d8, 0x000b),
1902 .driver_info = NO_DATA_INTERFACE,
1903 },
flintman5c2f9732014-08-11 21:52:25 -04001904#ifndef CONFIG_MACH_TENDERLOIN
Philippe Corbes5b239f02010-08-31 19:31:32 +02001905 /* control interfaces without any protocol set */
1906 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1907 USB_CDC_PROTO_NONE) },
flintman5c2f9732014-08-11 21:52:25 -04001908#else
1909 /* Exclude XMM6260 boot rom (not running modem software yet) */
1910 { USB_DEVICE(0x058b, 0x0041),
1911 .driver_info = NOT_REAL_ACM,
1912 },
1913#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914 /* control interfaces with various AT-command sets */
1915 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1916 USB_CDC_ACM_PROTO_AT_V25TER) },
1917 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1918 USB_CDC_ACM_PROTO_AT_PCCA101) },
1919 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1920 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1921 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1922 USB_CDC_ACM_PROTO_AT_GSM) },
1923 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001924 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1926 USB_CDC_ACM_PROTO_AT_CDMA) },
1927
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 { }
1929};
1930
Alan Cox6e47e062009-06-11 12:37:06 +01001931MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932
1933static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 .name = "cdc_acm",
1935 .probe = acm_probe,
1936 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001937#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001938 .suspend = acm_suspend,
1939 .resume = acm_resume,
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001940 .reset_resume = acm_reset_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001941#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001943#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001944 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001945#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946};
1947
1948/*
1949 * TTY driver structures.
1950 */
1951
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001952static const struct tty_operations acm_ops = {
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001953 .install = acm_tty_install,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 .open = acm_tty_open,
1955 .close = acm_tty_close,
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001956 .cleanup = acm_tty_cleanup,
Alan Cox10077d42009-06-11 12:36:09 +01001957 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 .write = acm_tty_write,
1959 .write_room = acm_tty_write_room,
1960 .ioctl = acm_tty_ioctl,
1961 .throttle = acm_tty_throttle,
1962 .unthrottle = acm_tty_unthrottle,
1963 .chars_in_buffer = acm_tty_chars_in_buffer,
1964 .break_ctl = acm_tty_break_ctl,
1965 .set_termios = acm_tty_set_termios,
1966 .tiocmget = acm_tty_tiocmget,
1967 .tiocmset = acm_tty_tiocmset,
1968};
1969
1970/*
1971 * Init / exit.
1972 */
1973
1974static int __init acm_init(void)
1975{
1976 int retval;
1977 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1978 if (!acm_tty_driver)
1979 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980 acm_tty_driver->driver_name = "acm",
1981 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 acm_tty_driver->major = ACM_TTY_MAJOR,
1983 acm_tty_driver->minor_start = 0,
1984 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1985 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001986 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001988 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1989 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 tty_set_operations(acm_tty_driver, &acm_ops);
1991
1992 retval = tty_register_driver(acm_tty_driver);
1993 if (retval) {
1994 put_tty_driver(acm_tty_driver);
1995 return retval;
1996 }
1997
1998 retval = usb_register(&acm_driver);
1999 if (retval) {
2000 tty_unregister_driver(acm_tty_driver);
2001 put_tty_driver(acm_tty_driver);
2002 return retval;
2003 }
2004
Johan Hovolda2c7b932011-03-22 11:12:18 +01002005 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006
2007 return 0;
2008}
2009
2010static void __exit acm_exit(void)
2011{
2012 usb_deregister(&acm_driver);
2013 tty_unregister_driver(acm_tty_driver);
2014 put_tty_driver(acm_tty_driver);
2015}
2016
2017module_init(acm_init);
2018module_exit(acm_exit);
2019
Alan Cox6e47e062009-06-11 12:37:06 +01002020MODULE_AUTHOR(DRIVER_AUTHOR);
2021MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002022MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01002023MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);