cdc-acm.c revision 23198fda7182969b619613a555f8645fdc3dc334
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * cdc-acm.c
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Armin Fuerst	<fuerst@in.tum.de>
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Pavel Machek	<pavel@suse.cz>
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Johannes Erdfelt	<johannes@erdfelt.com>
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2004 Oliver Neukum	<oliver@neukum.name>
961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek * Copyright (c) 2005 David Kubicek	<dave@awk.cz>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB Abstract Control Model driver for USB modems and ISDN adapters
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Sponsored by SuSE
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ChangeLog:
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.9  - thorough cleaning, URBification, almost a rewrite
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.10 - some more cleanups
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.11 - fixed flow control, read error doesn't stop reads
196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox *	v0.12 - added TIOCM ioctls, added break handling, made struct acm
206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox *		kmalloced
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.13 - added termios, added hangup
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.14 - sized down struct acm
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.15 - fixed flow control again - characters could be lost
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.16 - added code for modems with swapped data and control interfaces
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.17 - added new style probing
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.18 - fixed new style probing for devices with more configurations
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.20 - switched to probing on interface (rather than device) class
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.21 - revert to probing on device for devices with multiple configs
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.22 - probe only the control interface. if usbcore doesn't choose the
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		config we want, sysadmin changes bConfigurationValue in sysfs.
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.23 - use softirq for rx processing, as needed by tty layer
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.24 - change probe method to evaluate CDC union descriptor
3461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek *	v0.25 - downstream tasks paralelized to maximize throughput
35e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf *	v0.26 - multiple write urbs, writesize increased
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version.
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG
55e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#undef VERBOSE_DEBUG
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h>
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
654186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h>
6610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#include <linux/uaccess.h>
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
68a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h>
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h>
7161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h>
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h"
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
75e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
76e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define ACM_CLOSE_TIMEOUT	15	/* seconds to let writes drain */
77e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
81e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf#define DRIVER_VERSION "v0.26"
8261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver;
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver;
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS];
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
894186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex);
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#define ACM_READY(acm)	(acm && acm->dev && acm->port.count)
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
93739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Coxstatic const struct tty_port_operations acm_port_ops = {
94739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox};
95739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox
96e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#ifdef VERBOSE_DEBUG
97e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define verbose	1
98e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#else
99e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define verbose	0
100e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#endif
101e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages.
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_ctrl_msg(struct acm *acm, int request, int value,
1076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox							void *buf, int len)
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		request, USB_RT_ACM, value,
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm->control->altsetting[0].desc.bInterfaceNumber,
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf, len, 5000);
1136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d",
1146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						request, value, len, retval);
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval < 0 ? retval : 0;
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests.
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do...
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
129884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management.
130884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller.
131884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
132884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
133884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm)
134884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
135884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, wbn;
136884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
137884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
138e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wbn = 0;
139884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	i = 0;
140884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	for (;;) {
141884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb = &acm->wb[wbn];
142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->use) {
143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			wb->use = 1;
144884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return wbn;
145884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
14686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		wbn = (wbn + 1) % ACM_NW;
14786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		if (++i >= ACM_NW)
148884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -1;
149884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
150884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
151884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
152884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm)
153884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
154884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, n;
155e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	unsigned long flags;
156884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
15786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	n = ACM_NW;
158e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	spin_lock_irqsave(&acm->write_lock, flags);
1596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++)
16086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		n -= acm->wb[i].use;
161e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	spin_unlock_irqrestore(&acm->write_lock, flags);
162884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return n;
163884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
164884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
165884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
166ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips * Finish write. Caller must hold acm->write_lock
167884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
168e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic void acm_write_done(struct acm *acm, struct acm_wb *wb)
169884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
170e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wb->use = 0;
17111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting--;
172884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
175884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write.
17611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum *
17711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking
178884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
17911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb)
18111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
18211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rc;
18311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting++;
18511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer = wb->buf;
18711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_dma = wb->dmah;
18811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer_length = wb->len;
18911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->dev = acm->dev;
19011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
1916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
1926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (rc < 0) {
19311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("usb_submit_urb(write bulk) failed: %d", rc);
19411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_write_done(acm, wb);
19511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
19611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	return rc;
19711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
19811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
199e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn)
200884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
201884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
202934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	struct acm_wb *wb = &acm->wb[wbn];
203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int rc;
204884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
205884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
206884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->dev) {
207934da4635c2d05cef474e5243ef05df95b2ad264David Brownell		wb->use = 0;
208884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
209884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return -ENODEV;
210884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
211884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
21211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	dbg("%s susp_count: %d", __func__, acm->susp_count);
21311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->susp_count) {
21411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->delayed_wb = wb;
21511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		schedule_work(&acm->waker);
21611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
21711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return 0;	/* A white lie */
21811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
21911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
22011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
22111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rc = acm_start_wb(acm, wb);
222884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
223884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
224884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return rc;
22511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
226884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
227c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/*
228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs
229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */
230c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps
231c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->ctrl_caps);
237c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes
241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	memcpy(buf, acm->country_codes, acm->country_code_size);
247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return acm->country_code_size;
248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
249c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
250c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date
253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
254c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
258c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->country_rel_date);
259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
260884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
261c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
262884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */
2677d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb)
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = urb->context;
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_notification *dr = urb->transfer_buffer;
27110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data;
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl;
274185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int retval;
275185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int status = urb->status;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
277185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	switch (status) {
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* success */
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ECONNRESET:
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ENOENT:
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ESHUTDOWN:
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this urb is terminated, clean up */
285441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - urb shutting down with status: %d", __func__, status);
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
288441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - nonzero urb status received: %d", __func__, status);
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = (unsigned char *)(dr + 1);
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (dr->bNotificationType) {
2976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
2986e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dbg("%s network", dr->wValue ?
2996e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"connected to" : "disconnected from");
3006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		break;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	case USB_CDC_NOTIFY_SERIAL_STATE:
3036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		tty = tty_port_tty_get(&acm->port);
3046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl = get_unaligned_le16(data);
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (tty) {
3076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (!acm->clocal &&
3086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
3096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dbg("calling hangup");
3106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				tty_hangup(tty);
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			tty_kref_put(tty);
3136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		acm->ctrlin = newctrl;
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
3186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
3196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
3206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
3216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
3226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
3236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
3246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	default:
3286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
3296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dr->bNotificationType, dr->wIndex,
3306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dr->wLength, data[0], data[1]);
3316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		break;
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
33411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
3356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	retval = usb_submit_urb(urb, GFP_ATOMIC);
336185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	if (retval)
3379908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
3389908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			"result %d", __func__, retval);
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */
3427d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_read_bulk(struct urb *urb)
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
34461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
34561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv = urb->context;
34661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm *acm = rcv->instance;
34786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int status = urb->status;
348185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman
349185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	dbg("Entering acm_read_bulk with status %d", status);
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
35111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (!ACM_READY(acm)) {
35211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dev_dbg(&acm->data->dev, "Aborting, acm not ready");
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
35411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
35511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
35786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (status)
358898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
36061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = rcv->buffer;
36161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf->size = urb->actual_length;
36261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
36386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (likely(status == 0)) {
36486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
36511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->processing++;
36686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
36786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&buf->list, &acm->filled_read_bufs);
36886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
36986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	} else {
37086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* we drop the buffer due to an error */
37186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
37286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
37386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add(&buf->list, &acm->spare_read_bufs);
37486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
37586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* nevertheless the tasklet must be kicked unconditionally
37686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		so the queue cannot dry up */
37786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	}
37811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (likely(!acm->susp_count))
37911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		tasklet_schedule(&acm->urb_task);
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm)
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = (void *)_acm;
38561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
38610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
38761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv;
388762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	unsigned long flags;
389ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	unsigned char throttled;
3905186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev	struct usb_host_endpoint *ep;
39111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Entering acm_rx_tasklet");
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
39410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (!ACM_READY(acm)) {
39511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("acm_rx_tasklet: ACM not ready");
396ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		return;
39711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
398ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
399834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_lock_irqsave(&acm->throttle_lock, flags);
400ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	throttled = acm->throttle;
401834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_unlock_irqrestore(&acm->throttle_lock, flags);
40210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (throttled) {
40311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("acm_rx_tasklet: throttled");
40461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		return;
40511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
40661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
40710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
40810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
40961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer:
410762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
41161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	if (list_empty(&acm->filled_read_bufs)) {
412762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
41361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		goto urbs;
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
41561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = list_entry(acm->filled_read_bufs.next,
41661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			 struct acm_rb, list);
41761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_del(&buf->list);
418762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
41961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
4203dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
42161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
42210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (tty) {
42310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		spin_lock_irqsave(&acm->throttle_lock, flags);
42410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		throttled = acm->throttle;
42510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		spin_unlock_irqrestore(&acm->throttle_lock, flags);
42610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		if (!throttled) {
42710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			tty_buffer_request_room(tty, buf->size);
42810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			tty_insert_flip_string(tty, buf->base, buf->size);
42910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			tty_flip_buffer_push(tty);
43010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		} else {
43110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			tty_kref_put(tty);
43210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			dbg("Throttling noticed");
43310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			spin_lock_irqsave(&acm->read_lock, flags);
43410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			list_add(&buf->list, &acm->filled_read_bufs);
43510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			spin_unlock_irqrestore(&acm->read_lock, flags);
43610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			return;
43710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		}
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
440762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
44161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_add(&buf->list, &acm->spare_read_bufs);
442762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
44361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	goto next_buffer;
44461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
44561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs:
44610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_kref_put(tty);
44710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
44861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	while (!list_empty(&acm->spare_read_bufs)) {
449762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_lock_irqsave(&acm->read_lock, flags);
45061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (list_empty(&acm->spare_read_urbs)) {
45111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
452762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
45361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
45461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
45561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv = list_entry(acm->spare_read_urbs.next,
45661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_ru, list);
45761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&rcv->list);
458762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
45961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
46061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		buf = list_entry(acm->spare_read_bufs.next,
46161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_rb, list);
46261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&buf->list);
46361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
46461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->buffer = buf;
46561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
4665186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		ep = (usb_pipein(acm->rx_endpoint) ? acm->dev->ep_in : acm->dev->ep_out)
4675186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				[usb_pipeendpoint(acm->rx_endpoint)];
4685186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		if (usb_endpoint_xfer_int(&ep->desc))
4695186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_int_urb(rcv->urb, acm->dev,
4705186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 acm->rx_endpoint,
4715186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 buf->base,
4725186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 acm->readsize,
4735186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 acm_read_bulk, rcv, ep->desc.bInterval);
4745186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		else
4755186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_bulk_urb(rcv->urb, acm->dev,
4765186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  acm->rx_endpoint,
4775186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  buf->base,
4785186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  acm->readsize,
4795186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  acm_read_bulk, rcv);
48061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_dma = buf->dma;
48161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
48261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
4836e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		/* This shouldn't kill the driver as unsuccessful URBs are
4846e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		   returned to the free-urbs-pool and resubmited ASAP */
48511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irqsave(&acm->read_lock, flags);
4866e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (acm->susp_count ||
4876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
48861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&buf->list, &acm->spare_read_bufs);
48961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&rcv->list, &acm->spare_read_urbs);
49011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
491762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
49261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
49311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		} else {
49411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			spin_unlock_irqrestore(&acm->read_lock, flags);
49511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
49661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
49761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
49811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irqsave(&acm->read_lock, flags);
49911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->processing = 0;
50011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irqrestore(&acm->read_lock, flags);
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */
5047d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb)
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
506cdc97792289179974af6dda781c855696358d307Ming Lei	struct acm_wb *wb = urb->context;
507e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	struct acm *acm = wb->instance;
508ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	unsigned long flags;
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
510e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	if (verbose || urb->status
511e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			|| (urb->actual_length != urb->transfer_buffer_length))
512e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell		dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
513e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->actual_length,
514e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->transfer_buffer_length,
515e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->status);
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
517ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_lock_irqsave(&acm->write_lock, flags);
518e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm_write_done(acm, wb);
519ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_unlock_irqrestore(&acm->write_lock, flags);
520884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (ACM_READY(acm))
521884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		schedule_work(&acm->work);
522e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	else
523e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell		wake_up_interruptible(&acm->drain_wait);
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
526c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work)
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
528c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct acm *acm = container_of(work, struct acm, work);
52910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
530e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
531e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	dev_vdbg(&acm->data->dev, "tx work\n");
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
53410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
53510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_wakeup(tty);
53610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_kref_put(tty);
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
53911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic void acm_waker(struct work_struct *waker)
54011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
54111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	struct acm *acm = container_of(waker, struct acm, waker);
54211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rv;
54311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
54411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rv = usb_autopm_get_interface(acm->control);
54511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (rv < 0) {
5469908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__);
54711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return;
54811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
54911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->delayed_wb) {
55011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_start_wb(acm, acm->delayed_wb);
55111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->delayed_wb = NULL;
55211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
55311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
55411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
55511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp)
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
56342dd2aa6496a2e87e496aac5494d2e1d6096c85bThadeu Lima de Souza Cascardo	int rv = -ENODEV;
56461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
5653dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_open.");
5664186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven
5674186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm = acm_table[tty->index];
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->dev)
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out;
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rv = 0;
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
57528d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91David Engraf	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
57610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty->driver_data = acm;
57810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, tty);
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
58094409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	if (usb_autopm_get_interface(acm->control) < 0)
58194409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum		goto early_bail;
58211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	else
58311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->control->needs_remote_wakeup = 1;
5841365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5851365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
58610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count++) {
5871365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_autopm_put_interface(acm->control);
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto done;
58910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
5901365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->dev = acm->dev;
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("usb_submit_urb(ctrl irq) failed");
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto bail_out;
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
597ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
598ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	    (acm->ctrl_caps & USB_CDC_CAP_LINE))
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto full_bailout;
60010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
60111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
60361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_urbs);
60461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_bufs);
60561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->filled_read_bufs);
6066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
6076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < acm->rx_buflimit; i++)
60861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
6096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < acm->rx_buflimit; i++)
61061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
61161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
612ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	acm->throttle = 0;
613ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
61461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
61510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	rv = tty_port_block_til_ready(&acm->port, tty, filp);
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone:
6171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
61874573ee7096a4ffc2f098108d21c85801b9c7434Alexey Dobriyanerr_out:
61994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rv;
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout:
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(acm->ctrlurb);
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out:
6251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_autopm_put_interface(acm->control);
62610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	acm->port.count--;
6271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
62894409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukumearly_bail:
62994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
63010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, NULL);
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EIO;
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
63483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm)
63583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{
63610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	int i, nr;
63761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
63886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
63983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_unregister_device(acm_tty_driver, acm->minor);
64083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_put_intf(acm->control);
64183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	acm_table[acm->minor] = NULL;
64283ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_free_urb(acm->ctrlurb);
643e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
644e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
64586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < nr; i++)
64661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
647c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	kfree(acm->country_codes);
64883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	kfree(acm);
64983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk}
65083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk
651e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownellstatic int acm_tty_chars_in_buffer(struct tty_struct *tty);
652e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
65310077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_port_down(struct acm *acm, int drain)
65410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
65510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	int i, nr = acm->rx_buflimit;
65610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	mutex_lock(&open_mutex);
65710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->dev) {
65810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_get_interface(acm->control);
65910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm_set_control(acm, acm->ctrlout = 0);
66010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		/* try letting the last writes drain naturally */
66110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		if (drain) {
66210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			wait_event_interruptible_timeout(acm->drain_wait,
66310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox				(ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
66410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox					ACM_CLOSE_TIMEOUT * HZ);
66510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		}
66610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_kill_urb(acm->ctrlurb);
66710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		for (i = 0; i < ACM_NW; i++)
66810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			usb_kill_urb(acm->wb[i].urb);
66910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		for (i = 0; i < nr; i++)
67010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			usb_kill_urb(acm->ru[i].urb);
67110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm->control->needs_remote_wakeup = 0;
67210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_put_interface(acm->control);
67310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
67410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	mutex_unlock(&open_mutex);
67510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
67610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
67710077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_tty_hangup(struct tty_struct *tty)
67810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
67910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct acm *acm = tty->driver_data;
68010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_hangup(&acm->port);
68110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	acm_port_down(acm, 0);
68210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
68310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp)
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
68810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	/* Perform the closing process and see if we need to do the hardware
68910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	   shutdown */
690922b13565b6a826a925f9f91f053dc9cb0d6210eThadeu Lima de Souza Cascardo	if (!acm || tty_port_close_start(&acm->port, tty, filp) == 0)
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
69210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	acm_port_down(acm, 0);
69310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_close_end(&acm->port, tty);
6944186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
69510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, NULL);
69610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (!acm->dev)
69710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm_tty_unregister(acm);
6984186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_write(struct tty_struct *tty,
7026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					const unsigned char *buf, int count)
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int stat;
706884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
707884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
708884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
709884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
7103dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_write to write %d bytes,", count);
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!count)
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
717884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
7186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	wbn = acm_wb_alloc(acm);
7196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (wbn < 0) {
720884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
721884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
722884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
723884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
725884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	count = (count > acm->writesize) ? acm->writesize : count;
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Get %d bytes...", count);
727884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	memcpy(wb->buf, buf, count);
728884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb->len = count;
729884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	stat = acm_write_start(acm, wbn);
7326e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (stat < 0)
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return stat;
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty)
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
742884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
743884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * Do not let the line discipline to know that we have a reserve,
744884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * or it might get too enthusiastic.
745884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
746934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	return acm_wb_is_avail(acm) ? acm->writesize : 0;
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty)
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
75323198fda7182969b619613a555f8645fdc3dc334Alan Cox		return 0;
754884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
755884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * This is inaccurate (overcounts), but it works.
756884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
75786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty)
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 1;
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty)
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 0;
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
77861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7819e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state)
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7849e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	int retval;
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7869e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox		return -EINVAL;
7879e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	retval = acm_send_break(acm, state ? 0xffff : 0);
7889e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	if (retval < 0)
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("send break failed");
7909e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	return retval;
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TIOCM_CTS;
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    unsigned int set, unsigned int clear)
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int newctrl;
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = acm->ctrlout;
8186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
8196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
8206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
8216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = (newctrl & ~clear) | set;
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->ctrlout == newctrl)
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return acm_set_control(acm, acm->ctrlout = newctrl);
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_ioctl(struct tty_struct *tty, struct file *file,
8316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					unsigned int cmd, unsigned long arg)
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8414c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = {
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 50, 75, 110, 134, 150, 200, 300, 600,
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1200, 1800, 2400, 4800, 9600, 19200, 38400,
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	57600, 115200, 230400, 460800, 500000, 576000,
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	921600, 1000000, 1152000, 1500000, 2000000,
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	2500000, 3000000, 3500000, 4000000
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8494c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = {
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7, 8
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8536e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic void acm_tty_set_termios(struct tty_struct *tty,
8546e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						struct ktermios *termios_old)
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
857606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox	struct ktermios *termios = tty->termios;
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_line_coding newline;
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl = acm->ctrlout;
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8646e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	/* FIXME: Needs to support the tty_baud interface */
8656e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	/* FIXME: Broken on sparc */
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bParityType = termios->c_cflag & PARENB ?
8706e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & PARODD ? 1 : 2) +
8716e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & CMSPAR ? 2 : 0) : 0;
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
8736e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	/* FIXME: Needs to clear unsupported bits in the termios */
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!newline.dwDTERate) {
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newline.dwDTERate = acm->line.dwDTERate;
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newctrl &= ~ACM_CTRL_DTR;
8796e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	} else
8806e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl |=  ACM_CTRL_DTR;
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (newctrl != acm->ctrlout)
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_control(acm, acm->ctrlout = newctrl);
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(&acm->line, &newline, sizeof newline)) {
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&acm->line, &newline, sizeof newline);
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bCharFormat, newline.bParityType,
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bDataBits);
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_line(acm, &acm->line);
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines.
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
898830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */
899884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm)
900884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
901884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
902884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
903a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
904884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
9056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
906a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum		usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);
907884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
908884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
909830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm)
910830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{
911830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
912830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	int i, n = acm->rx_buflimit;
913830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
914830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	for (i = 0; i < n; i++)
9156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		usb_buffer_free(usb_dev, acm->readsize,
9166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					acm->rb[i].base, acm->rb[i].dma);
917830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum}
918830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
919884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */
920884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm)
921884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
922884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
923884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
924884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
92586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
926884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
927884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		    &wb->dmah);
928884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->buf) {
929884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			while (i != 0) {
930884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--i;
931884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--wb;
932884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				usb_buffer_free(acm->dev, acm->writesize,
933884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				    wb->buf, wb->dmah);
934884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			}
935884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -ENOMEM;
936884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
937884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
938884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return 0;
939884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
940884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
94110077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic int acm_probe(struct usb_interface *intf,
94210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		     const struct usb_device_id *id)
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_union_desc *union_header = NULL;
945c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_cdc_country_functional_desc *cfd = NULL;
946c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell	unsigned char *buffer = intf->altsetting->extra;
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buflen = intf->altsetting->extralen;
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *control_interface;
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *data_interface;
950a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epctrl = NULL;
951a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epread = NULL;
952a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epwrite = NULL;
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor;
9566e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	int ctrlsize, readsize;
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *buf;
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 ac_management_function = 0;
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 call_management_function = 0;
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int call_interface_num = -1;
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int data_interface_num;
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long quirks;
96386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int num_rx_buf;
96461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
965a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	int combined_interfaces = 0;
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
96786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* normal quirks */
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	quirks = (unsigned long)id->driver_info;
96986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
97086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum
97186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* handle quirks deadly to normal probing*/
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (quirks == NO_UNION_NORMAL) {
9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, 1);
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, 0);
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto skip_normal_probe;
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9776e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* normal probing*/
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
9809908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "Weird descriptor references\n");
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buflen) {
9856e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (intf->cur_altsetting->endpoint->extralen &&
9866e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				intf->cur_altsetting->endpoint->extra) {
9876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
9886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Seeking extra descriptors on endpoint\n");
9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buflen = intf->cur_altsetting->endpoint->extralen;
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = intf->cur_altsetting->endpoint->extra;
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
9929908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev,
9939908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman				"Zero length descriptor references\n");
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (buflen > 0) {
9996e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (buffer[1] != USB_DT_CS_INTERFACE) {
10009908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev, "skipping garbage\n");
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto next_desc;
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		switch (buffer[2]) {
10056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_UNION_TYPE: /* we've found it */
10066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (union_header) {
10076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "More than one "
10086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"union descriptor, skipping ...\n");
10096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				goto next_desc;
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
10116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			union_header = (struct usb_cdc_union_desc *)buffer;
10126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
10146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			cfd = (struct usb_cdc_country_functional_desc *)buffer;
10156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_HEADER_TYPE: /* maybe check version */
10176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break; /* for now we ignore it */
10186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_ACM_TYPE:
10196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			ac_management_function = buffer[3];
10206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_CALL_MANAGEMENT_TYPE:
10226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_management_function = buffer[3];
10236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_interface_num = buffer[4];
10246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if ((call_management_function & 3) != 3)
10256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
10266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		default:
10286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			/* there are LOTS more CDC descriptors that
10296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 * could legitimately be found here.
10306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 */
10316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "Ignoring descriptor: "
10326e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"type %02x, length %d\n",
10336e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					buffer[2], buffer[0]);
10346e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc:
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buflen -= buffer[0];
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer += buffer[0];
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!union_header) {
10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (call_interface_num > 0) {
10436e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = intf;
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
1047a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1048a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1049a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -ENODEV;
1050a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			} else {
1051a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1052a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				combined_interfaces = 1;
1053a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				control_interface = data_interface = intf;
1054a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				goto look_for_collapsed_interface;
1055a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			}
10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!control_interface || !data_interface) {
10616e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "no interfaces\n");
10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10656e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface_num != call_interface_num)
10676e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1069a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (control_interface == data_interface) {
1070a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* some broken devices designed for windows work this way */
1071a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1072a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		combined_interfaces = 1;
1073a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* a popular other OS doesn't use it */
1074a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		quirks |= NO_CAP_LINE;
1075a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1076a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1077a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -EINVAL;
1078a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
1079a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukumlook_for_collapsed_interface:
1080a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		for (i = 0; i < 3; i++) {
1081a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			struct usb_endpoint_descriptor *ep;
1082a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			ep = &data_interface->cur_altsetting->endpoint[i].desc;
1083a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
1084a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (usb_endpoint_is_int_in(ep))
1085a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epctrl = ep;
1086a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_out(ep))
1087a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epwrite = ep;
1088a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_in(ep))
1089a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epread = ep;
1090a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else
1091a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -EINVAL;
1092a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
1093a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (!epctrl || !epread || !epwrite)
1094a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -ENODEV;
1095a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		else
1096a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			goto made_compressed_probe;
1097a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	}
1098a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe:
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*workaround for switched interfaces */
11026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (data_interface->cur_altsetting->desc.bInterfaceClass
11036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						!= CDC_DATA_INTERFACE_TYPE) {
11046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (control_interface->cur_altsetting->desc.bInterfaceClass
11056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						== CDC_DATA_INTERFACE_TYPE) {
11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_interface *t;
11076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
11086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Your device has switched interfaces.\n");
11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = control_interface;
11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = data_interface;
11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = t;
11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
111674da5d68a54d66667664fbe233ededab2376a070Alan Stern
111774da5d68a54d66667664fbe233ededab2376a070Alan Stern	/* Accept probe requests only for the control interface */
1118a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && intf != control_interface)
111974da5d68a54d66667664fbe233ededab2376a070Alan Stern		return -ENODEV;
11206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
1121a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1122a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* valid in this context */
11236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "The data interface isn't available\n");
11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epread = &data_interface->cur_altsetting->endpoint[0].desc;
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* workaround for switched endpoints */
113745aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino	if (!usb_endpoint_dir_in(epread)) {
11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* descriptors are swapped */
11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_endpoint_descriptor *t;
11406e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev,
11416e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			"The data interface has switched endpoints\n");
11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = epread;
11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epread = epwrite;
11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epwrite = t;
11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1146a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukummade_compressed_probe:
11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("interfaces are valid");
11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (minor == ACM_TTY_MINORS) {
11519908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "no more free acm devices\n");
11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11556e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
11566e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm == NULL) {
1157898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail;
11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
11626e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	readsize = le16_to_cpu(epread->wMaxPacketSize) *
11636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(quirks == SINGLE_RX_URB ? 1 : 2);
1164a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	acm->combined_interfaces = combined_interfaces;
1165e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->control = control_interface;
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->data = data_interface;
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->minor = minor;
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = usb_dev;
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_caps = ac_management_function;
1171a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (quirks & NO_CAP_LINE)
1172a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlsize = ctrlsize;
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->readsize = readsize;
117586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->rx_buflimit = num_rx_buf;
117661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.func = acm_rx_tasklet;
117761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.data = (unsigned long) acm;
1178c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&acm->work, acm_softint);
117911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	INIT_WORK(&acm->waker, acm_waker);
1180e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	init_waitqueue_head(&acm->drain_wait);
11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&acm->throttle_lock);
1182884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_init(&acm->write_lock);
118361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock_init(&acm->read_lock);
11841365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_init(&acm->mutex);
118561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
1186739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	tty_port_init(&acm->port);
1187739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	acm->port.ops = &acm_port_ops;
11881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buf) {
1191898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail2;
11931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_buffer = buf;
11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1196884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (acm_write_buffers_alloc(acm) < 0) {
1197898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
11981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail4;
11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->ctrlurb) {
1203898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail5;
12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
120686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
120761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		struct acm_ru *rcv = &(acm->ru[i]);
120861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
12096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
12106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (rcv->urb == NULL) {
12116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
12126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"out of memory (read urbs usb_alloc_urb)\n");
121361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
121461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
121561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
121661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
121761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->instance = acm;
121861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
121986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
1220672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		struct acm_rb *rb = &(acm->rb[i]);
122161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
1222672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		rb->base = usb_buffer_alloc(acm->dev, readsize,
1223672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell				GFP_KERNEL, &rb->dma);
1224672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		if (!rb->base) {
12256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
12266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"out of memory (read bufs usb_buffer_alloc)\n");
122761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
122861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
12291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++) {
1231e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		struct acm_wb *snd = &(acm->wb[i]);
1232e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
12336e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
12346e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (snd->urb == NULL) {
12356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
12366e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"out of memory (write urbs usb_alloc_urb)");
1237e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf			goto alloc_fail7;
1238e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		}
1239e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
12405186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		if (usb_endpoint_xfer_int(epwrite))
12415186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_int_urb(snd->urb, usb_dev,
12425186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
12435186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
12445186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		else
12455186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_bulk_urb(snd->urb, usb_dev,
12465186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
12475186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd);
1248e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1249e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->instance = acm;
12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12526e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_set_intfdata(intf, acm);
1253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1254c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (i < 0)
1256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		goto alloc_fail8;
1257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1258c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (cfd) { /* export the country data */
1259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1260c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (!acm->country_codes)
1261c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1262c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_code_size = cfd->bLength - 4;
12636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
12646e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox							cfd->bLength - 4);
1265c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_rel_date = cfd->iCountryCodeRelDate;
1266c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1267c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1268c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1269c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1270c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1271c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1272c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
12736e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		i = device_create_file(&intf->dev,
12746e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						&dev_attr_iCountryCodeRelDate);
1275c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1276c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1277c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1278c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1279c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
1280c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1281c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries:
12826e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_fill_int_urb(acm->ctrlurb, usb_dev,
1283a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1284a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1285a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 /* works around buggy devices */
1286a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 epctrl->bInterval ? epctrl->bInterval : 0xff);
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
12881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_control(acm, acm->ctrlout);
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.dwDTERate = cpu_to_le32(9600);
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.bDataBits = 8;
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_line(acm, &acm->line);
12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_driver_claim_interface(&acm_driver, data_interface, acm);
1299672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	usb_set_intfdata(data_interface, acm);
13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
130183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_get_intf(control_interface);
130283ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_table[minor] = acm;
13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1306c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return 0;
1307c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumalloc_fail8:
1308e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
1309e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
13101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7:
1311830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
131286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
131361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
13141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_free_urb(acm->ctrlurb);
13151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5:
1316884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
13171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4:
13181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
13191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2:
13201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acm);
13211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail:
13221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOMEM;
13231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm)
13261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int i;
132811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	dbg("Entering stop_data_traffic");
13291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_disable(&acm->urb_task);
13311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_kill_urb(acm->ctrlurb);
13336e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++)
1334e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_kill_urb(acm->wb[i].urb);
13351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
13361365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_kill_urb(acm->ru[i].urb);
13371365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13381365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_enable(&acm->urb_task);
13391365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13401365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	cancel_work_sync(&acm->work);
134111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cancel_work_sync(&acm->waker);
13421365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
13431365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf)
13451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1346c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
13471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
134810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
13491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1350672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	/* sibling interface is already cleaning up */
1351672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	if (!acm)
135286067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum		return;
1353672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell
1354672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	mutex_lock(&open_mutex);
13556e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm->country_codes) {
135674da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
135774da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_wCountryCodes);
135874da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
135974da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_iCountryCodeRelDate);
1360c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
136174da5d68a54d66667664fbe233ededab2376a070Alan Stern	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
13621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = NULL;
136386067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->control, NULL);
136486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->data, NULL);
13651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13661365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	stop_data_traffic(acm);
13671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1368884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
13696e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
13706e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox								acm->ctrl_dma);
1371830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
13721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1373a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!acm->combined_interfaces)
1374a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		usb_driver_release_interface(&acm_driver, intf == acm->control ?
1375830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum					acm->data : acm->control);
13761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
137710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count == 0) {
137883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		acm_tty_unregister(acm);
13794186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
13801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
13811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
13821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13834186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
138410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
138510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (tty) {
138610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		tty_hangup(tty);
138710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		tty_kref_put(tty);
138810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
13891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
13921365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message)
13931365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13941365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
139511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
139611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
139765bfd2967c906ca322a4bb69a285fe0de8916ac6Alan Stern	if (message.event & PM_EVENT_AUTO) {
139811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		int b;
139911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
140011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irq(&acm->read_lock);
140111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock(&acm->write_lock);
140211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		b = acm->processing + acm->transmitting;
140311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock(&acm->write_lock);
140411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irq(&acm->read_lock);
140511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (b)
140611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			return -EBUSY;
140711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
140811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
140911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
141011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock(&acm->write_lock);
141111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count++;
141211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock(&acm->write_lock);
141311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
14141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
141511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
14161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
14171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	/*
14181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we treat opened interfaces differently,
14191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we must guard against open
14201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	*/
14211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
14221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
142310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count)
14241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		stop_data_traffic(acm);
14251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
14271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return 0;
14281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
14291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf)
14311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
14321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
14331365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int rv = 0;
143411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
14351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
143611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
143711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->susp_count -= 1;
143811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count;
143911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
144011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
144111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
14421365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
14431365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14441365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
144510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count) {
14461365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
14471365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		if (rv < 0)
144811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			goto err_out;
14491365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14501365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		tasklet_schedule(&acm->urb_task);
14511365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	}
14521365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out:
14541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
14551365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return rv;
14561365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
1457357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum
1458357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */
14591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
14601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure.
14611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id acm_ids[] = {
14641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* quirky and broken devices */
14651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
14661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1468b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1469b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1470b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	},
14710f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	{ USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
14720f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14730f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	},
14748753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
14758753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14768753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	},
147791a9c9214e34c364bf15406aadb922787ae7129bChris Malley	{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
147891a9c9214e34c364bf15406aadb922787ae7129bChris Malley	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
147991a9c9214e34c364bf15406aadb922787ae7129bChris Malley	},
14807abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	{ USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
14817abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	.driver_info = SINGLE_RX_URB,
14827abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	},
148386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
148486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
148586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	},
14863dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
14873dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
14883dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	},
14899be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
14909be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14919be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	},
14926149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	{ USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
14936149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14946149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	},
1495c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	{ USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1496c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1497c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	},
1498c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	{ USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1499c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1500c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	},
1501cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	{ USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1502cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1503cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	},
1504155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1505155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	},
1506c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	{ USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1507c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	.driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1508c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   data interface instead of
1509c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   communications interface.
1510c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   Maybe we should define a new
1511c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   quirk for this. */
1512c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	},
15131f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	{ USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
15141f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
15151f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	},
15169be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum
15171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* control interfaces with various AT-command sets */
15181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_V25TER) },
15201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101) },
15221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
15241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_GSM) },
15261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		USB_CDC_ACM_PROTO_AT_3G) },
15281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_CDMA) },
15301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* NOTE:  COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
15321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
15331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
15341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DEVICE_TABLE(usb, acm_ids);
15361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = {
15381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"cdc_acm",
15391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	acm_probe,
15401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	acm_disconnect,
1541357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
15421365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.suspend =	acm_suspend,
15431365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.resume =	acm_resume,
1544357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
15451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	acm_ids,
1546357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
15471365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.supports_autosuspend = 1,
1548357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
15491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
15501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
15521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures.
15531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
15541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1555b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = {
15561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			acm_tty_open,
15571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		acm_tty_close,
155810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	.hangup =		acm_tty_hangup,
15591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		acm_tty_write,
15601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		acm_tty_write_room,
15611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		acm_tty_ioctl,
15621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		acm_tty_throttle,
15631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		acm_tty_unthrottle,
15641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	acm_tty_chars_in_buffer,
15651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl =		acm_tty_break_ctl,
15661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios =		acm_tty_set_termios,
15671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmget =		acm_tty_tiocmget,
15681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmset =		acm_tty_tiocmset,
15691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
15701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
15721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit.
15731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
15741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void)
15761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
15771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
15781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
15791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm_tty_driver)
15801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
15811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->owner = THIS_MODULE,
15821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->driver_name = "acm",
15831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->name = "ttyACM",
15841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->major = ACM_TTY_MAJOR,
15851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->minor_start = 0,
15861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
15871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
1588331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
15891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios = tty_std_termios;
15906e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
15916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox								HUPCL | CLOCAL;
15921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(acm_tty_driver, &acm_ops);
15931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = tty_register_driver(acm_tty_driver);
15951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
15961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
15971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
15981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
15991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&acm_driver);
16011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
16021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_unregister_driver(acm_tty_driver);
16031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
16041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
16051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16075909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
16085909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman	       DRIVER_DESC "\n");
16091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
16111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void)
16141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister(&acm_driver);
16161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_unregister_driver(acm_tty_driver);
16171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	put_tty_driver(acm_tty_driver);
16181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init);
16211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit);
16221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_AUTHOR(DRIVER_AUTHOR);
16246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DESCRIPTION(DRIVER_DESC);
16251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
1626e766aeb882b41355d8732cf49aa9412baef852c5Scott James RemnantMODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);
1627