cdc-acm.c revision 9e98966c7bb94355689478bc84cc3e0c190f977e
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * cdc-acm.c
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Armin Fuerst	<fuerst@in.tum.de>
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Pavel Machek	<pavel@suse.cz>
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Johannes Erdfelt	<johannes@erdfelt.com>
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2004 Oliver Neukum	<oliver@neukum.name>
961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek * Copyright (c) 2005 David Kubicek	<dave@awk.cz>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB Abstract Control Model driver for USB modems and ISDN adapters
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Sponsored by SuSE
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ChangeLog:
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.9  - thorough cleaning, URBification, almost a rewrite
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.10 - some more cleanups
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.11 - fixed flow control, read error doesn't stop reads
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.13 - added termios, added hangup
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.14 - sized down struct acm
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.15 - fixed flow control again - characters could be lost
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.16 - added code for modems with swapped data and control interfaces
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.17 - added new style probing
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.18 - fixed new style probing for devices with more configurations
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.20 - switched to probing on interface (rather than device) class
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.21 - revert to probing on device for devices with multiple configs
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.22 - probe only the control interface. if usbcore doesn't choose the
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		config we want, sysadmin changes bConfigurationValue in sysfs.
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.23 - use softirq for rx processing, as needed by tty layer
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	v0.24 - change probe method to evaluate CDC union descriptor
3361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek *	v0.25 - downstream tasks paralelized to maximize throughput
34e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf *	v0.26 - multiple write urbs, writesize increased
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version.
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h>
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.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 */
76e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf#define DRIVER_VERSION "v0.26"
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
122e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wbn = 0;
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 int acm_wb_is_avail(struct acm *acm)
137884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
138884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, n;
139884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
14086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	n = ACM_NW;
14186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < ACM_NW; i++) {
14286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		n -= acm->wb[i].use;
143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
144884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return n;
145884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
146884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
147884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic inline int acm_wb_is_used(struct acm *acm, int wbn)
148884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
149884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return acm->wb[wbn].use;
150884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
151884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
152884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
153884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Finish write.
154884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
155e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic void acm_write_done(struct acm *acm, struct acm_wb *wb)
156884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
157884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
158884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
159884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
160884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->write_ready = 1;
161e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wb->use = 0;
16211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting--;
163884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
164884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
165884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
166884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
167884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write.
16811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum *
16911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking
170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
17111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
17211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb)
17311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
17411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rc;
17511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
17611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting++;
17711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
17811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer = wb->buf;
17911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_dma = wb->dmah;
18011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer_length = wb->len;
18111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->dev = acm->dev;
18211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
18311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) {
18411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("usb_submit_urb(write bulk) failed: %d", rc);
18511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_write_done(acm, wb);
18611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
18711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	return rc;
18811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
18911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
190e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn)
191884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
192884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
193884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
194884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int rc;
195884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
196884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
197884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->dev) {
198884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
199884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return -ENODEV;
200884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
201884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
202884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->write_ready) {
203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
204884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;	/* A white lie */
205884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
206884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
20711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb = &acm->wb[wbn];
20811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if(acm_wb_is_avail(acm) <= 1)
20911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->write_ready = 0;
21011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
21111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	dbg("%s susp_count: %d", __func__, acm->susp_count);
21211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->susp_count) {
21311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->old_ready = acm->write_ready;
21411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->delayed_wb = wb;
21511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->write_ready = 0;
21611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		schedule_work(&acm->waker);
21711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
21811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return 0;	/* A white lie */
21911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
22011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
22111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
222884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm_wb_is_used(acm, wbn)) {
223884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
224884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
225884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
226884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
22711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rc = acm_start_wb(acm, wb);
228884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
229884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
230884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return rc;
23111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
232884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/*
234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs
235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */
236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps
237c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->ctrl_caps);
243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes
247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
249c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
250c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	memcpy(buf, acm->country_codes, acm->country_code_size);
253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return acm->country_code_size;
254c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
258c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date
259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
260c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
261c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
262c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
263c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
264c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->country_rel_date);
265c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
266884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
267c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
268884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */
2737d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb)
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = urb->context;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_notification *dr = urb->transfer_buffer;
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl;
279185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int retval;
280185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int status = urb->status;
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
282185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	switch (status) {
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* success */
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ECONNRESET:
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ENOENT:
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ESHUTDOWN:
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this urb is terminated, clean up */
290441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - urb shutting down with status: %d", __func__, status);
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
293441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - nonzero urb status received: %d", __func__, status);
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = (unsigned char *)(dr + 1);
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (dr->bNotificationType) {
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_CDC_NOTIFY_NETWORK_CONNECTION:
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_CDC_NOTIFY_SERIAL_STATE:
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
310a5abdeafedf722b0f3f357f4a23089a686b1b80dHarvey Harrison			newctrl = get_unaligned_le16(data);
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dbg("calling hangup");
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				tty_hangup(acm->tty);
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			acm->ctrlin = newctrl;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',	acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',	acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',	acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dr->bNotificationType, dr->wIndex,
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dr->wLength, data[0], data[1]);
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
33411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
335185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	retval = usb_submit_urb (urb, GFP_ATOMIC);
336185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	if (retval)
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err ("%s - usb_submit_urb failed with result %d",
338441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		     __func__, retval);
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */
3427d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_read_bulk(struct urb *urb)
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
34461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
34561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv = urb->context;
34661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm *acm = rcv->instance;
34786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int status = urb->status;
348185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman
349185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	dbg("Entering acm_read_bulk with status %d", status);
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
35111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (!ACM_READY(acm)) {
35211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dev_dbg(&acm->data->dev, "Aborting, acm not ready");
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
35411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
35511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
35786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (status)
358898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
36061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = rcv->buffer;
36161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf->size = urb->actual_length;
36261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
36386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (likely(status == 0)) {
36486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
36511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->processing++;
36686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
36786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&buf->list, &acm->filled_read_bufs);
36886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
36986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	} else {
37086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* we drop the buffer due to an error */
37186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
37286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
37386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add(&buf->list, &acm->spare_read_bufs);
37486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
37586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* nevertheless the tasklet must be kicked unconditionally
37686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		so the queue cannot dry up */
37786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	}
37811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (likely(!acm->susp_count))
37911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		tasklet_schedule(&acm->urb_task);
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm)
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = (void *)_acm;
38561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct tty_struct *tty = acm->tty;
38761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv;
388762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	unsigned long flags;
389ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	unsigned char throttled;
39011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Entering acm_rx_tasklet");
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
393ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (!ACM_READY(acm))
39411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	{
39511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("acm_rx_tasklet: ACM not ready");
396ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		return;
39711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
398ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
399834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_lock_irqsave(&acm->throttle_lock, flags);
400ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	throttled = acm->throttle;
401834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_unlock_irqrestore(&acm->throttle_lock, flags);
402ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (throttled)
40311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	{
40411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		dbg("acm_rx_tasklet: throttled");
40561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		return;
40611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
40761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
40861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer:
409762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
41061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	if (list_empty(&acm->filled_read_bufs)) {
411762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
41261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		goto urbs;
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
41461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = list_entry(acm->filled_read_bufs.next,
41561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			 struct acm_rb, list);
41661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_del(&buf->list);
417762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
41861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
4193dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
42061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
42133f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox	tty_buffer_request_room(tty, buf->size);
422834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_lock_irqsave(&acm->throttle_lock, flags);
423ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	throttled = acm->throttle;
424834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_unlock_irqrestore(&acm->throttle_lock, flags);
425ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (!throttled)
42633f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox		tty_insert_flip_string(tty, buf->base, buf->size);
42761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tty_flip_buffer_push(tty);
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
429ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (throttled) {
430ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		dbg("Throttling noticed");
431762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_lock_irqsave(&acm->read_lock, flags);
43261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&buf->list, &acm->filled_read_bufs);
433762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
437762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
43861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_add(&buf->list, &acm->spare_read_bufs);
439762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
44061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	goto next_buffer;
44161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
44261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs:
44361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	while (!list_empty(&acm->spare_read_bufs)) {
444762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_lock_irqsave(&acm->read_lock, flags);
44561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (list_empty(&acm->spare_read_urbs)) {
44611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
447762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
44861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
44961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
45061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv = list_entry(acm->spare_read_urbs.next,
45161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_ru, list);
45261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&rcv->list);
453762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
45461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
45561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		buf = list_entry(acm->spare_read_bufs.next,
45661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_rb, list);
45761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&buf->list);
45861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
45961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->buffer = buf;
46061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
46161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_fill_bulk_urb(rcv->urb, acm->dev,
46261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm->rx_endpoint,
46361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  buf->base,
46461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm->readsize,
46561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				  acm_read_bulk, rcv);
46661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_dma = buf->dma;
46761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
46861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
46961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		/* This shouldn't kill the driver as unsuccessful URBs are returned to the
47061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		   free-urbs-pool and resubmited ASAP */
47111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irqsave(&acm->read_lock, flags);
47211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (acm->susp_count || usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
47361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&buf->list, &acm->spare_read_bufs);
47461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&rcv->list, &acm->spare_read_urbs);
47511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
476762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
47761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
47811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		} else {
47911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			spin_unlock_irqrestore(&acm->read_lock, flags);
48011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
48161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
48261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
48311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irqsave(&acm->read_lock, flags);
48411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->processing = 0;
48511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irqrestore(&acm->read_lock, flags);
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */
4897d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb)
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
491e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	struct acm *acm;
492cdc97792289179974af6dda781c855696358d307Ming Lei	struct acm_wb *wb = urb->context;
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4943dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_write_bulk with status %d", urb->status);
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
496e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm = wb->instance;
497e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm_write_done(acm, wb);
498884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (ACM_READY(acm))
499884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		schedule_work(&acm->work);
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
502c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work)
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
504c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct acm *acm = container_of(work, struct acm, work);
5053dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_softint.");
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_wakeup(acm->tty);
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic void acm_waker(struct work_struct *waker)
51311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
51411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	struct acm *acm = container_of(waker, struct acm, waker);
51511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	long flags;
51611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rv;
51711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
51811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rv = usb_autopm_get_interface(acm->control);
51911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (rv < 0) {
52011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		err("Autopm failure in %s", __func__);
52111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return;
52211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
52311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->delayed_wb) {
52411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_start_wb(acm, acm->delayed_wb);
52511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->delayed_wb = NULL;
52611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
52711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
52811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->write_ready = acm->old_ready;
52911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
53011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
53111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
53211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp)
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rv = -EINVAL;
54161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
5423dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_open.");
5434186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven
5444186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm = acm_table[tty->index];
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->dev)
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out;
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rv = 0;
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
55228d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91David Engraf	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty->driver_data = acm;
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->tty = tty;
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
55661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	/* force low_latency on so that our tty_push actually forces the data through,
55761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	   otherwise it is scheduled, and with high data rates data can get lost. */
55861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tty->low_latency = 1;
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
56094409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	if (usb_autopm_get_interface(acm->control) < 0)
56194409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum		goto early_bail;
56211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	else
56311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->control->needs_remote_wakeup = 1;
5641365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5651365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->used++) {
5671365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_autopm_put_interface(acm->control);
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto done;
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5711365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->dev = acm->dev;
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("usb_submit_urb(ctrl irq) failed");
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto bail_out;
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
578ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
579ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	    (acm->ctrl_caps & USB_CDC_CAP_LINE))
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto full_bailout;
58111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
58361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_urbs);
58461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_bufs);
58561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->filled_read_bufs);
58686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++) {
58761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
58861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
58986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < acm->rx_buflimit; i++) {
59061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
59161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
59261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
593ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	acm->throttle = 0;
594ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
59561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone:
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out:
5991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
60094409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rv;
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout:
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(acm->ctrlurb);
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out:
6061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_autopm_put_interface(acm->control);
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->used--;
6081365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
60994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukumearly_bail:
61094409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EIO;
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
61483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm)
61583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{
61686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int i,nr;
61761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
61886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
61983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_unregister_device(acm_tty_driver, acm->minor);
62083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_put_intf(acm->control);
62183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	acm_table[acm->minor] = NULL;
62283ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_free_urb(acm->ctrlurb);
623e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
624e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
62586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < nr; i++)
62661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
627c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	kfree(acm->country_codes);
62883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	kfree(acm);
62983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk}
63083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp)
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
63486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int i,nr;
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->used)
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
63986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	nr = acm->rx_buflimit;
6404186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!--acm->used) {
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (acm->dev) {
64311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			usb_autopm_get_interface(acm->control);
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			acm_set_control(acm, acm->ctrlout = 0);
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			usb_kill_urb(acm->ctrlurb);
646e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf			for (i = 0; i < ACM_NW; i++)
647e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf				usb_kill_urb(acm->wb[i].urb);
64886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum			for (i = 0; i < nr; i++)
64961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				usb_kill_urb(acm->ru[i].urb);
65011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->control->needs_remote_wakeup = 0;
6511365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum			usb_autopm_put_interface(acm->control);
65283ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		} else
65383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk			acm_tty_unregister(acm);
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6554186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int stat;
662884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
663884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
664884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
665884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
6663dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	dbg("Entering acm_tty_write to write %d bytes,", count);
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!count)
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
673884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
674884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if ((wbn = acm_wb_alloc(acm)) < 0) {
675884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
676884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
677884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
678884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
680884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	count = (count > acm->writesize) ? acm->writesize : count;
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("Get %d bytes...", count);
682884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	memcpy(wb->buf, buf, count);
683884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb->len = count;
684884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
686e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	if ((stat = acm_write_start(acm, wbn)) < 0)
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return stat;
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty)
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
696884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
697884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * Do not let the line discipline to know that we have a reserve,
698884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * or it might get too enthusiastic.
699884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
700884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty)
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
708884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
709884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * This is inaccurate (overcounts), but it works.
710884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
71186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty)
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 1;
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty)
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 0;
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
73261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7359e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state)
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7389e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	int retval;
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7409e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox		return -EINVAL;
7419e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	retval = acm_send_break(acm, state ? 0xffff : 0);
7429e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	if (retval < 0)
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("send break failed");
7449e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	return retval;
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TIOCM_CTS;
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    unsigned int set, unsigned int clear)
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int newctrl;
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = acm->ctrlout;
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = (newctrl & ~clear) | set;
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->ctrlout == newctrl)
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return acm_set_control(acm, acm->ctrlout = newctrl);
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7924c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = {
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 50, 75, 110, 134, 150, 200, 300, 600,
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1200, 1800, 2400, 4800, 9600, 19200, 38400,
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	57600, 115200, 230400, 460800, 500000, 576000,
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	921600, 1000000, 1152000, 1500000, 2000000,
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	2500000, 3000000, 3500000, 4000000
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8004c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = {
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7, 8
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
804606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Coxstatic void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old)
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
807606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox	struct ktermios *termios = tty->termios;
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_line_coding newline;
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl = acm->ctrlout;
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bParityType = termios->c_cflag & PARENB ?
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!newline.dwDTERate) {
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newline.dwDTERate = acm->line.dwDTERate;
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newctrl &= ~ACM_CTRL_DTR;
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else  newctrl |=  ACM_CTRL_DTR;
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (newctrl != acm->ctrlout)
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_control(acm, acm->ctrlout = newctrl);
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(&acm->line, &newline, sizeof newline)) {
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&acm->line, &newline, sizeof newline);
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bCharFormat, newline.bParityType,
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bDataBits);
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_line(acm, &acm->line);
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines.
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
844830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */
845884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm)
846884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
847884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
848884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
849884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
85086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
851884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah);
852884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
853884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
854884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
855830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm)
856830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{
857830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
858830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	int i, n = acm->rx_buflimit;
859830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
860830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	for (i = 0; i < n; i++)
861830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum		usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
862830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum}
863830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
864884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */
865884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm)
866884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
867884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
868884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
869884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
87086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
871884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
872884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		    &wb->dmah);
873884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->buf) {
874884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			while (i != 0) {
875884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--i;
876884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--wb;
877884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				usb_buffer_free(acm->dev, acm->writesize,
878884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				    wb->buf, wb->dmah);
879884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			}
880884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -ENOMEM;
881884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
882884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
883884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return 0;
884884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
885884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_probe (struct usb_interface *intf,
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      const struct usb_device_id *id)
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_union_desc *union_header = NULL;
890c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_cdc_country_functional_desc *cfd = NULL;
891c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell	unsigned char *buffer = intf->altsetting->extra;
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buflen = intf->altsetting->extralen;
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *control_interface;
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *data_interface;
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epctrl;
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epread;
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_endpoint_descriptor *epwrite;
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor;
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ctrlsize,readsize;
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *buf;
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 ac_management_function = 0;
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 call_management_function = 0;
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int call_interface_num = -1;
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int data_interface_num;
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long quirks;
90886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int num_rx_buf;
90961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* normal quirks */
9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	quirks = (unsigned long)id->driver_info;
91386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
91486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum
91586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* handle quirks deadly to normal probing*/
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (quirks == NO_UNION_NORMAL) {
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, 1);
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, 0);
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto skip_normal_probe;
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* normal probing*/
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
924898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		err("Weird descriptor references\n");
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buflen) {
9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
930898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buflen = intf->cur_altsetting->endpoint->extralen;
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = intf->cur_altsetting->endpoint->extra;
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			err("Zero length descriptor references\n");
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (buflen > 0) {
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (buffer [1] != USB_DT_CS_INTERFACE) {
9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			err("skipping garbage\n");
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto next_desc;
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (buffer [2]) {
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_UNION_TYPE: /* we've found it */
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (union_header) {
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					err("More than one union descriptor, skipping ...");
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					goto next_desc;
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				union_header = (struct usb_cdc_union_desc *)
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							buffer;
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
954c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
955c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum				cfd = (struct usb_cdc_country_functional_desc *)buffer;
956c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum				break;
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_HEADER_TYPE: /* maybe check version */
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break; /* for now we ignore it */
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_ACM_TYPE:
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ac_management_function = buffer[3];
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_CDC_CALL_MANAGEMENT_TYPE:
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				call_management_function = buffer[3];
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				call_interface_num = buffer[4];
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if ((call_management_function & 3) != 3)
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					err("This device cannot do calls on its own. It is no modem.");
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
969c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell				/* there are LOTS more CDC descriptors that
970c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell				 * could legitimately be found here.
971c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell				 */
972c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell				dev_dbg(&intf->dev, "Ignoring descriptor: "
973c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell						"type %02x, length %d\n",
974c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell						buffer[2], buffer[0]);
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc:
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buflen -= buffer[0];
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer += buffer[0];
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!union_header) {
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (call_interface_num > 0) {
984898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = intf;
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
988898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"No union descriptor, giving up\n");
9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!control_interface || !data_interface) {
995898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"no interfaces\n");
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface_num != call_interface_num)
1001dc0d5c1e5c7532e800fff6e313cd4af44af99976Joe Perches		dev_dbg(&intf->dev,"Separate call control interface. That is not fully supported.\n");
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe:
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*workaround for switched interfaces */
10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_interface *t;
1009898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = control_interface;
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = data_interface;
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = t;
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
101874da5d68a54d66667664fbe233ededab2376a070Alan Stern
101974da5d68a54d66667664fbe233ededab2376a070Alan Stern	/* Accept probe requests only for the control interface */
102074da5d68a54d66667664fbe233ededab2376a070Alan Stern	if (intf != control_interface)
102174da5d68a54d66667664fbe233ededab2376a070Alan Stern		return -ENODEV;
10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_interface_claimed(data_interface)) { /* valid in this context */
1024898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev,"The data interface isn't available\n");
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epread = &data_interface->cur_altsetting->endpoint[0].desc;
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* workaround for switched endpoints */
103845aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino	if (!usb_endpoint_dir_in(epread)) {
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* descriptors are swapped */
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_endpoint_descriptor *t;
1041898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = epread;
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epread = epwrite;
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epwrite = t;
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("interfaces are valid");
10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (minor == ACM_TTY_MINORS) {
10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err("no more free acm devices");
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
105546f116eab81b21c6ae8c4f169498c632b1f94bf1Oliver Neukum	if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
1056898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail;
10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
106186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
1062e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->control = control_interface;
10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->data = data_interface;
10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->minor = minor;
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = usb_dev;
10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_caps = ac_management_function;
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlsize = ctrlsize;
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->readsize = readsize;
107086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->rx_buflimit = num_rx_buf;
107161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.func = acm_rx_tasklet;
107261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.data = (unsigned long) acm;
1073c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&acm->work, acm_softint);
107411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	INIT_WORK(&acm->waker, acm_waker);
10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&acm->throttle_lock);
1076884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_init(&acm->write_lock);
107761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock_init(&acm->read_lock);
10781365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_init(&acm->mutex);
1079884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm->write_ready = 1;
108061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buf) {
1084898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail2;
10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_buffer = buf;
10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1089884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (acm_write_buffers_alloc(acm) < 0) {
1090898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail4;
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->ctrlurb) {
1096898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail5;
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
109986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
110061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		struct acm_ru *rcv = &(acm->ru[i]);
110161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
110261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
1103898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n");
110461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
110561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
110661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
110761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
110861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->instance = acm;
110961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
111086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
111161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		struct acm_rb *buf = &(acm->rb[i]);
111261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
111361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
1114898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches			dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
111561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			goto alloc_fail7;
111661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1118e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for(i = 0; i < ACM_NW; i++)
1119e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	{
1120e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		struct acm_wb *snd = &(acm->wb[i]);
1121e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
1122e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
1123e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf			dev_dbg(&intf->dev, "out of memory (write urbs usb_alloc_urb)");
1124e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf			goto alloc_fail7;
1125e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		}
1126e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
1127e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1128e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf				NULL, acm->writesize, acm_write_bulk, snd);
1129e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1130e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->instance = acm;
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1133c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	usb_set_intfdata (intf, acm);
1134c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1135c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1136c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (i < 0)
1137c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		goto alloc_fail8;
1138c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1139c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (cfd) { /* export the country data */
1140c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1141c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (!acm->country_codes)
1142c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1143c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_code_size = cfd->bLength - 4;
1144c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
1145c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_rel_date = cfd->iCountryCodeRelDate;
1146c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1147c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1148c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1149c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1150c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1151c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1152c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1153c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
1154c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1155c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1156c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1157c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1158c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
1159c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1160c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries:
11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_control(acm, acm->ctrlout);
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.dwDTERate = cpu_to_le32(9600);
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.bDataBits = 8;
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_line(acm, &acm->line);
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_driver_claim_interface(&acm_driver, data_interface, acm);
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
117683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_get_intf(control_interface);
117783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_table[minor] = acm;
11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1181c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return 0;
1182c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumalloc_fail8:
1183e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
1184e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7:
1186830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
118786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
118861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_free_urb(acm->ctrlurb);
11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5:
1191884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4:
11931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
11941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2:
11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acm);
11961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail:
11971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOMEM;
11981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12001365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm)
12011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
12021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int i;
120311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	dbg("Entering stop_data_traffic");
12041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_disable(&acm->urb_task);
12061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_kill_urb(acm->ctrlurb);
1208e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for(i = 0; i < ACM_NW; i++)
1209e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_kill_urb(acm->wb[i].urb);
12101365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
12111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_kill_urb(acm->ru[i].urb);
12121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_enable(&acm->urb_task);
12141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	cancel_work_sync(&acm->work);
121611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cancel_work_sync(&acm->waker);
12171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
12181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf)
12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1221c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
12221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
12231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12244186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
1225830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	if (!acm || !acm->dev) {
12264186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
122786067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum		return;
122886067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	}
1229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (acm->country_codes){
123074da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
123174da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_wCountryCodes);
123274da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
123374da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_iCountryCodeRelDate);
1234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
123574da5d68a54d66667664fbe233ededab2376a070Alan Stern	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
12361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = NULL;
123786067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->control, NULL);
123886067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->data, NULL);
12391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12401365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	stop_data_traffic(acm);
12411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1242884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
12431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
1244830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
12451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1246830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	usb_driver_release_interface(&acm_driver, intf == acm->control ?
1247830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum					acm->data : acm->control);
12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->used) {
125083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		acm_tty_unregister(acm);
12514186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
12531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12554186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->tty)
12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_hangup(acm->tty);
12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
12621365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message)
12631365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
12641365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
126511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
126611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
126711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->dev->auto_pm) {
126811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		int b;
126911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
127011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irq(&acm->read_lock);
127111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock(&acm->write_lock);
127211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		b = acm->processing + acm->transmitting;
127311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock(&acm->write_lock);
127411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irq(&acm->read_lock);
127511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (b)
127611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			return -EBUSY;
127711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
127811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
127911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
128011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock(&acm->write_lock);
128111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count++;
128211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock(&acm->write_lock);
128311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
12841365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
128511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
12861365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
12871365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	/*
12881365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we treat opened interfaces differently,
12891365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we must guard against open
12901365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	*/
12911365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
12921365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12931365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	if (acm->used)
12941365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		stop_data_traffic(acm);
12951365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12961365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
12971365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return 0;
12981365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
12991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13001365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf)
13011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
13031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int rv = 0;
130411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
13051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
130611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
130711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->susp_count -= 1;
130811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count;
130911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
131011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
131111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
13121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
13131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
13151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	if (acm->used) {
13161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
13171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		if (rv < 0)
131811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			goto err_out;
13191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		tasklet_schedule(&acm->urb_task);
13211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	}
13221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out:
13241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
13251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return rv;
13261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
1327357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum
1328357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */
13291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
13301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure.
13311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
13321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id acm_ids[] = {
13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* quirky and broken devices */
13351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1338b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1339b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1340b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	},
13418753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
13428753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13438753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	},
134491a9c9214e34c364bf15406aadb922787ae7129bChris Malley	{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
134591a9c9214e34c364bf15406aadb922787ae7129bChris Malley	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
134691a9c9214e34c364bf15406aadb922787ae7129bChris Malley	},
134786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
134886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
134986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	},
13503dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
13513dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
13523dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	},
13539be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
13549be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13559be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	},
13566149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	{ USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
13576149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
13586149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	},
13599be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum
13601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* control interfaces with various AT-command sets */
13611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_V25TER) },
13631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101) },
13651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
13671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_GSM) },
13691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_3G	) },
13711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
13721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_CDMA) },
13731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* NOTE:  COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
13751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
13761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
13771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE (usb, acm_ids);
13791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = {
13811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"cdc_acm",
13821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	acm_probe,
13831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	acm_disconnect,
1384357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
13851365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.suspend =	acm_suspend,
13861365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.resume =	acm_resume,
1387357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
13881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	acm_ids,
1389357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
13901365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.supports_autosuspend = 1,
1391357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
13921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
13931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
13951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures.
13961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
13971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1398b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = {
13991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			acm_tty_open,
14001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		acm_tty_close,
14011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		acm_tty_write,
14021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		acm_tty_write_room,
14031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		acm_tty_ioctl,
14041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		acm_tty_throttle,
14051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		acm_tty_unthrottle,
14061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	acm_tty_chars_in_buffer,
14071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl =		acm_tty_break_ctl,
14081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios =		acm_tty_set_termios,
14091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmget =		acm_tty_tiocmget,
14101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmset =		acm_tty_tiocmset,
14111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit.
14151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void)
14181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
14201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
14211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm_tty_driver)
14221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
14231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->owner = THIS_MODULE,
14241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->driver_name = "acm",
14251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->name = "ttyACM",
14261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->major = ACM_TTY_MAJOR,
14271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->minor_start = 0,
14281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
14291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
1430331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
14311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios = tty_std_termios;
14321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
14331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(acm_tty_driver, &acm_ops);
14341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = tty_register_driver(acm_tty_driver);
14361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
14371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
14381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
14391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&acm_driver);
14421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
14431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_unregister_driver(acm_tty_driver);
14441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
14451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
14461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info(DRIVER_VERSION ":" DRIVER_DESC);
14491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
14511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void)
14541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister(&acm_driver);
14561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_unregister_driver(acm_tty_driver);
14571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	put_tty_driver(acm_tty_driver);
14581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init);
14611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit);
14621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR( DRIVER_AUTHOR );
14641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION( DRIVER_DESC );
14651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
14661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1467