cdc-acm.c revision 1365baf7249bb2d05e774e7681237b8e86f5007a
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
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version.
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h>
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
624186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h>
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
65a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h>
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h>
6861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h>
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h"
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_VERSION "v0.25"
7661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver;
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS];
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
834186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex);
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACM_READY(acm)	(acm && acm->dev && acm->used)
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages.
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len)
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		request, USB_RT_ACM, value,
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm->control->altsetting[0].desc.bInterfaceNumber,
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf, len, 5000);
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval < 0 ? retval : 0;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests.
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do...
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
112884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management.
113884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller.
114884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
115884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
116884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm)
117884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
118884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, wbn;
119884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
120884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
121884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wbn = acm->write_current;
122884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	i = 0;
123884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	for (;;) {
124884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb = &acm->wb[wbn];
125884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->use) {
126884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			wb->use = 1;
127884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return wbn;
128884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
12986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		wbn = (wbn + 1) % ACM_NW;
13086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		if (++i >= ACM_NW)
131884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -1;
132884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
133884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
134884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
135884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_wb_free(struct acm *acm, int wbn)
136884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
137884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->wb[wbn].use = 0;
138884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
139884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
140884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm)
141884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, n;
143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
14486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	n = ACM_NW;
14586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < ACM_NW; i++) {
14686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		n -= acm->wb[i].use;
147884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
148884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return n;
149884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
150884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
151884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic inline int acm_wb_is_used(struct acm *acm, int wbn)
152884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
153884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return acm->wb[wbn].use;
154884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
155884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
156884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
157884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Finish write.
158884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
159884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_done(struct acm *acm)
160884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
161884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
162884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
163884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
164884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
165884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->write_ready = 1;
166884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wbn = acm->write_current;
167884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_wb_free(acm, wbn);
16886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->write_current = (wbn + 1) % ACM_NW;
169884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
171884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
172884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write.
174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
175884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_start(struct acm *acm)
176884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
177884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
178884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
179884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
180884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int rc;
181884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
182884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
183884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->dev) {
184884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
185884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return -ENODEV;
186884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
187884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
188884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->write_ready) {
189884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
190884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;	/* A white lie */
191884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
192884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
193884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wbn = acm->write_current;
194884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm_wb_is_used(acm, wbn)) {
195884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
196884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
197884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
198884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
199884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
200884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->write_ready = 0;
201884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
202884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->writeurb->transfer_buffer = wb->buf;
204884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->writeurb->transfer_dma = wb->dmah;
205884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->writeurb->transfer_buffer_length = wb->len;
206884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->writeurb->dev = acm->dev;
207884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
208884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) {
209884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		dbg("usb_submit_urb(write bulk) failed: %d", rc);
210884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		acm_write_done(acm);
211884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
212884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return rc;
213884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
214c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/*
215c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs
216c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */
217c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps
218c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
219c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
220c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
221c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
222c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
223c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->ctrl_caps);
224c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
225c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
226c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
227c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes
228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
230c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
231c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	memcpy(buf, acm->country_codes, acm->country_code_size);
234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return acm->country_code_size;
235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
237c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date
240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->country_rel_date);
246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
247884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
249884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */
2547d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb)
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = urb->context;
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_notification *dr = urb->transfer_buffer;
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data;
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl;
260185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int retval;
261185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int status = urb->status;
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
263185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	switch (status) {
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* success */
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ECONNRESET:
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ENOENT:
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ESHUTDOWN:
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this urb is terminated, clean up */
271185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman		dbg("%s - urb shutting down with status: %d", __FUNCTION__, status);
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
274185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman		dbg("%s - nonzero urb status received: %d", __FUNCTION__, status);
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = (unsigned char *)(dr + 1);
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (dr->bNotificationType) {
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_CDC_NOTIFY_NETWORK_CONNECTION:
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_CDC_NOTIFY_SERIAL_STATE:
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newctrl = le16_to_cpu(get_unaligned((__le16 *) data));
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dbg("calling hangup");
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				tty_hangup(acm->tty);
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			acm->ctrlin = newctrl;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',	acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',	acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',	acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dr->bNotificationType, dr->wIndex,
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dr->wLength, data[0], data[1]);
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
315185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	retval = usb_submit_urb (urb, GFP_ATOMIC);
316185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	if (retval)
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err ("%s - usb_submit_urb failed with result %d",
318185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman		     __FUNCTION__, retval);
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */
3227d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_read_bulk(struct urb *urb)
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
32461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
32561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv = urb->context;
32661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm *acm = rcv->instance;
32786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int status = urb->status;
328185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman
329185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	dbg("Entering acm_read_bulk with status %d", status);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
33486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (status)
335898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
33761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = rcv->buffer;
33861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf->size = urb->actual_length;
33961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
34086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (likely(status == 0)) {
34186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
34286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
34386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&buf->list, &acm->filled_read_bufs);
34486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
34586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	} else {
34686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* we drop the buffer due to an error */
34786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
34886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
34986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add(&buf->list, &acm->spare_read_bufs);
35086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
35186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* nevertheless the tasklet must be kicked unconditionally
35286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		so the queue cannot dry up */
35386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	}
35461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm)
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = (void *)_acm;
36061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct tty_struct *tty = acm->tty;
36261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv;
363762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	unsigned long flags;
364ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	unsigned char throttled;
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Entering acm_rx_tasklet");
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
367ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (!ACM_READY(acm))
368ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		return;
369ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
370834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_lock_irqsave(&acm->throttle_lock, flags);
371ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	throttled = acm->throttle;
372834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_unlock_irqrestore(&acm->throttle_lock, flags);
373ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (throttled)
37461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		return;
37561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
37661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer:
377762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
37861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	if (list_empty(&acm->filled_read_bufs)) {
379762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
38061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		goto urbs;
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
38261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = list_entry(acm->filled_read_bufs.next,
38361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			 struct acm_rb, list);
38461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_del(&buf->list);
385762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
38661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
3873dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
38861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
38933f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox	tty_buffer_request_room(tty, buf->size);
390834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_lock_irqsave(&acm->throttle_lock, flags);
391ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	throttled = acm->throttle;
392834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_unlock_irqrestore(&acm->throttle_lock, flags);
393ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (!throttled)
39433f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox		tty_insert_flip_string(tty, buf->base, buf->size);
39561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tty_flip_buffer_push(tty);
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
397ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (throttled) {
398ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		dbg("Throttling noticed");
399762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_lock_irqsave(&acm->read_lock, flags);
40061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&buf->list, &acm->filled_read_bufs);
401762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
405762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
40661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_add(&buf->list, &acm->spare_read_bufs);
407762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
40861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	goto next_buffer;
40961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
41061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs:
41161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	while (!list_empty(&acm->spare_read_bufs)) {
412762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_lock_irqsave(&acm->read_lock, flags);
41361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (list_empty(&acm->spare_read_urbs)) {
414762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
41561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
41661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
41761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv = list_entry(acm->spare_read_urbs.next,
41861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_ru, list);
41961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&rcv->list);
420762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
42161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
42261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		buf = list_entry(acm->spare_read_bufs.next,
42361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_rb, list);
42461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&buf->list);
42561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
42661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->buffer = buf;
42761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
42861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_fill_bulk_urb(rcv->urb, acm->dev,
42961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm->rx_endpoint,
43061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  buf->base,
43161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm->readsize,
43261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm_read_bulk, rcv);
43361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_dma = buf->dma;
43461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
43561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
4363dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum		dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
43761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
43861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		/* This shouldn't kill the driver as unsuccessful URBs are returned to the
43961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		   free-urbs-pool and resubmited ASAP */
44061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
44161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&buf->list, &acm->spare_read_bufs);
442762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_lock_irqsave(&acm->read_lock, flags);
44361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&rcv->list, &acm->spare_read_urbs);
444762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
44561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
44661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
44761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */
4517d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb)
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = (struct acm *)urb->context;
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4553dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_write_bulk with status %d", urb->status);
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
457884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_done(acm);
458884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_start(acm);
459884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (ACM_READY(acm))
460884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		schedule_work(&acm->work);
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
463c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work)
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
465c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct acm *acm = container_of(work, struct acm, work);
4663dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_softint.");
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_wakeup(acm->tty);
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp)
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rv = -EINVAL;
48161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
4823dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_open.");
4834186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven
4844186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm = acm_table[tty->index];
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->dev)
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out;
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rv = 0;
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty->driver_data = acm;
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->tty = tty;
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
49561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	/* force low_latency on so that our tty_push actually forces the data through,
49661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	   otherwise it is scheduled, and with high data rates data can get lost. */
49761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tty->low_latency = 1;
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	if (usb_autopm_get_interface(acm->control)) {
5001365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		mutex_unlock(&open_mutex);
5011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return -EIO;
5021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	}
5031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
5051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&open_mutex);
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->used++) {
5071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_autopm_put_interface(acm->control);
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto done;
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->dev = acm->dev;
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("usb_submit_urb(ctrl irq) failed");
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto bail_out;
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
518ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
519ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	    (acm->ctrl_caps & USB_CDC_CAP_LINE))
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto full_bailout;
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
52261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_urbs);
52361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_bufs);
52461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->filled_read_bufs);
52586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++) {
52661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
52761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
52886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++) {
52961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
53061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
53161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
532ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	acm->throttle = 0;
533ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
53461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone:
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out:
5381365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rv;
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout:
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(acm->ctrlurb);
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out:
5441365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_autopm_put_interface(acm->control);
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->used--;
5461365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EIO;
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
55083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm)
55183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{
55286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int i,nr;
55361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
55486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
55583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_unregister_device(acm_tty_driver, acm->minor);
55683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_put_intf(acm->control);
55783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	acm_table[acm->minor] = NULL;
55883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_free_urb(acm->ctrlurb);
55983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_free_urb(acm->writeurb);
56086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < nr; i++)
56161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
562c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	kfree(acm->country_codes);
56383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	kfree(acm);
56483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk}
56583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp)
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
56986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int i,nr;
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->used)
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
57486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
5754186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!--acm->used) {
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (acm->dev) {
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			acm_set_control(acm, acm->ctrlout = 0);
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			usb_kill_urb(acm->ctrlurb);
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			usb_kill_urb(acm->writeurb);
58186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum			for (i = 0; i < nr; i++)
58261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				usb_kill_urb(acm->ru[i].urb);
5831365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum			usb_autopm_put_interface(acm->control);
58483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		} else
58583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk			acm_tty_unregister(acm);
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5874186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int stat;
594884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
595884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
596884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
597884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
5983dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_write to write %d bytes,", count);
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!count)
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
605884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
606884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if ((wbn = acm_wb_alloc(acm)) < 0) {
607884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
608884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		acm_write_start(acm);
609884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
610884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
611884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
613884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	count = (count > acm->writesize) ? acm->writesize : count;
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Get %d bytes...", count);
615884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	memcpy(wb->buf, buf, count);
616884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb->len = count;
617884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
619884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if ((stat = acm_write_start(acm)) < 0)
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return stat;
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty)
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
629884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
630884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * Do not let the line discipline to know that we have a reserve,
631884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * or it might get too enthusiastic.
632884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
633884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty)
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
641884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
642884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * This is inaccurate (overcounts), but it works.
643884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
64486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty)
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 1;
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty)
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 0;
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
66561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_break_ctl(struct tty_struct *tty, int state)
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm_send_break(acm, state ? 0xffff : 0))
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("send break failed");
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TIOCM_CTS;
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    unsigned int set, unsigned int clear)
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int newctrl;
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = acm->ctrlout;
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = (newctrl & ~clear) | set;
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->ctrlout == newctrl)
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return acm_set_control(acm, acm->ctrlout = newctrl);
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7224c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = {
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 50, 75, 110, 134, 150, 200, 300, 600,
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1200, 1800, 2400, 4800, 9600, 19200, 38400,
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	57600, 115200, 230400, 460800, 500000, 576000,
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	921600, 1000000, 1152000, 1500000, 2000000,
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	2500000, 3000000, 3500000, 4000000
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7304c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = {
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7, 8
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
734606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Coxstatic void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old)
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
737606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox	struct ktermios *termios = tty->termios;
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_line_coding newline;
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl = acm->ctrlout;
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bParityType = termios->c_cflag & PARENB ?
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!newline.dwDTERate) {
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newline.dwDTERate = acm->line.dwDTERate;
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newctrl &= ~ACM_CTRL_DTR;
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else  newctrl |=  ACM_CTRL_DTR;
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (newctrl != acm->ctrlout)
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_control(acm, acm->ctrlout = newctrl);
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(&acm->line, &newline, sizeof newline)) {
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&acm->line, &newline, sizeof newline);
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bCharFormat, newline.bParityType,
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bDataBits);
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_line(acm, &acm->line);
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines.
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
774884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers free */
775884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm)
776884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
777884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
778884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
779884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
78086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
781884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah);
782884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
783884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
784884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
785884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */
786884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm)
787884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
788884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
789884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
790884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
79186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
792884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
793884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		    &wb->dmah);
794884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->buf) {
795884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			while (i != 0) {
796884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--i;
797884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--wb;
798884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				usb_buffer_free(acm->dev, acm->writesize,
799884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				    wb->buf, wb->dmah);
800884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			}
801884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -ENOMEM;
802884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
803884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
804884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return 0;
805884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
806884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_probe (struct usb_interface *intf,
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      const struct usb_device_id *id)
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_union_desc *union_header = NULL;
811c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_cdc_country_functional_desc *cfd = NULL;
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *buffer = intf->altsetting->extra;
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buflen = intf->altsetting->extralen;
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *control_interface;
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *data_interface;
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epctrl;
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epread;
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epwrite;
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor;
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ctrlsize,readsize;
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *buf;
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 ac_management_function = 0;
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 call_management_function = 0;
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int call_interface_num = -1;
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int data_interface_num;
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long quirks;
82986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int num_rx_buf;
83061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
83286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* normal quirks */
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	quirks = (unsigned long)id->driver_info;
83486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
83586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum
83686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* handle quirks deadly to normal probing*/
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (quirks == NO_UNION_NORMAL) {
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, 1);
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, 0);
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto skip_normal_probe;
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* normal probing*/
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
845898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		err("Weird descriptor references\n");
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buflen) {
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
851898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buflen = intf->cur_altsetting->endpoint->extralen;
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = intf->cur_altsetting->endpoint->extra;
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			err("Zero length descriptor references\n");
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (buflen > 0) {
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (buffer [1] != USB_DT_CS_INTERFACE) {
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			err("skipping garbage\n");
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto next_desc;
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (buffer [2]) {
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_UNION_TYPE: /* we've found it */
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (union_header) {
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					err("More than one union descriptor, skipping ...");
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					goto next_desc;
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				union_header = (struct usb_cdc_union_desc *)
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							buffer;
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
875c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
876c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum				cfd = (struct usb_cdc_country_functional_desc *)buffer;
877c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum				break;
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_HEADER_TYPE: /* maybe check version */
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break; /* for now we ignore it */
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_ACM_TYPE:
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ac_management_function = buffer[3];
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_CALL_MANAGEMENT_TYPE:
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				call_management_function = buffer[3];
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				call_interface_num = buffer[4];
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if ((call_management_function & 3) != 3)
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					err("This device cannot do calls on its own. It is no modem.");
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]);
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc:
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buflen -= buffer[0];
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer += buffer[0];
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!union_header) {
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (call_interface_num > 0) {
901898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = intf;
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
905898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"No union descriptor, giving up\n");
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!control_interface || !data_interface) {
912898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"no interfaces\n");
9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface_num != call_interface_num)
918898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.\n");
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe:
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*workaround for switched interfaces */
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_interface *t;
926898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = control_interface;
9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = data_interface;
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = t;
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
93574da5d68a54d66667664fbe233ededab2376a070Alan Stern
93674da5d68a54d66667664fbe233ededab2376a070Alan Stern	/* Accept probe requests only for the control interface */
93774da5d68a54d66667664fbe233ededab2376a070Alan Stern	if (intf != control_interface)
93874da5d68a54d66667664fbe233ededab2376a070Alan Stern		return -ENODEV;
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_interface_claimed(data_interface)) { /* valid in this context */
941898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev,"The data interface isn't available\n");
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epread = &data_interface->cur_altsetting->endpoint[0].desc;
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* workaround for switched endpoints */
95545aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino	if (!usb_endpoint_dir_in(epread)) {
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* descriptors are swapped */
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_endpoint_descriptor *t;
958898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = epread;
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epread = epwrite;
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epwrite = t;
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("interfaces are valid");
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (minor == ACM_TTY_MINORS) {
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err("no more free acm devices");
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
97246f116eab81b21c6ae8c4f169498c632b1f94bf1Oliver Neukum	if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
973898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail;
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
97886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize);
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->control = control_interface;
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->data = data_interface;
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->minor = minor;
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = usb_dev;
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_caps = ac_management_function;
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlsize = ctrlsize;
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->readsize = readsize;
98786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->rx_buflimit = num_rx_buf;
98861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.func = acm_rx_tasklet;
98961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.data = (unsigned long) acm;
990c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&acm->work, acm_softint);
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&acm->throttle_lock);
992884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_init(&acm->write_lock);
99361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock_init(&acm->read_lock);
9941365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_init(&acm->mutex);
995884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->write_ready = 1;
99661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buf) {
1000898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail2;
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_buffer = buf;
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1005884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (acm_write_buffers_alloc(acm) < 0) {
1006898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail4;
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->ctrlurb) {
1012898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail5;
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
101586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
101661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		struct acm_ru *rcv = &(acm->ru[i]);
101761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
101861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
1019898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n");
102061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
102161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
102261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
102361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
102461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->instance = acm;
102561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
102686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
102761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		struct acm_rb *buf = &(acm->rb[i]);
102861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
102961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
1030898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
103161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
103261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->writeurb = usb_alloc_urb(0, GFP_KERNEL);
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->writeurb) {
1036898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n");
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail7;
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1040c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	usb_set_intfdata (intf, acm);
1041c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1042c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1043c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (i < 0)
1044c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		goto alloc_fail8;
1045c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1046c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (cfd) { /* export the country data */
1047c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1048c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (!acm->country_codes)
1049c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1050c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_code_size = cfd->bLength - 4;
1051c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
1052c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_rel_date = cfd->iCountryCodeRelDate;
1053c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1054c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1055c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1056c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1057c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1058c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1059c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1060c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
1061c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1062c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1063c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1064c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1065c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
1066c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1067c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries:
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1074884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			  NULL, acm->writesize, acm_write_bulk, acm);
10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_control(acm, acm->ctrlout);
10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.dwDTERate = cpu_to_le32(9600);
10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.bDataBits = 8;
10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_line(acm, &acm->line);
10841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_driver_claim_interface(&acm_driver, data_interface, acm);
10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
108783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_get_intf(control_interface);
108883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_table[minor] = acm;
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1092c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return 0;
1093c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumalloc_fail8:
1094c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	usb_free_urb(acm->writeurb);
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7:
109686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
109761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
109886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
109961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_free_urb(acm->ctrlurb);
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5:
1102884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4:
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2:
11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acm);
11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail:
11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOMEM;
11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm)
11121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
11131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int i;
11141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
11151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_disable(&acm->urb_task);
11161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
11171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_kill_urb(acm->ctrlurb);
11181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_kill_urb(acm->writeurb);
11191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
11201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_kill_urb(acm->ru[i].urb);
11211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
11221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	INIT_LIST_HEAD(&acm->filled_read_bufs);
11231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	INIT_LIST_HEAD(&acm->spare_read_bufs);
11241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
11251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_enable(&acm->urb_task);
11261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
11271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	cancel_work_sync(&acm->work);
11281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
11291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf)
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1132c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
113461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->dev) {
11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("disconnect on nonexisting interface");
11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11414186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
114286067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	if (!usb_get_intfdata(intf)) {
11434186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
114486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum		return;
114586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	}
1146c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (acm->country_codes){
114774da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
114874da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_wCountryCodes);
114974da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
115074da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_iCountryCodeRelDate);
1151c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
115274da5d68a54d66667664fbe233ededab2376a070Alan Stern	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = NULL;
115486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->control, NULL);
115586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->data, NULL);
11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11571365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	stop_data_traffic(acm);
11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1159884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
116186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
116261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
116486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf);
11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->used) {
116783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		acm_tty_unregister(acm);
11684186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11724186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->tty)
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_hangup(acm->tty);
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11781365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message)
11791365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
11801365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
11811365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
11821365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	if (acm->susp_count++)
11831365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
11841365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	/*
11851365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we treat opened interfaces differently,
11861365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we must guard against open
11871365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	*/
11881365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
11891365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
11901365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	if (acm->used)
11911365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		stop_data_traffic(acm);
11921365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
11931365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
11941365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return 0;
11951365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
11961365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
11971365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf)
11981365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
11991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
12001365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int rv = 0;
12011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	if (--acm->susp_count)
12031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
12041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
12061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	if (acm->used) {
12071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
12081365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		if (rv < 0)
12091365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		goto err_out;
12101365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		tasklet_schedule(&acm->urb_task);
12121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	}
12131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out:
12151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
12161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return rv;
12171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
12181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
12191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure.
12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
12211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id acm_ids[] = {
12231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* quirky and broken devices */
12241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
12251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
12261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1227b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1228b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1229b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	},
12308753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
12318753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
12328753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	},
123391a9c9214e34c364bf15406aadb922787ae7129bChris Malley	{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
123491a9c9214e34c364bf15406aadb922787ae7129bChris Malley	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
123591a9c9214e34c364bf15406aadb922787ae7129bChris Malley	},
123686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
123786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
123886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	},
12393dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
12403dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
12413dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	},
12429be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
12439be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
12449be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	},
12459be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum
12461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* control interfaces with various AT-command sets */
12471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_V25TER) },
12491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101) },
12511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
12531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
12541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_GSM) },
12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_3G	) },
12571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_CDMA) },
12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* NOTE:  COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
12621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE (usb, acm_ids);
12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = {
12671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"cdc_acm",
12681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	acm_probe,
12691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	acm_disconnect,
12701365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.suspend =	acm_suspend,
12711365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.resume =	acm_resume,
12721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	acm_ids,
12731365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.supports_autosuspend = 1,
12741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures.
12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
12791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1280b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = {
12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			acm_tty_open,
12821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		acm_tty_close,
12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		acm_tty_write,
12841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		acm_tty_write_room,
12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		acm_tty_ioctl,
12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		acm_tty_throttle,
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		acm_tty_unthrottle,
12881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	acm_tty_chars_in_buffer,
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl =		acm_tty_break_ctl,
12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios =		acm_tty_set_termios,
12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmget =		acm_tty_tiocmget,
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmset =		acm_tty_tiocmset,
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit.
12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
12981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void)
13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
13011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm_tty_driver)
13041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->owner = THIS_MODULE,
13061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->driver_name = "acm",
13071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->name = "ttyACM",
13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->major = ACM_TTY_MAJOR,
13091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->minor_start = 0,
13101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
13111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
1312331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
13131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios = tty_std_termios;
13141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
13151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(acm_tty_driver, &acm_ops);
13161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = tty_register_driver(acm_tty_driver);
13181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
13191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
13201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
13211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
13221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&acm_driver);
13241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
13251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_unregister_driver(acm_tty_driver);
13261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
13271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
13281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
13291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info(DRIVER_VERSION ":" DRIVER_DESC);
13311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
13331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void)
13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister(&acm_driver);
13381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_unregister_driver(acm_tty_driver);
13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	put_tty_driver(acm_tty_driver);
13401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init);
13431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit);
13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR( DRIVER_AUTHOR );
13461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION( DRIVER_DESC );
13471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
13481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1349