cdc-acm.c revision a496c64f1363ec4d67ebdc1e1f619ad6372a574c
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/*
161884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Finish write.
162884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
163e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic void acm_write_done(struct acm *acm, struct acm_wb *wb)
164884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
165884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
166884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
167884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
168e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wb->use = 0;
16911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting--;
170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
171884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
172884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write.
17511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum *
17611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking
177884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
17811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
17911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb)
18011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
18111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rc;
18211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting++;
18411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer = wb->buf;
18611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_dma = wb->dmah;
18711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer_length = wb->len;
18811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->dev = acm->dev;
18911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
19011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) {
19111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("usb_submit_urb(write bulk) failed: %d", rc);
19211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_write_done(acm, wb);
19311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
19411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	return rc;
19511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
19611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
197e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn)
198884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
199884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
200934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	struct acm_wb *wb = &acm->wb[wbn];
201884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int rc;
202884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
204884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->dev) {
205934da4635c2d05cef474e5243ef05df95b2ad264David Brownell		wb->use = 0;
206884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
207884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return -ENODEV;
208884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
209884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
21011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	dbg("%s susp_count: %d", __func__, acm->susp_count);
21111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->susp_count) {
21211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->delayed_wb = wb;
21311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		schedule_work(&acm->waker);
21411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
21511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return 0;	/* A white lie */
21611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
21711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
21811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
21911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rc = acm_start_wb(acm, wb);
220884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
221884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
222884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return rc;
22311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
224884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
225c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/*
226c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs
227c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */
228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps
229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
230c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
231c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->ctrl_caps);
235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
237c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes
239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	memcpy(buf, acm->country_codes, acm->country_code_size);
245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return acm->country_code_size;
246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
249c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
250c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date
251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
254c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->country_rel_date);
257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
258884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
260884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */
2657d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb)
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = urb->context;
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_notification *dr = urb->transfer_buffer;
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data;
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl;
271185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int retval;
272185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int status = urb->status;
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
274185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	switch (status) {
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* success */
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ECONNRESET:
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ENOENT:
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ESHUTDOWN:
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this urb is terminated, clean up */
282441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - urb shutting down with status: %d", __func__, status);
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
285441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - nonzero urb status received: %d", __func__, status);
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = (unsigned char *)(dr + 1);
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (dr->bNotificationType) {
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_CDC_NOTIFY_NETWORK_CONNECTION:
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_CDC_NOTIFY_SERIAL_STATE:
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
302a5abdeafedf722b0f3f357f4a23089a686b1b80dHarvey Harrison			newctrl = get_unaligned_le16(data);
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dbg("calling hangup");
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				tty_hangup(acm->tty);
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			acm->ctrlin = newctrl;
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',	acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',	acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',	acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dr->bNotificationType, dr->wIndex,
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dr->wLength, data[0], data[1]);
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
32611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
327185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	retval = usb_submit_urb (urb, GFP_ATOMIC);
328185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	if (retval)
3299908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
3309908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			"result %d", __func__, retval);
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */
3347d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_read_bulk(struct urb *urb)
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
33661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
33761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv = urb->context;
33861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm *acm = rcv->instance;
33986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int status = urb->status;
340185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman
341185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	dbg("Entering acm_read_bulk with status %d", status);
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
34311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (!ACM_READY(acm)) {
34411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dev_dbg(&acm->data->dev, "Aborting, acm not ready");
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
34611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
34711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
34986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (status)
350898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
35261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = rcv->buffer;
35361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf->size = urb->actual_length;
35461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
35586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (likely(status == 0)) {
35686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
35711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->processing++;
35886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
35986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&buf->list, &acm->filled_read_bufs);
36086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
36186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	} else {
36286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* we drop the buffer due to an error */
36386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
36486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
36586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add(&buf->list, &acm->spare_read_bufs);
36686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
36786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* nevertheless the tasklet must be kicked unconditionally
36886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		so the queue cannot dry up */
36986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	}
37011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (likely(!acm->susp_count))
37111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		tasklet_schedule(&acm->urb_task);
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm)
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = (void *)_acm;
37761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct tty_struct *tty = acm->tty;
37961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv;
380762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	unsigned long flags;
381ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	unsigned char throttled;
38211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Entering acm_rx_tasklet");
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
385ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (!ACM_READY(acm))
38611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	{
38711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("acm_rx_tasklet: ACM not ready");
388ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		return;
38911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
390ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
391834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_lock_irqsave(&acm->throttle_lock, flags);
392ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	throttled = acm->throttle;
393834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_unlock_irqrestore(&acm->throttle_lock, flags);
394ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (throttled)
39511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	{
39611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("acm_rx_tasklet: throttled");
39761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		return;
39811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
39961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
40061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer:
401762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
40261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	if (list_empty(&acm->filled_read_bufs)) {
403762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
40461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		goto urbs;
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
40661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = list_entry(acm->filled_read_bufs.next,
40761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			 struct acm_rb, list);
40861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_del(&buf->list);
409762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
41061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
4113dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
41261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
41333f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox	tty_buffer_request_room(tty, buf->size);
414834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_lock_irqsave(&acm->throttle_lock, flags);
415ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	throttled = acm->throttle;
416834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_unlock_irqrestore(&acm->throttle_lock, flags);
417ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (!throttled)
41833f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox		tty_insert_flip_string(tty, buf->base, buf->size);
41961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tty_flip_buffer_push(tty);
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (throttled) {
422ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		dbg("Throttling noticed");
423762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_lock_irqsave(&acm->read_lock, flags);
42461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&buf->list, &acm->filled_read_bufs);
425762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
429762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
43061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_add(&buf->list, &acm->spare_read_bufs);
431762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
43261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	goto next_buffer;
43361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
43461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs:
43561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	while (!list_empty(&acm->spare_read_bufs)) {
436762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_lock_irqsave(&acm->read_lock, flags);
43761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (list_empty(&acm->spare_read_urbs)) {
43811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
439762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
44061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
44161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
44261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv = list_entry(acm->spare_read_urbs.next,
44361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_ru, list);
44461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&rcv->list);
445762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
44661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
44761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		buf = list_entry(acm->spare_read_bufs.next,
44861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_rb, list);
44961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&buf->list);
45061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
45161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->buffer = buf;
45261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
45361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_fill_bulk_urb(rcv->urb, acm->dev,
45461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm->rx_endpoint,
45561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  buf->base,
45661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm->readsize,
45761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm_read_bulk, rcv);
45861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_dma = buf->dma;
45961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
46061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
46161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		/* This shouldn't kill the driver as unsuccessful URBs are returned to the
46261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		   free-urbs-pool and resubmited ASAP */
46311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irqsave(&acm->read_lock, flags);
46411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (acm->susp_count || usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
46561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&buf->list, &acm->spare_read_bufs);
46661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&rcv->list, &acm->spare_read_urbs);
46711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
468762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
46961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
47011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		} else {
47111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			spin_unlock_irqrestore(&acm->read_lock, flags);
47211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
47361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
47461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
47511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irqsave(&acm->read_lock, flags);
47611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->processing = 0;
47711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irqrestore(&acm->read_lock, flags);
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */
4817d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb)
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
483cdc97792289179974af6dda781c855696358d307Ming Lei	struct acm_wb *wb = urb->context;
484e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	struct acm *acm = wb->instance;
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
486e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	if (verbose || urb->status
487e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			|| (urb->actual_length != urb->transfer_buffer_length))
488e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell		dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
489e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->actual_length,
490e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->transfer_buffer_length,
491e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->status);
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
493e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm_write_done(acm, wb);
494884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (ACM_READY(acm))
495884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		schedule_work(&acm->work);
496e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	else
497e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell		wake_up_interruptible(&acm->drain_wait);
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
500c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work)
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
502c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct acm *acm = container_of(work, struct acm, work);
503e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
504e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	dev_vdbg(&acm->data->dev, "tx work\n");
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_wakeup(acm->tty);
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic void acm_waker(struct work_struct *waker)
51111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
51211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	struct acm *acm = container_of(waker, struct acm, waker);
51311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rv;
51411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
51511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rv = usb_autopm_get_interface(acm->control);
51611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (rv < 0) {
5179908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__);
51811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return;
51911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
52011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->delayed_wb) {
52111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_start_wb(acm, acm->delayed_wb);
52211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->delayed_wb = NULL;
52311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
52411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
52511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
52611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp)
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rv = -EINVAL;
53561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
5363dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_open.");
5374186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven
5384186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm = acm_table[tty->index];
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->dev)
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out;
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rv = 0;
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
54628d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91David Engraf	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty->driver_data = acm;
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->tty = tty;
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
55061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	/* force low_latency on so that our tty_push actually forces the data through,
55161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	   otherwise it is scheduled, and with high data rates data can get lost. */
55261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tty->low_latency = 1;
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
55494409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	if (usb_autopm_get_interface(acm->control) < 0)
55594409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum		goto early_bail;
55611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	else
55711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->control->needs_remote_wakeup = 1;
5581365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5591365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->used++) {
5611365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_autopm_put_interface(acm->control);
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto done;
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5651365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->dev = acm->dev;
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("usb_submit_urb(ctrl irq) failed");
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto bail_out;
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
572ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
573ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	    (acm->ctrl_caps & USB_CDC_CAP_LINE))
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto full_bailout;
57511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
57761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_urbs);
57861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_bufs);
57961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->filled_read_bufs);
58086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++) {
58161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
58261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
58386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++) {
58461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
58561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
58661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
587ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	acm->throttle = 0;
588ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
58961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone:
5921365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
59374573ee7096a4ffc2f098108d21c85801b9c7434Alexey Dobriyanerr_out:
59494409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rv;
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout:
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(acm->ctrlurb);
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out:
6001365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_autopm_put_interface(acm->control);
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->used--;
6021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
60394409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukumearly_bail:
60494409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EIO;
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
60883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm)
60983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{
61086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int i,nr;
61161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
61286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
61383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_unregister_device(acm_tty_driver, acm->minor);
61483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_put_intf(acm->control);
61583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	acm_table[acm->minor] = NULL;
61683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_free_urb(acm->ctrlurb);
617e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
618e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
61986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < nr; i++)
62061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
621c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	kfree(acm->country_codes);
62283ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	kfree(acm);
62383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk}
62483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk
625e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownellstatic int acm_tty_chars_in_buffer(struct tty_struct *tty);
626e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp)
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
63086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int i,nr;
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->used)
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
63586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
6364186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!--acm->used) {
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (acm->dev) {
63911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			usb_autopm_get_interface(acm->control);
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			acm_set_control(acm, acm->ctrlout = 0);
641e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
642e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			/* try letting the last writes drain naturally */
643e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			wait_event_interruptible_timeout(acm->drain_wait,
644e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell					(ACM_NW == acm_wb_is_avail(acm))
645e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell						|| !acm->dev,
646e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell					ACM_CLOSE_TIMEOUT * HZ);
647e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			usb_kill_urb(acm->ctrlurb);
649e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf			for (i = 0; i < ACM_NW; i++)
650e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf				usb_kill_urb(acm->wb[i].urb);
65186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum			for (i = 0; i < nr; i++)
65261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				usb_kill_urb(acm->ru[i].urb);
65311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->control->needs_remote_wakeup = 0;
6541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum			usb_autopm_put_interface(acm->control);
65583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		} else
65683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk			acm_tty_unregister(acm);
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6584186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int stat;
665884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
666884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
667884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
668884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
6693dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_write to write %d bytes,", count);
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!count)
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
676884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
677884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if ((wbn = acm_wb_alloc(acm)) < 0) {
678884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
679884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
680884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
681884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
683884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	count = (count > acm->writesize) ? acm->writesize : count;
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Get %d bytes...", count);
685884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	memcpy(wb->buf, buf, count);
686884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb->len = count;
687884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
689e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	if ((stat = acm_write_start(acm, wbn)) < 0)
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return stat;
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty)
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
699884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
700884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * Do not let the line discipline to know that we have a reserve,
701884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * or it might get too enthusiastic.
702884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
703934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	return acm_wb_is_avail(acm) ? acm->writesize : 0;
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty)
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
711884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
712884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * This is inaccurate (overcounts), but it works.
713884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
71486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty)
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 1;
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty)
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 0;
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
73561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7389e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state)
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7419e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	int retval;
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7439e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox		return -EINVAL;
7449e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	retval = acm_send_break(acm, state ? 0xffff : 0);
7459e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	if (retval < 0)
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("send break failed");
7479e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	return retval;
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TIOCM_CTS;
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    unsigned int set, unsigned int clear)
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int newctrl;
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = acm->ctrlout;
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = (newctrl & ~clear) | set;
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->ctrlout == newctrl)
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return acm_set_control(acm, acm->ctrlout = newctrl);
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7954c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = {
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 50, 75, 110, 134, 150, 200, 300, 600,
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1200, 1800, 2400, 4800, 9600, 19200, 38400,
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	57600, 115200, 230400, 460800, 500000, 576000,
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	921600, 1000000, 1152000, 1500000, 2000000,
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	2500000, 3000000, 3500000, 4000000
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8034c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = {
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7, 8
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
807606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Coxstatic void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old)
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
810606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox	struct ktermios *termios = tty->termios;
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_line_coding newline;
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl = acm->ctrlout;
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bParityType = termios->c_cflag & PARENB ?
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!newline.dwDTERate) {
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newline.dwDTERate = acm->line.dwDTERate;
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newctrl &= ~ACM_CTRL_DTR;
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else  newctrl |=  ACM_CTRL_DTR;
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (newctrl != acm->ctrlout)
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_control(acm, acm->ctrlout = newctrl);
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(&acm->line, &newline, sizeof newline)) {
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&acm->line, &newline, sizeof newline);
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bCharFormat, newline.bParityType,
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bDataBits);
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_line(acm, &acm->line);
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines.
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
847830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */
848884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm)
849884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
850884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
851884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
852a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
853884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
85486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
855a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum		usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);
856884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
857884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
858884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
859830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm)
860830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{
861830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
862830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	int i, n = acm->rx_buflimit;
863830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
864830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	for (i = 0; i < n; i++)
865830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum		usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
866830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum}
867830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
868884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */
869884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm)
870884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
871884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
872884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
873884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
87486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
875884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
876884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		    &wb->dmah);
877884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->buf) {
878884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			while (i != 0) {
879884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--i;
880884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--wb;
881884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				usb_buffer_free(acm->dev, acm->writesize,
882884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				    wb->buf, wb->dmah);
883884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			}
884884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -ENOMEM;
885884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
886884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
887884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return 0;
888884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
889884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_probe (struct usb_interface *intf,
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      const struct usb_device_id *id)
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_union_desc *union_header = NULL;
894c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_cdc_country_functional_desc *cfd = NULL;
895c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell	unsigned char *buffer = intf->altsetting->extra;
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buflen = intf->altsetting->extralen;
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *control_interface;
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *data_interface;
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epctrl;
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epread;
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epwrite;
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor;
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ctrlsize,readsize;
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *buf;
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 ac_management_function = 0;
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 call_management_function = 0;
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int call_interface_num = -1;
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int data_interface_num;
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long quirks;
91286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int num_rx_buf;
91361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* normal quirks */
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	quirks = (unsigned long)id->driver_info;
91786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
91886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum
91986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* handle quirks deadly to normal probing*/
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (quirks == NO_UNION_NORMAL) {
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, 1);
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, 0);
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto skip_normal_probe;
9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* normal probing*/
9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
9289908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "Weird descriptor references\n");
9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buflen) {
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
934898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buflen = intf->cur_altsetting->endpoint->extralen;
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = intf->cur_altsetting->endpoint->extra;
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
9389908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev,
9399908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman				"Zero length descriptor references\n");
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (buflen > 0) {
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (buffer [1] != USB_DT_CS_INTERFACE) {
9469908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev, "skipping garbage\n");
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto next_desc;
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (buffer [2]) {
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_UNION_TYPE: /* we've found it */
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (union_header) {
9539908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman					dev_err(&intf->dev, "More than one "
9549908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman						"union descriptor, "
9559908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman						"skipping ...\n");
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					goto next_desc;
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				union_header = (struct usb_cdc_union_desc *)
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							buffer;
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
961c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
962c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum				cfd = (struct usb_cdc_country_functional_desc *)buffer;
963c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum				break;
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_HEADER_TYPE: /* maybe check version */
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break; /* for now we ignore it */
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_ACM_TYPE:
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ac_management_function = buffer[3];
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_CALL_MANAGEMENT_TYPE:
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				call_management_function = buffer[3];
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				call_interface_num = buffer[4];
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if ((call_management_function & 3) != 3)
9739908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman					dev_err(&intf->dev, "This device "
9749908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman						"cannot do calls on its own. "
9759908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman						"It is no modem.\n");
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
978c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell				/* there are LOTS more CDC descriptors that
979c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell				 * could legitimately be found here.
980c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell				 */
981c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell				dev_dbg(&intf->dev, "Ignoring descriptor: "
982c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell						"type %02x, length %d\n",
983c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell						buffer[2], buffer[0]);
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc:
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buflen -= buffer[0];
9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer += buffer[0];
9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!union_header) {
9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (call_interface_num > 0) {
993898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = intf;
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
997898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"No union descriptor, giving up\n");
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!control_interface || !data_interface) {
1004898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"no interfaces\n");
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface_num != call_interface_num)
1010dc0d5c1e5c7532e800fff6e313cd4af44af99976Joe Perches		dev_dbg(&intf->dev,"Separate call control interface. That is not fully supported.\n");
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe:
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*workaround for switched interfaces */
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_interface *t;
1018898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = control_interface;
10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = data_interface;
10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = t;
10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
102774da5d68a54d66667664fbe233ededab2376a070Alan Stern
102874da5d68a54d66667664fbe233ededab2376a070Alan Stern	/* Accept probe requests only for the control interface */
102974da5d68a54d66667664fbe233ededab2376a070Alan Stern	if (intf != control_interface)
103074da5d68a54d66667664fbe233ededab2376a070Alan Stern		return -ENODEV;
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_interface_claimed(data_interface)) { /* valid in this context */
1033898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev,"The data interface isn't available\n");
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epread = &data_interface->cur_altsetting->endpoint[0].desc;
10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* workaround for switched endpoints */
104745aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino	if (!usb_endpoint_dir_in(epread)) {
10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* descriptors are swapped */
10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_endpoint_descriptor *t;
1050898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = epread;
10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epread = epwrite;
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epwrite = t;
10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("interfaces are valid");
10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (minor == ACM_TTY_MINORS) {
10609908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "no more free acm devices\n");
10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
106446f116eab81b21c6ae8c4f169498c632b1f94bf1Oliver Neukum	if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
1065898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail;
10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
107086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
1071e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->control = control_interface;
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->data = data_interface;
10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->minor = minor;
10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = usb_dev;
10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_caps = ac_management_function;
10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlsize = ctrlsize;
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->readsize = readsize;
107986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->rx_buflimit = num_rx_buf;
108061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.func = acm_rx_tasklet;
108161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.data = (unsigned long) acm;
1082c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&acm->work, acm_softint);
108311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	INIT_WORK(&acm->waker, acm_waker);
1084e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	init_waitqueue_head(&acm->drain_wait);
10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&acm->throttle_lock);
1086884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_init(&acm->write_lock);
108761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock_init(&acm->read_lock);
10881365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_init(&acm->mutex);
108961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buf) {
1093898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail2;
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_buffer = buf;
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1098884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (acm_write_buffers_alloc(acm) < 0) {
1099898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail4;
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->ctrlurb) {
1105898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail5;
11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
110886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
110961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		struct acm_ru *rcv = &(acm->ru[i]);
111061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
111161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
1112898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n");
111361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
111461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
111561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
111661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
111761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->instance = acm;
111861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
111986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
1120672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		struct acm_rb *rb = &(acm->rb[i]);
112161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
1122672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		rb->base = usb_buffer_alloc(acm->dev, readsize,
1123672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell				GFP_KERNEL, &rb->dma);
1124672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell		if (!rb->base) {
1125898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
112661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
112761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1129e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for(i = 0; i < ACM_NW; i++)
1130e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	{
1131e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		struct acm_wb *snd = &(acm->wb[i]);
1132e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
1133e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
1134e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf			dev_dbg(&intf->dev, "out of memory (write urbs usb_alloc_urb)");
1135e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf			goto alloc_fail7;
1136e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		}
1137e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
1138e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1139e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf				NULL, acm->writesize, acm_write_bulk, snd);
1140e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1141e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->instance = acm;
11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1144c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	usb_set_intfdata (intf, acm);
1145c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1146c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1147c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (i < 0)
1148c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		goto alloc_fail8;
1149c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1150c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (cfd) { /* export the country data */
1151c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1152c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (!acm->country_codes)
1153c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1154c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_code_size = cfd->bLength - 4;
1155c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
1156c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_rel_date = cfd->iCountryCodeRelDate;
1157c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1158c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1159c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1160c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1161c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1162c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1163c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1164c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
1165c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1166c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1167c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1168c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1169c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
1170c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1171c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries:
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_control(acm, acm->ctrlout);
11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.dwDTERate = cpu_to_le32(9600);
11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.bDataBits = 8;
11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_line(acm, &acm->line);
11841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_driver_claim_interface(&acm_driver, data_interface, acm);
1186672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	usb_set_intfdata(data_interface, acm);
11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
118883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_get_intf(control_interface);
118983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_table[minor] = acm;
11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1193c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return 0;
1194c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumalloc_fail8:
1195e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
1196e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
11971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7:
1198830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
119986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
120061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
12011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_free_urb(acm->ctrlurb);
12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5:
1203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4:
12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2:
12071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acm);
12081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail:
12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOMEM;
12101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm)
12131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
12141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int i;
121511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	dbg("Entering stop_data_traffic");
12161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_disable(&acm->urb_task);
12181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_kill_urb(acm->ctrlurb);
1220e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for(i = 0; i < ACM_NW; i++)
1221e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_kill_urb(acm->wb[i].urb);
12221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
12231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_kill_urb(acm->ru[i].urb);
12241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_enable(&acm->urb_task);
12261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	cancel_work_sync(&acm->work);
122811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cancel_work_sync(&acm->waker);
12291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
12301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf)
12321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
12341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
12351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1236672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	/* sibling interface is already cleaning up */
1237672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	if (!acm)
123886067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum		return;
1239672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell
1240672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	mutex_lock(&open_mutex);
1241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (acm->country_codes){
124274da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
124374da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_wCountryCodes);
124474da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
124574da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_iCountryCodeRelDate);
1246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
124774da5d68a54d66667664fbe233ededab2376a070Alan Stern	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = NULL;
124986067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->control, NULL);
125086067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->data, NULL);
12511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12521365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	stop_data_traffic(acm);
12531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1254884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
1256830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
12571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1258830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	usb_driver_release_interface(&acm_driver, intf == acm->control ?
1259830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum					acm->data : acm->control);
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->used) {
126283ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		acm_tty_unregister(acm);
12634186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12674186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
12681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->tty)
12701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_hangup(acm->tty);
12711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1273357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
12741365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message)
12751365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
12761365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
127711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
127811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
127911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->dev->auto_pm) {
128011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		int b;
128111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
128211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irq(&acm->read_lock);
128311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock(&acm->write_lock);
128411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		b = acm->processing + acm->transmitting;
128511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock(&acm->write_lock);
128611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irq(&acm->read_lock);
128711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (b)
128811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			return -EBUSY;
128911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
129011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
129111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
129211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock(&acm->write_lock);
129311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count++;
129411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock(&acm->write_lock);
129511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
12961365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
129711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
12981365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
12991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	/*
13001365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we treat opened interfaces differently,
13011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we must guard against open
13021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	*/
13031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
13041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	if (acm->used)
13061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		stop_data_traffic(acm);
13071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13081365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
13091365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return 0;
13101365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
13111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf)
13131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
13151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int rv = 0;
131611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
13171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
131811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
131911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->susp_count -= 1;
132011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count;
132111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
132211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
132311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
13241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
13251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
13271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	if (acm->used) {
13281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
13291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		if (rv < 0)
133011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			goto err_out;
13311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		tasklet_schedule(&acm->urb_task);
13331365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	}
13341365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out:
13361365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
13371365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return rv;
13381365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
1339357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum
1340357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */
13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
13421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure.
13431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id acm_ids[] = {
13461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* quirky and broken devices */
13471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
13481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1350b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1351b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1352b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	},
13538753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
13548753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13558753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	},
135691a9c9214e34c364bf15406aadb922787ae7129bChris Malley	{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
135791a9c9214e34c364bf15406aadb922787ae7129bChris Malley	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
135891a9c9214e34c364bf15406aadb922787ae7129bChris Malley	},
135986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
136086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
136186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	},
13623dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
13633dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
13643dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	},
13659be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
13669be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13679be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	},
13686149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	{ USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
13696149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13706149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	},
1371c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	{ USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1372c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1373c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	},
13749be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum
13751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* control interfaces with various AT-command sets */
13761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_V25TER) },
13781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101) },
13801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
13821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_GSM) },
13841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_3G	) },
13861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_CDMA) },
13881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* NOTE:  COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
13901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
13911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
13921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE (usb, acm_ids);
13941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = {
13961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"cdc_acm",
13971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	acm_probe,
13981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	acm_disconnect,
1399357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
14001365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.suspend =	acm_suspend,
14011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.resume =	acm_resume,
1402357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
14031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	acm_ids,
1404357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
14051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.supports_autosuspend = 1,
1406357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
14071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
14081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
14101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures.
14111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1413b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = {
14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			acm_tty_open,
14151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		acm_tty_close,
14161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		acm_tty_write,
14171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		acm_tty_write_room,
14181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		acm_tty_ioctl,
14191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		acm_tty_throttle,
14201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		acm_tty_unthrottle,
14211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	acm_tty_chars_in_buffer,
14221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl =		acm_tty_break_ctl,
14231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios =		acm_tty_set_termios,
14241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmget =		acm_tty_tiocmget,
14251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmset =		acm_tty_tiocmset,
14261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
14271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
14291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit.
14301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void)
14331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
14351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
14361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm_tty_driver)
14371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
14381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->owner = THIS_MODULE,
14391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->driver_name = "acm",
14401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->name = "ttyACM",
14411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->major = ACM_TTY_MAJOR,
14421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->minor_start = 0,
14431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
14441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
1445331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
14461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios = tty_std_termios;
14471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
14481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(acm_tty_driver, &acm_ops);
14491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = tty_register_driver(acm_tty_driver);
14511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
14521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
14531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
14541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&acm_driver);
14571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
14581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_unregister_driver(acm_tty_driver);
14591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
14601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
14611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14635909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
14645909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman	       DRIVER_DESC "\n");
14651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
14671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void)
14701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister(&acm_driver);
14721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_unregister_driver(acm_tty_driver);
14731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	put_tty_driver(acm_tty_driver);
14741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init);
14771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit);
14781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR( DRIVER_AUTHOR );
14801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION( DRIVER_DESC );
14811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
14821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1483