blob: 7b723150ef1f6cf5d1387a8dc34a2918c0b1eea5 [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
995 if (!newline.dwDTERate) {
996 newline.dwDTERate = acm->line.dwDTERate;
997 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +0100998 } else
999 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000
1001 if (newctrl != acm->ctrlout)
1002 acm_set_control(acm, acm->ctrlout = newctrl);
1003
1004 if (memcmp(&acm->line, &newline, sizeof newline)) {
1005 memcpy(&acm->line, &newline, sizeof newline);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001006 dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
1007 __func__,
1008 le32_to_cpu(newline.dwDTERate),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 newline.bCharFormat, newline.bParityType,
1010 newline.bDataBits);
1011 acm_set_line(acm, &acm->line);
1012 }
1013}
1014
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001015static const struct tty_port_operations acm_port_ops = {
1016 .shutdown = acm_port_shutdown,
1017 .activate = acm_port_activate,
1018 .destruct = acm_port_destruct,
1019};
1020
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021/*
1022 * USB probe and disconnect routines.
1023 */
1024
Oliver Neukum830f4022008-06-25 14:17:16 +02001025/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +02001026static void acm_write_buffers_free(struct acm *acm)
1027{
1028 int i;
1029 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +02001030 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +02001031
Alan Cox6e47e062009-06-11 12:37:06 +01001032 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Daniel Mack997ea582010-04-12 13:17:25 +02001033 usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +02001034}
1035
Oliver Neukum830f4022008-06-25 14:17:16 +02001036static void acm_read_buffers_free(struct acm *acm)
1037{
1038 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Johan Hovolddab54c92011-03-22 11:12:21 +01001039 int i;
Oliver Neukum830f4022008-06-25 14:17:16 +02001040
Johan Hovolddab54c92011-03-22 11:12:21 +01001041 for (i = 0; i < acm->rx_buflimit; i++)
Daniel Mack997ea582010-04-12 13:17:25 +02001042 usb_free_coherent(usb_dev, acm->readsize,
Johan Hovold088c64f2011-03-25 11:06:02 +01001043 acm->read_buffers[i].base, acm->read_buffers[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001044}
1045
Oliver Neukum884b6002005-04-21 21:28:02 +02001046/* Little helper: write buffers allocate */
1047static int acm_write_buffers_alloc(struct acm *acm)
1048{
1049 int i;
1050 struct acm_wb *wb;
1051
Oliver Neukum86478942006-05-13 22:50:47 +02001052 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Daniel Mack997ea582010-04-12 13:17:25 +02001053 wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
Oliver Neukum884b6002005-04-21 21:28:02 +02001054 &wb->dmah);
1055 if (!wb->buf) {
1056 while (i != 0) {
1057 --i;
1058 --wb;
Daniel Mack997ea582010-04-12 13:17:25 +02001059 usb_free_coherent(acm->dev, acm->writesize,
Oliver Neukum884b6002005-04-21 21:28:02 +02001060 wb->buf, wb->dmah);
1061 }
1062 return -ENOMEM;
1063 }
1064 }
1065 return 0;
1066}
1067
Alan Cox10077d42009-06-11 12:36:09 +01001068static int acm_probe(struct usb_interface *intf,
1069 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070{
1071 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001072 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -07001073 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 int buflen = intf->altsetting->extralen;
1075 struct usb_interface *control_interface;
1076 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001077 struct usb_endpoint_descriptor *epctrl = NULL;
1078 struct usb_endpoint_descriptor *epread = NULL;
1079 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 struct usb_device *usb_dev = interface_to_usbdev(intf);
1081 struct acm *acm;
1082 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +01001083 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 u8 *buf;
1085 u8 ac_management_function = 0;
1086 u8 call_management_function = 0;
1087 int call_interface_num = -1;
Erik Slagterfd5054c2011-05-11 12:06:55 +02001088 int data_interface_num = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +02001090 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001091 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001092 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093
Oliver Neukum86478942006-05-13 22:50:47 +02001094 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +02001096 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
1097
flintman5c2f9732014-08-11 21:52:25 -04001098 /* MBM, Not a real CDC ACM device */
1099 if (quirks == NOT_REAL_ACM)
1100 return -ENODEV;
1101
Oliver Neukum86478942006-05-13 22:50:47 +02001102 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 if (quirks == NO_UNION_NORMAL) {
1104 data_interface = usb_ifnum_to_if(usb_dev, 1);
1105 control_interface = usb_ifnum_to_if(usb_dev, 0);
1106 goto skip_normal_probe;
1107 }
Alan Cox6e47e062009-06-11 12:37:06 +01001108
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 /* normal probing*/
1110 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001111 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 return -EINVAL;
1113 }
1114
1115 if (!buflen) {
flintman5c2f9732014-08-11 21:52:25 -04001116#ifndef CONFIG_MACH_TENDERLOIN
Toby Gray577045c2010-09-02 10:46:20 +01001117 if (intf->cur_altsetting->endpoint &&
1118 intf->cur_altsetting->endpoint->extralen &&
Alan Cox6e47e062009-06-11 12:37:06 +01001119 intf->cur_altsetting->endpoint->extra) {
flintman5c2f9732014-08-11 21:52:25 -04001120#else
1121 /* MBM */
1122 if (intf->cur_altsetting->endpoint->extralen &&
1123 intf->cur_altsetting->endpoint->extra) {
1124#endif
Alan Cox6e47e062009-06-11 12:37:06 +01001125 dev_dbg(&intf->dev,
1126 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 buflen = intf->cur_altsetting->endpoint->extralen;
1128 buffer = intf->cur_altsetting->endpoint->extra;
1129 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001130 dev_err(&intf->dev,
1131 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 return -EINVAL;
1133 }
1134 }
1135
1136 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001137 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001138 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 goto next_desc;
1140 }
1141
Alan Cox6e47e062009-06-11 12:37:06 +01001142 switch (buffer[2]) {
1143 case USB_CDC_UNION_TYPE: /* we've found it */
1144 if (union_header) {
1145 dev_err(&intf->dev, "More than one "
1146 "union descriptor, skipping ...\n");
1147 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 }
Alan Cox6e47e062009-06-11 12:37:06 +01001149 union_header = (struct usb_cdc_union_desc *)buffer;
1150 break;
1151 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
1152 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1153 break;
1154 case USB_CDC_HEADER_TYPE: /* maybe check version */
1155 break; /* for now we ignore it */
1156 case USB_CDC_ACM_TYPE:
1157 ac_management_function = buffer[3];
1158 break;
1159 case USB_CDC_CALL_MANAGEMENT_TYPE:
1160 call_management_function = buffer[3];
1161 call_interface_num = buffer[4];
Julian Calabyce126642010-01-05 23:58:20 +11001162 if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
Alan Cox6e47e062009-06-11 12:37:06 +01001163 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1164 break;
1165 default:
1166 /* there are LOTS more CDC descriptors that
1167 * could legitimately be found here.
1168 */
1169 dev_dbg(&intf->dev, "Ignoring descriptor: "
1170 "type %02x, length %d\n",
1171 buffer[2], buffer[0]);
1172 break;
1173 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174next_desc:
1175 buflen -= buffer[0];
1176 buffer += buffer[0];
1177 }
1178
1179 if (!union_header) {
1180 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001181 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Erik Slagterfd5054c2011-05-11 12:06:55 +02001182 /* quirks for Droids MuIn LCD */
1183 if (quirks & NO_DATA_INTERFACE)
1184 data_interface = usb_ifnum_to_if(usb_dev, 0);
1185 else
1186 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 control_interface = intf;
1188 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001189 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1190 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1191 return -ENODEV;
1192 } else {
1193 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1194 combined_interfaces = 1;
1195 control_interface = data_interface = intf;
1196 goto look_for_collapsed_interface;
1197 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 }
1199 } else {
1200 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1201 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1202 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001203 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 return -ENODEV;
1205 }
1206 }
Alan Cox6e47e062009-06-11 12:37:06 +01001207
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001209 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001211 if (control_interface == data_interface) {
1212 /* some broken devices designed for windows work this way */
1213 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1214 combined_interfaces = 1;
1215 /* a popular other OS doesn't use it */
1216 quirks |= NO_CAP_LINE;
1217 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1218 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1219 return -EINVAL;
1220 }
1221look_for_collapsed_interface:
1222 for (i = 0; i < 3; i++) {
1223 struct usb_endpoint_descriptor *ep;
1224 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1225
1226 if (usb_endpoint_is_int_in(ep))
1227 epctrl = ep;
1228 else if (usb_endpoint_is_bulk_out(ep))
1229 epwrite = ep;
1230 else if (usb_endpoint_is_bulk_in(ep))
1231 epread = ep;
1232 else
1233 return -EINVAL;
1234 }
1235 if (!epctrl || !epread || !epwrite)
1236 return -ENODEV;
1237 else
1238 goto made_compressed_probe;
1239 }
1240
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241skip_normal_probe:
1242
1243 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001244 if (data_interface->cur_altsetting->desc.bInterfaceClass
1245 != CDC_DATA_INTERFACE_TYPE) {
1246 if (control_interface->cur_altsetting->desc.bInterfaceClass
1247 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001249 dev_dbg(&intf->dev,
1250 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 t = control_interface;
1252 control_interface = data_interface;
1253 data_interface = t;
1254 } else {
1255 return -EINVAL;
1256 }
1257 }
Alan Stern74da5d62007-08-02 13:29:10 -04001258
1259 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001260 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001261 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001262
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001263 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1264 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001265 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 return -EBUSY;
1267 }
1268
1269
Sven Schnellea9959bb2012-08-17 21:43:43 +02001270 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
1271 control_interface->cur_altsetting->desc.bNumEndpoints == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 return -EINVAL;
1273
1274 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1275 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1276 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1277
1278
1279 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001280 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 /* descriptors are swapped */
1282 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001283 dev_dbg(&intf->dev,
1284 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 t = epread;
1286 epread = epwrite;
1287 epwrite = t;
1288 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001289made_compressed_probe:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001290 dev_dbg(&intf->dev, "interfaces are valid\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291
Alan Cox6e47e062009-06-11 12:37:06 +01001292 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1293 if (acm == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001294 dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 goto alloc_fail;
1296 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001298 minor = acm_alloc_minor(acm);
1299 if (minor == ACM_TTY_MINORS) {
1300 dev_err(&intf->dev, "no more free acm devices\n");
1301 kfree(acm);
1302 return -ENODEV;
1303 }
1304
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001305 ctrlsize = usb_endpoint_maxp(epctrl);
1306 readsize = usb_endpoint_maxp(epread) *
flintman5c2f9732014-08-11 21:52:25 -04001307#ifndef CONFIG_MACH_TENDERLOIN
Alan Cox6e47e062009-06-11 12:37:06 +01001308 (quirks == SINGLE_RX_URB ? 1 : 2);
flintman5c2f9732014-08-11 21:52:25 -04001309#else
1310 /* MBM */
1311 (quirks == SINGLE_RX_URB ? 1 : 4);
1312#endif
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001313 acm->combined_interfaces = combined_interfaces;
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001314 acm->writesize = usb_endpoint_maxp(epwrite) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 acm->control = control_interface;
1316 acm->data = data_interface;
1317 acm->minor = minor;
1318 acm->dev = usb_dev;
1319 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001320 if (quirks & NO_CAP_LINE)
1321 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 acm->ctrlsize = ctrlsize;
1323 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001324 acm->rx_buflimit = num_rx_buf;
David Howellsc4028952006-11-22 14:57:56 +00001325 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum884b6002005-04-21 21:28:02 +02001326 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001327 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001328 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001329 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001330 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1331 if (acm->is_int_ep)
1332 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001333 tty_port_init(&acm->port);
1334 acm->port.ops = &acm_port_ops;
flintman5c2f9732014-08-11 21:52:25 -04001335#ifdef CONFIG_MACH_TENDERLOIN
1336 /* MBM */
1337 acm->response = usb_alloc_urb(0, GFP_KERNEL);
1338 if (!acm->response) {
1339 dev_dbg(&intf->dev, "out of memory (response kmalloc)\n");
1340 goto alloc_fail2;
1341 }
1342
1343 /* MBM */
1344 acm->bMaxPacketSize0 = usb_dev->descriptor.bMaxPacketSize0;
1345 acm->inbuf = usb_alloc_coherent(usb_dev,
1346 acm->bMaxPacketSize0,
1347 GFP_KERNEL,
1348 &acm->response->transfer_dma);
1349 /* MBM */
1350 if (!acm->inbuf) {
1351 dev_dbg(&intf->dev, "out of memory (inbuf kmalloc)\n");
1352 goto alloc_fail3;
1353 }
1354#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
Daniel Mack997ea582010-04-12 13:17:25 +02001356 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 if (!buf) {
Johan Hovold255ab562011-03-22 11:12:13 +01001358 dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
flintman5c2f9732014-08-11 21:52:25 -04001359#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 goto alloc_fail2;
flintman5c2f9732014-08-11 21:52:25 -04001361#else
1362 /* MBM */
1363 goto alloc_fail3_1;
1364#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 }
1366 acm->ctrl_buffer = buf;
1367
Oliver Neukum884b6002005-04-21 21:28:02 +02001368 if (acm_write_buffers_alloc(acm) < 0) {
Johan Hovold255ab562011-03-22 11:12:13 +01001369 dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 goto alloc_fail4;
1371 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
1373 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1374 if (!acm->ctrlurb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001375 dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 goto alloc_fail5;
1377 }
Oliver Neukum86478942006-05-13 22:50:47 +02001378 for (i = 0; i < num_rx_buf; i++) {
Johan Hovold088c64f2011-03-25 11:06:02 +01001379 struct acm_rb *rb = &(acm->read_buffers[i]);
1380 struct urb *urb;
David Kubicek61a87ad2005-11-01 18:51:34 +01001381
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001382 rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
1383 &rb->dma);
1384 if (!rb->base) {
1385 dev_err(&intf->dev, "out of memory "
1386 "(read bufs usb_alloc_coherent)\n");
1387 goto alloc_fail6;
1388 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001389 rb->index = i;
1390 rb->instance = acm;
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001391
Johan Hovold088c64f2011-03-25 11:06:02 +01001392 urb = usb_alloc_urb(0, GFP_KERNEL);
1393 if (!urb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001394 dev_err(&intf->dev,
Alan Cox6e47e062009-06-11 12:37:06 +01001395 "out of memory (read urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001396 goto alloc_fail6;
David Kubicek61a87ad2005-11-01 18:51:34 +01001397 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001398 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1399 urb->transfer_dma = rb->dma;
1400 if (acm->is_int_ep) {
1401 usb_fill_int_urb(urb, acm->dev,
1402 acm->rx_endpoint,
1403 rb->base,
1404 acm->readsize,
1405 acm_read_bulk_callback, rb,
1406 acm->bInterval);
1407 } else {
1408 usb_fill_bulk_urb(urb, acm->dev,
1409 acm->rx_endpoint,
1410 rb->base,
1411 acm->readsize,
1412 acm_read_bulk_callback, rb);
1413 }
David Kubicek61a87ad2005-11-01 18:51:34 +01001414
Johan Hovold088c64f2011-03-25 11:06:02 +01001415 acm->read_urbs[i] = urb;
1416 __set_bit(i, &acm->read_urbs_free);
David Kubicek61a87ad2005-11-01 18:51:34 +01001417 }
Alan Cox6e47e062009-06-11 12:37:06 +01001418 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001419 struct acm_wb *snd = &(acm->wb[i]);
1420
Alan Cox6e47e062009-06-11 12:37:06 +01001421 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1422 if (snd->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001423 dev_err(&intf->dev,
Johan Hovold59d7fec2011-03-22 11:12:12 +01001424 "out of memory (write urbs usb_alloc_urb)\n");
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001425 goto alloc_fail7;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001426 }
1427
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001428 if (usb_endpoint_xfer_int(epwrite))
1429 usb_fill_int_urb(snd->urb, usb_dev,
Ming Leie83863d2012-10-16 21:21:21 +08001430 usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001431 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1432 else
1433 usb_fill_bulk_urb(snd->urb, usb_dev,
1434 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1435 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001436 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1437 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 }
1439
Alan Cox6e47e062009-06-11 12:37:06 +01001440 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001441
1442 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1443 if (i < 0)
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001444 goto alloc_fail7;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001445
1446 if (cfd) { /* export the country data */
1447 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1448 if (!acm->country_codes)
1449 goto skip_countries;
1450 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001451 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1452 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001453 acm->country_rel_date = cfd->iCountryCodeRelDate;
1454
1455 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1456 if (i < 0) {
1457 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001458 acm->country_codes = NULL;
1459 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001460 goto skip_countries;
1461 }
1462
Alan Cox6e47e062009-06-11 12:37:06 +01001463 i = device_create_file(&intf->dev,
1464 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001465 if (i < 0) {
Axel Linc2572b72010-05-31 08:04:47 +08001466 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001467 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001468 acm->country_codes = NULL;
1469 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001470 goto skip_countries;
1471 }
1472 }
1473
1474skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001475 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001476 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1477 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1478 /* works around buggy devices */
1479 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1481 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
flintman5c2f9732014-08-11 21:52:25 -04001482#ifdef CONFIG_MACH_TENDERLOIN
1483 /* MBM */
1484 acm->ctrlurb->dev = acm->dev;
1485 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
1486 dev_err(&intf->dev, "usb_submit_urb(ctrl irq) failed");
1487 goto kill_urb;
1488 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489
flintman5c2f9732014-08-11 21:52:25 -04001490 /* MBM */
1491 acm->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
1492 if (!acm->irq)
1493 goto kill_urb;
1494#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1496
1497 acm_set_control(acm, acm->ctrlout);
flintman5c2f9732014-08-11 21:52:25 -04001498#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 acm->line.dwDTERate = cpu_to_le32(9600);
flintman5c2f9732014-08-11 21:52:25 -04001500#else
1501 acm->line.dwDTERate = cpu_to_le32(115200);
1502#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 acm->line.bDataBits = 8;
1504 acm_set_line(acm, &acm->line);
flintman5c2f9732014-08-11 21:52:25 -04001505#ifdef CONFIG_MACH_TENDERLOIN
1506 /* MBM */
1507 acm->state |= ACM_ABS_IDLE;
1508 acm_set_comm_feature(acm, ACM_ABSTRACT_STATE, &acm->state);
1509#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510
1511 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001512 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001514 usb_get_intf(control_interface);
1515 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001517 return 0;
flintman5c2f9732014-08-11 21:52:25 -04001518#ifdef CONFIG_MACH_TENDERLOIN
1519 /* MBM */
1520kill_urb:
1521 usb_kill_urb(acm->ctrlurb);
1522#endif
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001523alloc_fail7:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001524 for (i = 0; i < ACM_NW; i++)
1525 usb_free_urb(acm->wb[i].urb);
Axel Linc2572b72010-05-31 08:04:47 +08001526alloc_fail6:
Oliver Neukum86478942006-05-13 22:50:47 +02001527 for (i = 0; i < num_rx_buf; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001528 usb_free_urb(acm->read_urbs[i]);
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001529 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 usb_free_urb(acm->ctrlurb);
1531alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001532 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533alloc_fail4:
Daniel Mack997ea582010-04-12 13:17:25 +02001534 usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
flintman5c2f9732014-08-11 21:52:25 -04001535#ifdef CONFIG_MACH_TENDERLOIN
1536 /* MBM */
1537alloc_fail3_1:
1538 usb_free_coherent(usb_dev,acm->bMaxPacketSize0, acm->inbuf, acm->response->transfer_dma);
1539alloc_fail3:
1540 usb_free_urb(acm->response);
1541#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542alloc_fail2:
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001543 acm_release_minor(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 kfree(acm);
1545alloc_fail:
1546 return -ENOMEM;
1547}
1548
Oliver Neukum1365baf2007-10-12 17:24:28 +02001549static void stop_data_traffic(struct acm *acm)
1550{
1551 int i;
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001552
1553 dev_dbg(&acm->control->dev, "%s\n", __func__);
flintman5c2f9732014-08-11 21:52:25 -04001554#ifndef CONFIG_MACH_TENDERLOIN
Oliver Neukum1365baf2007-10-12 17:24:28 +02001555 usb_kill_urb(acm->ctrlurb);
flintman5c2f9732014-08-11 21:52:25 -04001556#endif
Alan Cox6e47e062009-06-11 12:37:06 +01001557 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001558 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001559 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001560 usb_kill_urb(acm->read_urbs[i]);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001561
1562 cancel_work_sync(&acm->work);
1563}
1564
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565static void acm_disconnect(struct usb_interface *intf)
1566{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001567 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001569 struct tty_struct *tty;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001570 int i;
1571
1572 dev_dbg(&intf->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573
David Brownell672c4e12008-08-06 18:41:12 -07001574 /* sibling interface is already cleaning up */
1575 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001576 return;
David Brownell672c4e12008-08-06 18:41:12 -07001577
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001578 mutex_lock(&acm->mutex);
1579 acm->disconnected = true;
Alan Cox6e47e062009-06-11 12:37:06 +01001580 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001581 device_remove_file(&acm->control->dev,
1582 &dev_attr_wCountryCodes);
1583 device_remove_file(&acm->control->dev,
1584 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001585 }
Alan Stern74da5d62007-08-02 13:29:10 -04001586 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001587 usb_set_intfdata(acm->control, NULL);
1588 usb_set_intfdata(acm->data, NULL);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001589 mutex_unlock(&acm->mutex);
1590
1591 tty = tty_port_tty_get(&acm->port);
1592 if (tty) {
1593 tty_vhangup(tty);
1594 tty_kref_put(tty);
1595 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596
Oliver Neukum1365baf2007-10-12 17:24:28 +02001597 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598
Johan Hovold10a00e32013-03-19 09:21:06 +01001599 tty_unregister_device(acm_tty_driver, acm->minor);
1600
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001601 usb_free_urb(acm->ctrlurb);
1602 for (i = 0; i < ACM_NW; i++)
1603 usb_free_urb(acm->wb[i].urb);
1604 for (i = 0; i < acm->rx_buflimit; i++)
1605 usb_free_urb(acm->read_urbs[i]);
Oliver Neukum884b6002005-04-21 21:28:02 +02001606 acm_write_buffers_free(acm);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001607 usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001608 acm_read_buffers_free(acm);
flintman5c2f9732014-08-11 21:52:25 -04001609#ifdef CONFIG_MACH_TENDERLOIN
1610 /* MBM */
1611 usb_free_coherent(usb_dev,acm->bMaxPacketSize0, acm->inbuf, acm->response->transfer_dma);
1612#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001614 if (!acm->combined_interfaces)
1615 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001616 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001618 tty_port_put(&acm->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619}
1620
Oliver Neukum35758582008-07-01 19:10:08 +02001621#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001622static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1623{
1624 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001625 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001626
Oliver Neukum11ea8592008-06-20 11:25:57 +02001627 spin_lock_irq(&acm->read_lock);
1628 spin_lock(&acm->write_lock);
Johan Hovoldbe7978b2014-05-26 19:23:36 +02001629 if (PMSG_IS_AUTO(message)) {
1630 if (acm->transmitting) {
1631 spin_unlock(&acm->write_lock);
1632 spin_unlock_irq(&acm->read_lock);
1633 return -EBUSY;
1634 }
1635 }
Oliver Neukum11ea8592008-06-20 11:25:57 +02001636 cnt = acm->susp_count++;
1637 spin_unlock(&acm->write_lock);
1638 spin_unlock_irq(&acm->read_lock);
1639
1640 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001641 return 0;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001642
flintman5c2f9732014-08-11 21:52:25 -04001643#ifdef CONFIG_MACH_TENDERLOIN
1644 /* MBM, Kill URB here! */
1645 usb_kill_urb(acm->ctrlurb);
1646#endif
1647
Johan Hovold181614f2014-05-26 19:23:40 +02001648 stop_data_traffic(acm);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001649
Oliver Neukum1365baf2007-10-12 17:24:28 +02001650 return 0;
1651}
1652
1653static int acm_resume(struct usb_interface *intf)
1654{
1655 struct acm *acm = usb_get_intfdata(intf);
flintmand53cff62015-03-04 20:21:21 -05001656 struct acm_wb *wb;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001657 int rv = 0;
1658
Oliver Neukum11ea8592008-06-20 11:25:57 +02001659 spin_lock_irq(&acm->read_lock);
Johan Hovold97e72c42014-05-26 19:23:37 +02001660 spin_lock(&acm->write_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001661
Johan Hovold97e72c42014-05-26 19:23:37 +02001662 if (--acm->susp_count)
1663 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001664
flintman5c2f9732014-08-11 21:52:25 -04001665#ifdef CONFIG_MACH_TENDERLOIN
1666 /* MBM, We have to resubmit the INT URB regardless of the port is open or not */
1667 if (usb_submit_urb(acm->ctrlurb, GFP_ATOMIC)) {
1668 dev_err(&intf->dev, "usb_submit_urb(ctrl irq) failed");
1669 }
1670#endif
1671
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001672 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
flintman5c2f9732014-08-11 21:52:25 -04001673#ifndef CONFIG_MACH_TENDERLOIN
Johan Hovold97e72c42014-05-26 19:23:37 +02001674 rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
flintman5c2f9732014-08-11 21:52:25 -04001675#endif
Oliver Neukum97d35f92009-12-16 17:05:57 +01001676
flintmand53cff62015-03-04 20:21:21 -05001677 if (acm->delayed_wb) {
1678 wb = acm->delayed_wb;
1679 acm->delayed_wb = NULL;
1680 acm_start_wb(acm, wb);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001681 }
1682
1683 /*
1684 * delayed error checking because we must
1685 * do the write path at all cost
1686 */
Oliver Neukum1365baf2007-10-12 17:24:28 +02001687 if (rv < 0)
Johan Hovold97e72c42014-05-26 19:23:37 +02001688 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001689
Johan Hovold97e72c42014-05-26 19:23:37 +02001690 rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001691 }
Johan Hovold97e72c42014-05-26 19:23:37 +02001692out:
1693 spin_unlock(&acm->write_lock);
1694 spin_unlock_irq(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001695
Oliver Neukum1365baf2007-10-12 17:24:28 +02001696 return rv;
1697}
Oliver Neukum35758582008-07-01 19:10:08 +02001698
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001699static int acm_reset_resume(struct usb_interface *intf)
1700{
1701 struct acm *acm = usb_get_intfdata(intf);
1702 struct tty_struct *tty;
1703
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001704 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001705 tty = tty_port_tty_get(&acm->port);
1706 if (tty) {
1707 tty_hangup(tty);
1708 tty_kref_put(tty);
1709 }
1710 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001711
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001712 return acm_resume(intf);
1713}
1714
Oliver Neukum35758582008-07-01 19:10:08 +02001715#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001716
1717#define NOKIA_PCSUITE_ACM_INFO(x) \
1718 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1719 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1720 USB_CDC_ACM_PROTO_VENDOR)
1721
Toby Gray4035e452010-09-01 16:01:19 +01001722#define SAMSUNG_PCSUITE_ACM_INFO(x) \
1723 USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
1724 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1725 USB_CDC_ACM_PROTO_VENDOR)
1726
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727/*
1728 * USB driver structure.
1729 */
1730
Németh Márton6ef48522010-01-10 15:33:45 +01001731static const struct usb_device_id acm_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 /* quirky and broken devices */
David Cluytensbd73e382013-12-03 14:18:57 +01001733 { USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */
1734 .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1736 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1737 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001738 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1739 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1740 },
Andrew Lunn0f9c7b42008-12-23 17:31:23 +01001741 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1742 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1743 },
Masahito Omote8753e652005-07-29 12:17:25 -07001744 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1745 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1746 },
Chris Malley91a9c922006-10-03 10:08:28 +01001747 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1748 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1749 },
Alan Cox7abcf202009-04-06 17:35:01 +01001750 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1751 .driver_info = SINGLE_RX_URB,
1752 },
Oliver Neukum86478942006-05-13 22:50:47 +02001753 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1754 .driver_info = SINGLE_RX_URB, /* firmware bug */
1755 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001756 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1757 .driver_info = SINGLE_RX_URB, /* firmware bug */
1758 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001759 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1760 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1761 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001762 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1763 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1764 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001765 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1766 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1767 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001768 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1769 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1770 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001771 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1772 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1773 },
Johan Hovold1ff3bbb2014-10-27 18:34:33 +01001774 { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001775 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1776 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001777 /* Motorola H24 HSPA module: */
1778 { USB_DEVICE(0x22b8, 0x2d91) }, /* modem */
Michael Ulbrichtb08f08b2014-03-25 10:34:18 +01001779 { USB_DEVICE(0x22b8, 0x2d92), /* modem + diagnostics */
1780 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1781 },
1782 { USB_DEVICE(0x22b8, 0x2d93), /* modem + AT port */
1783 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1784 },
1785 { USB_DEVICE(0x22b8, 0x2d95), /* modem + AT port + diagnostics */
1786 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1787 },
1788 { USB_DEVICE(0x22b8, 0x2d96), /* modem + NMEA */
1789 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1790 },
1791 { USB_DEVICE(0x22b8, 0x2d97), /* modem + diagnostics + NMEA */
1792 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1793 },
1794 { USB_DEVICE(0x22b8, 0x2d99), /* modem + AT port + NMEA */
1795 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1796 },
1797 { USB_DEVICE(0x22b8, 0x2d9a), /* modem + AT port + diagnostics + NMEA */
1798 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1799 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001800
Adam Richterc332b4e2009-02-18 16:17:15 -08001801 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1802 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1803 data interface instead of
1804 communications interface.
1805 Maybe we should define a new
1806 quirk for this. */
1807 },
Jean-Christian de Rivaz52538352012-10-10 12:49:02 +00001808 { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */
1809 .driver_info = NO_UNION_NORMAL,
1810 },
Denis N Ladinb756d752012-12-26 18:29:44 +05001811 { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */
1812 .driver_info = NO_UNION_NORMAL,
1813 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001814 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1815 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1816 },
Russ Nelsonc3baa192010-04-21 23:07:03 -04001817 { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1818 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1819 },
flintman5c2f9732014-08-11 21:52:25 -04001820 /* MBM */
1821 { USB_DEVICE(0x1519, 0x0020), /* IMC_MAIN - XMM6260, XMM6262 */
1822 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1823 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001824
Adrian Taylorc1479a92009-11-19 10:35:33 +00001825 /* Nokia S60 phones expose two ACM channels. The first is
1826 * a modem and is picked up by the standard AT-command
1827 * information below. The second is 'vendor-specific' but
1828 * is treated as a serial device at the S60 end, so we want
1829 * to expose it on Linux too. */
1830 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1831 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1832 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1833 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1834 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1835 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1836 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1837 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1838 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1839 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1840 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1841 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1842 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1843 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1844 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1845 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1846 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1847 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1848 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1849 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1850 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1851 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1852 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1853 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1854 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1855 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1856 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1857 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1858 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1859 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1860 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1861 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1862 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1863 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1864 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1865 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1866 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1867 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1868 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1869 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1870 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1871 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1872 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
Przemo Firszt83a4eae2010-06-28 21:29:34 +01001873 { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
Toby Gray4035e452010-09-01 16:01:19 +01001874 { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
1875 { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
1876 { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
1877 { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
1878 { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
1879 { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
1880 { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
1881 { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
1882 { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
1883 { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
Arvid Ephraim Picciani721d92f2011-01-25 15:58:40 +01001884 { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
Toby Gray4061fde2011-06-06 14:52:48 +01001885 { NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */
1886 { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
Toby Gray4035e452010-09-01 16:01:19 +01001887 { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001888
Denis Pershin65e52f42011-09-04 17:37:21 +07001889 /* Support for Owen devices */
1890 { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */
1891
Adrian Taylorc1479a92009-11-19 10:35:33 +00001892 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1893
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001894 /* Support Lego NXT using pbLua firmware */
Julian Calabyce126642010-01-05 23:58:20 +11001895 { USB_DEVICE(0x0694, 0xff00),
1896 .driver_info = NOT_A_MODEM,
Otavio Salvador7893afc2010-09-26 23:35:05 -03001897 },
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001898
Erik Slagterfd5054c2011-05-11 12:06:55 +02001899 /* Support for Droids MuIn LCD */
1900 { USB_DEVICE(0x04d8, 0x000b),
1901 .driver_info = NO_DATA_INTERFACE,
1902 },
flintman5c2f9732014-08-11 21:52:25 -04001903#ifndef CONFIG_MACH_TENDERLOIN
Philippe Corbes5b239f02010-08-31 19:31:32 +02001904 /* control interfaces without any protocol set */
1905 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1906 USB_CDC_PROTO_NONE) },
flintman5c2f9732014-08-11 21:52:25 -04001907#else
1908 /* Exclude XMM6260 boot rom (not running modem software yet) */
1909 { USB_DEVICE(0x058b, 0x0041),
1910 .driver_info = NOT_REAL_ACM,
1911 },
1912#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 /* control interfaces with various AT-command sets */
1914 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1915 USB_CDC_ACM_PROTO_AT_V25TER) },
1916 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1917 USB_CDC_ACM_PROTO_AT_PCCA101) },
1918 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1919 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1920 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1921 USB_CDC_ACM_PROTO_AT_GSM) },
1922 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001923 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1925 USB_CDC_ACM_PROTO_AT_CDMA) },
1926
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927 { }
1928};
1929
Alan Cox6e47e062009-06-11 12:37:06 +01001930MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931
1932static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933 .name = "cdc_acm",
1934 .probe = acm_probe,
1935 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001936#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001937 .suspend = acm_suspend,
1938 .resume = acm_resume,
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001939 .reset_resume = acm_reset_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001940#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001942#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001943 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001944#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945};
1946
1947/*
1948 * TTY driver structures.
1949 */
1950
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001951static const struct tty_operations acm_ops = {
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001952 .install = acm_tty_install,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953 .open = acm_tty_open,
1954 .close = acm_tty_close,
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001955 .cleanup = acm_tty_cleanup,
Alan Cox10077d42009-06-11 12:36:09 +01001956 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957 .write = acm_tty_write,
1958 .write_room = acm_tty_write_room,
1959 .ioctl = acm_tty_ioctl,
1960 .throttle = acm_tty_throttle,
1961 .unthrottle = acm_tty_unthrottle,
1962 .chars_in_buffer = acm_tty_chars_in_buffer,
1963 .break_ctl = acm_tty_break_ctl,
1964 .set_termios = acm_tty_set_termios,
1965 .tiocmget = acm_tty_tiocmget,
1966 .tiocmset = acm_tty_tiocmset,
1967};
1968
1969/*
1970 * Init / exit.
1971 */
1972
1973static int __init acm_init(void)
1974{
1975 int retval;
1976 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1977 if (!acm_tty_driver)
1978 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979 acm_tty_driver->driver_name = "acm",
1980 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981 acm_tty_driver->major = ACM_TTY_MAJOR,
1982 acm_tty_driver->minor_start = 0,
1983 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1984 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001985 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001987 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1988 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989 tty_set_operations(acm_tty_driver, &acm_ops);
1990
1991 retval = tty_register_driver(acm_tty_driver);
1992 if (retval) {
1993 put_tty_driver(acm_tty_driver);
1994 return retval;
1995 }
1996
1997 retval = usb_register(&acm_driver);
1998 if (retval) {
1999 tty_unregister_driver(acm_tty_driver);
2000 put_tty_driver(acm_tty_driver);
2001 return retval;
2002 }
2003
Johan Hovolda2c7b932011-03-22 11:12:18 +01002004 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005
2006 return 0;
2007}
2008
2009static void __exit acm_exit(void)
2010{
2011 usb_deregister(&acm_driver);
2012 tty_unregister_driver(acm_tty_driver);
2013 put_tty_driver(acm_tty_driver);
2014}
2015
2016module_init(acm_init);
2017module_exit(acm_exit);
2018
Alan Cox6e47e062009-06-11 12:37:06 +01002019MODULE_AUTHOR(DRIVER_AUTHOR);
2020MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01002022MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);