cdc-acm.c revision a8c28f2389942bab376e39351d27525499630248
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>
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/smp_lock.h>
634186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h>
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
66a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h>
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h>
6961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h>
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h"
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_VERSION "v0.25"
7761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS];
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
844186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex);
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACM_READY(acm)	(acm && acm->dev && acm->used)
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages.
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len)
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		request, USB_RT_ACM, value,
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm->control->altsetting[0].desc.bInterfaceNumber,
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf, len, 5000);
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval < 0 ? retval : 0;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests.
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do...
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
113884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management.
114884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller.
115884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
116884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
117884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm)
118884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
119884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, wbn;
120884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
121884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
122884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wbn = acm->write_current;
123884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	i = 0;
124884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	for (;;) {
125884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb = &acm->wb[wbn];
126884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->use) {
127884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			wb->use = 1;
128884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return wbn;
129884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
13086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		wbn = (wbn + 1) % ACM_NW;
13186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		if (++i >= ACM_NW)
132884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -1;
133884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
134884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
135884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
136884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_wb_free(struct acm *acm, int wbn)
137884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
138884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->wb[wbn].use = 0;
139884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
140884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
141884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm)
142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, n;
144884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
14586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	n = ACM_NW;
14686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < ACM_NW; i++) {
14786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		n -= acm->wb[i].use;
148884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
149884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return n;
150884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
151884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
152884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic inline int acm_wb_is_used(struct acm *acm, int wbn)
153884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
154884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return acm->wb[wbn].use;
155884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
156884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
157884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
158884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Finish write.
159884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
160884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_done(struct acm *acm)
161884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
162884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
163884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
164884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
165884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
166884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->write_ready = 1;
167884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wbn = acm->write_current;
168884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_wb_free(acm, wbn);
16986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->write_current = (wbn + 1) % ACM_NW;
170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
171884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
172884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write.
175884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
176884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_start(struct acm *acm)
177884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
178884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
179884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
180884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
181884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int rc;
182884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
183884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
184884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->dev) {
185884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
186884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return -ENODEV;
187884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
188884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
189884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->write_ready) {
190884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
191884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;	/* A white lie */
192884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
193884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
194884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wbn = acm->write_current;
195884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm_wb_is_used(acm, wbn)) {
196884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
197884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
198884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
199884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
200884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
201884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->write_ready = 0;
202884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
204884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->writeurb->transfer_buffer = wb->buf;
205884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->writeurb->transfer_dma = wb->dmah;
206884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->writeurb->transfer_buffer_length = wb->len;
207884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->writeurb->dev = acm->dev;
208884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
209884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) {
210884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		dbg("usb_submit_urb(write bulk) failed: %d", rc);
211884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		acm_write_done(acm);
212884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
213884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return rc;
214884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
215884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
216884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs)
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = urb->context;
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_notification *dr = urb->transfer_buffer;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int status;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (urb->status) {
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* success */
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ECONNRESET:
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ENOENT:
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ESHUTDOWN:
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this urb is terminated, clean up */
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = (unsigned char *)(dr + 1);
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (dr->bNotificationType) {
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_CDC_NOTIFY_NETWORK_CONNECTION:
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_CDC_NOTIFY_SERIAL_STATE:
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newctrl = le16_to_cpu(get_unaligned((__le16 *) data));
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dbg("calling hangup");
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				tty_hangup(acm->tty);
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			acm->ctrlin = newctrl;
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',	acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',	acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',	acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dr->bNotificationType, dr->wIndex,
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dr->wLength, data[0], data[1]);
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	status = usb_submit_urb (urb, GFP_ATOMIC);
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (status)
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err ("%s - usb_submit_urb failed with result %d",
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		     __FUNCTION__, status);
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_read_bulk(struct urb *urb, struct pt_regs *regs)
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
29061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
29161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv = urb->context;
29261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm *acm = rcv->instance;
29386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int status = urb->status;
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Entering acm_read_bulk with status %d\n", urb->status);
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (status)
30086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
30261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = rcv->buffer;
30361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf->size = urb->actual_length;
30461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
30586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (likely(status == 0)) {
30686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
30786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
30886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&buf->list, &acm->filled_read_bufs);
30986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
31086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	} else {
31186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* we drop the buffer due to an error */
31286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
31386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
31486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add(&buf->list, &acm->spare_read_bufs);
31586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
31686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* nevertheless the tasklet must be kicked unconditionally
31786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		so the queue cannot dry up */
31886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	}
31961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm)
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = (void *)_acm;
32561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct tty_struct *tty = acm->tty;
32761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv;
32861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	//unsigned long flags;
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i = 0;
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Entering acm_rx_tasklet");
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
33261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	if (!ACM_READY(acm) || acm->throttle)
33361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		return;
33461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
33561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer:
33661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock(&acm->read_lock);
33761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	if (list_empty(&acm->filled_read_bufs)) {
33861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		spin_unlock(&acm->read_lock);
33961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		goto urbs;
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
34161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = list_entry(acm->filled_read_bufs.next,
34261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			 struct acm_rb, list);
34361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_del(&buf->list);
34461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_unlock(&acm->read_lock);
34561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
34661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d\n", buf, buf->size);
34761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
34833f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox	tty_buffer_request_room(tty, buf->size);
34933f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox	if (!acm->throttle)
35033f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox		tty_insert_flip_string(tty, buf->base, buf->size);
35161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tty_flip_buffer_push(tty);
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock(&acm->throttle_lock);
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->throttle) {
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("Throtteling noticed");
35661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		memmove(buf->base, buf->base + i, buf->size - i);
35761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		buf->size -= i;
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock(&acm->throttle_lock);
35961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		spin_lock(&acm->read_lock);
36061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&buf->list, &acm->filled_read_bufs);
36161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		spin_unlock(&acm->read_lock);
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock(&acm->throttle_lock);
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
36661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock(&acm->read_lock);
36761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_add(&buf->list, &acm->spare_read_bufs);
36861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_unlock(&acm->read_lock);
36961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	goto next_buffer;
37061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
37161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs:
37261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	while (!list_empty(&acm->spare_read_bufs)) {
37361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		spin_lock(&acm->read_lock);
37461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (list_empty(&acm->spare_read_urbs)) {
37561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			spin_unlock(&acm->read_lock);
37661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
37761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
37861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv = list_entry(acm->spare_read_urbs.next,
37961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_ru, list);
38061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&rcv->list);
38161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		spin_unlock(&acm->read_lock);
38261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
38361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		buf = list_entry(acm->spare_read_bufs.next,
38461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_rb, list);
38561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&buf->list);
38661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
38761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->buffer = buf;
38861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
38961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_fill_bulk_urb(rcv->urb, acm->dev,
39061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm->rx_endpoint,
39161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  buf->base,
39261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm->readsize,
39361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm_read_bulk, rcv);
39461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_dma = buf->dma;
39561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
39661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
39761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p\n", rcv->urb, rcv, buf);
39861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
39961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		/* This shouldn't kill the driver as unsuccessful URBs are returned to the
40061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		   free-urbs-pool and resubmited ASAP */
40161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
40261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&buf->list, &acm->spare_read_bufs);
40361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			spin_lock(&acm->read_lock);
40461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&rcv->list, &acm->spare_read_urbs);
40561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			spin_unlock(&acm->read_lock);
40661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
40761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
40861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_write_bulk(struct urb *urb, struct pt_regs *regs)
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = (struct acm *)urb->context;
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
416884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	dbg("Entering acm_write_bulk with status %d\n", urb->status);
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
418884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_done(acm);
419884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_start(acm);
420884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (ACM_READY(acm))
421884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		schedule_work(&acm->work);
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_softint(void *private)
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = private;
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Entering acm_softint.\n");
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_wakeup(acm->tty);
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp)
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rv = -EINVAL;
44261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Entering acm_tty_open.\n");
4444186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven
4454186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm = acm_table[tty->index];
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->dev)
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out;
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rv = 0;
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty->driver_data = acm;
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->tty = tty;
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
45661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	/* force low_latency on so that our tty_push actually forces the data through,
45761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	   otherwise it is scheduled, and with high data rates data can get lost. */
45861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tty->low_latency = 1;
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->used++) {
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto done;
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->dev = acm->dev;
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("usb_submit_urb(ctrl irq) failed");
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto bail_out;
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS))
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto full_bailout;
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
47361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_urbs);
47461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_bufs);
47561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->filled_read_bufs);
47686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++) {
47761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
47861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
47986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++) {
48061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
48161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
48261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
48361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone:
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out:
4874186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rv;
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout:
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(acm->ctrlurb);
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out:
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->used--;
4944186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EIO;
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
49883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm)
49983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{
50086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int i,nr;
50161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
50286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
50383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_unregister_device(acm_tty_driver, acm->minor);
50483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_put_intf(acm->control);
50583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	acm_table[acm->minor] = NULL;
50683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_free_urb(acm->ctrlurb);
50783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_free_urb(acm->writeurb);
50886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < nr; i++)
50961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
51083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	kfree(acm);
51183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk}
51283ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp)
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
51686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int i,nr;
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->used)
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
52186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
5224186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!--acm->used) {
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (acm->dev) {
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			acm_set_control(acm, acm->ctrlout = 0);
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			usb_kill_urb(acm->ctrlurb);
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			usb_kill_urb(acm->writeurb);
52886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum			for (i = 0; i < nr; i++)
52961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				usb_kill_urb(acm->ru[i].urb);
53083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		} else
53183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk			acm_tty_unregister(acm);
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5334186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int stat;
540884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
541884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
542884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
543884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Entering acm_tty_write to write %d bytes,\n", count);
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!count)
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
551884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
552884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if ((wbn = acm_wb_alloc(acm)) < 0) {
553884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
554884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		acm_write_start(acm);
555884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
556884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
557884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
559884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	count = (count > acm->writesize) ? acm->writesize : count;
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Get %d bytes...", count);
561884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	memcpy(wb->buf, buf, count);
562884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb->len = count;
563884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
565884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if ((stat = acm_write_start(acm)) < 0)
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return stat;
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty)
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
575884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
576884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * Do not let the line discipline to know that we have a reserve,
577884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * or it might get too enthusiastic.
578884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
579884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty)
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
587884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
588884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * This is inaccurate (overcounts), but it works.
589884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
59086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty)
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 1;
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty)
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 0;
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
61161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_break_ctl(struct tty_struct *tty, int state)
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm_send_break(acm, state ? 0xffff : 0))
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("send break failed");
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TIOCM_CTS;
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    unsigned int set, unsigned int clear)
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int newctrl;
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = acm->ctrlout;
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = (newctrl & ~clear) | set;
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->ctrlout == newctrl)
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return acm_set_control(acm, acm->ctrlout = newctrl);
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6684c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = {
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 50, 75, 110, 134, 150, 200, 300, 600,
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1200, 1800, 2400, 4800, 9600, 19200, 38400,
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	57600, 115200, 230400, 460800, 500000, 576000,
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	921600, 1000000, 1152000, 1500000, 2000000,
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	2500000, 3000000, 3500000, 4000000
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6764c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = {
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7, 8
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_old)
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct termios *termios = tty->termios;
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_line_coding newline;
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl = acm->ctrlout;
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bParityType = termios->c_cflag & PARENB ?
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!newline.dwDTERate) {
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newline.dwDTERate = acm->line.dwDTERate;
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newctrl &= ~ACM_CTRL_DTR;
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else  newctrl |=  ACM_CTRL_DTR;
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (newctrl != acm->ctrlout)
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_control(acm, acm->ctrlout = newctrl);
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(&acm->line, &newline, sizeof newline)) {
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&acm->line, &newline, sizeof newline);
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bCharFormat, newline.bParityType,
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bDataBits);
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_line(acm, &acm->line);
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines.
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
720884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers free */
721884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm)
722884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
723884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
724884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
725884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
72686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
727884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah);
728884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
729884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
730884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
731884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */
732884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm)
733884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
734884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
735884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
736884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
73786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
738884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
739884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		    &wb->dmah);
740884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->buf) {
741884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			while (i != 0) {
742884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--i;
743884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--wb;
744884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				usb_buffer_free(acm->dev, acm->writesize,
745884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				    wb->buf, wb->dmah);
746884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			}
747884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -ENOMEM;
748884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
749884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
750884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return 0;
751884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
752884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_probe (struct usb_interface *intf,
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      const struct usb_device_id *id)
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_union_desc *union_header = NULL;
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *buffer = intf->altsetting->extra;
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buflen = intf->altsetting->extralen;
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *control_interface;
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *data_interface;
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epctrl;
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epread;
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epwrite;
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor;
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ctrlsize,readsize;
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *buf;
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 ac_management_function = 0;
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 call_management_function = 0;
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int call_interface_num = -1;
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int data_interface_num;
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long quirks;
77486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int num_rx_buf;
77561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
77786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* normal quirks */
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	quirks = (unsigned long)id->driver_info;
77986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
78086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum
78186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* handle quirks deadly to normal probing*/
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (quirks == NO_UNION_NORMAL) {
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, 1);
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, 0);
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto skip_normal_probe;
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* normal probing*/
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err("Wierd descriptor references\n");
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buflen) {
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buflen = intf->cur_altsetting->endpoint->extralen;
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = intf->cur_altsetting->endpoint->extra;
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			err("Zero length descriptor references\n");
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (buflen > 0) {
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (buffer [1] != USB_DT_CS_INTERFACE) {
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			err("skipping garbage\n");
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto next_desc;
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (buffer [2]) {
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_UNION_TYPE: /* we've found it */
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (union_header) {
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					err("More than one union descriptor, skipping ...");
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					goto next_desc;
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				union_header = (struct usb_cdc_union_desc *)
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							buffer;
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break; /* for now we ignore it */
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_HEADER_TYPE: /* maybe check version */
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break; /* for now we ignore it */
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_ACM_TYPE:
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ac_management_function = buffer[3];
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_CALL_MANAGEMENT_TYPE:
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				call_management_function = buffer[3];
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				call_interface_num = buffer[4];
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if ((call_management_function & 3) != 3)
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					err("This device cannot do calls on its own. It is no modem.");
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]);
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc:
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buflen -= buffer[0];
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer += buffer[0];
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!union_header) {
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (call_interface_num > 0) {
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = intf;
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev_dbg(&intf->dev,"No union descriptor, giving up\n");
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!control_interface || !data_interface) {
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev_dbg(&intf->dev,"no interfaces\n");
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface_num != call_interface_num)
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.\n");
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe:
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*workaround for switched interfaces */
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_interface *t;
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = control_interface;
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = data_interface;
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = t;
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_interface_claimed(data_interface)) { /* valid in this context */
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_dbg(&intf->dev,"The data interface isn't available\n");
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epread = &data_interface->cur_altsetting->endpoint[0].desc;
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* workaround for switched endpoints */
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((epread->bEndpointAddress & USB_DIR_IN) != USB_DIR_IN) {
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* descriptors are swapped */
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_endpoint_descriptor *t;
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = epread;
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epread = epwrite;
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epwrite = t;
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("interfaces are valid");
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (minor == ACM_TTY_MINORS) {
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err("no more free acm devices");
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91246f116eab81b21c6ae8c4f169498c632b1f94bf1Oliver Neukum	if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
91346f116eab81b21c6ae8c4f169498c632b1f94bf1Oliver Neukum		dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail;
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
91886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize);
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->control = control_interface;
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->data = data_interface;
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->minor = minor;
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = usb_dev;
9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_caps = ac_management_function;
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlsize = ctrlsize;
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->readsize = readsize;
92786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->rx_buflimit = num_rx_buf;
92861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.func = acm_rx_tasklet;
92961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.data = (unsigned long) acm;
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	INIT_WORK(&acm->work, acm_softint, acm);
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&acm->throttle_lock);
932884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_init(&acm->write_lock);
93361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock_init(&acm->read_lock);
934884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->write_ready = 1;
93561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buf) {
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail2;
9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_buffer = buf;
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
944884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (acm_write_buffers_alloc(acm) < 0) {
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail4;
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->ctrlurb) {
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail5;
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
95486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
95561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		struct acm_ru *rcv = &(acm->ru[i]);
95661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
95761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
95861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n");
95961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
96061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
96161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
96261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
96361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->instance = acm;
96461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
96586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
96661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		struct acm_rb *buf = &(acm->rb[i]);
96761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
96861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
96961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
97061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
97161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->writeurb = usb_alloc_urb(0, GFP_KERNEL);
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->writeurb) {
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n");
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail7;
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
985884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			  NULL, acm->writesize, acm_write_bulk, acm);
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_control(acm, acm->ctrlout);
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.dwDTERate = cpu_to_le32(9600);
9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.bDataBits = 8;
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_line(acm, &acm->line);
9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_driver_claim_interface(&acm_driver, data_interface, acm);
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
99883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_get_intf(control_interface);
99983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_table[minor] = acm;
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_set_intfdata (intf, acm);
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7:
100686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
100761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
100886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
100961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_free_urb(acm->ctrlurb);
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5:
1012884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4:
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2:
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acm);
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail:
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOMEM;
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf)
10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = usb_get_intfdata (intf);
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
102561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->dev) {
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("disconnect on nonexisting interface");
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10324186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
103386067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	if (!usb_get_intfdata(intf)) {
10344186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
103586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum		return;
103686067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	}
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = NULL;
103886067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->control, NULL);
103986067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->data, NULL);
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
104161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_disable(&acm->urb_task);
104261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(acm->ctrlurb);
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(acm->writeurb);
104586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
104661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_kill_urb(acm->ru[i].urb);
104761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
104861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->filled_read_bufs);
104961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_bufs);
105061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
105161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_enable(&acm->urb_task);
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	flush_scheduled_work(); /* wait for acm_softint */
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1055884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
105786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
105861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
106086067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf);
10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->used) {
106383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		acm_tty_unregister(acm);
10644186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10684186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->tty)
10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_hangup(acm->tty);
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure.
10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id acm_ids[] = {
10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* quirky and broken devices */
10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
10838753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
10848753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
10858753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	},
108686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
108786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
108886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	},
10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* control interfaces with various AT-command sets */
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_V25TER) },
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101) },
10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_GSM) },
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_3G	) },
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_CDMA) },
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* NOTE:  COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE (usb, acm_ids);
11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = {
11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"cdc_acm",
11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	acm_probe,
11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	acm_disconnect,
11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	acm_ids,
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures.
11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_operations acm_ops = {
11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			acm_tty_open,
11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		acm_tty_close,
11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		acm_tty_write,
11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		acm_tty_write_room,
11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		acm_tty_ioctl,
11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		acm_tty_throttle,
11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		acm_tty_unthrottle,
11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	acm_tty_chars_in_buffer,
11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl =		acm_tty_break_ctl,
11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios =		acm_tty_set_termios,
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmget =		acm_tty_tiocmget,
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmset =		acm_tty_tiocmset,
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit.
11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void)
11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
11411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm_tty_driver)
11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->owner = THIS_MODULE,
11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->driver_name = "acm",
11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->name = "ttyACM",
11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->devfs_name = "usb/acm/",
11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->major = ACM_TTY_MAJOR,
11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->minor_start = 0,
11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios = tty_std_termios;
11551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(acm_tty_driver, &acm_ops);
11571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = tty_register_driver(acm_tty_driver);
11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&acm_driver);
11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_unregister_driver(acm_tty_driver);
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info(DRIVER_VERSION ":" DRIVER_DESC);
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void)
11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister(&acm_driver);
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_unregister_driver(acm_tty_driver);
11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	put_tty_driver(acm_tty_driver);
11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init);
11841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit);
11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR( DRIVER_AUTHOR );
11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION( DRIVER_DESC );
11881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1190