cdc-acm.c revision 59d7fec7c6908604862658a3679ac44c2c3eea44
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * cdc-acm.c
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Armin Fuerst	<fuerst@in.tum.de>
5a2531293dbb7608fa672ff28efe3ab4027917a2fPavel Machek * Copyright (c) 1999 Pavel Machek	<pavel@ucw.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--;
17397d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	usb_autopm_put_interface_async(acm->control);
174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
175884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
176884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
177884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write.
17811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum *
17911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking
180884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
18111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb)
18311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
18411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rc;
18511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting++;
18711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer = wb->buf;
18911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_dma = wb->dmah;
19011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer_length = wb->len;
19111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->dev = acm->dev;
19211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
1936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
1946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (rc < 0) {
19511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("usb_submit_urb(write bulk) failed: %d", rc);
19611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_write_done(acm, wb);
19711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
19811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	return rc;
19911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
20011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
201e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn)
202884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
204934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	struct acm_wb *wb = &acm->wb[wbn];
205884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int rc;
206884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
207884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
208884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->dev) {
209934da4635c2d05cef474e5243ef05df95b2ad264David Brownell		wb->use = 0;
210884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
211884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return -ENODEV;
212884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
213884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
21411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	dbg("%s susp_count: %d", __func__, acm->susp_count);
21597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	usb_autopm_get_interface_async(acm->control);
21611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->susp_count) {
21797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		if (!acm->delayed_wb)
21897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			acm->delayed_wb = wb;
21997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		else
22097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			usb_autopm_put_interface_async(acm->control);
22111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
22211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return 0;	/* A white lie */
22311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
22411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
22511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
22611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rc = acm_start_wb(acm, wb);
227884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
228884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
229884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return rc;
23011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
231884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/*
233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs
234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */
235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps
236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
237c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->ctrl_caps);
242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes
246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
249c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
250c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	memcpy(buf, acm->country_codes, acm->country_code_size);
252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return acm->country_code_size;
253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
254c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date
258c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
260c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
261c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
262c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
263c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->country_rel_date);
264c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
265884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
266c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
267884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */
2727d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb)
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = urb->context;
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_notification *dr = urb->transfer_buffer;
27610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl;
279185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int retval;
280185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int status = urb->status;
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
282185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	switch (status) {
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* success */
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ECONNRESET:
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ENOENT:
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ESHUTDOWN:
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this urb is terminated, clean up */
290441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - urb shutting down with status: %d", __func__, status);
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
293441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - nonzero urb status received: %d", __func__, status);
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3007e7797e7f6f7bfab73fca02c65e40eaa5bb9000cJohan Hovold	usb_mark_last_busy(acm->dev);
3017e7797e7f6f7bfab73fca02c65e40eaa5bb9000cJohan Hovold
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = (unsigned char *)(dr + 1);
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (dr->bNotificationType) {
3046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
3056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dbg("%s network", dr->wValue ?
3066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"connected to" : "disconnected from");
3076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		break;
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	case USB_CDC_NOTIFY_SERIAL_STATE:
3106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		tty = tty_port_tty_get(&acm->port);
3116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl = get_unaligned_le16(data);
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (tty) {
3146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (!acm->clocal &&
3156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
3166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dbg("calling hangup");
3176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				tty_hangup(tty);
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			tty_kref_put(tty);
3206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		acm->ctrlin = newctrl;
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
3256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
3266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
3276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
3286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
3296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
3306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
3316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3346e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	default:
3356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
3366e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dr->bNotificationType, dr->wIndex,
3376e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dr->wLength, data[0], data[1]);
3386e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		break;
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
3416e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	retval = usb_submit_urb(urb, GFP_ATOMIC);
342185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	if (retval)
3439908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
34459d7fec7c6908604862658a3679ac44c2c3eea44Johan Hovold			"result %d\n", __func__, retval);
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */
3487d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_read_bulk(struct urb *urb)
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
35061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
35161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv = urb->context;
35261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm *acm = rcv->instance;
35386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int status = urb->status;
354185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman
355185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	dbg("Entering acm_read_bulk with status %d", status);
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
35711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (!ACM_READY(acm)) {
35859d7fec7c6908604862658a3679ac44c2c3eea44Johan Hovold		dev_dbg(&acm->data->dev, "Aborting, acm not ready\n");
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
36011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
36111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
36386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (status)
364898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
36661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = rcv->buffer;
36761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf->size = urb->actual_length;
36861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
36986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (likely(status == 0)) {
37086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
37111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->processing++;
37286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
37386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&buf->list, &acm->filled_read_bufs);
37486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
37586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	} else {
37686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* we drop the buffer due to an error */
37786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
37886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
37986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add(&buf->list, &acm->spare_read_bufs);
38086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
38186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* nevertheless the tasklet must be kicked unconditionally
38286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		so the queue cannot dry up */
38386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	}
38411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (likely(!acm->susp_count))
38511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		tasklet_schedule(&acm->urb_task);
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm)
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = (void *)_acm;
39161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
39210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
39361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv;
394762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	unsigned long flags;
395ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	unsigned char throttled;
39611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Entering acm_rx_tasklet");
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
39910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (!ACM_READY(acm)) {
40011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("acm_rx_tasklet: ACM not ready");
401ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		return;
40211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
403ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
404834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_lock_irqsave(&acm->throttle_lock, flags);
405ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	throttled = acm->throttle;
406834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_unlock_irqrestore(&acm->throttle_lock, flags);
40710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (throttled) {
40811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("acm_rx_tasklet: throttled");
40961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		return;
41011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
41161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
41210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
41310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
41461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer:
415762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
41661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	if (list_empty(&acm->filled_read_bufs)) {
417762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
41861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		goto urbs;
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
42061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = list_entry(acm->filled_read_bufs.next,
42161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			 struct acm_rb, list);
42261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_del(&buf->list);
423762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
42461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
4253dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
42661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
42710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (tty) {
42810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		spin_lock_irqsave(&acm->throttle_lock, flags);
42910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		throttled = acm->throttle;
43010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		spin_unlock_irqrestore(&acm->throttle_lock, flags);
43110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		if (!throttled) {
43210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			tty_insert_flip_string(tty, buf->base, buf->size);
43310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			tty_flip_buffer_push(tty);
43410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		} else {
43510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			tty_kref_put(tty);
43610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			dbg("Throttling noticed");
43710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			spin_lock_irqsave(&acm->read_lock, flags);
43810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			list_add(&buf->list, &acm->filled_read_bufs);
43910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			spin_unlock_irqrestore(&acm->read_lock, flags);
44010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			return;
44110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		}
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
444762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
44561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_add(&buf->list, &acm->spare_read_bufs);
446762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
44761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	goto next_buffer;
44861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
44961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs:
45010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_kref_put(tty);
45110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
45261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	while (!list_empty(&acm->spare_read_bufs)) {
453762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_lock_irqsave(&acm->read_lock, flags);
45461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (list_empty(&acm->spare_read_urbs)) {
45511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
456762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
45761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
45861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
45961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv = list_entry(acm->spare_read_urbs.next,
46061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_ru, list);
46161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&rcv->list);
462762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
46361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
46461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		buf = list_entry(acm->spare_read_bufs.next,
46561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_rb, list);
46661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&buf->list);
46761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
46861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->buffer = buf;
46961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
470cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum		if (acm->is_int_ep)
4715186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_int_urb(rcv->urb, acm->dev,
4725186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 acm->rx_endpoint,
4735186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 buf->base,
4745186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 acm->readsize,
475cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum					 acm_read_bulk, rcv, acm->bInterval);
4765186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		else
4775186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_bulk_urb(rcv->urb, acm->dev,
4785186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  acm->rx_endpoint,
4795186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  buf->base,
4805186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  acm->readsize,
4815186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  acm_read_bulk, rcv);
48261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_dma = buf->dma;
48361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
48461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
4856e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		/* This shouldn't kill the driver as unsuccessful URBs are
4866e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		   returned to the free-urbs-pool and resubmited ASAP */
48711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irqsave(&acm->read_lock, flags);
4886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (acm->susp_count ||
4896e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
49061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&buf->list, &acm->spare_read_bufs);
49161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&rcv->list, &acm->spare_read_urbs);
49211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
493762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
49461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
49511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		} else {
49611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			spin_unlock_irqrestore(&acm->read_lock, flags);
49711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
49861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
49961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
50011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irqsave(&acm->read_lock, flags);
50111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->processing = 0;
50211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irqrestore(&acm->read_lock, flags);
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */
5067d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb)
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
508cdc97792289179974af6dda781c855696358d307Ming Lei	struct acm_wb *wb = urb->context;
509e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	struct acm *acm = wb->instance;
510ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	unsigned long flags;
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
512e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	if (verbose || urb->status
513e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			|| (urb->actual_length != urb->transfer_buffer_length))
514e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell		dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
515e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->actual_length,
516e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->transfer_buffer_length,
517e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->status);
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
519ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_lock_irqsave(&acm->write_lock, flags);
520e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm_write_done(acm, wb);
521ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_unlock_irqrestore(&acm->write_lock, flags);
522884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (ACM_READY(acm))
523884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		schedule_work(&acm->work);
524e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	else
525e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell		wake_up_interruptible(&acm->drain_wait);
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
528c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work)
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
530c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct acm *acm = container_of(work, struct acm, work);
53110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
532e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
533e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	dev_vdbg(&acm->data->dev, "tx work\n");
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
53610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
53715e5bee33ffc11d0e5c6f819a65e7881c5c407beJohan Hovold	if (!tty)
53815e5bee33ffc11d0e5c6f819a65e7881c5c407beJohan Hovold		return;
53910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_wakeup(tty);
54010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_kref_put(tty);
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp)
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
55042dd2aa6496a2e87e496aac5494d2e1d6096c85bThadeu Lima de Souza Cascardo	int rv = -ENODEV;
55161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
5523dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_open.");
5534186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven
5544186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm = acm_table[tty->index];
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->dev)
5582b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum		goto out;
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rv = 0;
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
56228d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91David Engraf	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
56310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty->driver_data = acm;
56510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, tty);
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
56794409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	if (usb_autopm_get_interface(acm->control) < 0)
56894409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum		goto early_bail;
56911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	else
57011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->control->needs_remote_wakeup = 1;
5711365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5721365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
57310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count++) {
5742b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum		mutex_unlock(&acm->mutex);
5751365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_autopm_put_interface(acm->control);
5762b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum		goto out;
57710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
5781365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->dev = acm->dev;
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("usb_submit_urb(ctrl irq) failed");
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto bail_out;
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
585ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
586ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	    (acm->ctrl_caps & USB_CDC_CAP_LINE))
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto full_bailout;
58810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
58911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
59161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_urbs);
59261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_bufs);
59361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->filled_read_bufs);
5946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
5956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < acm->rx_buflimit; i++)
59661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
5976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < acm->rx_buflimit; i++)
59861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
59961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
600ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	acm->throttle = 0;
601ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
6027af25b4b34a2439020d78da765a3bed0ff73f25cOliver Neukum	set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
60310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	rv = tty_port_block_til_ready(&acm->port, tty, filp);
60418a77b5d237a67d2c621a46f5271a3b51da1b380Henry Gebhardt	tasklet_schedule(&acm->urb_task);
6052b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum
6061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
6072b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukumout:
60894409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rv;
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout:
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(acm->ctrlurb);
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out:
61410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	acm->port.count--;
6151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
6162b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum	usb_autopm_put_interface(acm->control);
61794409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukumearly_bail:
61894409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
61910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, NULL);
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EIO;
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
62383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm)
62483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{
62510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	int i, nr;
62661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
62786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
62883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_unregister_device(acm_tty_driver, acm->minor);
62983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_put_intf(acm->control);
63083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	acm_table[acm->minor] = NULL;
63183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_free_urb(acm->ctrlurb);
632e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
633e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
63486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < nr; i++)
63561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
636c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	kfree(acm->country_codes);
63783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	kfree(acm);
63883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk}
63983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk
640e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownellstatic int acm_tty_chars_in_buffer(struct tty_struct *tty);
641e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
6424e608671674b62e97166f903830d5553e37970e8Arnd Bergmannstatic void acm_port_down(struct acm *acm)
64310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
64410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	int i, nr = acm->rx_buflimit;
64510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	mutex_lock(&open_mutex);
64610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->dev) {
64710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_get_interface(acm->control);
64810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm_set_control(acm, acm->ctrlout = 0);
64910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_kill_urb(acm->ctrlurb);
65010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		for (i = 0; i < ACM_NW; i++)
65110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			usb_kill_urb(acm->wb[i].urb);
65223b80550e2aa61d0ba3af98b831b9195be0db9eeJohan Hovold		tasklet_disable(&acm->urb_task);
65310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		for (i = 0; i < nr; i++)
65410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			usb_kill_urb(acm->ru[i].urb);
65523b80550e2aa61d0ba3af98b831b9195be0db9eeJohan Hovold		tasklet_enable(&acm->urb_task);
65610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm->control->needs_remote_wakeup = 0;
65710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_put_interface(acm->control);
65810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
65910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	mutex_unlock(&open_mutex);
66010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
66110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
66210077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_tty_hangup(struct tty_struct *tty)
66310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
66410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct acm *acm = tty->driver_data;
66510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_hangup(&acm->port);
6664e608671674b62e97166f903830d5553e37970e8Arnd Bergmann	acm_port_down(acm);
66710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
66810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp)
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
67310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	/* Perform the closing process and see if we need to do the hardware
67410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	   shutdown */
675051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	if (!acm)
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
677051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	if (tty_port_close_start(&acm->port, tty, filp) == 0) {
678051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		mutex_lock(&open_mutex);
679051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		if (!acm->dev) {
680051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			tty_port_tty_set(&acm->port, NULL);
681051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			acm_tty_unregister(acm);
682051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			tty->driver_data = NULL;
683051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		}
684051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		mutex_unlock(&open_mutex);
685051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		return;
686051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	}
6874e608671674b62e97166f903830d5553e37970e8Arnd Bergmann	acm_port_down(acm);
68810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_close_end(&acm->port, tty);
68910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, NULL);
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_write(struct tty_struct *tty,
6936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					const unsigned char *buf, int count)
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int stat;
697884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
698884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
699884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
700884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
7013dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_write to write %d bytes,", count);
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!count)
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
708884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
7096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	wbn = acm_wb_alloc(acm);
7106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (wbn < 0) {
711884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
712884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
713884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
714884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
716884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	count = (count > acm->writesize) ? acm->writesize : count;
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Get %d bytes...", count);
718884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	memcpy(wb->buf, buf, count);
719884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb->len = count;
720884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	stat = acm_write_start(acm, wbn);
7236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (stat < 0)
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return stat;
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty)
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
733884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
734884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * Do not let the line discipline to know that we have a reserve,
735884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * or it might get too enthusiastic.
736884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
737934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	return acm_wb_is_avail(acm) ? acm->writesize : 0;
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty)
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
74423198fda7182969b619613a555f8645fdc3dc334Alan Cox		return 0;
745884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
746884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * This is inaccurate (overcounts), but it works.
747884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
74886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty)
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 1;
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty)
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 0;
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
76961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7729e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state)
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7759e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	int retval;
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7779e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox		return -EINVAL;
7789e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	retval = acm_send_break(acm, state ? 0xffff : 0);
7799e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	if (retval < 0)
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("send break failed");
7819e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	return retval;
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
78460b33c133ca0b7c0b6072c87234b63fee6e80558Alan Coxstatic int acm_tty_tiocmget(struct tty_struct *tty)
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TIOCM_CTS;
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
79920b9d17715017ae4dd4ec87fabc36d33b9de708eAlan Coxstatic int acm_tty_tiocmset(struct tty_struct *tty,
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    unsigned int set, unsigned int clear)
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int newctrl;
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = acm->ctrlout;
8096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
8106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
8116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
8126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = (newctrl & ~clear) | set;
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->ctrlout == newctrl)
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return acm_set_control(acm, acm->ctrlout = newctrl);
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8216caa76b7786891b42b66a0e61e2c2fff2c884620Alan Coxstatic int acm_tty_ioctl(struct tty_struct *tty,
8226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					unsigned int cmd, unsigned long arg)
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8324c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = {
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 50, 75, 110, 134, 150, 200, 300, 600,
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1200, 1800, 2400, 4800, 9600, 19200, 38400,
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	57600, 115200, 230400, 460800, 500000, 576000,
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	921600, 1000000, 1152000, 1500000, 2000000,
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	2500000, 3000000, 3500000, 4000000
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8404c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = {
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7, 8
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8446e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic void acm_tty_set_termios(struct tty_struct *tty,
8456e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						struct ktermios *termios_old)
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
848606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox	struct ktermios *termios = tty->termios;
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_line_coding newline;
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl = acm->ctrlout;
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8559b80fee149a875a6292b2556ab2c64dc7ab7d6f5Alan Cox	newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bParityType = termios->c_cflag & PARENB ?
8586e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & PARODD ? 1 : 2) +
8596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & CMSPAR ? 2 : 0) : 0;
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
8616e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	/* FIXME: Needs to clear unsupported bits in the termios */
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!newline.dwDTERate) {
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newline.dwDTERate = acm->line.dwDTERate;
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newctrl &= ~ACM_CTRL_DTR;
8676e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	} else
8686e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl |=  ACM_CTRL_DTR;
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (newctrl != acm->ctrlout)
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_control(acm, acm->ctrlout = newctrl);
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(&acm->line, &newline, sizeof newline)) {
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&acm->line, &newline, sizeof newline);
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bCharFormat, newline.bParityType,
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bDataBits);
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_line(acm, &acm->line);
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines.
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
886830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */
887884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm)
888884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
889884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
890884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
891a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
892884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
8936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
894997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
895884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
896884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
897830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm)
898830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{
899830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
900830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	int i, n = acm->rx_buflimit;
901830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
902830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	for (i = 0; i < n; i++)
903997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		usb_free_coherent(usb_dev, acm->readsize,
904997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack				  acm->rb[i].base, acm->rb[i].dma);
905830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum}
906830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
907884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */
908884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm)
909884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
910884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
911884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
912884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
91386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
914997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
915884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		    &wb->dmah);
916884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->buf) {
917884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			while (i != 0) {
918884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--i;
919884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--wb;
920997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack				usb_free_coherent(acm->dev, acm->writesize,
921884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				    wb->buf, wb->dmah);
922884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			}
923884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -ENOMEM;
924884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
925884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
926884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return 0;
927884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
928884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
92910077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic int acm_probe(struct usb_interface *intf,
93010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		     const struct usb_device_id *id)
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_union_desc *union_header = NULL;
933c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_cdc_country_functional_desc *cfd = NULL;
934c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell	unsigned char *buffer = intf->altsetting->extra;
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buflen = intf->altsetting->extralen;
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *control_interface;
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *data_interface;
938a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epctrl = NULL;
939a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epread = NULL;
940a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epwrite = NULL;
9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor;
9446e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	int ctrlsize, readsize;
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *buf;
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 ac_management_function = 0;
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 call_management_function = 0;
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int call_interface_num = -1;
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int data_interface_num;
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long quirks;
95186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int num_rx_buf;
95261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
953a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	int combined_interfaces = 0;
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
95586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* normal quirks */
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	quirks = (unsigned long)id->driver_info;
95786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
95886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum
95986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* handle quirks deadly to normal probing*/
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (quirks == NO_UNION_NORMAL) {
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, 1);
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, 0);
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto skip_normal_probe;
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9656e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* normal probing*/
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
9689908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "Weird descriptor references\n");
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buflen) {
973577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray		if (intf->cur_altsetting->endpoint &&
974577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray				intf->cur_altsetting->endpoint->extralen &&
9756e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				intf->cur_altsetting->endpoint->extra) {
9766e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
9776e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Seeking extra descriptors on endpoint\n");
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buflen = intf->cur_altsetting->endpoint->extralen;
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = intf->cur_altsetting->endpoint->extra;
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
9819908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev,
9829908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman				"Zero length descriptor references\n");
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (buflen > 0) {
9886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (buffer[1] != USB_DT_CS_INTERFACE) {
9899908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev, "skipping garbage\n");
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto next_desc;
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		switch (buffer[2]) {
9946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_UNION_TYPE: /* we've found it */
9956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (union_header) {
9966e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "More than one "
9976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"union descriptor, skipping ...\n");
9986e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				goto next_desc;
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
10006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			union_header = (struct usb_cdc_union_desc *)buffer;
10016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
10036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			cfd = (struct usb_cdc_country_functional_desc *)buffer;
10046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_HEADER_TYPE: /* maybe check version */
10066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break; /* for now we ignore it */
10076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_ACM_TYPE:
10086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			ac_management_function = buffer[3];
10096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_CALL_MANAGEMENT_TYPE:
10116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_management_function = buffer[3];
10126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_interface_num = buffer[4];
1013ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby			if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
10146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
10156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		default:
10176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			/* there are LOTS more CDC descriptors that
10186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 * could legitimately be found here.
10196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 */
10206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "Ignoring descriptor: "
10216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"type %02x, length %d\n",
10226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					buffer[2], buffer[0]);
10236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc:
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buflen -= buffer[0];
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer += buffer[0];
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!union_header) {
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (call_interface_num > 0) {
10326e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = intf;
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
1036a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1037a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1038a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -ENODEV;
1039a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			} else {
1040a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1041a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				combined_interfaces = 1;
1042a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				control_interface = data_interface = intf;
1043a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				goto look_for_collapsed_interface;
1044a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			}
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!control_interface || !data_interface) {
10506e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "no interfaces\n");
10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10546e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface_num != call_interface_num)
10566e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1058a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (control_interface == data_interface) {
1059a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* some broken devices designed for windows work this way */
1060a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1061a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		combined_interfaces = 1;
1062a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* a popular other OS doesn't use it */
1063a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		quirks |= NO_CAP_LINE;
1064a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1065a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1066a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -EINVAL;
1067a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
1068a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukumlook_for_collapsed_interface:
1069a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		for (i = 0; i < 3; i++) {
1070a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			struct usb_endpoint_descriptor *ep;
1071a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			ep = &data_interface->cur_altsetting->endpoint[i].desc;
1072a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
1073a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (usb_endpoint_is_int_in(ep))
1074a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epctrl = ep;
1075a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_out(ep))
1076a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epwrite = ep;
1077a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_in(ep))
1078a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epread = ep;
1079a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else
1080a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -EINVAL;
1081a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
1082a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (!epctrl || !epread || !epwrite)
1083a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -ENODEV;
1084a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		else
1085a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			goto made_compressed_probe;
1086a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	}
1087a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe:
10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*workaround for switched interfaces */
10916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (data_interface->cur_altsetting->desc.bInterfaceClass
10926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						!= CDC_DATA_INTERFACE_TYPE) {
10936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (control_interface->cur_altsetting->desc.bInterfaceClass
10946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						== CDC_DATA_INTERFACE_TYPE) {
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_interface *t;
10966e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
10976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Your device has switched interfaces.\n");
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = control_interface;
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = data_interface;
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = t;
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
110574da5d68a54d66667664fbe233ededab2376a070Alan Stern
110674da5d68a54d66667664fbe233ededab2376a070Alan Stern	/* Accept probe requests only for the control interface */
1107a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && intf != control_interface)
110874da5d68a54d66667664fbe233ededab2376a070Alan Stern		return -ENODEV;
11096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
1110a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1111a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* valid in this context */
11126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "The data interface isn't available\n");
11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epread = &data_interface->cur_altsetting->endpoint[0].desc;
11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* workaround for switched endpoints */
112645aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino	if (!usb_endpoint_dir_in(epread)) {
11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* descriptors are swapped */
11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_endpoint_descriptor *t;
11296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev,
11306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			"The data interface has switched endpoints\n");
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = epread;
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epread = epwrite;
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epwrite = t;
11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1135a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukummade_compressed_probe:
11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("interfaces are valid");
11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (minor == ACM_TTY_MINORS) {
11409908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "no more free acm devices\n");
11411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11446e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
11456e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm == NULL) {
1146898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail;
11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
11516e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	readsize = le16_to_cpu(epread->wMaxPacketSize) *
11526e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(quirks == SINGLE_RX_URB ? 1 : 2);
1153a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	acm->combined_interfaces = combined_interfaces;
1154e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
11551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->control = control_interface;
11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->data = data_interface;
11571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->minor = minor;
11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = usb_dev;
11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_caps = ac_management_function;
1160a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (quirks & NO_CAP_LINE)
1161a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlsize = ctrlsize;
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->readsize = readsize;
116486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->rx_buflimit = num_rx_buf;
116561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.func = acm_rx_tasklet;
116661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.data = (unsigned long) acm;
1167c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&acm->work, acm_softint);
1168e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	init_waitqueue_head(&acm->drain_wait);
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&acm->throttle_lock);
1170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_init(&acm->write_lock);
117161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock_init(&acm->read_lock);
11721365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_init(&acm->mutex);
117361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
1174cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum	acm->is_int_ep = usb_endpoint_xfer_int(epread);
1175cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum	if (acm->is_int_ep)
1176cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum		acm->bInterval = epread->bInterval;
1177739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	tty_port_init(&acm->port);
1178739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	acm->port.ops = &acm_port_ops;
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1180997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buf) {
1182898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail2;
11841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_buffer = buf;
11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1187884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (acm_write_buffers_alloc(acm) < 0) {
1188898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail4;
11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
11931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->ctrlurb) {
1194898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail5;
11961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
119786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
119861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		struct acm_ru *rcv = &(acm->ru[i]);
119961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
12006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
12016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (rcv->urb == NULL) {
12026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
12036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"out of memory (read urbs usb_alloc_urb)\n");
1204c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin			goto alloc_fail6;
120561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
120661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
120761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
120861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->instance = acm;
120961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
121086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
1211672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		struct acm_rb *rb = &(acm->rb[i]);
121261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
1213997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		rb->base = usb_alloc_coherent(acm->dev, readsize,
1214672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell				GFP_KERNEL, &rb->dma);
1215672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		if (!rb->base) {
12166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
1217997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack				"out of memory (read bufs usb_alloc_coherent)\n");
121861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
121961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++) {
1222e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		struct acm_wb *snd = &(acm->wb[i]);
1223e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
12246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
12256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (snd->urb == NULL) {
12266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
122759d7fec7c6908604862658a3679ac44c2c3eea44Johan Hovold				"out of memory (write urbs usb_alloc_urb)\n");
1228c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin			goto alloc_fail8;
1229e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		}
1230e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
12315186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		if (usb_endpoint_xfer_int(epwrite))
12325186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_int_urb(snd->urb, usb_dev,
12335186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
12345186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
12355186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		else
12365186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_bulk_urb(snd->urb, usb_dev,
12375186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
12385186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd);
1239e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1240e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->instance = acm;
12411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12436e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_set_intfdata(intf, acm);
1244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (i < 0)
1247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		goto alloc_fail8;
1248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1249c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (cfd) { /* export the country data */
1250c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (!acm->country_codes)
1252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_code_size = cfd->bLength - 4;
12546e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
12556e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox							cfd->bLength - 4);
1256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_rel_date = cfd->iCountryCodeRelDate;
1257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1258c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1260c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1261c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1262c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1263c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
12646e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		i = device_create_file(&intf->dev,
12656e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						&dev_attr_iCountryCodeRelDate);
1266c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1267c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin			device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
1268c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1269c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1270c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1271c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
1272c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1273c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries:
12746e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_fill_int_urb(acm->ctrlurb, usb_dev,
1275a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1276a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1277a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 /* works around buggy devices */
1278a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 epctrl->bInterval ? epctrl->bInterval : 0xff);
12791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
12801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_control(acm, acm->ctrlout);
12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.dwDTERate = cpu_to_le32(9600);
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.bDataBits = 8;
12881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_line(acm, &acm->line);
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_driver_claim_interface(&acm_driver, data_interface, acm);
1291672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	usb_set_intfdata(data_interface, acm);
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
129383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_get_intf(control_interface);
129483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_table[minor] = acm;
12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1298c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return 0;
1299c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumalloc_fail8:
1300e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
1301e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7:
1303830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
1304c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Linalloc_fail6:
130586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
130661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
13071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_free_urb(acm->ctrlurb);
13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5:
1309884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
13101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4:
1311997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
13121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2:
13131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acm);
13141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail:
13151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOMEM;
13161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm)
13191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int i;
132111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	dbg("Entering stop_data_traffic");
13221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_disable(&acm->urb_task);
13241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_kill_urb(acm->ctrlurb);
13266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++)
1327e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_kill_urb(acm->wb[i].urb);
13281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
13291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_kill_urb(acm->ru[i].urb);
13301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_enable(&acm->urb_task);
13321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13331365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	cancel_work_sync(&acm->work);
13341365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
13351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf)
13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1338c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
134010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1342672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	/* sibling interface is already cleaning up */
1343672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	if (!acm)
134486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum		return;
1345672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell
1346672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	mutex_lock(&open_mutex);
13476e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm->country_codes) {
134874da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
134974da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_wCountryCodes);
135074da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
135174da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_iCountryCodeRelDate);
1352c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
135374da5d68a54d66667664fbe233ededab2376a070Alan Stern	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
13541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = NULL;
135586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->control, NULL);
135686067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->data, NULL);
13571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13581365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	stop_data_traffic(acm);
13591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1360884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
1361997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
1362997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack			  acm->ctrl_dma);
1363830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
13641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1365a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!acm->combined_interfaces)
1366a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		usb_driver_release_interface(&acm_driver, intf == acm->control ?
1367830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum					acm->data : acm->control);
13681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
136910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count == 0) {
137083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		acm_tty_unregister(acm);
13714186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
13721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
13731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
13741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13754186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
137610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
137710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (tty) {
137810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		tty_hangup(tty);
137910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		tty_kref_put(tty);
138010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
13811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1383357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
13841365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message)
13851365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13861365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
138711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
138811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
138965bfd2967c906ca322a4bb69a285fe0de8916ac6Alan Stern	if (message.event & PM_EVENT_AUTO) {
139011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		int b;
139111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
139211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irq(&acm->read_lock);
139311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock(&acm->write_lock);
139411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		b = acm->processing + acm->transmitting;
139511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock(&acm->write_lock);
139611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irq(&acm->read_lock);
139711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (b)
139811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			return -EBUSY;
139911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
140011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
140111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
140211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock(&acm->write_lock);
140311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count++;
140411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock(&acm->write_lock);
140511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
14061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
140711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
14081365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
14091365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	/*
14101365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we treat opened interfaces differently,
14111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we must guard against open
14121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	*/
14131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
14141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
141510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count)
14161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		stop_data_traffic(acm);
14171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
14191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return 0;
14201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
14211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf)
14231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
14241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
142597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	struct acm_wb *wb;
14261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int rv = 0;
142711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
14281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
142911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
143011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->susp_count -= 1;
143111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count;
143211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
143311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
143411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
14351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
14361365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14371365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
143810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count) {
14391365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
144097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum
144197d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		spin_lock_irq(&acm->write_lock);
144297d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		if (acm->delayed_wb) {
144397d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			wb = acm->delayed_wb;
144497d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			acm->delayed_wb = NULL;
144597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			spin_unlock_irq(&acm->write_lock);
1446f0730924e9e32bb8935c60040a26d94179355088Oliver Neukum			acm_start_wb(acm, wb);
144797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		} else {
144897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			spin_unlock_irq(&acm->write_lock);
144997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		}
145097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum
145197d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		/*
145297d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 * delayed error checking because we must
145397d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 * do the write path at all cost
145497d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 */
14551365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		if (rv < 0)
145611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			goto err_out;
14571365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14581365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		tasklet_schedule(&acm->urb_task);
14591365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	}
14601365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14611365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out:
14621365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
14631365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return rv;
14641365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
1465357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum
1466a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavrastatic int acm_reset_resume(struct usb_interface *intf)
1467a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra{
1468a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	struct acm *acm = usb_get_intfdata(intf);
1469a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	struct tty_struct *tty;
1470a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra
1471a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	mutex_lock(&acm->mutex);
1472a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	if (acm->port.count) {
1473a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		tty = tty_port_tty_get(&acm->port);
1474a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		if (tty) {
1475a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra			tty_hangup(tty);
1476a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra			tty_kref_put(tty);
1477a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		}
1478a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	}
1479a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	mutex_unlock(&acm->mutex);
1480a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	return acm_resume(intf);
1481a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra}
1482a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra
1483357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */
1484c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
1485c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor#define NOKIA_PCSUITE_ACM_INFO(x) \
1486c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1487c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1488c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_CDC_ACM_PROTO_VENDOR)
1489c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
14904035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray#define SAMSUNG_PCSUITE_ACM_INFO(x) \
14914035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
14924035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
14934035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_CDC_ACM_PROTO_VENDOR)
14944035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray
14951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
14961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure.
14971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14996ef4852b1326301f6e9657e99b2c3221be1a3a44Németh Mártonstatic const struct usb_device_id acm_ids[] = {
15001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* quirky and broken devices */
15011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
15021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1504b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1505b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1506b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	},
15070f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	{ USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
15080f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15090f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	},
15108753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
15118753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15128753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	},
151391a9c9214e34c364bf15406aadb922787ae7129bChris Malley	{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
151491a9c9214e34c364bf15406aadb922787ae7129bChris Malley	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
151591a9c9214e34c364bf15406aadb922787ae7129bChris Malley	},
15167abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	{ USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
15177abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	.driver_info = SINGLE_RX_URB,
15187abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	},
151986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
152086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
152186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	},
15223dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
15233dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
15243dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	},
15259be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
15269be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15279be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	},
15286149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	{ USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
15296149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15306149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	},
1531c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	{ USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1532c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1533c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	},
1534c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	{ USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1535c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1536c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	},
1537cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	{ USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1538cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1539cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	},
1540155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1541155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	},
1542c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	{ USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1543c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	.driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1544c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   data interface instead of
1545c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   communications interface.
1546c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   Maybe we should define a new
1547c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   quirk for this. */
1548c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	},
15491f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	{ USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
15501f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
15511f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	},
1552c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	{ USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1553c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1554c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	},
15559be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum
1556c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	/* Nokia S60 phones expose two ACM channels. The first is
1557c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * a modem and is picked up by the standard AT-command
1558c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * information below. The second is 'vendor-specific' but
1559c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * is treated as a serial device at the S60 end, so we want
1560c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * to expose it on Linux too. */
1561c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1562c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1563c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1564c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1565c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1566c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1567c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1568c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1569c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1570c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1571c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1572c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1573c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1574c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1575c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1576c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1577c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1578c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i  */
1579c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1580c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1581c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1582c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic &  */
1583c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1584c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1585c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1586c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1587c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1588c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1589c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1590c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1591c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1592c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB  */
1593c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1594c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1595c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1596c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1597c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1598c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1599c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1600c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3  */
1601c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1602c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1603c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
160483a4eae9aeed4a69e89e323a105e653ae06e7c1fPrzemo Firszt	{ NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
16054035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
16064035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
16074035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
16084035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
16094035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
16104035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
16114035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
16124035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
16134035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
16144035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
1615721d92fc6373dee15846216f9d178ec240ec0fd7Arvid Ephraim Picciani	{ NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
16164035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
1617c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
1618c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	/* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1619c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
16207c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby	/* Support Lego NXT using pbLua firmware */
1621ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby	{ USB_DEVICE(0x0694, 0xff00),
1622ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby	.driver_info = NOT_A_MODEM,
16237893afc035590383a14b176c1497cba984276ef4Otavio Salvador	},
16247c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby
16255b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes	/* control interfaces without any protocol set */
16265b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16275b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes		USB_CDC_PROTO_NONE) },
16285b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes
16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* control interfaces with various AT-command sets */
16301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_V25TER) },
16321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101) },
16341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
16361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_GSM) },
16381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16396e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		USB_CDC_ACM_PROTO_AT_3G) },
16401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_CDMA) },
16421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16466e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DEVICE_TABLE(usb, acm_ids);
16471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = {
16491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"cdc_acm",
16501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	acm_probe,
16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	acm_disconnect,
1652357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
16531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.suspend =	acm_suspend,
16541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.resume =	acm_resume,
1655a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	.reset_resume =	acm_reset_resume,
1656357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
16571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	acm_ids,
1658357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
16591365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.supports_autosuspend = 1,
1660357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
16611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
16641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures.
16651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
16661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1667b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = {
16681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			acm_tty_open,
16691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		acm_tty_close,
167010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	.hangup =		acm_tty_hangup,
16711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		acm_tty_write,
16721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		acm_tty_write_room,
16731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		acm_tty_ioctl,
16741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		acm_tty_throttle,
16751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		acm_tty_unthrottle,
16761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	acm_tty_chars_in_buffer,
16771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl =		acm_tty_break_ctl,
16781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios =		acm_tty_set_termios,
16791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmget =		acm_tty_tiocmget,
16801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmset =		acm_tty_tiocmset,
16811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
16841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit.
16851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
16861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void)
16881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
16901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
16911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm_tty_driver)
16921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
16931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->owner = THIS_MODULE,
16941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->driver_name = "acm",
16951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->name = "ttyACM",
16961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->major = ACM_TTY_MAJOR,
16971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->minor_start = 0,
16981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
16991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
1700331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
17011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios = tty_std_termios;
17026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
17036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox								HUPCL | CLOCAL;
17041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(acm_tty_driver, &acm_ops);
17051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = tty_register_driver(acm_tty_driver);
17071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
17081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
17091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
17101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&acm_driver);
17131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
17141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_unregister_driver(acm_tty_driver);
17151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
17161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
17171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17195909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
17205909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman	       DRIVER_DESC "\n");
17211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
17231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void)
17261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister(&acm_driver);
17281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_unregister_driver(acm_tty_driver);
17291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	put_tty_driver(acm_tty_driver);
17301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init);
17331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit);
17341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_AUTHOR(DRIVER_AUTHOR);
17366e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DESCRIPTION(DRIVER_DESC);
17371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
1738e766aeb882b41355d8732cf49aa9412baef852c5Scott James RemnantMODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);
1739