cdc-acm.c revision a91b0c502285fd0c569fae1222fdd945ef739233
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>
627af25b4b34a2439020d78da765a3bed0ff73f25cOliver Neukum#include <linux/serial.h>
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h>
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
664186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h>
6710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#include <linux/uaccess.h>
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
69a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h>
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h>
7261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h>
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h"
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
76e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
77e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define ACM_CLOSE_TIMEOUT	15	/* seconds to let writes drain */
78e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
82e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf#define DRIVER_VERSION "v0.26"
8361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver;
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver;
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS];
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
904186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex);
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#define ACM_READY(acm)	(acm && acm->dev && acm->port.count)
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
94739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Coxstatic const struct tty_port_operations acm_port_ops = {
95739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox};
96739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox
97e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#ifdef VERBOSE_DEBUG
98e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define verbose	1
99e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#else
100e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define verbose	0
101e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#endif
102e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages.
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_ctrl_msg(struct acm *acm, int request, int value,
1086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox							void *buf, int len)
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		request, USB_RT_ACM, value,
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm->control->altsetting[0].desc.bInterfaceNumber,
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf, len, 5000);
1146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d",
1156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						request, value, len, retval);
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval < 0 ? retval : 0;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests.
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do...
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
130884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management.
131884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller.
132884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
133884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
134884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm)
135884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
136884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, wbn;
137884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
138884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
139e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wbn = 0;
140884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	i = 0;
141884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	for (;;) {
142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb = &acm->wb[wbn];
143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->use) {
144884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			wb->use = 1;
145884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return wbn;
146884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
14786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		wbn = (wbn + 1) % ACM_NW;
14886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		if (++i >= ACM_NW)
149884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -1;
150884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
151884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
152884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
153884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm)
154884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
155884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, n;
156e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	unsigned long flags;
157884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
15886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	n = ACM_NW;
159e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	spin_lock_irqsave(&acm->write_lock, flags);
1606e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++)
16186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		n -= acm->wb[i].use;
162e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	spin_unlock_irqrestore(&acm->write_lock, flags);
163884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return n;
164884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
165884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
166884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
167ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips * Finish write. Caller must hold acm->write_lock
168884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
169e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic void acm_write_done(struct acm *acm, struct acm_wb *wb)
170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
171e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wb->use = 0;
17211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting--;
173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
175884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
176884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write.
17711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum *
17811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking
179884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
18011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb)
18211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
18311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rc;
18411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting++;
18611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer = wb->buf;
18811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_dma = wb->dmah;
18911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer_length = wb->len;
19011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->dev = acm->dev;
19111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
1926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
1936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (rc < 0) {
19411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("usb_submit_urb(write bulk) failed: %d", rc);
19511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_write_done(acm, wb);
19611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
19711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	return rc;
19811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
19911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
200e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn)
201884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
202884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
203934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	struct acm_wb *wb = &acm->wb[wbn];
204884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int rc;
205884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
206884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
207884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->dev) {
208934da4635c2d05cef474e5243ef05df95b2ad264David Brownell		wb->use = 0;
209884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
210884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return -ENODEV;
211884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
212884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
21311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	dbg("%s susp_count: %d", __func__, acm->susp_count);
21411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->susp_count) {
21511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->delayed_wb = wb;
21611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		schedule_work(&acm->waker);
21711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
21811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return 0;	/* A white lie */
21911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
22011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
22111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
22211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rc = acm_start_wb(acm, wb);
223884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
224884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
225884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return rc;
22611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
227884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/*
229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs
230c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */
231c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps
232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
237c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->ctrl_caps);
238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes
242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	memcpy(buf, acm->country_codes, acm->country_code_size);
248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return acm->country_code_size;
249c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
250c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date
254c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
258c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->country_rel_date);
260c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
261884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
262c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
263884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */
2687d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb)
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = urb->context;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_notification *dr = urb->transfer_buffer;
27210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data;
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl;
275185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int retval;
276185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int status = urb->status;
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
278185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	switch (status) {
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* success */
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ECONNRESET:
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ENOENT:
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ESHUTDOWN:
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this urb is terminated, clean up */
286441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - urb shutting down with status: %d", __func__, status);
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
289441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - nonzero urb status received: %d", __func__, status);
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = (unsigned char *)(dr + 1);
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (dr->bNotificationType) {
2986e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
2996e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dbg("%s network", dr->wValue ?
3006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"connected to" : "disconnected from");
3016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		break;
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	case USB_CDC_NOTIFY_SERIAL_STATE:
3046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		tty = tty_port_tty_get(&acm->port);
3056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl = get_unaligned_le16(data);
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (tty) {
3086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (!acm->clocal &&
3096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
3106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dbg("calling hangup");
3116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				tty_hangup(tty);
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			tty_kref_put(tty);
3146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		acm->ctrlin = newctrl;
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
3196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
3206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
3216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
3226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
3236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
3246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
3256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	default:
3296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
3306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dr->bNotificationType, dr->wIndex,
3316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dr->wLength, data[0], data[1]);
3326e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		break;
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
33511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
3366e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	retval = usb_submit_urb(urb, GFP_ATOMIC);
337185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	if (retval)
3389908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
3399908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			"result %d", __func__, retval);
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */
3437d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_read_bulk(struct urb *urb)
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
34561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
34661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv = urb->context;
34761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm *acm = rcv->instance;
34886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int status = urb->status;
349185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman
350185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	dbg("Entering acm_read_bulk with status %d", status);
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
35211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (!ACM_READY(acm)) {
35311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dev_dbg(&acm->data->dev, "Aborting, acm not ready");
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
35511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
35611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
35886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (status)
359898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
36161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = rcv->buffer;
36261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf->size = urb->actual_length;
36361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
36486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (likely(status == 0)) {
36586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
36611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->processing++;
36786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
36886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&buf->list, &acm->filled_read_bufs);
36986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
37086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	} else {
37186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* we drop the buffer due to an error */
37286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
37386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
37486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add(&buf->list, &acm->spare_read_bufs);
37586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
37686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* nevertheless the tasklet must be kicked unconditionally
37786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		so the queue cannot dry up */
37886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	}
37911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (likely(!acm->susp_count))
38011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		tasklet_schedule(&acm->urb_task);
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm)
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = (void *)_acm;
38661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
38710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
38861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv;
389762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	unsigned long flags;
390ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	unsigned char throttled;
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
466cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum		if (acm->is_int_ep)
4675186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_int_urb(rcv->urb, acm->dev,
4685186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 acm->rx_endpoint,
4695186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 buf->base,
4705186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 acm->readsize,
471cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum					 acm_read_bulk, rcv, acm->bInterval);
4725186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		else
4735186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_bulk_urb(rcv->urb, acm->dev,
4745186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  acm->rx_endpoint,
4755186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  buf->base,
4765186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  acm->readsize,
4775186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  acm_read_bulk, rcv);
47861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_dma = buf->dma;
47961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
48061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
4816e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		/* This shouldn't kill the driver as unsuccessful URBs are
4826e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		   returned to the free-urbs-pool and resubmited ASAP */
48311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irqsave(&acm->read_lock, flags);
4846e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (acm->susp_count ||
4856e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
48661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&buf->list, &acm->spare_read_bufs);
48761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&rcv->list, &acm->spare_read_urbs);
48811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
489762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
49061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
49111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		} else {
49211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			spin_unlock_irqrestore(&acm->read_lock, flags);
49311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
49461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
49561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
49611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irqsave(&acm->read_lock, flags);
49711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->processing = 0;
49811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irqrestore(&acm->read_lock, flags);
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */
5027d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb)
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
504cdc97792289179974af6dda781c855696358d307Ming Lei	struct acm_wb *wb = urb->context;
505e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	struct acm *acm = wb->instance;
506ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	unsigned long flags;
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
508e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	if (verbose || urb->status
509e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			|| (urb->actual_length != urb->transfer_buffer_length))
510e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell		dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
511e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->actual_length,
512e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->transfer_buffer_length,
513e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->status);
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
515ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_lock_irqsave(&acm->write_lock, flags);
516e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm_write_done(acm, wb);
517ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_unlock_irqrestore(&acm->write_lock, flags);
518884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (ACM_READY(acm))
519884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		schedule_work(&acm->work);
520e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	else
521e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell		wake_up_interruptible(&acm->drain_wait);
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
524c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work)
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
526c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct acm *acm = container_of(work, struct acm, work);
52710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
528e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
529e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	dev_vdbg(&acm->data->dev, "tx work\n");
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
53210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
53310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_wakeup(tty);
53410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_kref_put(tty);
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
53711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic void acm_waker(struct work_struct *waker)
53811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
53911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	struct acm *acm = container_of(waker, struct acm, waker);
54011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rv;
54111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
54211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rv = usb_autopm_get_interface(acm->control);
54311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (rv < 0) {
5449908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__);
54511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return;
54611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
54711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->delayed_wb) {
54811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_start_wb(acm, acm->delayed_wb);
54911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->delayed_wb = NULL;
55011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
55111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
55211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
55311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp)
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
56142dd2aa6496a2e87e496aac5494d2e1d6096c85bThadeu Lima de Souza Cascardo	int rv = -ENODEV;
56261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
5633dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_open.");
5644186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven
5654186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm = acm_table[tty->index];
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->dev)
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out;
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rv = 0;
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
57328d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91David Engraf	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
57410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty->driver_data = acm;
57610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, tty);
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
57894409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	if (usb_autopm_get_interface(acm->control) < 0)
57994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum		goto early_bail;
58011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	else
58111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->control->needs_remote_wakeup = 1;
5821365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5831365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
58410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count++) {
5851365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_autopm_put_interface(acm->control);
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto done;
58710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
5881365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->dev = acm->dev;
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("usb_submit_urb(ctrl irq) failed");
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto bail_out;
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
595ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
596ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	    (acm->ctrl_caps & USB_CDC_CAP_LINE))
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto full_bailout;
59810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
59911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
60161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_urbs);
60261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_bufs);
60361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->filled_read_bufs);
6046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
6056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < acm->rx_buflimit; i++)
60661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
6076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < acm->rx_buflimit; i++)
60861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
60961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
610ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	acm->throttle = 0;
611ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
6127af25b4b34a2439020d78da765a3bed0ff73f25cOliver Neukum	set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
61310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	rv = tty_port_block_til_ready(&acm->port, tty, filp);
61418a77b5d237a67d2c621a46f5271a3b51da1b380Henry Gebhardt	tasklet_schedule(&acm->urb_task);
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone:
6161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
61774573ee7096a4ffc2f098108d21c85801b9c7434Alexey Dobriyanerr_out:
61894409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rv;
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout:
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(acm->ctrlurb);
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out:
6241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_autopm_put_interface(acm->control);
62510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	acm->port.count--;
6261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
62794409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukumearly_bail:
62894409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
62910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, NULL);
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EIO;
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
63383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm)
63483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{
63510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	int i, nr;
63661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
63786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
63883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_unregister_device(acm_tty_driver, acm->minor);
63983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_put_intf(acm->control);
64083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	acm_table[acm->minor] = NULL;
64183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_free_urb(acm->ctrlurb);
642e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
643e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
64486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < nr; i++)
64561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
646c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	kfree(acm->country_codes);
64783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	kfree(acm);
64883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk}
64983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk
650e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownellstatic int acm_tty_chars_in_buffer(struct tty_struct *tty);
651e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
65210077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_port_down(struct acm *acm, int drain)
65310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
65410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	int i, nr = acm->rx_buflimit;
65510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	mutex_lock(&open_mutex);
65610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->dev) {
65710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_get_interface(acm->control);
65810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm_set_control(acm, acm->ctrlout = 0);
65910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		/* try letting the last writes drain naturally */
66010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		if (drain) {
66110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			wait_event_interruptible_timeout(acm->drain_wait,
66210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox				(ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
66310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox					ACM_CLOSE_TIMEOUT * HZ);
66410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		}
66510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_kill_urb(acm->ctrlurb);
66610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		for (i = 0; i < ACM_NW; i++)
66710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			usb_kill_urb(acm->wb[i].urb);
66810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		for (i = 0; i < nr; i++)
66910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			usb_kill_urb(acm->ru[i].urb);
67010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm->control->needs_remote_wakeup = 0;
67110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_put_interface(acm->control);
67210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
67310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	mutex_unlock(&open_mutex);
67410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
67510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
67610077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_tty_hangup(struct tty_struct *tty)
67710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
67810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct acm *acm = tty->driver_data;
67910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_hangup(&acm->port);
68010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	acm_port_down(acm, 0);
68110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
68210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp)
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
68710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	/* Perform the closing process and see if we need to do the hardware
68810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	   shutdown */
689051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	if (!acm)
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
691051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	if (tty_port_close_start(&acm->port, tty, filp) == 0) {
692051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		mutex_lock(&open_mutex);
693051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		if (!acm->dev) {
694051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			tty_port_tty_set(&acm->port, NULL);
695051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			acm_tty_unregister(acm);
696051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			tty->driver_data = NULL;
697051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		}
698051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		mutex_unlock(&open_mutex);
699051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		return;
700051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	}
70110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	acm_port_down(acm, 0);
70210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_close_end(&acm->port, tty);
70310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, NULL);
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_write(struct tty_struct *tty,
7076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					const unsigned char *buf, int count)
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int stat;
711884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
712884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
713884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
714884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
7153dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_write to write %d bytes,", count);
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!count)
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
722884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
7236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	wbn = acm_wb_alloc(acm);
7246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (wbn < 0) {
725884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
726884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
727884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
728884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
730884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	count = (count > acm->writesize) ? acm->writesize : count;
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Get %d bytes...", count);
732884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	memcpy(wb->buf, buf, count);
733884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb->len = count;
734884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7366e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	stat = acm_write_start(acm, wbn);
7376e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (stat < 0)
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return stat;
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty)
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
747884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
748884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * Do not let the line discipline to know that we have a reserve,
749884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * or it might get too enthusiastic.
750884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
751934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	return acm_wb_is_avail(acm) ? acm->writesize : 0;
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty)
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
75823198fda7182969b619613a555f8645fdc3dc334Alan Cox		return 0;
759884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
760884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * This is inaccurate (overcounts), but it works.
761884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
76286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty)
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 1;
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty)
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 0;
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
78361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7869e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state)
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7899e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	int retval;
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7919e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox		return -EINVAL;
7929e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	retval = acm_send_break(acm, state ? 0xffff : 0);
7939e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	if (retval < 0)
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("send break failed");
7959e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	return retval;
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TIOCM_CTS;
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    unsigned int set, unsigned int clear)
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int newctrl;
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = acm->ctrlout;
8236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
8246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
8256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
8266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = (newctrl & ~clear) | set;
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->ctrlout == newctrl)
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return acm_set_control(acm, acm->ctrlout = newctrl);
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_ioctl(struct tty_struct *tty, struct file *file,
8366e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					unsigned int cmd, unsigned long arg)
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8464c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = {
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 50, 75, 110, 134, 150, 200, 300, 600,
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1200, 1800, 2400, 4800, 9600, 19200, 38400,
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	57600, 115200, 230400, 460800, 500000, 576000,
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	921600, 1000000, 1152000, 1500000, 2000000,
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	2500000, 3000000, 3500000, 4000000
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8544c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = {
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7, 8
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8586e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic void acm_tty_set_termios(struct tty_struct *tty,
8596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						struct ktermios *termios_old)
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
862606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox	struct ktermios *termios = tty->termios;
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_line_coding newline;
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl = acm->ctrlout;
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8699b80fee149a875a6292b2556ab2c64dc7ab7d6f5Alan Cox	newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bParityType = termios->c_cflag & PARENB ?
8726e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & PARODD ? 1 : 2) +
8736e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & CMSPAR ? 2 : 0) : 0;
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
8756e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	/* FIXME: Needs to clear unsupported bits in the termios */
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!newline.dwDTERate) {
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newline.dwDTERate = acm->line.dwDTERate;
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newctrl &= ~ACM_CTRL_DTR;
8816e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	} else
8826e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl |=  ACM_CTRL_DTR;
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (newctrl != acm->ctrlout)
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_control(acm, acm->ctrlout = newctrl);
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(&acm->line, &newline, sizeof newline)) {
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&acm->line, &newline, sizeof newline);
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bCharFormat, newline.bParityType,
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bDataBits);
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_line(acm, &acm->line);
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines.
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
900830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */
901884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm)
902884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
903884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
904884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
905a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
906884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
9076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
908a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum		usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);
909884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
910884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
911830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm)
912830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{
913830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
914830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	int i, n = acm->rx_buflimit;
915830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
916830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	for (i = 0; i < n; i++)
9176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		usb_buffer_free(usb_dev, acm->readsize,
9186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					acm->rb[i].base, acm->rb[i].dma);
919830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum}
920830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
921884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */
922884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm)
923884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
924884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
925884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
926884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
92786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
928884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
929884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		    &wb->dmah);
930884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->buf) {
931884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			while (i != 0) {
932884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--i;
933884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--wb;
934884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				usb_buffer_free(acm->dev, acm->writesize,
935884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				    wb->buf, wb->dmah);
936884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			}
937884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -ENOMEM;
938884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
939884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
940884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return 0;
941884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
942884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
94310077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic int acm_probe(struct usb_interface *intf,
94410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		     const struct usb_device_id *id)
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_union_desc *union_header = NULL;
947c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_cdc_country_functional_desc *cfd = NULL;
948c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell	unsigned char *buffer = intf->altsetting->extra;
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buflen = intf->altsetting->extralen;
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *control_interface;
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *data_interface;
952a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epctrl = NULL;
953a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epread = NULL;
954a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epwrite = NULL;
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor;
9586e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	int ctrlsize, readsize;
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *buf;
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 ac_management_function = 0;
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 call_management_function = 0;
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int call_interface_num = -1;
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int data_interface_num;
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long quirks;
96586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int num_rx_buf;
96661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
967a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	int combined_interfaces = 0;
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
96986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* normal quirks */
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	quirks = (unsigned long)id->driver_info;
97186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
97286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum
97386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* handle quirks deadly to normal probing*/
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (quirks == NO_UNION_NORMAL) {
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, 1);
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, 0);
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto skip_normal_probe;
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9796e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* normal probing*/
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
9829908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "Weird descriptor references\n");
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buflen) {
9876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (intf->cur_altsetting->endpoint->extralen &&
9886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				intf->cur_altsetting->endpoint->extra) {
9896e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
9906e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Seeking extra descriptors on endpoint\n");
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buflen = intf->cur_altsetting->endpoint->extralen;
9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = intf->cur_altsetting->endpoint->extra;
9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
9949908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev,
9959908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman				"Zero length descriptor references\n");
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (buflen > 0) {
10016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (buffer[1] != USB_DT_CS_INTERFACE) {
10029908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev, "skipping garbage\n");
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto next_desc;
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		switch (buffer[2]) {
10076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_UNION_TYPE: /* we've found it */
10086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (union_header) {
10096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "More than one "
10106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"union descriptor, skipping ...\n");
10116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				goto next_desc;
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
10136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			union_header = (struct usb_cdc_union_desc *)buffer;
10146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
10166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			cfd = (struct usb_cdc_country_functional_desc *)buffer;
10176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_HEADER_TYPE: /* maybe check version */
10196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break; /* for now we ignore it */
10206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_ACM_TYPE:
10216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			ac_management_function = buffer[3];
10226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_CALL_MANAGEMENT_TYPE:
10246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_management_function = buffer[3];
10256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_interface_num = buffer[4];
10266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if ((call_management_function & 3) != 3)
10276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
10286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		default:
10306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			/* there are LOTS more CDC descriptors that
10316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 * could legitimately be found here.
10326e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 */
10336e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "Ignoring descriptor: "
10346e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"type %02x, length %d\n",
10356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					buffer[2], buffer[0]);
10366e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10376e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc:
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buflen -= buffer[0];
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer += buffer[0];
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!union_header) {
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (call_interface_num > 0) {
10456e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = intf;
10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
1049a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1050a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1051a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -ENODEV;
1052a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			} else {
1053a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1054a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				combined_interfaces = 1;
1055a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				control_interface = data_interface = intf;
1056a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				goto look_for_collapsed_interface;
1057a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			}
10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!control_interface || !data_interface) {
10636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "no interfaces\n");
10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10676e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface_num != call_interface_num)
10696e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1071a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (control_interface == data_interface) {
1072a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* some broken devices designed for windows work this way */
1073a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1074a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		combined_interfaces = 1;
1075a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* a popular other OS doesn't use it */
1076a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		quirks |= NO_CAP_LINE;
1077a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1078a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1079a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -EINVAL;
1080a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
1081a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukumlook_for_collapsed_interface:
1082a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		for (i = 0; i < 3; i++) {
1083a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			struct usb_endpoint_descriptor *ep;
1084a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			ep = &data_interface->cur_altsetting->endpoint[i].desc;
1085a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
1086a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (usb_endpoint_is_int_in(ep))
1087a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epctrl = ep;
1088a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_out(ep))
1089a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epwrite = ep;
1090a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_in(ep))
1091a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epread = ep;
1092a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else
1093a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -EINVAL;
1094a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
1095a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (!epctrl || !epread || !epwrite)
1096a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -ENODEV;
1097a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		else
1098a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			goto made_compressed_probe;
1099a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	}
1100a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe:
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*workaround for switched interfaces */
11046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (data_interface->cur_altsetting->desc.bInterfaceClass
11056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						!= CDC_DATA_INTERFACE_TYPE) {
11066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (control_interface->cur_altsetting->desc.bInterfaceClass
11076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						== CDC_DATA_INTERFACE_TYPE) {
11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_interface *t;
11096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
11106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Your device has switched interfaces.\n");
11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = control_interface;
11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = data_interface;
11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = t;
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
111874da5d68a54d66667664fbe233ededab2376a070Alan Stern
111974da5d68a54d66667664fbe233ededab2376a070Alan Stern	/* Accept probe requests only for the control interface */
1120a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && intf != control_interface)
112174da5d68a54d66667664fbe233ededab2376a070Alan Stern		return -ENODEV;
11226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
1123a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1124a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* valid in this context */
11256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "The data interface isn't available\n");
11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epread = &data_interface->cur_altsetting->endpoint[0].desc;
11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* workaround for switched endpoints */
113945aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino	if (!usb_endpoint_dir_in(epread)) {
11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* descriptors are swapped */
11411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_endpoint_descriptor *t;
11426e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev,
11436e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			"The data interface has switched endpoints\n");
11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = epread;
11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epread = epwrite;
11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epwrite = t;
11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1148a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukummade_compressed_probe:
11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("interfaces are valid");
11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (minor == ACM_TTY_MINORS) {
11539908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "no more free acm devices\n");
11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
11551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11576e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
11586e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm == NULL) {
1159898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail;
11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
11646e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	readsize = le16_to_cpu(epread->wMaxPacketSize) *
11656e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(quirks == SINGLE_RX_URB ? 1 : 2);
1166a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	acm->combined_interfaces = combined_interfaces;
1167e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->control = control_interface;
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->data = data_interface;
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->minor = minor;
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = usb_dev;
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_caps = ac_management_function;
1173a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (quirks & NO_CAP_LINE)
1174a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlsize = ctrlsize;
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->readsize = readsize;
117786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->rx_buflimit = num_rx_buf;
117861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.func = acm_rx_tasklet;
117961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.data = (unsigned long) acm;
1180c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&acm->work, acm_softint);
118111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	INIT_WORK(&acm->waker, acm_waker);
1182e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	init_waitqueue_head(&acm->drain_wait);
11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&acm->throttle_lock);
1184884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_init(&acm->write_lock);
118561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock_init(&acm->read_lock);
11861365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_init(&acm->mutex);
118761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
1188cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum	acm->is_int_ep = usb_endpoint_xfer_int(epread);
1189cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum	if (acm->is_int_ep)
1190cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum		acm->bInterval = epread->bInterval;
1191739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	tty_port_init(&acm->port);
1192739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	acm->port.ops = &acm_port_ops;
11931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buf) {
1196898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
11971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail2;
11981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_buffer = buf;
12001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1201884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (acm_write_buffers_alloc(acm) < 0) {
1202898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail4;
12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
12071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->ctrlurb) {
1208898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail5;
12101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
121186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
121261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		struct acm_ru *rcv = &(acm->ru[i]);
121361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
12146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
12156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (rcv->urb == NULL) {
12166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
12176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"out of memory (read urbs usb_alloc_urb)\n");
121861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
121961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
122061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
122161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
122261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->instance = acm;
122361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
122486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
1225672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		struct acm_rb *rb = &(acm->rb[i]);
122661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
1227672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		rb->base = usb_buffer_alloc(acm->dev, readsize,
1228672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell				GFP_KERNEL, &rb->dma);
1229672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		if (!rb->base) {
12306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
12316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"out of memory (read bufs usb_buffer_alloc)\n");
123261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
123361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
12341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++) {
1236e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		struct acm_wb *snd = &(acm->wb[i]);
1237e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
12386e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
12396e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (snd->urb == NULL) {
12406e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
12416e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"out of memory (write urbs usb_alloc_urb)");
1242e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf			goto alloc_fail7;
1243e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		}
1244e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
12455186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		if (usb_endpoint_xfer_int(epwrite))
12465186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_int_urb(snd->urb, usb_dev,
12475186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
12485186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
12495186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		else
12505186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_bulk_urb(snd->urb, usb_dev,
12515186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
12525186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd);
1253e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1254e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->instance = acm;
12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12576e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_set_intfdata(intf, acm);
1258c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1260c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (i < 0)
1261c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		goto alloc_fail8;
1262c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1263c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (cfd) { /* export the country data */
1264c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1265c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (!acm->country_codes)
1266c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1267c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_code_size = cfd->bLength - 4;
12686e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
12696e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox							cfd->bLength - 4);
1270c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_rel_date = cfd->iCountryCodeRelDate;
1271c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1272c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1273c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1274c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1275c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1276c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1277c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
12786e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		i = device_create_file(&intf->dev,
12796e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						&dev_attr_iCountryCodeRelDate);
1280c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1281c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1282c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1283c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1284c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
1285c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1286c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries:
12876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_fill_int_urb(acm->ctrlurb, usb_dev,
1288a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1289a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1290a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 /* works around buggy devices */
1291a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 epctrl->bInterval ? epctrl->bInterval : 0xff);
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_control(acm, acm->ctrlout);
12981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.dwDTERate = cpu_to_le32(9600);
13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.bDataBits = 8;
13011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_line(acm, &acm->line);
13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_driver_claim_interface(&acm_driver, data_interface, acm);
1304672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	usb_set_intfdata(data_interface, acm);
13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
130683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_get_intf(control_interface);
130783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_table[minor] = acm;
13101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1311c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return 0;
1312c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumalloc_fail8:
1313e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
1314e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
13151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7:
1316830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
131786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
131861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
13191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_free_urb(acm->ctrlurb);
13201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5:
1321884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
13221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4:
13231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
13241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2:
13251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acm);
13261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail:
13271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOMEM;
13281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm)
13311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int i;
133311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	dbg("Entering stop_data_traffic");
13341365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_disable(&acm->urb_task);
13361365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13371365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_kill_urb(acm->ctrlurb);
13386e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++)
1339e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_kill_urb(acm->wb[i].urb);
13401365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
13411365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_kill_urb(acm->ru[i].urb);
13421365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13431365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_enable(&acm->urb_task);
13441365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13451365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	cancel_work_sync(&acm->work);
134611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cancel_work_sync(&acm->waker);
13471365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
13481365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf)
13501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1351c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
13521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
135310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
13541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1355672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	/* sibling interface is already cleaning up */
1356672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	if (!acm)
135786067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum		return;
1358672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell
1359672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	mutex_lock(&open_mutex);
13606e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm->country_codes) {
136174da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
136274da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_wCountryCodes);
136374da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
136474da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_iCountryCodeRelDate);
1365c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
136674da5d68a54d66667664fbe233ededab2376a070Alan Stern	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
13671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = NULL;
136886067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->control, NULL);
136986067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->data, NULL);
13701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13711365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	stop_data_traffic(acm);
13721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1373884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
13746e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
13756e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox								acm->ctrl_dma);
1376830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
13771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1378a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!acm->combined_interfaces)
1379a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		usb_driver_release_interface(&acm_driver, intf == acm->control ?
1380830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum					acm->data : acm->control);
13811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
138210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count == 0) {
138383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		acm_tty_unregister(acm);
13844186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
13851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
13861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
13871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13884186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
138910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
139010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (tty) {
139110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		tty_hangup(tty);
139210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		tty_kref_put(tty);
139310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
13941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1396357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
13971365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message)
13981365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
140011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
140111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
140265bfd2967c906ca322a4bb69a285fe0de8916ac6Alan Stern	if (message.event & PM_EVENT_AUTO) {
140311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		int b;
140411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
140511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irq(&acm->read_lock);
140611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock(&acm->write_lock);
140711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		b = acm->processing + acm->transmitting;
140811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock(&acm->write_lock);
140911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irq(&acm->read_lock);
141011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (b)
141111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			return -EBUSY;
141211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
141311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
141411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
141511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock(&acm->write_lock);
141611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count++;
141711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock(&acm->write_lock);
141811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
14191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
142011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
14211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
14221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	/*
14231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we treat opened interfaces differently,
14241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we must guard against open
14251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	*/
14261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
14271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
142810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count)
14291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		stop_data_traffic(acm);
14301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
14321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return 0;
14331365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
14341365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf)
14361365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
14371365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
14381365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int rv = 0;
143911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
14401365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
144111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
144211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->susp_count -= 1;
144311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count;
144411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
144511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
144611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
14471365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
14481365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14491365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
145010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count) {
14511365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
14521365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		if (rv < 0)
145311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			goto err_out;
14541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14551365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		tasklet_schedule(&acm->urb_task);
14561365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	}
14571365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14581365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out:
14591365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
14601365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return rv;
14611365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
1462357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum
1463a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavrastatic int acm_reset_resume(struct usb_interface *intf)
1464a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra{
1465a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	struct acm *acm = usb_get_intfdata(intf);
1466a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	struct tty_struct *tty;
1467a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra
1468a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	mutex_lock(&acm->mutex);
1469a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	if (acm->port.count) {
1470a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		tty = tty_port_tty_get(&acm->port);
1471a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		if (tty) {
1472a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra			tty_hangup(tty);
1473a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra			tty_kref_put(tty);
1474a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		}
1475a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	}
1476a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	mutex_unlock(&acm->mutex);
1477a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	return acm_resume(intf);
1478a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra}
1479a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra
1480357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */
1481c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
1482c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor#define NOKIA_PCSUITE_ACM_INFO(x) \
1483c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1484c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1485c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_CDC_ACM_PROTO_VENDOR)
1486c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
14871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
14881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure.
14891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id acm_ids[] = {
14921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* quirky and broken devices */
14931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
14941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1496b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1497b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1498b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	},
14990f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	{ USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
15000f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15010f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	},
15028753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
15038753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15048753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	},
150591a9c9214e34c364bf15406aadb922787ae7129bChris Malley	{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
150691a9c9214e34c364bf15406aadb922787ae7129bChris Malley	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
150791a9c9214e34c364bf15406aadb922787ae7129bChris Malley	},
15087abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	{ USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
15097abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	.driver_info = SINGLE_RX_URB,
15107abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	},
151186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
151286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
151386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	},
15143dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
15153dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
15163dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	},
15179be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
15189be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15199be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	},
15206149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	{ USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
15216149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15226149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	},
1523c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	{ USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1524c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1525c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	},
1526c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	{ USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1527c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1528c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	},
1529cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	{ USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1530cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1531cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	},
1532155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1533155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	},
1534c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	{ USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1535c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	.driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1536c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   data interface instead of
1537c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   communications interface.
1538c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   Maybe we should define a new
1539c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   quirk for this. */
1540c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	},
15411f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	{ USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
15421f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
15431f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	},
15449be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum
1545c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	/* Nokia S60 phones expose two ACM channels. The first is
1546c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * a modem and is picked up by the standard AT-command
1547c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * information below. The second is 'vendor-specific' but
1548c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * is treated as a serial device at the S60 end, so we want
1549c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * to expose it on Linux too. */
1550c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1551c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1552c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1553c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1554c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1555c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1556c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1557c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1558c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1559c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1560c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1561c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1562c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1563c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1564c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1565c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1566c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1567c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i  */
1568c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1569c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1570c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1571c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic &  */
1572c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1573c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1574c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1575c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1576c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1577c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1578c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1579c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1580c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1581c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB  */
1582c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1583c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1584c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1585c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1586c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1587c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1588c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1589c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3  */
1590c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1591c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1592c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
1593c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
1594c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	/* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1595c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
15961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* control interfaces with various AT-command sets */
15971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_V25TER) },
15991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101) },
16011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
16031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_GSM) },
16051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		USB_CDC_ACM_PROTO_AT_3G) },
16071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_CDMA) },
16091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
16111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DEVICE_TABLE(usb, acm_ids);
16141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = {
16161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"cdc_acm",
16171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	acm_probe,
16181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	acm_disconnect,
1619357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
16201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.suspend =	acm_suspend,
16211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.resume =	acm_resume,
1622a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	.reset_resume =	acm_reset_resume,
1623357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
16241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	acm_ids,
1625357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
16261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.supports_autosuspend = 1,
1627357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
16281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures.
16321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
16331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1634b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = {
16351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			acm_tty_open,
16361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		acm_tty_close,
163710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	.hangup =		acm_tty_hangup,
16381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		acm_tty_write,
16391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		acm_tty_write_room,
16401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		acm_tty_ioctl,
16411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		acm_tty_throttle,
16421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		acm_tty_unthrottle,
16431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	acm_tty_chars_in_buffer,
16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl =		acm_tty_break_ctl,
16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios =		acm_tty_set_termios,
16461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmget =		acm_tty_tiocmget,
16471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmset =		acm_tty_tiocmset,
16481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit.
16521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
16531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void)
16551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
16571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
16581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm_tty_driver)
16591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
16601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->owner = THIS_MODULE,
16611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->driver_name = "acm",
16621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->name = "ttyACM",
16631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->major = ACM_TTY_MAJOR,
16641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->minor_start = 0,
16651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
16661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
1667331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
16681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios = tty_std_termios;
16696e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
16706e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox								HUPCL | CLOCAL;
16711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(acm_tty_driver, &acm_ops);
16721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = tty_register_driver(acm_tty_driver);
16741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
16751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
16761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
16771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&acm_driver);
16801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
16811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_unregister_driver(acm_tty_driver);
16821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
16831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
16841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16865909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
16875909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman	       DRIVER_DESC "\n");
16881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
16901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void)
16931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister(&acm_driver);
16951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_unregister_driver(acm_tty_driver);
16961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	put_tty_driver(acm_tty_driver);
16971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init);
17001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit);
17011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_AUTHOR(DRIVER_AUTHOR);
17036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DESCRIPTION(DRIVER_DESC);
17041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
1705e766aeb882b41355d8732cf49aa9412baef852c5Scott James RemnantMODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);
1706