cdc-acm.c revision cab98a0a349829b145d924c0649a2d30cd6a9e3d
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * cdc-acm.c
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Armin Fuerst	<fuerst@in.tum.de>
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Pavel Machek	<pavel@suse.cz>
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Johannes Erdfelt	<johannes@erdfelt.com>
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2004 Oliver Neukum	<oliver@neukum.name>
961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek * Copyright (c) 2005 David Kubicek	<dave@awk.cz>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB Abstract Control Model driver for USB modems and ISDN adapters
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Sponsored by SuSE
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ChangeLog:
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.9  - thorough cleaning, URBification, almost a rewrite
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.10 - some more cleanups
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.11 - fixed flow control, read error doesn't stop reads
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.13 - added termios, added hangup
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.14 - sized down struct acm
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.15 - fixed flow control again - characters could be lost
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.16 - added code for modems with swapped data and control interfaces
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.17 - added new style probing
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.18 - fixed new style probing for devices with more configurations
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.20 - switched to probing on interface (rather than device) class
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.21 - revert to probing on device for devices with multiple configs
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.22 - probe only the control interface. if usbcore doesn't choose the
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		config we want, sysadmin changes bConfigurationValue in sysfs.
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.23 - use softirq for rx processing, as needed by tty layer
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.24 - change probe method to evaluate CDC union descriptor
3361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek *	v0.25 - downstream tasks paralelized to maximize throughput
34e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf *	v0.26 - multiple write urbs, writesize increased
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version.
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG
54e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#undef VERBOSE_DEBUG
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h>
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
644186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h>
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
67a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h>
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h>
7061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h>
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h"
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
74e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
75e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define ACM_CLOSE_TIMEOUT	15	/* seconds to let writes drain */
76e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
80e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf#define DRIVER_VERSION "v0.26"
8161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver;
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS];
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
884186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex);
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACM_READY(acm)	(acm && acm->dev && acm->used)
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
92e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#ifdef VERBOSE_DEBUG
93e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define verbose	1
94e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#else
95e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define verbose	0
96e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#endif
97e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages.
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len)
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		request, USB_RT_ACM, value,
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm->control->altsetting[0].desc.bInterfaceNumber,
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf, len, 5000);
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval < 0 ? retval : 0;
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests.
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do...
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
123884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management.
124884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller.
125884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
126884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
127884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm)
128884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
129884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, wbn;
130884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
131884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
132e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wbn = 0;
133884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	i = 0;
134884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	for (;;) {
135884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb = &acm->wb[wbn];
136884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->use) {
137884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			wb->use = 1;
138884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return wbn;
139884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
14086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		wbn = (wbn + 1) % ACM_NW;
14186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		if (++i >= ACM_NW)
142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -1;
143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
144884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
145884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
146884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm)
147884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
148884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, n;
149e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	unsigned long flags;
150884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
15186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	n = ACM_NW;
152e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	spin_lock_irqsave(&acm->write_lock, flags);
15386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < ACM_NW; i++) {
15486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		n -= acm->wb[i].use;
155884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
156e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	spin_unlock_irqrestore(&acm->write_lock, flags);
157884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return n;
158884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
159884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
160884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
161ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips * Finish write. Caller must hold acm->write_lock
162884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
163e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic void acm_write_done(struct acm *acm, struct acm_wb *wb)
164884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
165e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wb->use = 0;
16611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting--;
167884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
168884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
169884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write.
17111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum *
17211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking
173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
17411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
17511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb)
17611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
17711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rc;
17811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
17911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting++;
18011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer = wb->buf;
18211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_dma = wb->dmah;
18311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer_length = wb->len;
18411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->dev = acm->dev;
18511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) {
18711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("usb_submit_urb(write bulk) failed: %d", rc);
18811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_write_done(acm, wb);
18911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
19011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	return rc;
19111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
19211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
193e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn)
194884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
195884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
196934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	struct acm_wb *wb = &acm->wb[wbn];
197884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int rc;
198884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
199884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
200884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->dev) {
201934da4635c2d05cef474e5243ef05df95b2ad264David Brownell		wb->use = 0;
202884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return -ENODEV;
204884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
205884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
20611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	dbg("%s susp_count: %d", __func__, acm->susp_count);
20711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->susp_count) {
20811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->delayed_wb = wb;
20911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		schedule_work(&acm->waker);
21011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
21111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return 0;	/* A white lie */
21211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
21311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
21411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
21511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rc = acm_start_wb(acm, wb);
216884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
217884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
218884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return rc;
21911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
220884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
221c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/*
222c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs
223c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */
224c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps
225c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
226c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
227c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
230c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->ctrl_caps);
231c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes
235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
237c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	memcpy(buf, acm->country_codes, acm->country_code_size);
241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return acm->country_code_size;
242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date
247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
249c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
250c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->country_rel_date);
253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
254884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
256884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */
2617d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb)
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = urb->context;
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_notification *dr = urb->transfer_buffer;
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl;
267185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int retval;
268185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int status = urb->status;
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
270185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	switch (status) {
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* success */
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ECONNRESET:
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ENOENT:
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ESHUTDOWN:
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this urb is terminated, clean up */
278441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - urb shutting down with status: %d", __func__, status);
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
281441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - nonzero urb status received: %d", __func__, status);
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = (unsigned char *)(dr + 1);
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (dr->bNotificationType) {
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_CDC_NOTIFY_NETWORK_CONNECTION:
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_CDC_NOTIFY_SERIAL_STATE:
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
298a5abdeafedf722b0f3f357f4a23089a686b1b80dHarvey Harrison			newctrl = get_unaligned_le16(data);
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dbg("calling hangup");
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				tty_hangup(acm->tty);
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			acm->ctrlin = newctrl;
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',	acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',	acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',	acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dr->bNotificationType, dr->wIndex,
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dr->wLength, data[0], data[1]);
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
32211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
323185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	retval = usb_submit_urb (urb, GFP_ATOMIC);
324185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	if (retval)
3259908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
3269908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			"result %d", __func__, retval);
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */
3307d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_read_bulk(struct urb *urb)
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
33261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
33361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv = urb->context;
33461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm *acm = rcv->instance;
33586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int status = urb->status;
336185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman
337185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	dbg("Entering acm_read_bulk with status %d", status);
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
33911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (!ACM_READY(acm)) {
34011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dev_dbg(&acm->data->dev, "Aborting, acm not ready");
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
34211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
34311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
34586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (status)
346898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
34861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = rcv->buffer;
34961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf->size = urb->actual_length;
35061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
35186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (likely(status == 0)) {
35286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
35311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->processing++;
35486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
35586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&buf->list, &acm->filled_read_bufs);
35686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
35786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	} else {
35886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* we drop the buffer due to an error */
35986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
36086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
36186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add(&buf->list, &acm->spare_read_bufs);
36286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
36386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* nevertheless the tasklet must be kicked unconditionally
36486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		so the queue cannot dry up */
36586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	}
36611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (likely(!acm->susp_count))
36711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		tasklet_schedule(&acm->urb_task);
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm)
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = (void *)_acm;
37361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct tty_struct *tty = acm->tty;
37561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv;
376762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	unsigned long flags;
377ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	unsigned char throttled;
37811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Entering acm_rx_tasklet");
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (!ACM_READY(acm))
38211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	{
38311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("acm_rx_tasklet: ACM not ready");
384ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		return;
38511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
386ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
387834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_lock_irqsave(&acm->throttle_lock, flags);
388ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	throttled = acm->throttle;
389834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_unlock_irqrestore(&acm->throttle_lock, flags);
390ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (throttled)
39111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	{
39211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("acm_rx_tasklet: throttled");
39361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		return;
39411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
39561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
39661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer:
397762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
39861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	if (list_empty(&acm->filled_read_bufs)) {
399762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
40061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		goto urbs;
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
40261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = list_entry(acm->filled_read_bufs.next,
40361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			 struct acm_rb, list);
40461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_del(&buf->list);
405762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
40661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
4073dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
40861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
40933f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox	tty_buffer_request_room(tty, buf->size);
410834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_lock_irqsave(&acm->throttle_lock, flags);
411ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	throttled = acm->throttle;
412834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_unlock_irqrestore(&acm->throttle_lock, flags);
413ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (!throttled)
41433f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox		tty_insert_flip_string(tty, buf->base, buf->size);
41561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tty_flip_buffer_push(tty);
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
417ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (throttled) {
418ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		dbg("Throttling noticed");
419762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_lock_irqsave(&acm->read_lock, flags);
42061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&buf->list, &acm->filled_read_bufs);
421762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
425762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
42661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_add(&buf->list, &acm->spare_read_bufs);
427762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
42861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	goto next_buffer;
42961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
43061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs:
43161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	while (!list_empty(&acm->spare_read_bufs)) {
432762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_lock_irqsave(&acm->read_lock, flags);
43361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (list_empty(&acm->spare_read_urbs)) {
43411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
435762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
43661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
43761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
43861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv = list_entry(acm->spare_read_urbs.next,
43961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_ru, list);
44061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&rcv->list);
441762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
44261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
44361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		buf = list_entry(acm->spare_read_bufs.next,
44461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_rb, list);
44561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&buf->list);
44661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
44761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->buffer = buf;
44861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
44961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_fill_bulk_urb(rcv->urb, acm->dev,
45061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm->rx_endpoint,
45161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  buf->base,
45261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm->readsize,
45361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm_read_bulk, rcv);
45461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_dma = buf->dma;
45561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
45661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
45761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		/* This shouldn't kill the driver as unsuccessful URBs are returned to the
45861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		   free-urbs-pool and resubmited ASAP */
45911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irqsave(&acm->read_lock, flags);
46011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (acm->susp_count || usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
46161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&buf->list, &acm->spare_read_bufs);
46261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&rcv->list, &acm->spare_read_urbs);
46311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
464762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
46561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
46611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		} else {
46711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			spin_unlock_irqrestore(&acm->read_lock, flags);
46811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
46961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
47061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
47111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irqsave(&acm->read_lock, flags);
47211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->processing = 0;
47311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irqrestore(&acm->read_lock, flags);
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */
4777d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb)
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
479cdc97792289179974af6dda781c855696358d307Ming Lei	struct acm_wb *wb = urb->context;
480e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	struct acm *acm = wb->instance;
481ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	unsigned long flags;
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
483e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	if (verbose || urb->status
484e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			|| (urb->actual_length != urb->transfer_buffer_length))
485e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell		dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
486e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->actual_length,
487e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->transfer_buffer_length,
488e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->status);
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
490ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_lock_irqsave(&acm->write_lock, flags);
491e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm_write_done(acm, wb);
492ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_unlock_irqrestore(&acm->write_lock, flags);
493884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (ACM_READY(acm))
494884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		schedule_work(&acm->work);
495e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	else
496e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell		wake_up_interruptible(&acm->drain_wait);
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
499c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work)
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
501c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct acm *acm = container_of(work, struct acm, work);
502e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
503e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	dev_vdbg(&acm->data->dev, "tx work\n");
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_wakeup(acm->tty);
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
50911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic void acm_waker(struct work_struct *waker)
51011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
51111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	struct acm *acm = container_of(waker, struct acm, waker);
51211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rv;
51311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
51411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rv = usb_autopm_get_interface(acm->control);
51511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (rv < 0) {
5169908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__);
51711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return;
51811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
51911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->delayed_wb) {
52011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_start_wb(acm, acm->delayed_wb);
52111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->delayed_wb = NULL;
52211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
52311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
52411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
52511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp)
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rv = -EINVAL;
53461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
5353dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_open.");
5364186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven
5374186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm = acm_table[tty->index];
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->dev)
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out;
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rv = 0;
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
54528d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91David Engraf	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty->driver_data = acm;
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->tty = tty;
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
54994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	if (usb_autopm_get_interface(acm->control) < 0)
55094409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum		goto early_bail;
55111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	else
55211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->control->needs_remote_wakeup = 1;
5531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->used++) {
5561365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_autopm_put_interface(acm->control);
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto done;
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5601365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->dev = acm->dev;
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("usb_submit_urb(ctrl irq) failed");
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto bail_out;
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
567ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
568ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	    (acm->ctrl_caps & USB_CDC_CAP_LINE))
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto full_bailout;
57011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
57261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_urbs);
57361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_bufs);
57461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->filled_read_bufs);
57586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++) {
57661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
57761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
57886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++) {
57961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
58061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
58161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
582ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	acm->throttle = 0;
583ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
58461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone:
5871365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
58874573ee7096a4ffc2f098108d21c85801b9c7434Alexey Dobriyanerr_out:
58994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rv;
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout:
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(acm->ctrlurb);
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out:
5951365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_autopm_put_interface(acm->control);
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->used--;
5971365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
59894409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukumearly_bail:
59994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EIO;
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
60383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm)
60483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{
60586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int i,nr;
60661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
60786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
60883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_unregister_device(acm_tty_driver, acm->minor);
60983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_put_intf(acm->control);
61083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	acm_table[acm->minor] = NULL;
61183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_free_urb(acm->ctrlurb);
612e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
613e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
61486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < nr; i++)
61561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
616c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	kfree(acm->country_codes);
61783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	kfree(acm);
61883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk}
61983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk
620e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownellstatic int acm_tty_chars_in_buffer(struct tty_struct *tty);
621e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp)
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
62586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int i,nr;
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->used)
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
63086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
6314186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!--acm->used) {
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (acm->dev) {
63411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			usb_autopm_get_interface(acm->control);
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			acm_set_control(acm, acm->ctrlout = 0);
636e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
637e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			/* try letting the last writes drain naturally */
638e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			wait_event_interruptible_timeout(acm->drain_wait,
639e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell					(ACM_NW == acm_wb_is_avail(acm))
640e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell						|| !acm->dev,
641e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell					ACM_CLOSE_TIMEOUT * HZ);
642e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			usb_kill_urb(acm->ctrlurb);
644e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf			for (i = 0; i < ACM_NW; i++)
645e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf				usb_kill_urb(acm->wb[i].urb);
64686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum			for (i = 0; i < nr; i++)
64761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				usb_kill_urb(acm->ru[i].urb);
64811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->control->needs_remote_wakeup = 0;
6491365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum			usb_autopm_put_interface(acm->control);
65083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		} else
65183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk			acm_tty_unregister(acm);
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6534186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int stat;
660884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
661884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
662884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
663884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
6643dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_write to write %d bytes,", count);
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!count)
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
672884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if ((wbn = acm_wb_alloc(acm)) < 0) {
673884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
674884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
675884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
676884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
678884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	count = (count > acm->writesize) ? acm->writesize : count;
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Get %d bytes...", count);
680884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	memcpy(wb->buf, buf, count);
681884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb->len = count;
682884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
684e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	if ((stat = acm_write_start(acm, wbn)) < 0)
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return stat;
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty)
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
694884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
695884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * Do not let the line discipline to know that we have a reserve,
696884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * or it might get too enthusiastic.
697884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
698934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	return acm_wb_is_avail(acm) ? acm->writesize : 0;
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty)
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
706884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
707884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * This is inaccurate (overcounts), but it works.
708884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
70986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty)
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 1;
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty)
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 0;
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
73061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7339e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state)
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7369e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	int retval;
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7389e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox		return -EINVAL;
7399e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	retval = acm_send_break(acm, state ? 0xffff : 0);
7409e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	if (retval < 0)
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("send break failed");
7429e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	return retval;
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TIOCM_CTS;
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    unsigned int set, unsigned int clear)
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int newctrl;
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = acm->ctrlout;
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = (newctrl & ~clear) | set;
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->ctrlout == newctrl)
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return acm_set_control(acm, acm->ctrlout = newctrl);
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7904c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = {
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 50, 75, 110, 134, 150, 200, 300, 600,
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1200, 1800, 2400, 4800, 9600, 19200, 38400,
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	57600, 115200, 230400, 460800, 500000, 576000,
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	921600, 1000000, 1152000, 1500000, 2000000,
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	2500000, 3000000, 3500000, 4000000
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7984c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = {
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7, 8
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
802606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Coxstatic void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old)
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
805606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox	struct ktermios *termios = tty->termios;
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_line_coding newline;
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl = acm->ctrlout;
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bParityType = termios->c_cflag & PARENB ?
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!newline.dwDTERate) {
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newline.dwDTERate = acm->line.dwDTERate;
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newctrl &= ~ACM_CTRL_DTR;
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else  newctrl |=  ACM_CTRL_DTR;
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (newctrl != acm->ctrlout)
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_control(acm, acm->ctrlout = newctrl);
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(&acm->line, &newline, sizeof newline)) {
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&acm->line, &newline, sizeof newline);
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bCharFormat, newline.bParityType,
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bDataBits);
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_line(acm, &acm->line);
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines.
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
842830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */
843884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm)
844884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
845884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
846884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
847a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
848884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
84986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
850a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum		usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);
851884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
852884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
853884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
854830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm)
855830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{
856830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
857830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	int i, n = acm->rx_buflimit;
858830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
859830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	for (i = 0; i < n; i++)
860830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum		usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
861830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum}
862830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
863884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */
864884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm)
865884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
866884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
867884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
868884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
86986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
870884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
871884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		    &wb->dmah);
872884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->buf) {
873884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			while (i != 0) {
874884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--i;
875884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--wb;
876884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				usb_buffer_free(acm->dev, acm->writesize,
877884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				    wb->buf, wb->dmah);
878884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			}
879884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -ENOMEM;
880884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
881884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
882884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return 0;
883884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
884884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_probe (struct usb_interface *intf,
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      const struct usb_device_id *id)
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_union_desc *union_header = NULL;
889c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_cdc_country_functional_desc *cfd = NULL;
890c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell	unsigned char *buffer = intf->altsetting->extra;
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buflen = intf->altsetting->extralen;
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *control_interface;
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *data_interface;
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epctrl;
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epread;
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epwrite;
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor;
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ctrlsize,readsize;
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *buf;
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 ac_management_function = 0;
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 call_management_function = 0;
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int call_interface_num = -1;
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int data_interface_num;
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long quirks;
90786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int num_rx_buf;
90861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* normal quirks */
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	quirks = (unsigned long)id->driver_info;
91286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
91386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum
91486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* handle quirks deadly to normal probing*/
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (quirks == NO_UNION_NORMAL) {
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, 1);
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, 0);
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto skip_normal_probe;
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* normal probing*/
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
9239908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "Weird descriptor references\n");
9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buflen) {
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
929898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buflen = intf->cur_altsetting->endpoint->extralen;
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = intf->cur_altsetting->endpoint->extra;
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
9339908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev,
9349908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman				"Zero length descriptor references\n");
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (buflen > 0) {
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (buffer [1] != USB_DT_CS_INTERFACE) {
9419908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev, "skipping garbage\n");
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto next_desc;
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (buffer [2]) {
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_UNION_TYPE: /* we've found it */
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (union_header) {
9489908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman					dev_err(&intf->dev, "More than one "
9499908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman						"union descriptor, "
9509908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman						"skipping ...\n");
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					goto next_desc;
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				union_header = (struct usb_cdc_union_desc *)
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							buffer;
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
956c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
957c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum				cfd = (struct usb_cdc_country_functional_desc *)buffer;
958c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum				break;
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_HEADER_TYPE: /* maybe check version */
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break; /* for now we ignore it */
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_ACM_TYPE:
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ac_management_function = buffer[3];
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_CALL_MANAGEMENT_TYPE:
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				call_management_function = buffer[3];
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				call_interface_num = buffer[4];
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if ((call_management_function & 3) != 3)
9689908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman					dev_err(&intf->dev, "This device "
9699908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman						"cannot do calls on its own. "
9709908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman						"It is no modem.\n");
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
973c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell				/* there are LOTS more CDC descriptors that
974c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell				 * could legitimately be found here.
975c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell				 */
976c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell				dev_dbg(&intf->dev, "Ignoring descriptor: "
977c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell						"type %02x, length %d\n",
978c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell						buffer[2], buffer[0]);
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc:
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buflen -= buffer[0];
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer += buffer[0];
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!union_header) {
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (call_interface_num > 0) {
988898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = intf;
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
992898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"No union descriptor, giving up\n");
9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!control_interface || !data_interface) {
999898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"no interfaces\n");
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface_num != call_interface_num)
1005dc0d5c1e5c7532e800fff6e313cd4af44af99976Joe Perches		dev_dbg(&intf->dev,"Separate call control interface. That is not fully supported.\n");
10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe:
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*workaround for switched interfaces */
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_interface *t;
1013898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = control_interface;
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = data_interface;
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = t;
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
102274da5d68a54d66667664fbe233ededab2376a070Alan Stern
102374da5d68a54d66667664fbe233ededab2376a070Alan Stern	/* Accept probe requests only for the control interface */
102474da5d68a54d66667664fbe233ededab2376a070Alan Stern	if (intf != control_interface)
102574da5d68a54d66667664fbe233ededab2376a070Alan Stern		return -ENODEV;
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_interface_claimed(data_interface)) { /* valid in this context */
1028898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev,"The data interface isn't available\n");
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epread = &data_interface->cur_altsetting->endpoint[0].desc;
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* workaround for switched endpoints */
104245aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino	if (!usb_endpoint_dir_in(epread)) {
10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* descriptors are swapped */
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_endpoint_descriptor *t;
1045898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = epread;
10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epread = epwrite;
10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epwrite = t;
10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("interfaces are valid");
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (minor == ACM_TTY_MINORS) {
10559908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "no more free acm devices\n");
10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
105946f116eab81b21c6ae8c4f169498c632b1f94bf1Oliver Neukum	if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
1060898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail;
10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
106586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
1066e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->control = control_interface;
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->data = data_interface;
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->minor = minor;
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = usb_dev;
10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_caps = ac_management_function;
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlsize = ctrlsize;
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->readsize = readsize;
107486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->rx_buflimit = num_rx_buf;
107561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.func = acm_rx_tasklet;
107661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.data = (unsigned long) acm;
1077c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&acm->work, acm_softint);
107811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	INIT_WORK(&acm->waker, acm_waker);
1079e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	init_waitqueue_head(&acm->drain_wait);
10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&acm->throttle_lock);
1081884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_init(&acm->write_lock);
108261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock_init(&acm->read_lock);
10831365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_init(&acm->mutex);
108461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buf) {
1088898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail2;
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_buffer = buf;
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1093884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (acm_write_buffers_alloc(acm) < 0) {
1094898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail4;
10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->ctrlurb) {
1100898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail5;
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
110386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
110461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		struct acm_ru *rcv = &(acm->ru[i]);
110561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
110661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
1107898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n");
110861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
110961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
111061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
111161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
111261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->instance = acm;
111361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
111486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
1115672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		struct acm_rb *rb = &(acm->rb[i]);
111661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
1117672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		rb->base = usb_buffer_alloc(acm->dev, readsize,
1118672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell				GFP_KERNEL, &rb->dma);
1119672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		if (!rb->base) {
1120898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
112161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
112261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1124e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for(i = 0; i < ACM_NW; i++)
1125e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	{
1126e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		struct acm_wb *snd = &(acm->wb[i]);
1127e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
1128e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
1129e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf			dev_dbg(&intf->dev, "out of memory (write urbs usb_alloc_urb)");
1130e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf			goto alloc_fail7;
1131e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		}
1132e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
1133e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1134e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf				NULL, acm->writesize, acm_write_bulk, snd);
1135e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1136e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->instance = acm;
11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1139c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	usb_set_intfdata (intf, acm);
1140c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1141c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1142c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (i < 0)
1143c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		goto alloc_fail8;
1144c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1145c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (cfd) { /* export the country data */
1146c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1147c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (!acm->country_codes)
1148c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1149c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_code_size = cfd->bLength - 4;
1150c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
1151c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_rel_date = cfd->iCountryCodeRelDate;
1152c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1153c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1154c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1155c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1156c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1157c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1158c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1159c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
1160c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1161c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1162c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1163c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1164c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
1165c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1166c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries:
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_control(acm, acm->ctrlout);
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.dwDTERate = cpu_to_le32(9600);
11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.bDataBits = 8;
11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_line(acm, &acm->line);
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_driver_claim_interface(&acm_driver, data_interface, acm);
1181672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	usb_set_intfdata(data_interface, acm);
11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
118383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_get_intf(control_interface);
118483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_table[minor] = acm;
11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1188c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return 0;
1189c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumalloc_fail8:
1190e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
1191e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7:
1193830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
119486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
119561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
11961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_free_urb(acm->ctrlurb);
11971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5:
1198884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4:
12001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
12011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2:
12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acm);
12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail:
12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOMEM;
12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm)
12081365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
12091365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int i;
121011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	dbg("Entering stop_data_traffic");
12111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_disable(&acm->urb_task);
12131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_kill_urb(acm->ctrlurb);
1215e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for(i = 0; i < ACM_NW; i++)
1216e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_kill_urb(acm->wb[i].urb);
12171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
12181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_kill_urb(acm->ru[i].urb);
12191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_enable(&acm->urb_task);
12211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	cancel_work_sync(&acm->work);
122311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cancel_work_sync(&acm->waker);
12241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
12251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf)
12271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
12291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
12301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1231672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	/* sibling interface is already cleaning up */
1232672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	if (!acm)
123386067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum		return;
1234672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell
1235672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	mutex_lock(&open_mutex);
1236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (acm->country_codes){
123774da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
123874da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_wCountryCodes);
123974da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
124074da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_iCountryCodeRelDate);
1241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
124274da5d68a54d66667664fbe233ededab2376a070Alan Stern	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
12431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = NULL;
124486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->control, NULL);
124586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->data, NULL);
12461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12471365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	stop_data_traffic(acm);
12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1249884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
1251830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1253830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	usb_driver_release_interface(&acm_driver, intf == acm->control ?
1254830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum					acm->data : acm->control);
12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->used) {
125783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		acm_tty_unregister(acm);
12584186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12624186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->tty)
12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_hangup(acm->tty);
12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1268357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
12691365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message)
12701365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
12711365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
127211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
127311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
127465bfd2967c906ca322a4bb69a285fe0de8916ac6Alan Stern	if (message.event & PM_EVENT_AUTO) {
127511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		int b;
127611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
127711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irq(&acm->read_lock);
127811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock(&acm->write_lock);
127911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		b = acm->processing + acm->transmitting;
128011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock(&acm->write_lock);
128111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irq(&acm->read_lock);
128211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (b)
128311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			return -EBUSY;
128411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
128511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
128611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
128711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock(&acm->write_lock);
128811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count++;
128911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock(&acm->write_lock);
129011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
12911365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
129211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
12931365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
12941365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	/*
12951365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we treat opened interfaces differently,
12961365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we must guard against open
12971365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	*/
12981365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
12991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13001365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	if (acm->used)
13011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		stop_data_traffic(acm);
13021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
13041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return 0;
13051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
13061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf)
13081365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13091365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
13101365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int rv = 0;
131111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
13121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
131311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
131411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->susp_count -= 1;
131511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count;
131611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
131711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
131811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
13191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
13201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
13221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	if (acm->used) {
13231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
13241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		if (rv < 0)
132511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			goto err_out;
13261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		tasklet_schedule(&acm->urb_task);
13281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	}
13291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out:
13311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
13321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return rv;
13331365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
1334357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum
1335357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */
13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure.
13381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id acm_ids[] = {
13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* quirky and broken devices */
13421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
13431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1345b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1346b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1347b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	},
13480f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	{ USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
13490f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13500f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	},
13518753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
13528753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13538753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	},
135491a9c9214e34c364bf15406aadb922787ae7129bChris Malley	{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
135591a9c9214e34c364bf15406aadb922787ae7129bChris Malley	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
135691a9c9214e34c364bf15406aadb922787ae7129bChris Malley	},
13577abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	{ USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
13587abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	.driver_info = SINGLE_RX_URB,
13597abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	},
136086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
136186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
136286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	},
13633dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
13643dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
13653dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	},
13669be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
13679be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13689be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	},
13696149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	{ USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
13706149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13716149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	},
1372c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	{ USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1373c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1374c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	},
1375c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	{ USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1376c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1377c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	},
1378cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	{ USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1379cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1380cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	},
1381155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1382155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	},
1383c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	{ USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1384c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	.driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1385c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   data interface instead of
1386c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   communications interface.
1387c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   Maybe we should define a new
1388c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   quirk for this. */
1389c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	},
13909be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum
13911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* control interfaces with various AT-command sets */
13921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_V25TER) },
13941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101) },
13961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
13981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_GSM) },
14001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
14011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_3G	) },
14021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
14031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_CDMA) },
14041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* NOTE:  COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
14061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
14071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
14081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE (usb, acm_ids);
14101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = {
14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"cdc_acm",
14131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	acm_probe,
14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	acm_disconnect,
1415357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
14161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.suspend =	acm_suspend,
14171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.resume =	acm_resume,
1418357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
14191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	acm_ids,
1420357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
14211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.supports_autosuspend = 1,
1422357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
14231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
14241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
14261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures.
14271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1429b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = {
14301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			acm_tty_open,
14311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		acm_tty_close,
14321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		acm_tty_write,
14331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		acm_tty_write_room,
14341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		acm_tty_ioctl,
14351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		acm_tty_throttle,
14361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		acm_tty_unthrottle,
14371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	acm_tty_chars_in_buffer,
14381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl =		acm_tty_break_ctl,
14391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios =		acm_tty_set_termios,
14401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmget =		acm_tty_tiocmget,
14411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmset =		acm_tty_tiocmset,
14421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
14431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
14451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit.
14461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void)
14491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
14511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
14521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm_tty_driver)
14531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
14541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->owner = THIS_MODULE,
14551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->driver_name = "acm",
14561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->name = "ttyACM",
14571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->major = ACM_TTY_MAJOR,
14581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->minor_start = 0,
14591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
14601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
1461331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
14621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios = tty_std_termios;
14631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
14641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(acm_tty_driver, &acm_ops);
14651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = tty_register_driver(acm_tty_driver);
14671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
14681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
14691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
14701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&acm_driver);
14731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
14741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_unregister_driver(acm_tty_driver);
14751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
14761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
14771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14795909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
14805909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman	       DRIVER_DESC "\n");
14811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
14831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void)
14861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister(&acm_driver);
14881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_unregister_driver(acm_tty_driver);
14891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	put_tty_driver(acm_tty_driver);
14901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init);
14931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit);
14941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR( DRIVER_AUTHOR );
14961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION( DRIVER_DESC );
14971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
1498e766aeb882b41355d8732cf49aa9412baef852c5Scott James RemnantMODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);
1499