blob: d7f23a5559b7c2597c6f5a431b6bc8287407edcc [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) {
Nick Reuter8f6a9072015-03-02 21:43:15 -0600265 usb_anchor_urb(wb->urb, &acm->delayed);
Oliver Neukum11ea8592008-06-20 11:25:57 +0200266 spin_unlock_irqrestore(&acm->write_lock, flags);
Nick Reuter8f6a9072015-03-02 21:43:15 -0600267 return 0;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200268 }
269 usb_mark_last_busy(acm->dev);
270
Oliver Neukum11ea8592008-06-20 11:25:57 +0200271 rc = acm_start_wb(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200272 spin_unlock_irqrestore(&acm->write_lock, flags);
273
Oliver Neukum884b6002005-04-21 21:28:02 +0200274 return rc;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200275
Oliver Neukum884b6002005-04-21 21:28:02 +0200276}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100277/*
278 * attributes exported through sysfs
279 */
280static ssize_t show_caps
281(struct device *dev, struct device_attribute *attr, char *buf)
282{
283 struct usb_interface *intf = to_usb_interface(dev);
284 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200285
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100286 return sprintf(buf, "%d", acm->ctrl_caps);
287}
288static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
289
290static ssize_t show_country_codes
291(struct device *dev, struct device_attribute *attr, char *buf)
292{
293 struct usb_interface *intf = to_usb_interface(dev);
294 struct acm *acm = usb_get_intfdata(intf);
295
296 memcpy(buf, acm->country_codes, acm->country_code_size);
297 return acm->country_code_size;
298}
299
300static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
301
302static ssize_t show_country_rel_date
303(struct device *dev, struct device_attribute *attr, char *buf)
304{
305 struct usb_interface *intf = to_usb_interface(dev);
306 struct acm *acm = usb_get_intfdata(intf);
307
308 return sprintf(buf, "%d", acm->country_rel_date);
309}
310
311static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200312/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 * Interrupt handlers for various ACM device responses
314 */
315
flintman5c2f9732014-08-11 21:52:25 -0400316/* MBM */
317static void acm_in_callback(struct urb *urb)
318{
319 struct acm *acm = urb->context;
320 int status = urb->status;
321
322 if (status) {
323 switch (status) {
324 case -ENOENT:
325 dev_dbg(&urb->dev->dev,
326 "nonzero urb status received: -ENOENT");
327 goto skip_error;
328 case -ECONNRESET:
329 dev_dbg(&urb->dev->dev,
330 "nonzero urb status received: -ECONNRESET");
331 goto skip_error;
332 case -ESHUTDOWN:
333 dev_dbg(&urb->dev->dev,
334 "nonzero urb status received: -ESHUTDOWN");
335 goto skip_error;
336 case -EPIPE:
337 dev_err(&urb->dev->dev,
338 "nonzero urb status received: -EPIPE\n");
339 break;
340 default:
341 dev_err(&urb->dev->dev,
342 "Unexpected error %d\n", status);
343 break;
344 }
345 }
346
347 dbg("unsupported encap: %s", (char *)acm->inbuf);
348skip_error:
349 dbg("");
350}
351
352
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100354static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
356 struct acm *acm = urb->context;
357 struct usb_cdc_notification *dr = urb->transfer_buffer;
flintman5c2f9732014-08-11 21:52:25 -0400358 /* MBM */
359 struct usb_ctrlrequest *req = acm->irq;
Alan Cox10077d42009-06-11 12:36:09 +0100360 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 unsigned char *data;
362 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700363 int retval;
364 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700366 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 case 0:
368 /* success */
369 break;
370 case -ECONNRESET:
371 case -ENOENT:
372 case -ESHUTDOWN:
373 /* this urb is terminated, clean up */
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100374 dev_dbg(&acm->control->dev,
375 "%s - urb shutting down with status: %d\n",
376 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 return;
378 default:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100379 dev_dbg(&acm->control->dev,
380 "%s - nonzero urb status received: %d\n",
381 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 goto exit;
383 }
384
Johan Hovold7e7797e2011-03-22 11:12:11 +0100385 usb_mark_last_busy(acm->dev);
386
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 data = (unsigned char *)(dr + 1);
388 switch (dr->bNotificationType) {
Alan Cox6e47e062009-06-11 12:37:06 +0100389 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
flintman5c2f9732014-08-11 21:52:25 -0400390 /* MBM */
391 dev_info(&urb->dev->dev, "%s network", dr->wValue ?
392 "connected to" : "disconnected from");
Alan Cox6e47e062009-06-11 12:37:06 +0100393 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394
Alan Cox6e47e062009-06-11 12:37:06 +0100395 case USB_CDC_NOTIFY_SERIAL_STATE:
396 tty = tty_port_tty_get(&acm->port);
397 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398
Alan Cox6e47e062009-06-11 12:37:06 +0100399 if (tty) {
400 if (!acm->clocal &&
401 (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100402 dev_dbg(&acm->control->dev,
403 "%s - calling hangup\n", __func__);
Alan Cox6e47e062009-06-11 12:37:06 +0100404 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 }
Alan Cox6e47e062009-06-11 12:37:06 +0100406 tty_kref_put(tty);
407 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
Alan Cox6e47e062009-06-11 12:37:06 +0100409 acm->ctrlin = newctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100411 dev_dbg(&acm->control->dev,
412 "%s - input control lines: dcd%c dsr%c break%c "
413 "ring%c framing%c parity%c overrun%c\n",
414 __func__,
Alan Cox6e47e062009-06-11 12:37:06 +0100415 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
416 acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
417 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
418 acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
419 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
420 acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
421 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 break;
423
flintman5c2f9732014-08-11 21:52:25 -0400424 /* MBM */
425 case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
426 dev_err(&urb->dev->dev, "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
427 dr->wIndex, dr->wLength);
428 req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
429 req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
430 req->wValue = 0;
431 req->wIndex = cpu_to_le16(dr->wIndex);
432 req->wLength = cpu_to_le16(acm->bMaxPacketSize0);
433
434 usb_fill_control_urb(
435 acm->response,
436 acm->dev,
437 usb_sndctrlpipe(acm->dev, 0),
438 (unsigned char *)req,
439 acm->inbuf,
440 acm->bMaxPacketSize0,
441 acm_in_callback,
442 acm);
443
444 break;
445
Alan Cox6e47e062009-06-11 12:37:06 +0100446 default:
flintman5c2f9732014-08-11 21:52:25 -0400447 /* MBM */
448 dev_info(&urb->dev->dev,"unknown notification %d received: index %d len %d data0 %d data1 %d",
Alan Cox6e47e062009-06-11 12:37:06 +0100449 dr->bNotificationType, dr->wIndex,
450 dr->wLength, data[0], data[1]);
451 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 }
453exit:
Alan Cox6e47e062009-06-11 12:37:06 +0100454 retval = usb_submit_urb(urb, GFP_ATOMIC);
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700455 if (retval)
Johan Hovold1d9846e2011-03-22 11:12:14 +0100456 dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
457 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458}
459
Johan Hovold088c64f2011-03-25 11:06:02 +0100460static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461{
Johan Hovold088c64f2011-03-25 11:06:02 +0100462 int res;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700463
Johan Hovold088c64f2011-03-25 11:06:02 +0100464 if (!test_and_clear_bit(index, &acm->read_urbs_free))
465 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466
Johan Hovold088c64f2011-03-25 11:06:02 +0100467 dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
468
469 res = usb_submit_urb(acm->read_urbs[index], mem_flags);
470 if (res) {
471 if (res != -EPERM) {
472 dev_err(&acm->data->dev,
473 "%s - usb_submit_urb failed: %d\n",
474 __func__, res);
475 }
476 set_bit(index, &acm->read_urbs_free);
477 return res;
478 }
479
480 return 0;
481}
482
483static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
484{
485 int res;
486 int i;
487
488 for (i = 0; i < acm->rx_buflimit; ++i) {
489 res = acm_submit_read_urb(acm, i, mem_flags);
490 if (res)
491 return res;
492 }
493
494 return 0;
495}
496
497static void acm_process_read_urb(struct acm *acm, struct urb *urb)
498{
499 struct tty_struct *tty;
500
501 if (!urb->actual_length)
502 return;
503
504 tty = tty_port_tty_get(&acm->port);
505 if (!tty)
506 return;
507
508 tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
509 tty_flip_buffer_push(tty);
510
511 tty_kref_put(tty);
512}
513
514static void acm_read_bulk_callback(struct urb *urb)
515{
516 struct acm_rb *rb = urb->context;
517 struct acm *acm = rb->instance;
518 unsigned long flags;
519
520 dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
521 rb->index, urb->actual_length);
522 set_bit(rb->index, &acm->read_urbs_free);
523
524 if (!acm->dev) {
525 dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 return;
Oliver Neukum11ea8592008-06-20 11:25:57 +0200527 }
528 usb_mark_last_busy(acm->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529
Johan Hovold088c64f2011-03-25 11:06:02 +0100530 if (urb->status) {
Johan Hovold1d9846e2011-03-22 11:12:14 +0100531 dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
Johan Hovold088c64f2011-03-25 11:06:02 +0100532 __func__, urb->status);
533 return;
534 }
535 acm_process_read_urb(acm, urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Johan Hovold088c64f2011-03-25 11:06:02 +0100537 /* throttle device if requested by tty */
538 spin_lock_irqsave(&acm->read_lock, flags);
539 acm->throttled = acm->throttle_req;
540 if (!acm->throttled && !acm->susp_count) {
541 spin_unlock_irqrestore(&acm->read_lock, flags);
542 acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
Oliver Neukum86478942006-05-13 22:50:47 +0200543 } else {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200544 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546}
547
548/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100549static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550{
Ming Leicdc97792008-02-24 18:41:47 +0800551 struct acm_wb *wb = urb->context;
David Brownelle5fbab52008-08-06 18:46:10 -0700552 struct acm *acm = wb->instance;
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800553 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200554
Johan Hovold4fa46262011-03-22 11:12:16 +0100555 if (urb->status || (urb->actual_length != urb->transfer_buffer_length))
556 dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
Johan Hovold1d9846e2011-03-22 11:12:14 +0100557 __func__,
David Brownelle5fbab52008-08-06 18:46:10 -0700558 urb->actual_length,
559 urb->transfer_buffer_length,
560 urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800562 spin_lock_irqsave(&acm->write_lock, flags);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100563 acm_write_done(acm, wb);
Brandon Philipsad0b65e2008-11-06 11:19:11 -0800564 spin_unlock_irqrestore(&acm->write_lock, flags);
Havard Skinnemoen99823f42011-12-09 16:51:54 -0800565 schedule_work(&acm->work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566}
567
David Howellsc4028952006-11-22 14:57:56 +0000568static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
David Howellsc4028952006-11-22 14:57:56 +0000570 struct acm *acm = container_of(work, struct acm, work);
Alan Cox10077d42009-06-11 12:36:09 +0100571 struct tty_struct *tty;
David Brownelle5fbab52008-08-06 18:46:10 -0700572
Johan Hovold1d9846e2011-03-22 11:12:14 +0100573 dev_vdbg(&acm->data->dev, "%s\n", __func__);
574
Alan Cox10077d42009-06-11 12:36:09 +0100575 tty = tty_port_tty_get(&acm->port);
Johan Hovold15e5bee2011-03-22 11:12:10 +0100576 if (!tty)
577 return;
Alan Cox10077d42009-06-11 12:36:09 +0100578 tty_wakeup(tty);
579 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580}
581
582/*
583 * TTY handlers
584 */
585
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800586static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587{
588 struct acm *acm;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800589 int retval;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100590
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800591 dev_dbg(tty->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800593 acm = acm_get_by_index(tty->index);
594 if (!acm)
595 return -ENODEV;
596
Jiri Slabyf8a8c102012-03-05 14:51:48 +0100597 retval = tty_standard_install(driver, tty);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800598 if (retval)
599 goto error_init_termios;
600
601 tty->driver_data = acm;
602
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800603 return 0;
604
605error_init_termios:
606 tty_port_put(&acm->port);
607 return retval;
608}
609
610static int acm_tty_open(struct tty_struct *tty, struct file *filp)
611{
612 struct acm *acm = tty->driver_data;
613
614 dev_dbg(tty->dev, "%s\n", __func__);
615
616 return tty_port_open(&acm->port, tty, filp);
617}
618
619static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
620{
621 struct acm *acm = container_of(port, struct acm, port);
622 int retval = -ENODEV;
Johan Hovold8fd20d52014-05-26 19:23:44 +0200623 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100625 dev_dbg(&acm->control->dev, "%s\n", __func__);
626
Oliver Neukum1365baf2007-10-12 17:24:28 +0200627 mutex_lock(&acm->mutex);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800628 if (acm->disconnected)
629 goto disconnected;
630
631 retval = usb_autopm_get_interface(acm->control);
632 if (retval)
633 goto error_get_interface;
634
635 /*
636 * FIXME: Why do we need this? Allocating 64K of physically contiguous
637 * memory is really nasty...
638 */
639 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
640 acm->control->needs_remote_wakeup = 1;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200641
flintman5c2f9732014-08-11 21:52:25 -0400642#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 acm->ctrlurb->dev = acm->dev;
644 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100645 dev_err(&acm->control->dev,
646 "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800647 goto error_submit_urb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 }
flintman5c2f9732014-08-11 21:52:25 -0400649#endif
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800650 acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS;
651 if (acm_set_control(acm, acm->ctrlout) < 0 &&
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100652 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800653 goto error_set_control;
Alan Cox10077d42009-06-11 12:36:09 +0100654
flintman5c2f9732014-08-11 21:52:25 -0400655 /* MBM */
656 acm->state &= ~ACM_ABS_IDLE;
657 if (0 > acm_set_comm_feature(acm, ACM_ABSTRACT_STATE, &acm->state) &&
658 (acm->ctrl_caps & USB_CDC_COMM_FEATURE))
659 goto error_set_control;
660
Oliver Neukum11ea8592008-06-20 11:25:57 +0200661 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662
Otto Meta9cc2d462012-06-06 18:46:21 +0200663 /*
664 * Unthrottle device in case the TTY was closed while throttled.
665 */
666 spin_lock_irq(&acm->read_lock);
667 acm->throttled = 0;
668 acm->throttle_req = 0;
669 spin_unlock_irq(&acm->read_lock);
670
Johan Hovold088c64f2011-03-25 11:06:02 +0100671 if (acm_submit_read_urbs(acm, GFP_KERNEL))
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800672 goto error_submit_read_urbs;
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100673
Oliver Neukum1365baf2007-10-12 17:24:28 +0200674 mutex_unlock(&acm->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800676 return 0;
677
678error_submit_read_urbs:
Johan Hovold8fd20d52014-05-26 19:23:44 +0200679 for (i = 0; i < acm->rx_buflimit; i++)
680 usb_kill_urb(acm->read_urbs[i]);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800681 acm->ctrlout = 0;
682 acm_set_control(acm, acm->ctrlout);
683error_set_control:
684 usb_kill_urb(acm->ctrlurb);
flintman5c2f9732014-08-11 21:52:25 -0400685#ifndef CONFIG_MACH_TENDERLOIN
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800686error_submit_urb:
Oliver Neukum2b626dc2010-02-03 17:10:22 +0100687 usb_autopm_put_interface(acm->control);
flintman5c2f9732014-08-11 21:52:25 -0400688#endif
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800689error_get_interface:
690disconnected:
691 mutex_unlock(&acm->mutex);
692 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693}
694
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800695static void acm_port_destruct(struct tty_port *port)
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700696{
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800697 struct acm *acm = container_of(port, struct acm, port);
698
699 dev_dbg(&acm->control->dev, "%s\n", __func__);
David Kubicek61a87ad2005-11-01 18:51:34 +0100700
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800701 acm_release_minor(acm);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700702 usb_put_intf(acm->control);
flintman5c2f9732014-08-11 21:52:25 -0400703 /* MBM */
704 usb_free_urb(acm->response);
705 kfree(acm->irq);
706
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100707 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700708 kfree(acm);
709}
710
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800711static void acm_port_shutdown(struct tty_port *port)
Alan Cox10077d42009-06-11 12:36:09 +0100712{
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800713 struct acm *acm = container_of(port, struct acm, port);
Johan Hovold21ffba32014-05-26 19:23:38 +0200714 struct urb *urb;
715 struct acm_wb *wb;
Johan Hovolddab54c92011-03-22 11:12:21 +0100716 int i;
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200717 int pm_err;
Johan Hovolddab54c92011-03-22 11:12:21 +0100718
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800719 dev_dbg(&acm->control->dev, "%s\n", __func__);
720
721 mutex_lock(&acm->mutex);
722 if (!acm->disconnected) {
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200723 pm_err = usb_autopm_get_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100724 acm_set_control(acm, acm->ctrlout = 0);
Johan Hovold21ffba32014-05-26 19:23:38 +0200725
726 for (;;) {
727 urb = usb_get_from_anchor(&acm->delayed);
728 if (!urb)
729 break;
730 wb = urb->context;
731 wb->use = 0;
732 usb_autopm_put_interface_async(acm->control);
733 }
734
Alan Cox10077d42009-06-11 12:36:09 +0100735 usb_kill_urb(acm->ctrlurb);
736 for (i = 0; i < ACM_NW; i++)
737 usb_kill_urb(acm->wb[i].urb);
Johan Hovolddab54c92011-03-22 11:12:21 +0100738 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +0100739 usb_kill_urb(acm->read_urbs[i]);
Alan Cox10077d42009-06-11 12:36:09 +0100740 acm->control->needs_remote_wakeup = 0;
Johan Hovold7be9d6c2014-05-26 19:23:45 +0200741 if (!pm_err)
742 usb_autopm_put_interface(acm->control);
Alan Cox10077d42009-06-11 12:36:09 +0100743 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800744 mutex_unlock(&acm->mutex);
745}
746
747static void acm_tty_cleanup(struct tty_struct *tty)
748{
749 struct acm *acm = tty->driver_data;
750 dev_dbg(&acm->control->dev, "%s\n", __func__);
751 tty_port_put(&acm->port);
Alan Cox10077d42009-06-11 12:36:09 +0100752}
753
754static void acm_tty_hangup(struct tty_struct *tty)
755{
756 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800757 dev_dbg(&acm->control->dev, "%s\n", __func__);
Alan Cox10077d42009-06-11 12:36:09 +0100758 tty_port_hangup(&acm->port);
Alan Cox10077d42009-06-11 12:36:09 +0100759}
760
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761static void acm_tty_close(struct tty_struct *tty, struct file *filp)
762{
763 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800764 dev_dbg(&acm->control->dev, "%s\n", __func__);
765 tty_port_close(&acm->port, tty, filp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766}
767
Alan Cox6e47e062009-06-11 12:37:06 +0100768static int acm_tty_write(struct tty_struct *tty,
769 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770{
771 struct acm *acm = tty->driver_data;
772 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200773 unsigned long flags;
774 int wbn;
775 struct acm_wb *wb;
776
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 if (!count)
778 return 0;
779
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100780 dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100781
Oliver Neukum884b6002005-04-21 21:28:02 +0200782 spin_lock_irqsave(&acm->write_lock, flags);
Alan Cox6e47e062009-06-11 12:37:06 +0100783 wbn = acm_wb_alloc(acm);
784 if (wbn < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200785 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200786 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200788 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789
Oliver Neukum884b6002005-04-21 21:28:02 +0200790 count = (count > acm->writesize) ? acm->writesize : count;
Johan Hovold5e9e75f2011-03-22 11:12:17 +0100791 dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
Oliver Neukum884b6002005-04-21 21:28:02 +0200792 memcpy(wb->buf, buf, count);
793 wb->len = count;
794 spin_unlock_irqrestore(&acm->write_lock, flags);
795
Alan Cox6e47e062009-06-11 12:37:06 +0100796 stat = acm_write_start(acm, wbn);
797 if (stat < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200798 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 return count;
800}
801
802static int acm_tty_write_room(struct tty_struct *tty)
803{
804 struct acm *acm = tty->driver_data;
Oliver Neukum884b6002005-04-21 21:28:02 +0200805 /*
806 * Do not let the line discipline to know that we have a reserve,
807 * or it might get too enthusiastic.
808 */
David Brownell934da462008-08-06 18:44:12 -0700809 return acm_wb_is_avail(acm) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810}
811
812static int acm_tty_chars_in_buffer(struct tty_struct *tty)
813{
814 struct acm *acm = tty->driver_data;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800815 /*
816 * if the device was unplugged then any remaining characters fell out
817 * of the connector ;)
818 */
819 if (acm->disconnected)
Alan Cox23198fd2009-07-20 16:05:27 +0100820 return 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200821 /*
822 * This is inaccurate (overcounts), but it works.
823 */
Oliver Neukum86478942006-05-13 22:50:47 +0200824 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825}
826
827static void acm_tty_throttle(struct tty_struct *tty)
828{
829 struct acm *acm = tty->driver_data;
Johan Hovold088c64f2011-03-25 11:06:02 +0100830
Johan Hovold088c64f2011-03-25 11:06:02 +0100831 spin_lock_irq(&acm->read_lock);
832 acm->throttle_req = 1;
833 spin_unlock_irq(&acm->read_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834}
835
836static void acm_tty_unthrottle(struct tty_struct *tty)
837{
838 struct acm *acm = tty->driver_data;
Johan Hovold088c64f2011-03-25 11:06:02 +0100839 unsigned int was_throttled;
840
Johan Hovold088c64f2011-03-25 11:06:02 +0100841 spin_lock_irq(&acm->read_lock);
842 was_throttled = acm->throttled;
843 acm->throttled = 0;
844 acm->throttle_req = 0;
845 spin_unlock_irq(&acm->read_lock);
846
847 if (was_throttled)
848 acm_submit_read_urbs(acm, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849}
850
Alan Cox9e989662008-07-22 11:18:03 +0100851static int acm_tty_break_ctl(struct tty_struct *tty, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852{
853 struct acm *acm = tty->driver_data;
Alan Cox9e989662008-07-22 11:18:03 +0100854 int retval;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -0800855
Alan Cox9e989662008-07-22 11:18:03 +0100856 retval = acm_send_break(acm, state ? 0xffff : 0);
857 if (retval < 0)
Johan Hovolda5cc7ef2011-03-22 11:12:15 +0100858 dev_dbg(&acm->control->dev, "%s - send break failed\n",
859 __func__);
Alan Cox9e989662008-07-22 11:18:03 +0100860 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861}
862
Alan Cox60b33c12011-02-14 16:26:14 +0000863static int acm_tty_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864{
865 struct acm *acm = tty->driver_data;
866
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
868 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
869 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
870 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
871 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
872 TIOCM_CTS;
873}
874
Alan Cox20b9d172011-02-14 16:26:50 +0000875static int acm_tty_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 unsigned int set, unsigned int clear)
877{
878 struct acm *acm = tty->driver_data;
879 unsigned int newctrl;
880
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 newctrl = acm->ctrlout;
Alan Cox6e47e062009-06-11 12:37:06 +0100882 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
883 (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
884 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
885 (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886
887 newctrl = (newctrl & ~clear) | set;
888
889 if (acm->ctrlout == newctrl)
890 return 0;
891 return acm_set_control(acm, acm->ctrlout = newctrl);
892}
893
Oliver Neukum18c75722012-02-17 17:21:24 -0500894static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
895{
896 struct serial_struct tmp;
897
898 if (!info)
899 return -EINVAL;
900
901 memset(&tmp, 0, sizeof(tmp));
902 tmp.flags = ASYNC_LOW_LATENCY;
903 tmp.xmit_fifo_size = acm->writesize;
904 tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
Dan Williams1229a832012-11-08 12:47:41 -0600905 tmp.close_delay = acm->port.close_delay / 10;
906 tmp.closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
907 ASYNC_CLOSING_WAIT_NONE :
908 acm->port.closing_wait / 10;
Oliver Neukum18c75722012-02-17 17:21:24 -0500909
910 if (copy_to_user(info, &tmp, sizeof(tmp)))
911 return -EFAULT;
912 else
913 return 0;
914}
915
Dan Williams1229a832012-11-08 12:47:41 -0600916static int set_serial_info(struct acm *acm,
917 struct serial_struct __user *newinfo)
918{
919 struct serial_struct new_serial;
920 unsigned int closing_wait, close_delay;
921 int retval = 0;
922
923 if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
924 return -EFAULT;
925
926 close_delay = new_serial.close_delay * 10;
927 closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
928 ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
929
930 mutex_lock(&acm->port.mutex);
931
932 if (!capable(CAP_SYS_ADMIN)) {
933 if ((close_delay != acm->port.close_delay) ||
934 (closing_wait != acm->port.closing_wait))
935 retval = -EPERM;
936 else
937 retval = -EOPNOTSUPP;
938 } else {
939 acm->port.close_delay = close_delay;
940 acm->port.closing_wait = closing_wait;
941 }
942
943 mutex_unlock(&acm->port.mutex);
944 return retval;
945}
946
Alan Cox6caa76b2011-02-14 16:27:22 +0000947static int acm_tty_ioctl(struct tty_struct *tty,
Alan Cox6e47e062009-06-11 12:37:06 +0100948 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949{
Oliver Neukum18c75722012-02-17 17:21:24 -0500950 struct acm *acm = tty->driver_data;
951 int rv = -ENOIOCTLCMD;
952
953 switch (cmd) {
954 case TIOCGSERIAL: /* gets serial port data */
955 rv = get_serial_info(acm, (struct serial_struct __user *) arg);
956 break;
Dan Williams1229a832012-11-08 12:47:41 -0600957 case TIOCSSERIAL:
958 rv = set_serial_info(acm, (struct serial_struct __user *) arg);
959 break;
Oliver Neukum18c75722012-02-17 17:21:24 -0500960 }
961
962 return rv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963}
964
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100965static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 0, 50, 75, 110, 134, 150, 200, 300, 600,
967 1200, 1800, 2400, 4800, 9600, 19200, 38400,
968 57600, 115200, 230400, 460800, 500000, 576000,
969 921600, 1000000, 1152000, 1500000, 2000000,
970 2500000, 3000000, 3500000, 4000000
971};
972
Alan Cox6e47e062009-06-11 12:37:06 +0100973static void acm_tty_set_termios(struct tty_struct *tty,
974 struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975{
976 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800977 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 struct usb_cdc_line_coding newline;
979 int newctrl = acm->ctrlout;
980
Alan Cox9b80fee2009-09-19 13:13:23 -0700981 newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
983 newline.bParityType = termios->c_cflag & PARENB ?
Alan Cox6e47e062009-06-11 12:37:06 +0100984 (termios->c_cflag & PARODD ? 1 : 2) +
985 (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
Nicolas Boullisa5204462012-10-16 00:06:23 +0200986 switch (termios->c_cflag & CSIZE) {
987 case CS5:
988 newline.bDataBits = 5;
989 break;
990 case CS6:
991 newline.bDataBits = 6;
992 break;
993 case CS7:
994 newline.bDataBits = 7;
995 break;
996 case CS8:
997 default:
998 newline.bDataBits = 8;
999 break;
1000 }
Alan Cox6e47e062009-06-11 12:37:06 +01001001 /* FIXME: Needs to clear unsupported bits in the termios */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
1003
1004 if (!newline.dwDTERate) {
1005 newline.dwDTERate = acm->line.dwDTERate;
1006 newctrl &= ~ACM_CTRL_DTR;
Alan Cox6e47e062009-06-11 12:37:06 +01001007 } else
1008 newctrl |= ACM_CTRL_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009
1010 if (newctrl != acm->ctrlout)
1011 acm_set_control(acm, acm->ctrlout = newctrl);
1012
1013 if (memcmp(&acm->line, &newline, sizeof newline)) {
1014 memcpy(&acm->line, &newline, sizeof newline);
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001015 dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
1016 __func__,
1017 le32_to_cpu(newline.dwDTERate),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 newline.bCharFormat, newline.bParityType,
1019 newline.bDataBits);
1020 acm_set_line(acm, &acm->line);
1021 }
1022}
1023
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001024static const struct tty_port_operations acm_port_ops = {
1025 .shutdown = acm_port_shutdown,
1026 .activate = acm_port_activate,
1027 .destruct = acm_port_destruct,
1028};
1029
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030/*
1031 * USB probe and disconnect routines.
1032 */
1033
Oliver Neukum830f4022008-06-25 14:17:16 +02001034/* Little helpers: write/read buffers free */
Oliver Neukum884b6002005-04-21 21:28:02 +02001035static void acm_write_buffers_free(struct acm *acm)
1036{
1037 int i;
1038 struct acm_wb *wb;
Oliver Neukuma496c642008-10-21 10:39:04 +02001039 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Oliver Neukum884b6002005-04-21 21:28:02 +02001040
Alan Cox6e47e062009-06-11 12:37:06 +01001041 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
Daniel Mack997ea582010-04-12 13:17:25 +02001042 usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
Oliver Neukum884b6002005-04-21 21:28:02 +02001043}
1044
Oliver Neukum830f4022008-06-25 14:17:16 +02001045static void acm_read_buffers_free(struct acm *acm)
1046{
1047 struct usb_device *usb_dev = interface_to_usbdev(acm->control);
Johan Hovolddab54c92011-03-22 11:12:21 +01001048 int i;
Oliver Neukum830f4022008-06-25 14:17:16 +02001049
Johan Hovolddab54c92011-03-22 11:12:21 +01001050 for (i = 0; i < acm->rx_buflimit; i++)
Daniel Mack997ea582010-04-12 13:17:25 +02001051 usb_free_coherent(usb_dev, acm->readsize,
Johan Hovold088c64f2011-03-25 11:06:02 +01001052 acm->read_buffers[i].base, acm->read_buffers[i].dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001053}
1054
Oliver Neukum884b6002005-04-21 21:28:02 +02001055/* Little helper: write buffers allocate */
1056static int acm_write_buffers_alloc(struct acm *acm)
1057{
1058 int i;
1059 struct acm_wb *wb;
1060
Oliver Neukum86478942006-05-13 22:50:47 +02001061 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Daniel Mack997ea582010-04-12 13:17:25 +02001062 wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
Oliver Neukum884b6002005-04-21 21:28:02 +02001063 &wb->dmah);
1064 if (!wb->buf) {
1065 while (i != 0) {
1066 --i;
1067 --wb;
Daniel Mack997ea582010-04-12 13:17:25 +02001068 usb_free_coherent(acm->dev, acm->writesize,
Oliver Neukum884b6002005-04-21 21:28:02 +02001069 wb->buf, wb->dmah);
1070 }
1071 return -ENOMEM;
1072 }
1073 }
1074 return 0;
1075}
1076
Alan Cox10077d42009-06-11 12:36:09 +01001077static int acm_probe(struct usb_interface *intf,
1078 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079{
1080 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001081 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -07001082 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 int buflen = intf->altsetting->extralen;
1084 struct usb_interface *control_interface;
1085 struct usb_interface *data_interface;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001086 struct usb_endpoint_descriptor *epctrl = NULL;
1087 struct usb_endpoint_descriptor *epread = NULL;
1088 struct usb_endpoint_descriptor *epwrite = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 struct usb_device *usb_dev = interface_to_usbdev(intf);
1090 struct acm *acm;
1091 int minor;
Alan Cox6e47e062009-06-11 12:37:06 +01001092 int ctrlsize, readsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 u8 *buf;
1094 u8 ac_management_function = 0;
1095 u8 call_management_function = 0;
1096 int call_interface_num = -1;
Erik Slagterfd5054c2011-05-11 12:06:55 +02001097 int data_interface_num = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +02001099 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +01001100 int i;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001101 int combined_interfaces = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102
Oliver Neukum86478942006-05-13 22:50:47 +02001103 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +02001105 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
1106
flintman5c2f9732014-08-11 21:52:25 -04001107 /* MBM, Not a real CDC ACM device */
1108 if (quirks == NOT_REAL_ACM)
1109 return -ENODEV;
1110
Oliver Neukum86478942006-05-13 22:50:47 +02001111 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 if (quirks == NO_UNION_NORMAL) {
1113 data_interface = usb_ifnum_to_if(usb_dev, 1);
1114 control_interface = usb_ifnum_to_if(usb_dev, 0);
1115 goto skip_normal_probe;
1116 }
Alan Cox6e47e062009-06-11 12:37:06 +01001117
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 /* normal probing*/
1119 if (!buffer) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001120 dev_err(&intf->dev, "Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 return -EINVAL;
1122 }
1123
1124 if (!buflen) {
flintman5c2f9732014-08-11 21:52:25 -04001125#ifndef CONFIG_MACH_TENDERLOIN
Toby Gray577045c2010-09-02 10:46:20 +01001126 if (intf->cur_altsetting->endpoint &&
1127 intf->cur_altsetting->endpoint->extralen &&
Alan Cox6e47e062009-06-11 12:37:06 +01001128 intf->cur_altsetting->endpoint->extra) {
flintman5c2f9732014-08-11 21:52:25 -04001129#else
1130 /* MBM */
1131 if (intf->cur_altsetting->endpoint->extralen &&
1132 intf->cur_altsetting->endpoint->extra) {
1133#endif
Alan Cox6e47e062009-06-11 12:37:06 +01001134 dev_dbg(&intf->dev,
1135 "Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 buflen = intf->cur_altsetting->endpoint->extralen;
1137 buffer = intf->cur_altsetting->endpoint->extra;
1138 } else {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001139 dev_err(&intf->dev,
1140 "Zero length descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 return -EINVAL;
1142 }
1143 }
1144
1145 while (buflen > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001146 if (buffer[1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -07001147 dev_err(&intf->dev, "skipping garbage\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 goto next_desc;
1149 }
1150
Alan Cox6e47e062009-06-11 12:37:06 +01001151 switch (buffer[2]) {
1152 case USB_CDC_UNION_TYPE: /* we've found it */
1153 if (union_header) {
1154 dev_err(&intf->dev, "More than one "
1155 "union descriptor, skipping ...\n");
1156 goto next_desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 }
Alan Cox6e47e062009-06-11 12:37:06 +01001158 union_header = (struct usb_cdc_union_desc *)buffer;
1159 break;
1160 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
1161 cfd = (struct usb_cdc_country_functional_desc *)buffer;
1162 break;
1163 case USB_CDC_HEADER_TYPE: /* maybe check version */
1164 break; /* for now we ignore it */
1165 case USB_CDC_ACM_TYPE:
1166 ac_management_function = buffer[3];
1167 break;
1168 case USB_CDC_CALL_MANAGEMENT_TYPE:
1169 call_management_function = buffer[3];
1170 call_interface_num = buffer[4];
Julian Calabyce126642010-01-05 23:58:20 +11001171 if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
Alan Cox6e47e062009-06-11 12:37:06 +01001172 dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
1173 break;
1174 default:
1175 /* there are LOTS more CDC descriptors that
1176 * could legitimately be found here.
1177 */
1178 dev_dbg(&intf->dev, "Ignoring descriptor: "
1179 "type %02x, length %d\n",
1180 buffer[2], buffer[0]);
1181 break;
1182 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183next_desc:
1184 buflen -= buffer[0];
1185 buffer += buffer[0];
1186 }
1187
1188 if (!union_header) {
1189 if (call_interface_num > 0) {
Alan Cox6e47e062009-06-11 12:37:06 +01001190 dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
Erik Slagterfd5054c2011-05-11 12:06:55 +02001191 /* quirks for Droids MuIn LCD */
1192 if (quirks & NO_DATA_INTERFACE)
1193 data_interface = usb_ifnum_to_if(usb_dev, 0);
1194 else
1195 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 control_interface = intf;
1197 } else {
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001198 if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1199 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1200 return -ENODEV;
1201 } else {
1202 dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1203 combined_interfaces = 1;
1204 control_interface = data_interface = intf;
1205 goto look_for_collapsed_interface;
1206 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 }
1208 } else {
1209 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
1210 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
1211 if (!control_interface || !data_interface) {
Alan Cox6e47e062009-06-11 12:37:06 +01001212 dev_dbg(&intf->dev, "no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 return -ENODEV;
1214 }
1215 }
Alan Cox6e47e062009-06-11 12:37:06 +01001216
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 if (data_interface_num != call_interface_num)
Alan Cox6e47e062009-06-11 12:37:06 +01001218 dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001220 if (control_interface == data_interface) {
1221 /* some broken devices designed for windows work this way */
1222 dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1223 combined_interfaces = 1;
1224 /* a popular other OS doesn't use it */
1225 quirks |= NO_CAP_LINE;
1226 if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1227 dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1228 return -EINVAL;
1229 }
1230look_for_collapsed_interface:
1231 for (i = 0; i < 3; i++) {
1232 struct usb_endpoint_descriptor *ep;
1233 ep = &data_interface->cur_altsetting->endpoint[i].desc;
1234
1235 if (usb_endpoint_is_int_in(ep))
1236 epctrl = ep;
1237 else if (usb_endpoint_is_bulk_out(ep))
1238 epwrite = ep;
1239 else if (usb_endpoint_is_bulk_in(ep))
1240 epread = ep;
1241 else
1242 return -EINVAL;
1243 }
1244 if (!epctrl || !epread || !epwrite)
1245 return -ENODEV;
1246 else
1247 goto made_compressed_probe;
1248 }
1249
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250skip_normal_probe:
1251
1252 /*workaround for switched interfaces */
Alan Cox6e47e062009-06-11 12:37:06 +01001253 if (data_interface->cur_altsetting->desc.bInterfaceClass
1254 != CDC_DATA_INTERFACE_TYPE) {
1255 if (control_interface->cur_altsetting->desc.bInterfaceClass
1256 == CDC_DATA_INTERFACE_TYPE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 struct usb_interface *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001258 dev_dbg(&intf->dev,
1259 "Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 t = control_interface;
1261 control_interface = data_interface;
1262 data_interface = t;
1263 } else {
1264 return -EINVAL;
1265 }
1266 }
Alan Stern74da5d62007-08-02 13:29:10 -04001267
1268 /* Accept probe requests only for the control interface */
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001269 if (!combined_interfaces && intf != control_interface)
Alan Stern74da5d62007-08-02 13:29:10 -04001270 return -ENODEV;
Alan Cox6e47e062009-06-11 12:37:06 +01001271
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001272 if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1273 /* valid in this context */
Alan Cox6e47e062009-06-11 12:37:06 +01001274 dev_dbg(&intf->dev, "The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 return -EBUSY;
1276 }
1277
1278
Sven Schnellea9959bb2012-08-17 21:43:43 +02001279 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
1280 control_interface->cur_altsetting->desc.bNumEndpoints == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 return -EINVAL;
1282
1283 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
1284 epread = &data_interface->cur_altsetting->endpoint[0].desc;
1285 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
1286
1287
1288 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -03001289 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 /* descriptors are swapped */
1291 struct usb_endpoint_descriptor *t;
Alan Cox6e47e062009-06-11 12:37:06 +01001292 dev_dbg(&intf->dev,
1293 "The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 t = epread;
1295 epread = epwrite;
1296 epwrite = t;
1297 }
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001298made_compressed_probe:
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001299 dev_dbg(&intf->dev, "interfaces are valid\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300
Alan Cox6e47e062009-06-11 12:37:06 +01001301 acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
1302 if (acm == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001303 dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 goto alloc_fail;
1305 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001307 minor = acm_alloc_minor(acm);
1308 if (minor == ACM_TTY_MINORS) {
1309 dev_err(&intf->dev, "no more free acm devices\n");
1310 kfree(acm);
1311 return -ENODEV;
1312 }
1313
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001314 ctrlsize = usb_endpoint_maxp(epctrl);
1315 readsize = usb_endpoint_maxp(epread) *
flintman5c2f9732014-08-11 21:52:25 -04001316#ifndef CONFIG_MACH_TENDERLOIN
Alan Cox6e47e062009-06-11 12:37:06 +01001317 (quirks == SINGLE_RX_URB ? 1 : 2);
flintman5c2f9732014-08-11 21:52:25 -04001318#else
1319 /* MBM */
1320 (quirks == SINGLE_RX_URB ? 1 : 4);
1321#endif
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001322 acm->combined_interfaces = combined_interfaces;
Kuninori Morimoto29cc8892011-08-23 03:12:03 -07001323 acm->writesize = usb_endpoint_maxp(epwrite) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 acm->control = control_interface;
1325 acm->data = data_interface;
1326 acm->minor = minor;
1327 acm->dev = usb_dev;
1328 acm->ctrl_caps = ac_management_function;
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001329 if (quirks & NO_CAP_LINE)
1330 acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 acm->ctrlsize = ctrlsize;
1332 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +02001333 acm->rx_buflimit = num_rx_buf;
David Howellsc4028952006-11-22 14:57:56 +00001334 INIT_WORK(&acm->work, acm_softint);
Oliver Neukum884b6002005-04-21 21:28:02 +02001335 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +01001336 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001337 mutex_init(&acm->mutex);
David Kubicek61a87ad2005-11-01 18:51:34 +01001338 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Oliver Neukumcf7fdd52009-08-04 23:52:09 +02001339 acm->is_int_ep = usb_endpoint_xfer_int(epread);
1340 if (acm->is_int_ep)
1341 acm->bInterval = epread->bInterval;
Alan Cox739e0282009-06-11 12:27:50 +01001342 tty_port_init(&acm->port);
1343 acm->port.ops = &acm_port_ops;
flintman5c2f9732014-08-11 21:52:25 -04001344#ifdef CONFIG_MACH_TENDERLOIN
1345 /* MBM */
1346 acm->response = usb_alloc_urb(0, GFP_KERNEL);
1347 if (!acm->response) {
1348 dev_dbg(&intf->dev, "out of memory (response kmalloc)\n");
1349 goto alloc_fail2;
1350 }
1351
1352 /* MBM */
1353 acm->bMaxPacketSize0 = usb_dev->descriptor.bMaxPacketSize0;
1354 acm->inbuf = usb_alloc_coherent(usb_dev,
1355 acm->bMaxPacketSize0,
1356 GFP_KERNEL,
1357 &acm->response->transfer_dma);
1358 /* MBM */
1359 if (!acm->inbuf) {
1360 dev_dbg(&intf->dev, "out of memory (inbuf kmalloc)\n");
1361 goto alloc_fail3;
1362 }
1363#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
Daniel Mack997ea582010-04-12 13:17:25 +02001365 buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 if (!buf) {
Johan Hovold255ab562011-03-22 11:12:13 +01001367 dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
flintman5c2f9732014-08-11 21:52:25 -04001368#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 goto alloc_fail2;
flintman5c2f9732014-08-11 21:52:25 -04001370#else
1371 /* MBM */
1372 goto alloc_fail3_1;
1373#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 }
1375 acm->ctrl_buffer = buf;
1376
Oliver Neukum884b6002005-04-21 21:28:02 +02001377 if (acm_write_buffers_alloc(acm) < 0) {
Johan Hovold255ab562011-03-22 11:12:13 +01001378 dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 goto alloc_fail4;
1380 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381
1382 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1383 if (!acm->ctrlurb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001384 dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 goto alloc_fail5;
1386 }
Oliver Neukum86478942006-05-13 22:50:47 +02001387 for (i = 0; i < num_rx_buf; i++) {
Johan Hovold088c64f2011-03-25 11:06:02 +01001388 struct acm_rb *rb = &(acm->read_buffers[i]);
1389 struct urb *urb;
David Kubicek61a87ad2005-11-01 18:51:34 +01001390
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001391 rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
1392 &rb->dma);
1393 if (!rb->base) {
1394 dev_err(&intf->dev, "out of memory "
1395 "(read bufs usb_alloc_coherent)\n");
1396 goto alloc_fail6;
1397 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001398 rb->index = i;
1399 rb->instance = acm;
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001400
Johan Hovold088c64f2011-03-25 11:06:02 +01001401 urb = usb_alloc_urb(0, GFP_KERNEL);
1402 if (!urb) {
Johan Hovold255ab562011-03-22 11:12:13 +01001403 dev_err(&intf->dev,
Alan Cox6e47e062009-06-11 12:37:06 +01001404 "out of memory (read urbs usb_alloc_urb)\n");
Axel Linc2572b72010-05-31 08:04:47 +08001405 goto alloc_fail6;
David Kubicek61a87ad2005-11-01 18:51:34 +01001406 }
Johan Hovold088c64f2011-03-25 11:06:02 +01001407 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1408 urb->transfer_dma = rb->dma;
1409 if (acm->is_int_ep) {
1410 usb_fill_int_urb(urb, acm->dev,
1411 acm->rx_endpoint,
1412 rb->base,
1413 acm->readsize,
1414 acm_read_bulk_callback, rb,
1415 acm->bInterval);
1416 } else {
1417 usb_fill_bulk_urb(urb, acm->dev,
1418 acm->rx_endpoint,
1419 rb->base,
1420 acm->readsize,
1421 acm_read_bulk_callback, rb);
1422 }
David Kubicek61a87ad2005-11-01 18:51:34 +01001423
Johan Hovold088c64f2011-03-25 11:06:02 +01001424 acm->read_urbs[i] = urb;
1425 __set_bit(i, &acm->read_urbs_free);
David Kubicek61a87ad2005-11-01 18:51:34 +01001426 }
Alan Cox6e47e062009-06-11 12:37:06 +01001427 for (i = 0; i < ACM_NW; i++) {
David Engrafe4cf3aa2008-03-20 10:01:34 +01001428 struct acm_wb *snd = &(acm->wb[i]);
1429
Alan Cox6e47e062009-06-11 12:37:06 +01001430 snd->urb = usb_alloc_urb(0, GFP_KERNEL);
1431 if (snd->urb == NULL) {
Johan Hovold255ab562011-03-22 11:12:13 +01001432 dev_err(&intf->dev,
Johan Hovold59d7fec2011-03-22 11:12:12 +01001433 "out of memory (write urbs usb_alloc_urb)\n");
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001434 goto alloc_fail7;
David Engrafe4cf3aa2008-03-20 10:01:34 +01001435 }
1436
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001437 if (usb_endpoint_xfer_int(epwrite))
1438 usb_fill_int_urb(snd->urb, usb_dev,
Ming Leie83863d2012-10-16 21:21:21 +08001439 usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),
Arseniy Lartsev5186ffe2009-07-01 16:27:26 +04001440 NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
1441 else
1442 usb_fill_bulk_urb(snd->urb, usb_dev,
1443 usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1444 NULL, acm->writesize, acm_write_bulk, snd);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001445 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1446 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 }
1448
Alan Cox6e47e062009-06-11 12:37:06 +01001449 usb_set_intfdata(intf, acm);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001450
1451 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1452 if (i < 0)
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001453 goto alloc_fail7;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001454
1455 if (cfd) { /* export the country data */
1456 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1457 if (!acm->country_codes)
1458 goto skip_countries;
1459 acm->country_code_size = cfd->bLength - 4;
Alan Cox6e47e062009-06-11 12:37:06 +01001460 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
1461 cfd->bLength - 4);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001462 acm->country_rel_date = cfd->iCountryCodeRelDate;
1463
1464 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1465 if (i < 0) {
1466 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001467 acm->country_codes = NULL;
1468 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001469 goto skip_countries;
1470 }
1471
Alan Cox6e47e062009-06-11 12:37:06 +01001472 i = device_create_file(&intf->dev,
1473 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001474 if (i < 0) {
Axel Linc2572b72010-05-31 08:04:47 +08001475 device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001476 kfree(acm->country_codes);
Julia Lawalle7c8e862011-12-23 14:02:55 +01001477 acm->country_codes = NULL;
1478 acm->country_code_size = 0;
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001479 goto skip_countries;
1480 }
1481 }
1482
1483skip_countries:
Alan Cox6e47e062009-06-11 12:37:06 +01001484 usb_fill_int_urb(acm->ctrlurb, usb_dev,
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001485 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1486 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1487 /* works around buggy devices */
1488 epctrl->bInterval ? epctrl->bInterval : 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1490 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
flintman5c2f9732014-08-11 21:52:25 -04001491#ifdef CONFIG_MACH_TENDERLOIN
1492 /* MBM */
1493 acm->ctrlurb->dev = acm->dev;
1494 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
1495 dev_err(&intf->dev, "usb_submit_urb(ctrl irq) failed");
1496 goto kill_urb;
1497 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498
flintman5c2f9732014-08-11 21:52:25 -04001499 /* MBM */
1500 acm->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
1501 if (!acm->irq)
1502 goto kill_urb;
1503#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1505
1506 acm_set_control(acm, acm->ctrlout);
flintman5c2f9732014-08-11 21:52:25 -04001507#ifndef CONFIG_MACH_TENDERLOIN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 acm->line.dwDTERate = cpu_to_le32(9600);
flintman5c2f9732014-08-11 21:52:25 -04001509#else
1510 acm->line.dwDTERate = cpu_to_le32(115200);
1511#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 acm->line.bDataBits = 8;
1513 acm_set_line(acm, &acm->line);
flintman5c2f9732014-08-11 21:52:25 -04001514#ifdef CONFIG_MACH_TENDERLOIN
1515 /* MBM */
1516 acm->state |= ACM_ABS_IDLE;
1517 acm_set_comm_feature(acm, ACM_ABSTRACT_STATE, &acm->state);
1518#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519
1520 usb_driver_claim_interface(&acm_driver, data_interface, acm);
David Brownell672c4e12008-08-06 18:41:12 -07001521 usb_set_intfdata(data_interface, acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001523 usb_get_intf(control_interface);
1524 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001526 return 0;
flintman5c2f9732014-08-11 21:52:25 -04001527#ifdef CONFIG_MACH_TENDERLOIN
1528 /* MBM */
1529kill_urb:
1530 usb_kill_urb(acm->ctrlurb);
1531#endif
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001532alloc_fail7:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001533 for (i = 0; i < ACM_NW; i++)
1534 usb_free_urb(acm->wb[i].urb);
Axel Linc2572b72010-05-31 08:04:47 +08001535alloc_fail6:
Oliver Neukum86478942006-05-13 22:50:47 +02001536 for (i = 0; i < num_rx_buf; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001537 usb_free_urb(acm->read_urbs[i]);
Johan Hovold74f5e1b2011-03-22 11:12:23 +01001538 acm_read_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 usb_free_urb(acm->ctrlurb);
1540alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001541 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542alloc_fail4:
Daniel Mack997ea582010-04-12 13:17:25 +02001543 usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
flintman5c2f9732014-08-11 21:52:25 -04001544#ifdef CONFIG_MACH_TENDERLOIN
1545 /* MBM */
1546alloc_fail3_1:
1547 usb_free_coherent(usb_dev,acm->bMaxPacketSize0, acm->inbuf, acm->response->transfer_dma);
1548alloc_fail3:
1549 usb_free_urb(acm->response);
1550#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551alloc_fail2:
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001552 acm_release_minor(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 kfree(acm);
1554alloc_fail:
1555 return -ENOMEM;
1556}
1557
Oliver Neukum1365baf2007-10-12 17:24:28 +02001558static void stop_data_traffic(struct acm *acm)
1559{
1560 int i;
Johan Hovolda5cc7ef2011-03-22 11:12:15 +01001561
1562 dev_dbg(&acm->control->dev, "%s\n", __func__);
flintman5c2f9732014-08-11 21:52:25 -04001563#ifndef CONFIG_MACH_TENDERLOIN
Oliver Neukum1365baf2007-10-12 17:24:28 +02001564 usb_kill_urb(acm->ctrlurb);
flintman5c2f9732014-08-11 21:52:25 -04001565#endif
Alan Cox6e47e062009-06-11 12:37:06 +01001566 for (i = 0; i < ACM_NW; i++)
David Engrafe4cf3aa2008-03-20 10:01:34 +01001567 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001568 for (i = 0; i < acm->rx_buflimit; i++)
Johan Hovold088c64f2011-03-25 11:06:02 +01001569 usb_kill_urb(acm->read_urbs[i]);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001570
1571 cancel_work_sync(&acm->work);
1572}
1573
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574static void acm_disconnect(struct usb_interface *intf)
1575{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001576 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577 struct usb_device *usb_dev = interface_to_usbdev(intf);
Alan Cox10077d42009-06-11 12:36:09 +01001578 struct tty_struct *tty;
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001579 int i;
1580
1581 dev_dbg(&intf->dev, "%s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582
David Brownell672c4e12008-08-06 18:41:12 -07001583 /* sibling interface is already cleaning up */
1584 if (!acm)
Oliver Neukum86067eea2006-01-08 12:39:13 +01001585 return;
David Brownell672c4e12008-08-06 18:41:12 -07001586
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001587 mutex_lock(&acm->mutex);
1588 acm->disconnected = true;
Alan Cox6e47e062009-06-11 12:37:06 +01001589 if (acm->country_codes) {
Alan Stern74da5d62007-08-02 13:29:10 -04001590 device_remove_file(&acm->control->dev,
1591 &dev_attr_wCountryCodes);
1592 device_remove_file(&acm->control->dev,
1593 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001594 }
Alan Stern74da5d62007-08-02 13:29:10 -04001595 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001596 usb_set_intfdata(acm->control, NULL);
1597 usb_set_intfdata(acm->data, NULL);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001598 mutex_unlock(&acm->mutex);
1599
1600 tty = tty_port_tty_get(&acm->port);
1601 if (tty) {
1602 tty_vhangup(tty);
1603 tty_kref_put(tty);
1604 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605
Oliver Neukum1365baf2007-10-12 17:24:28 +02001606 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607
Johan Hovold10a00e32013-03-19 09:21:06 +01001608 tty_unregister_device(acm_tty_driver, acm->minor);
1609
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001610 usb_free_urb(acm->ctrlurb);
1611 for (i = 0; i < ACM_NW; i++)
1612 usb_free_urb(acm->wb[i].urb);
1613 for (i = 0; i < acm->rx_buflimit; i++)
1614 usb_free_urb(acm->read_urbs[i]);
Oliver Neukum884b6002005-04-21 21:28:02 +02001615 acm_write_buffers_free(acm);
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001616 usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Oliver Neukum830f4022008-06-25 14:17:16 +02001617 acm_read_buffers_free(acm);
flintman5c2f9732014-08-11 21:52:25 -04001618#ifdef CONFIG_MACH_TENDERLOIN
1619 /* MBM */
1620 usb_free_coherent(usb_dev,acm->bMaxPacketSize0, acm->inbuf, acm->response->transfer_dma);
1621#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622
Oliver Neukuma2bfb4a2009-05-16 21:13:19 +02001623 if (!acm->combined_interfaces)
1624 usb_driver_release_interface(&acm_driver, intf == acm->control ?
Oliver Neukum830f4022008-06-25 14:17:16 +02001625 acm->data : acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001627 tty_port_put(&acm->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628}
1629
Oliver Neukum35758582008-07-01 19:10:08 +02001630#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001631static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1632{
1633 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001634 int cnt;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001635
Oliver Neukum11ea8592008-06-20 11:25:57 +02001636 spin_lock_irq(&acm->read_lock);
1637 spin_lock(&acm->write_lock);
Johan Hovoldbe7978b2014-05-26 19:23:36 +02001638 if (PMSG_IS_AUTO(message)) {
1639 if (acm->transmitting) {
1640 spin_unlock(&acm->write_lock);
1641 spin_unlock_irq(&acm->read_lock);
1642 return -EBUSY;
1643 }
1644 }
Oliver Neukum11ea8592008-06-20 11:25:57 +02001645 cnt = acm->susp_count++;
1646 spin_unlock(&acm->write_lock);
1647 spin_unlock_irq(&acm->read_lock);
1648
1649 if (cnt)
Oliver Neukum1365baf2007-10-12 17:24:28 +02001650 return 0;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001651
flintman5c2f9732014-08-11 21:52:25 -04001652#ifdef CONFIG_MACH_TENDERLOIN
1653 /* MBM, Kill URB here! */
1654 usb_kill_urb(acm->ctrlurb);
1655#endif
1656
Johan Hovold181614f2014-05-26 19:23:40 +02001657 stop_data_traffic(acm);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001658
Oliver Neukum1365baf2007-10-12 17:24:28 +02001659 return 0;
1660}
1661
1662static int acm_resume(struct usb_interface *intf)
1663{
1664 struct acm *acm = usb_get_intfdata(intf);
Johan Hovold21ffba32014-05-26 19:23:38 +02001665 struct urb *urb;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001666 int rv = 0;
1667
Oliver Neukum11ea8592008-06-20 11:25:57 +02001668 spin_lock_irq(&acm->read_lock);
Johan Hovold97e72c42014-05-26 19:23:37 +02001669 spin_lock(&acm->write_lock);
Oliver Neukum11ea8592008-06-20 11:25:57 +02001670
Johan Hovold97e72c42014-05-26 19:23:37 +02001671 if (--acm->susp_count)
1672 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001673
flintman5c2f9732014-08-11 21:52:25 -04001674#ifdef CONFIG_MACH_TENDERLOIN
1675 /* MBM, We have to resubmit the INT URB regardless of the port is open or not */
1676 if (usb_submit_urb(acm->ctrlurb, GFP_ATOMIC)) {
1677 dev_err(&intf->dev, "usb_submit_urb(ctrl irq) failed");
1678 }
1679#endif
1680
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001681 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
flintman5c2f9732014-08-11 21:52:25 -04001682#ifndef CONFIG_MACH_TENDERLOIN
Johan Hovold97e72c42014-05-26 19:23:37 +02001683 rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
flintman5c2f9732014-08-11 21:52:25 -04001684#endif
Oliver Neukum97d35f92009-12-16 17:05:57 +01001685
Johan Hovold21ffba32014-05-26 19:23:38 +02001686 for (;;) {
1687 urb = usb_get_from_anchor(&acm->delayed);
1688 if (!urb)
1689 break;
1690
1691 acm_start_wb(acm, urb->context);
Oliver Neukum97d35f92009-12-16 17:05:57 +01001692 }
1693
1694 /*
1695 * delayed error checking because we must
1696 * do the write path at all cost
1697 */
Oliver Neukum1365baf2007-10-12 17:24:28 +02001698 if (rv < 0)
Johan Hovold97e72c42014-05-26 19:23:37 +02001699 goto out;
Oliver Neukum1365baf2007-10-12 17:24:28 +02001700
Johan Hovold97e72c42014-05-26 19:23:37 +02001701 rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001702 }
Johan Hovold97e72c42014-05-26 19:23:37 +02001703out:
1704 spin_unlock(&acm->write_lock);
1705 spin_unlock_irq(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001706
Oliver Neukum1365baf2007-10-12 17:24:28 +02001707 return rv;
1708}
Oliver Neukum35758582008-07-01 19:10:08 +02001709
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001710static int acm_reset_resume(struct usb_interface *intf)
1711{
1712 struct acm *acm = usb_get_intfdata(intf);
1713 struct tty_struct *tty;
1714
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001715 if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001716 tty = tty_port_tty_get(&acm->port);
1717 if (tty) {
1718 tty_hangup(tty);
1719 tty_kref_put(tty);
1720 }
1721 }
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001722
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001723 return acm_resume(intf);
1724}
1725
Oliver Neukum35758582008-07-01 19:10:08 +02001726#endif /* CONFIG_PM */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001727
1728#define NOKIA_PCSUITE_ACM_INFO(x) \
1729 USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1730 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1731 USB_CDC_ACM_PROTO_VENDOR)
1732
Toby Gray4035e452010-09-01 16:01:19 +01001733#define SAMSUNG_PCSUITE_ACM_INFO(x) \
1734 USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
1735 USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1736 USB_CDC_ACM_PROTO_VENDOR)
1737
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738/*
1739 * USB driver structure.
1740 */
1741
Németh Márton6ef48522010-01-10 15:33:45 +01001742static const struct usb_device_id acm_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 /* quirky and broken devices */
David Cluytensbd73e382013-12-03 14:18:57 +01001744 { USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */
1745 .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1747 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1748 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001749 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1750 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1751 },
Andrew Lunn0f9c7b42008-12-23 17:31:23 +01001752 { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
1753 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1754 },
Masahito Omote8753e652005-07-29 12:17:25 -07001755 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1756 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1757 },
Chris Malley91a9c922006-10-03 10:08:28 +01001758 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1759 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1760 },
Alan Cox7abcf202009-04-06 17:35:01 +01001761 { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
1762 .driver_info = SINGLE_RX_URB,
1763 },
Oliver Neukum86478942006-05-13 22:50:47 +02001764 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1765 .driver_info = SINGLE_RX_URB, /* firmware bug */
1766 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001767 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1768 .driver_info = SINGLE_RX_URB, /* firmware bug */
1769 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001770 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1771 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1772 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001773 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1774 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1775 },
Eric Sandeenc8fd2c32008-08-14 08:25:40 -05001776 { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1777 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1778 },
Alan Coxc89c60e2009-01-11 19:53:10 +00001779 { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1780 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1781 },
Xiao Kaijiancab98a02009-05-08 00:48:23 +08001782 { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1783 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1784 },
Dmitriy Taychenachev155df652009-02-25 12:36:51 +08001785 { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1786 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001787 /* Motorola H24 HSPA module: */
1788 { USB_DEVICE(0x22b8, 0x2d91) }, /* modem */
Michael Ulbrichtb08f08b2014-03-25 10:34:18 +01001789 { USB_DEVICE(0x22b8, 0x2d92), /* modem + diagnostics */
1790 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1791 },
1792 { USB_DEVICE(0x22b8, 0x2d93), /* modem + AT port */
1793 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1794 },
1795 { USB_DEVICE(0x22b8, 0x2d95), /* modem + AT port + diagnostics */
1796 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1797 },
1798 { USB_DEVICE(0x22b8, 0x2d96), /* modem + NMEA */
1799 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1800 },
1801 { USB_DEVICE(0x22b8, 0x2d97), /* modem + diagnostics + NMEA */
1802 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1803 },
1804 { USB_DEVICE(0x22b8, 0x2d99), /* modem + AT port + NMEA */
1805 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1806 },
1807 { USB_DEVICE(0x22b8, 0x2d9a), /* modem + AT port + diagnostics + NMEA */
1808 .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
1809 },
Krzysztof Hałasa6abff5d2011-12-12 14:51:00 +01001810
Adam Richterc332b4e2009-02-18 16:17:15 -08001811 { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1812 .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1813 data interface instead of
1814 communications interface.
1815 Maybe we should define a new
1816 quirk for this. */
1817 },
Jean-Christian de Rivaz52538352012-10-10 12:49:02 +00001818 { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */
1819 .driver_info = NO_UNION_NORMAL,
1820 },
Denis N Ladinb756d752012-12-26 18:29:44 +05001821 { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */
1822 .driver_info = NO_UNION_NORMAL,
1823 },
Kir Kolyshkin1f17c502009-05-28 20:33:58 +04001824 { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
1825 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1826 },
Russ Nelsonc3baa192010-04-21 23:07:03 -04001827 { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1828 .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1829 },
flintman5c2f9732014-08-11 21:52:25 -04001830 /* MBM */
1831 { USB_DEVICE(0x1519, 0x0020), /* IMC_MAIN - XMM6260, XMM6262 */
1832 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1833 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001834
Adrian Taylorc1479a92009-11-19 10:35:33 +00001835 /* Nokia S60 phones expose two ACM channels. The first is
1836 * a modem and is picked up by the standard AT-command
1837 * information below. The second is 'vendor-specific' but
1838 * is treated as a serial device at the S60 end, so we want
1839 * to expose it on Linux too. */
1840 { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1841 { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1842 { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1843 { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1844 { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1845 { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1846 { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1847 { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1848 { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1849 { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1850 { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1851 { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1852 { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1853 { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1854 { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1855 { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1856 { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1857 { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
1858 { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1859 { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1860 { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1861 { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
1862 { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1863 { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1864 { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1865 { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1866 { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1867 { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1868 { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1869 { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1870 { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1871 { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
1872 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1873 { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1874 { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1875 { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1876 { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1877 { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1878 { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1879 { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
1880 { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1881 { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1882 { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
Przemo Firszt83a4eae2010-06-28 21:29:34 +01001883 { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
Toby Gray4035e452010-09-01 16:01:19 +01001884 { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
1885 { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
1886 { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
1887 { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
1888 { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
1889 { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
1890 { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
1891 { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
1892 { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
1893 { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
Arvid Ephraim Picciani721d92f2011-01-25 15:58:40 +01001894 { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
Toby Gray4061fde2011-06-06 14:52:48 +01001895 { NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */
1896 { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
Toby Gray4035e452010-09-01 16:01:19 +01001897 { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
Adrian Taylorc1479a92009-11-19 10:35:33 +00001898
Denis Pershin65e52f42011-09-04 17:37:21 +07001899 /* Support for Owen devices */
1900 { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */
1901
Adrian Taylorc1479a92009-11-19 10:35:33 +00001902 /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1903
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001904 /* Support Lego NXT using pbLua firmware */
Julian Calabyce126642010-01-05 23:58:20 +11001905 { USB_DEVICE(0x0694, 0xff00),
1906 .driver_info = NOT_A_MODEM,
Otavio Salvador7893afc2010-09-26 23:35:05 -03001907 },
Julian Calaby7c5d8c32010-01-05 23:57:46 +11001908
Erik Slagterfd5054c2011-05-11 12:06:55 +02001909 /* Support for Droids MuIn LCD */
1910 { USB_DEVICE(0x04d8, 0x000b),
1911 .driver_info = NO_DATA_INTERFACE,
1912 },
flintman5c2f9732014-08-11 21:52:25 -04001913#ifndef CONFIG_MACH_TENDERLOIN
Philippe Corbes5b239f02010-08-31 19:31:32 +02001914 /* control interfaces without any protocol set */
1915 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1916 USB_CDC_PROTO_NONE) },
flintman5c2f9732014-08-11 21:52:25 -04001917#else
1918 /* Exclude XMM6260 boot rom (not running modem software yet) */
1919 { USB_DEVICE(0x058b, 0x0041),
1920 .driver_info = NOT_REAL_ACM,
1921 },
1922#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 /* control interfaces with various AT-command sets */
1924 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1925 USB_CDC_ACM_PROTO_AT_V25TER) },
1926 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1927 USB_CDC_ACM_PROTO_AT_PCCA101) },
1928 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1929 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1930 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1931 USB_CDC_ACM_PROTO_AT_GSM) },
1932 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
Alan Cox6e47e062009-06-11 12:37:06 +01001933 USB_CDC_ACM_PROTO_AT_3G) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1935 USB_CDC_ACM_PROTO_AT_CDMA) },
1936
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 { }
1938};
1939
Alan Cox6e47e062009-06-11 12:37:06 +01001940MODULE_DEVICE_TABLE(usb, acm_ids);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941
1942static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 .name = "cdc_acm",
1944 .probe = acm_probe,
1945 .disconnect = acm_disconnect,
Oliver Neukum35758582008-07-01 19:10:08 +02001946#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001947 .suspend = acm_suspend,
1948 .resume = acm_resume,
Francesco Lavraa91b0c52009-12-08 09:54:11 +01001949 .reset_resume = acm_reset_resume,
Oliver Neukum35758582008-07-01 19:10:08 +02001950#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951 .id_table = acm_ids,
Oliver Neukum35758582008-07-01 19:10:08 +02001952#ifdef CONFIG_PM
Oliver Neukum1365baf2007-10-12 17:24:28 +02001953 .supports_autosuspend = 1,
Oliver Neukum35758582008-07-01 19:10:08 +02001954#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955};
1956
1957/*
1958 * TTY driver structures.
1959 */
1960
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001961static const struct tty_operations acm_ops = {
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001962 .install = acm_tty_install,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963 .open = acm_tty_open,
1964 .close = acm_tty_close,
Havard Skinnemoen7fb57a02011-11-30 13:28:14 -08001965 .cleanup = acm_tty_cleanup,
Alan Cox10077d42009-06-11 12:36:09 +01001966 .hangup = acm_tty_hangup,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 .write = acm_tty_write,
1968 .write_room = acm_tty_write_room,
1969 .ioctl = acm_tty_ioctl,
1970 .throttle = acm_tty_throttle,
1971 .unthrottle = acm_tty_unthrottle,
1972 .chars_in_buffer = acm_tty_chars_in_buffer,
1973 .break_ctl = acm_tty_break_ctl,
1974 .set_termios = acm_tty_set_termios,
1975 .tiocmget = acm_tty_tiocmget,
1976 .tiocmset = acm_tty_tiocmset,
1977};
1978
1979/*
1980 * Init / exit.
1981 */
1982
1983static int __init acm_init(void)
1984{
1985 int retval;
1986 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1987 if (!acm_tty_driver)
1988 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989 acm_tty_driver->driver_name = "acm",
1990 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 acm_tty_driver->major = ACM_TTY_MAJOR,
1992 acm_tty_driver->minor_start = 0,
1993 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1994 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001995 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996 acm_tty_driver->init_termios = tty_std_termios;
Alan Cox6e47e062009-06-11 12:37:06 +01001997 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
1998 HUPCL | CLOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 tty_set_operations(acm_tty_driver, &acm_ops);
2000
2001 retval = tty_register_driver(acm_tty_driver);
2002 if (retval) {
2003 put_tty_driver(acm_tty_driver);
2004 return retval;
2005 }
2006
2007 retval = usb_register(&acm_driver);
2008 if (retval) {
2009 tty_unregister_driver(acm_tty_driver);
2010 put_tty_driver(acm_tty_driver);
2011 return retval;
2012 }
2013
Johan Hovolda2c7b932011-03-22 11:12:18 +01002014 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015
2016 return 0;
2017}
2018
2019static void __exit acm_exit(void)
2020{
2021 usb_deregister(&acm_driver);
2022 tty_unregister_driver(acm_tty_driver);
2023 put_tty_driver(acm_tty_driver);
2024}
2025
2026module_init(acm_init);
2027module_exit(acm_exit);
2028
Alan Cox6e47e062009-06-11 12:37:06 +01002029MODULE_AUTHOR(DRIVER_AUTHOR);
2030MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031MODULE_LICENSE("GPL");
Scott James Remnante766aeb2009-04-06 17:33:18 +01002032MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);