11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * cdc-acm.c
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Armin Fuerst	<fuerst@in.tum.de>
5a2531293dbb7608fa672ff28efe3ab4027917a2fPavel Machek * Copyright (c) 1999 Pavel Machek	<pavel@ucw.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>
10088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold * Copyright (c) 2011 Johan Hovold	<jhovold@gmail.com>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB Abstract Control Model driver for USB modems and ISDN adapters
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Sponsored by SuSE
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version.
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG
32e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#undef VERBOSE_DEBUG
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
397af25b4b34a2439020d78da765a3bed0ff73f25cOliver Neukum#include <linux/serial.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h>
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
4218c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum#include <linux/serial.h>
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
444186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h>
4510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#include <linux/uaccess.h>
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
47a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h>
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h>
5061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h>
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h"
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
54e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
55088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek, Johan Hovold"
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver;
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver;
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS];
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
627fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenstatic DEFINE_MUTEX(acm_table_lock);
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
647fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen/*
657fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen * acm_table accessors
667fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen */
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
687fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen/*
697fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen * Look up an ACM structure by index. If found and not disconnected, increment
707fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen * its refcount and return it with its mutex held.
717fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen */
727fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenstatic struct acm *acm_get_by_index(unsigned index)
737fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen{
747fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	struct acm *acm;
757fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
767fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	mutex_lock(&acm_table_lock);
777fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	acm = acm_table[index];
787fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (acm) {
797fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		mutex_lock(&acm->mutex);
807fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		if (acm->disconnected) {
817fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen			mutex_unlock(&acm->mutex);
827fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen			acm = NULL;
837fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		} else {
847fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen			tty_port_get(&acm->port);
857fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen			mutex_unlock(&acm->mutex);
867fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		}
877fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	}
887fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	mutex_unlock(&acm_table_lock);
897fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	return acm;
907fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen}
917fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
927fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen/*
937fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen * Try to find an available minor number and if found, associate it with 'acm'.
947fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen */
957fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenstatic int acm_alloc_minor(struct acm *acm)
967fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen{
977fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	int minor;
987fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
997fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	mutex_lock(&acm_table_lock);
1007fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	for (minor = 0; minor < ACM_TTY_MINORS; minor++) {
1017fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		if (!acm_table[minor]) {
1027fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen			acm_table[minor] = acm;
1037fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen			break;
1047fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		}
1057fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	}
1067fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	mutex_unlock(&acm_table_lock);
1077fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
1087fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	return minor;
1097fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen}
1107fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
1117fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen/* Release the minor number associated with 'acm'.  */
1127fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenstatic void acm_release_minor(struct acm *acm)
1137fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen{
1147fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	mutex_lock(&acm_table_lock);
1157fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	acm_table[acm->minor] = NULL;
1167fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	mutex_unlock(&acm_table_lock);
1177fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen}
118739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages.
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_ctrl_msg(struct acm *acm, int request, int value,
1246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox							void *buf, int len)
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		request, USB_RT_ACM, value,
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm->control->altsetting[0].desc.bInterfaceNumber,
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf, len, 5000);
130a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&acm->control->dev,
131a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - rq 0x%02x, val %#x, len %#x, result %d\n",
132a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__, request, value, len, retval);
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval < 0 ? retval : 0;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests.
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do...
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
147884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management.
148884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller.
149884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
150884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
151884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm)
152884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
153884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, wbn;
154884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
155884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
156e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wbn = 0;
157884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	i = 0;
158884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	for (;;) {
159884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb = &acm->wb[wbn];
160884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->use) {
161884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			wb->use = 1;
162884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return wbn;
163884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
16486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		wbn = (wbn + 1) % ACM_NW;
16586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		if (++i >= ACM_NW)
166884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -1;
167884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
168884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
169884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm)
171884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
172884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, n;
173e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	unsigned long flags;
174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
17586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	n = ACM_NW;
176e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	spin_lock_irqsave(&acm->write_lock, flags);
1776e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++)
17886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		n -= acm->wb[i].use;
179e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	spin_unlock_irqrestore(&acm->write_lock, flags);
180884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return n;
181884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
182884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
183884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
184ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips * Finish write. Caller must hold acm->write_lock
185884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
186e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic void acm_write_done(struct acm *acm, struct acm_wb *wb)
187884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
188e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wb->use = 0;
18911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting--;
19097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	usb_autopm_put_interface_async(acm->control);
191884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
192884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
193884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
194884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write.
19511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum *
19611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking
197884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
19811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
19911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb)
20011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
20111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rc;
20211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
20311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting++;
20411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
20511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer = wb->buf;
20611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_dma = wb->dmah;
20711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer_length = wb->len;
20811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->dev = acm->dev;
20911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
2106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
2116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (rc < 0) {
212a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_err(&acm->data->dev,
213a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - usb_submit_urb(write bulk) failed: %d\n",
214a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__, rc);
21511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_write_done(acm, wb);
21611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
21711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	return rc;
21811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
21911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
220e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn)
221884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
222884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
223934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	struct acm_wb *wb = &acm->wb[wbn];
224884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int rc;
225884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
226884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
227884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->dev) {
228934da4635c2d05cef474e5243ef05df95b2ad264David Brownell		wb->use = 0;
229884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
230884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return -ENODEV;
231884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
232884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
2335e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
234a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold							acm->susp_count);
23597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	usb_autopm_get_interface_async(acm->control);
23611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->susp_count) {
23797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		if (!acm->delayed_wb)
23897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			acm->delayed_wb = wb;
23997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		else
24097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			usb_autopm_put_interface_async(acm->control);
24111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
24211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return 0;	/* A white lie */
24311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
24411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
24511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
24611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rc = acm_start_wb(acm, wb);
247884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
248884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
249884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return rc;
25011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
251884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/*
253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs
254c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */
255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps
256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
258c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
260c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
261c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->ctrl_caps);
262c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
263c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
264c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
265c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes
266c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
267c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
268c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
269c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
270c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
271c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	memcpy(buf, acm->country_codes, acm->country_code_size);
272c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return acm->country_code_size;
273c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
274c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
275c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
276c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
277c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date
278c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
279c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
280c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
281c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
282c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
283c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->country_rel_date);
284c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
285884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
286c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
287884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */
2927d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb)
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = urb->context;
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_notification *dr = urb->transfer_buffer;
29610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data;
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl;
299185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int retval;
300185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int status = urb->status;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
302185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	switch (status) {
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* success */
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ECONNRESET:
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ENOENT:
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ESHUTDOWN:
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this urb is terminated, clean up */
310a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev,
311a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				"%s - urb shutting down with status: %d\n",
312a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				__func__, status);
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
315a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev,
316a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				"%s - nonzero urb status received: %d\n",
317a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				__func__, status);
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3217e7797e7f6f7bfab73fca02c65e40eaa5bb9000cJohan Hovold	usb_mark_last_busy(acm->dev);
3227e7797e7f6f7bfab73fca02c65e40eaa5bb9000cJohan Hovold
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = (unsigned char *)(dr + 1);
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (dr->bNotificationType) {
3256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
326a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev, "%s - network connection: %d\n",
327a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold							__func__, dr->wValue);
3286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		break;
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	case USB_CDC_NOTIFY_SERIAL_STATE:
3316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		tty = tty_port_tty_get(&acm->port);
3326e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl = get_unaligned_le16(data);
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3346e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (tty) {
3356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (!acm->clocal &&
3366e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
337a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				dev_dbg(&acm->control->dev,
338a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold					"%s - calling hangup\n", __func__);
3396e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				tty_hangup(tty);
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3416e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			tty_kref_put(tty);
3426e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3446e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		acm->ctrlin = newctrl;
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
346a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev,
347a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - input control lines: dcd%c dsr%c break%c "
348a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"ring%c framing%c parity%c overrun%c\n",
349a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__,
3506e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
3516e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
3526e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
3536e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
3546e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
3556e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
3566e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	default:
360a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev,
361a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - unknown notification %d received: index %d "
362a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"len %d data0 %d data1 %d\n",
363a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__,
3646e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dr->bNotificationType, dr->wIndex,
3656e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dr->wLength, data[0], data[1]);
3666e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		break;
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
3696e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	retval = usb_submit_urb(urb, GFP_ATOMIC);
370185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	if (retval)
3711d9846e505febb71255b098910ace741433312b7Johan Hovold		dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
3721d9846e505febb71255b098910ace741433312b7Johan Hovold							__func__, retval);
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
375088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovoldstatic int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
377088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	int res;
378185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman
379088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (!test_and_clear_bit(index, &acm->read_urbs_free))
380088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		return 0;
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
382088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
383088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
384088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	res = usb_submit_urb(acm->read_urbs[index], mem_flags);
385088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (res) {
386088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		if (res != -EPERM) {
387088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			dev_err(&acm->data->dev,
388088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					"%s - usb_submit_urb failed: %d\n",
389088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					__func__, res);
390088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		}
391088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		set_bit(index, &acm->read_urbs_free);
392088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		return res;
39311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
395088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	return 0;
396088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold}
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
398088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovoldstatic int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
399088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold{
400088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	int res;
401088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	int i;
40261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
403088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	for (i = 0; i < acm->rx_buflimit; ++i) {
404088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		res = acm_submit_read_urb(acm, i, mem_flags);
405088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		if (res)
406088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			return res;
40786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	}
408088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
409088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	return 0;
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
412088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovoldstatic void acm_process_read_urb(struct acm *acm, struct urb *urb)
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
41410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
416088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (!urb->actual_length)
417ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		return;
41861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
41910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
420088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (!tty)
421088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		return;
42210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
423088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
424088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	tty_flip_buffer_push(tty);
42561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
42610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_kref_put(tty);
427088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold}
42810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
429088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovoldstatic void acm_read_bulk_callback(struct urb *urb)
430088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold{
431088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	struct acm_rb *rb = urb->context;
432088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	struct acm *acm = rb->instance;
433088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	unsigned long flags;
43461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
435088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
436088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					rb->index, urb->actual_length);
437088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	set_bit(rb->index, &acm->read_urbs_free);
43861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
439088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (!acm->dev) {
440088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
441088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		return;
442088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	}
443088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	usb_mark_last_busy(acm->dev);
44461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
445088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (urb->status) {
446088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
447088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold							__func__, urb->status);
448088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		return;
44961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
450088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm_process_read_urb(acm, urb);
451088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
452088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	/* throttle device if requested by tty */
45311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irqsave(&acm->read_lock, flags);
454088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm->throttled = acm->throttle_req;
455088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (!acm->throttled && !acm->susp_count) {
456088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		spin_unlock_irqrestore(&acm->read_lock, flags);
457088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
458088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	} else {
459088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		spin_unlock_irqrestore(&acm->read_lock, flags);
460088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	}
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */
4647d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb)
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
466cdc97792289179974af6dda781c855696358d307Ming Lei	struct acm_wb *wb = urb->context;
467e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	struct acm *acm = wb->instance;
468ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	unsigned long flags;
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4704fa4626cd43679dc62a73ee3e347665e761abc9cJohan Hovold	if (urb->status	|| (urb->actual_length != urb->transfer_buffer_length))
4714fa4626cd43679dc62a73ee3e347665e761abc9cJohan Hovold		dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
4721d9846e505febb71255b098910ace741433312b7Johan Hovold			__func__,
473e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->actual_length,
474e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->transfer_buffer_length,
475e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->status);
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
477ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_lock_irqsave(&acm->write_lock, flags);
478e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm_write_done(acm, wb);
479ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_unlock_irqrestore(&acm->write_lock, flags);
48099823f457d5994b3bd3775515578c8bfacc64b04Havard Skinnemoen	schedule_work(&acm->work);
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
483c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work)
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
485c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct acm *acm = container_of(work, struct acm, work);
48610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
487e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
4881d9846e505febb71255b098910ace741433312b7Johan Hovold	dev_vdbg(&acm->data->dev, "%s\n", __func__);
4891d9846e505febb71255b098910ace741433312b7Johan Hovold
49010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
49115e5bee33ffc11d0e5c6f819a65e7881c5c407beJohan Hovold	if (!tty)
49215e5bee33ffc11d0e5c6f819a65e7881c5c407beJohan Hovold		return;
49310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_wakeup(tty);
49410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_kref_put(tty);
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5017fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenstatic int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
5047fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	int retval;
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5067fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	dev_dbg(tty->dev, "%s\n", __func__);
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5087fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	acm = acm_get_by_index(tty->index);
5097fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (!acm)
5107fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		return -ENODEV;
511a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold
512f8a8c10f4a662dcf3cb621d7a3eba564c5963284Jiri Slaby	retval = tty_standard_install(driver, tty);
5137fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (retval)
5147fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		goto error_init_termios;
51510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty->driver_data = acm;
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5187fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	return 0;
5197fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
5207fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenerror_init_termios:
5217fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	tty_port_put(&acm->port);
5227fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	return retval;
5237fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen}
5247fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
5257fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenstatic int acm_tty_open(struct tty_struct *tty, struct file *filp)
5267fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen{
5277fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	struct acm *acm = tty->driver_data;
5287fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
5297fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	dev_dbg(tty->dev, "%s\n", __func__);
5307fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
5317fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	return tty_port_open(&acm->port, tty, filp);
5327fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen}
5337fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
5347fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenstatic int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
5357fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen{
5367fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	struct acm *acm = container_of(port, struct acm, port);
5377fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	int retval = -ENODEV;
5387fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
5397fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	dev_dbg(&acm->control->dev, "%s\n", __func__);
5401365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5411365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
5427fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (acm->disconnected)
5437fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		goto disconnected;
5447fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
5457fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	retval = usb_autopm_get_interface(acm->control);
5467fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (retval)
5477fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		goto error_get_interface;
5487fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
5497fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	/*
5507fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	 * FIXME: Why do we need this? Allocating 64K of physically contiguous
5517fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	 * memory is really nasty...
5527fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	 */
5537fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
5547fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	acm->control->needs_remote_wakeup = 1;
5551365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->dev = acm->dev;
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
558a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_err(&acm->control->dev,
559a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - usb_submit_urb(ctrl irq) failed\n", __func__);
5607fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		goto error_submit_urb;
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5637fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS;
5647fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (acm_set_control(acm, acm->ctrlout) < 0 &&
565ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	    (acm->ctrl_caps & USB_CDC_CAP_LINE))
5667fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		goto error_set_control;
56710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
56811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5709cc2d461d9ae92fdabd9097fb5ff2977fe59d995Otto Meta	/*
5719cc2d461d9ae92fdabd9097fb5ff2977fe59d995Otto Meta	 * Unthrottle device in case the TTY was closed while throttled.
5729cc2d461d9ae92fdabd9097fb5ff2977fe59d995Otto Meta	 */
5739cc2d461d9ae92fdabd9097fb5ff2977fe59d995Otto Meta	spin_lock_irq(&acm->read_lock);
5749cc2d461d9ae92fdabd9097fb5ff2977fe59d995Otto Meta	acm->throttled = 0;
5759cc2d461d9ae92fdabd9097fb5ff2977fe59d995Otto Meta	acm->throttle_req = 0;
5769cc2d461d9ae92fdabd9097fb5ff2977fe59d995Otto Meta	spin_unlock_irq(&acm->read_lock);
5779cc2d461d9ae92fdabd9097fb5ff2977fe59d995Otto Meta
578088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (acm_submit_read_urbs(acm, GFP_KERNEL))
5797fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		goto error_submit_read_urbs;
5802b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum
5811365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5837fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	return 0;
5847fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
5857fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenerror_submit_read_urbs:
5867fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	acm->ctrlout = 0;
5877fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	acm_set_control(acm, acm->ctrlout);
5887fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenerror_set_control:
5897fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	usb_kill_urb(acm->ctrlurb);
5907fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenerror_submit_urb:
5912b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum	usb_autopm_put_interface(acm->control);
5927fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenerror_get_interface:
5937fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoendisconnected:
5947fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	mutex_unlock(&acm->mutex);
5957fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	return retval;
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5987fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenstatic void acm_port_destruct(struct tty_port *port)
59983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{
6007fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	struct acm *acm = container_of(port, struct acm, port);
6017fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
6027fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	dev_dbg(&acm->control->dev, "%s\n", __func__);
60361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
60483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_unregister_device(acm_tty_driver, acm->minor);
6057fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	acm_release_minor(acm);
60683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_put_intf(acm->control);
607c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	kfree(acm->country_codes);
60883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	kfree(acm);
60983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk}
61083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk
6117fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenstatic void acm_port_shutdown(struct tty_port *port)
61210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
6137fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	struct acm *acm = container_of(port, struct acm, port);
614dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	int i;
615dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold
6167fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	dev_dbg(&acm->control->dev, "%s\n", __func__);
6177fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
6187fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	mutex_lock(&acm->mutex);
6197fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (!acm->disconnected) {
62010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_get_interface(acm->control);
62110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm_set_control(acm, acm->ctrlout = 0);
62210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_kill_urb(acm->ctrlurb);
62310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		for (i = 0; i < ACM_NW; i++)
62410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			usb_kill_urb(acm->wb[i].urb);
625dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold		for (i = 0; i < acm->rx_buflimit; i++)
626088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			usb_kill_urb(acm->read_urbs[i]);
62710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm->control->needs_remote_wakeup = 0;
62810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_put_interface(acm->control);
62910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
6307fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	mutex_unlock(&acm->mutex);
6317fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen}
6327fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
6337fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenstatic void acm_tty_cleanup(struct tty_struct *tty)
6347fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen{
6357fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	struct acm *acm = tty->driver_data;
6367fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	dev_dbg(&acm->control->dev, "%s\n", __func__);
6377fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	tty_port_put(&acm->port);
63810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
63910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
64010077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_tty_hangup(struct tty_struct *tty)
64110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
64210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct acm *acm = tty->driver_data;
6437fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	dev_dbg(&acm->control->dev, "%s\n", __func__);
64410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_hangup(&acm->port);
64510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
64610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp)
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6507fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	dev_dbg(&acm->control->dev, "%s\n", __func__);
6517fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	tty_port_close(&acm->port, tty, filp);
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6546e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_write(struct tty_struct *tty,
6556e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					const unsigned char *buf, int count)
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int stat;
659884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
660884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
661884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
662884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!count)
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6665e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
667a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold
668884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
6696e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	wbn = acm_wb_alloc(acm);
6706e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (wbn < 0) {
671884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
672884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
673884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
674884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
676884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	count = (count > acm->writesize) ? acm->writesize : count;
6775e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
678884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	memcpy(wb->buf, buf, count);
679884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb->len = count;
680884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6826e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	stat = acm_write_start(acm, wbn);
6836e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (stat < 0)
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return stat;
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty)
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
691884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
692884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * Do not let the line discipline to know that we have a reserve,
693884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * or it might get too enthusiastic.
694884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
695934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	return acm_wb_is_avail(acm) ? acm->writesize : 0;
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty)
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7017fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	/*
7027fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	 * if the device was unplugged then any remaining characters fell out
7037fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	 * of the connector ;)
7047fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	 */
7057fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (acm->disconnected)
70623198fda7182969b619613a555f8645fdc3dc334Alan Cox		return 0;
707884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
708884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * This is inaccurate (overcounts), but it works.
709884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
71086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty)
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
716088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
717088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	spin_lock_irq(&acm->read_lock);
718088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm->throttle_req = 1;
719088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	spin_unlock_irq(&acm->read_lock);
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty)
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
725088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	unsigned int was_throttled;
726088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
727088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	spin_lock_irq(&acm->read_lock);
728088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	was_throttled = acm->throttled;
729088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm->throttled = 0;
730088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm->throttle_req = 0;
731088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	spin_unlock_irq(&acm->read_lock);
732088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
733088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (was_throttled)
734088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		acm_submit_read_urbs(acm, GFP_KERNEL);
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7379e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state)
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7409e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	int retval;
7417fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
7429e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	retval = acm_send_break(acm, state ? 0xffff : 0);
7439e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	if (retval < 0)
744a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev, "%s - send break failed\n",
745a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold								__func__);
7469e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	return retval;
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
74960b33c133ca0b7c0b6072c87234b63fee6e80558Alan Coxstatic int acm_tty_tiocmget(struct tty_struct *tty)
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TIOCM_CTS;
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
76120b9d17715017ae4dd4ec87fabc36d33b9de708eAlan Coxstatic int acm_tty_tiocmset(struct tty_struct *tty,
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    unsigned int set, unsigned int clear)
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int newctrl;
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = acm->ctrlout;
7686e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
7696e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7706e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
7716e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = (newctrl & ~clear) | set;
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->ctrlout == newctrl)
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return acm_set_control(acm, acm->ctrlout = newctrl);
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
78018c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukumstatic int get_serial_info(struct acm *acm, struct serial_struct __user *info)
78118c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum{
78218c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	struct serial_struct tmp;
78318c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum
78418c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	if (!info)
78518c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum		return -EINVAL;
78618c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum
78718c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	memset(&tmp, 0, sizeof(tmp));
78818c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	tmp.flags = ASYNC_LOW_LATENCY;
78918c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	tmp.xmit_fifo_size = acm->writesize;
79018c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
79118c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum
79218c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	if (copy_to_user(info, &tmp, sizeof(tmp)))
79318c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum		return -EFAULT;
79418c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	else
79518c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum		return 0;
79618c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum}
79718c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum
7986caa76b7786891b42b66a0e61e2c2fff2c884620Alan Coxstatic int acm_tty_ioctl(struct tty_struct *tty,
7996e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					unsigned int cmd, unsigned long arg)
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
80118c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	struct acm *acm = tty->driver_data;
80218c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	int rv = -ENOIOCTLCMD;
80318c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum
80418c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	switch (cmd) {
80518c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	case TIOCGSERIAL: /* gets serial port data */
80618c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum		rv = get_serial_info(acm, (struct serial_struct __user *) arg);
80718c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum		break;
80818c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	}
80918c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum
81018c75720e667719c923e0547abb60dfcd9c4ee90Oliver Neukum	return rv;
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8134c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = {
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 50, 75, 110, 134, 150, 200, 300, 600,
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1200, 1800, 2400, 4800, 9600, 19200, 38400,
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	57600, 115200, 230400, 460800, 500000, 576000,
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	921600, 1000000, 1152000, 1500000, 2000000,
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	2500000, 3000000, 3500000, 4000000
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8214c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = {
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7, 8
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic void acm_tty_set_termios(struct tty_struct *tty,
8266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						struct ktermios *termios_old)
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
829606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox	struct ktermios *termios = tty->termios;
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_line_coding newline;
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl = acm->ctrlout;
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8339b80fee149a875a6292b2556ab2c64dc7ab7d6f5Alan Cox	newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bParityType = termios->c_cflag & PARENB ?
8366e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & PARODD ? 1 : 2) +
8376e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & CMSPAR ? 2 : 0) : 0;
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
8396e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	/* FIXME: Needs to clear unsupported bits in the termios */
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!newline.dwDTERate) {
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newline.dwDTERate = acm->line.dwDTERate;
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newctrl &= ~ACM_CTRL_DTR;
8456e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	} else
8466e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl |=  ACM_CTRL_DTR;
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (newctrl != acm->ctrlout)
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_control(acm, acm->ctrlout = newctrl);
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(&acm->line, &newline, sizeof newline)) {
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&acm->line, &newline, sizeof newline);
853a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
854a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__,
855a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			le32_to_cpu(newline.dwDTERate),
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bCharFormat, newline.bParityType,
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bDataBits);
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_line(acm, &acm->line);
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8627fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoenstatic const struct tty_port_operations acm_port_ops = {
8637fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	.shutdown = acm_port_shutdown,
8647fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	.activate = acm_port_activate,
8657fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	.destruct = acm_port_destruct,
8667fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen};
8677fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines.
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
872830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */
873884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm)
874884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
875884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
876884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
877a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
878884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
8796e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
880997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
881884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
882884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
883830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm)
884830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{
885830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
886dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	int i;
887830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
888dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	for (i = 0; i < acm->rx_buflimit; i++)
889997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		usb_free_coherent(usb_dev, acm->readsize,
890088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			  acm->read_buffers[i].base, acm->read_buffers[i].dma);
891830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum}
892830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
893884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */
894884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm)
895884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
896884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
897884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
898884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
89986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
900997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
901884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		    &wb->dmah);
902884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->buf) {
903884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			while (i != 0) {
904884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--i;
905884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--wb;
906997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack				usb_free_coherent(acm->dev, acm->writesize,
907884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				    wb->buf, wb->dmah);
908884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			}
909884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -ENOMEM;
910884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
911884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
912884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return 0;
913884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
914884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
91510077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic int acm_probe(struct usb_interface *intf,
91610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		     const struct usb_device_id *id)
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_union_desc *union_header = NULL;
919c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_cdc_country_functional_desc *cfd = NULL;
920c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell	unsigned char *buffer = intf->altsetting->extra;
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buflen = intf->altsetting->extralen;
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *control_interface;
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *data_interface;
924a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epctrl = NULL;
925a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epread = NULL;
926a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epwrite = NULL;
9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor;
9306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	int ctrlsize, readsize;
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *buf;
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 ac_management_function = 0;
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 call_management_function = 0;
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int call_interface_num = -1;
935fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	int data_interface_num = -1;
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long quirks;
93786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int num_rx_buf;
93861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
939a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	int combined_interfaces = 0;
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
94186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* normal quirks */
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	quirks = (unsigned long)id->driver_info;
94386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
94486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum
94586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* handle quirks deadly to normal probing*/
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (quirks == NO_UNION_NORMAL) {
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, 1);
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, 0);
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto skip_normal_probe;
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9516e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* normal probing*/
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
9549908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "Weird descriptor references\n");
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buflen) {
959577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray		if (intf->cur_altsetting->endpoint &&
960577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray				intf->cur_altsetting->endpoint->extralen &&
9616e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				intf->cur_altsetting->endpoint->extra) {
9626e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
9636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Seeking extra descriptors on endpoint\n");
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buflen = intf->cur_altsetting->endpoint->extralen;
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = intf->cur_altsetting->endpoint->extra;
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
9679908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev,
9689908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman				"Zero length descriptor references\n");
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (buflen > 0) {
9746e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (buffer[1] != USB_DT_CS_INTERFACE) {
9759908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev, "skipping garbage\n");
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto next_desc;
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9796e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		switch (buffer[2]) {
9806e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_UNION_TYPE: /* we've found it */
9816e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (union_header) {
9826e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "More than one "
9836e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"union descriptor, skipping ...\n");
9846e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				goto next_desc;
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9866e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			union_header = (struct usb_cdc_union_desc *)buffer;
9876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
9896e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			cfd = (struct usb_cdc_country_functional_desc *)buffer;
9906e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_HEADER_TYPE: /* maybe check version */
9926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break; /* for now we ignore it */
9936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_ACM_TYPE:
9946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			ac_management_function = buffer[3];
9956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9966e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_CALL_MANAGEMENT_TYPE:
9976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_management_function = buffer[3];
9986e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_interface_num = buffer[4];
999ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby			if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
10006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
10016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		default:
10036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			/* there are LOTS more CDC descriptors that
10046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 * could legitimately be found here.
10056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 */
10066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "Ignoring descriptor: "
10076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"type %02x, length %d\n",
10086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					buffer[2], buffer[0]);
10096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc:
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buflen -= buffer[0];
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer += buffer[0];
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!union_header) {
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (call_interface_num > 0) {
10186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
1019fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter			/* quirks for Droids MuIn LCD */
1020fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter			if (quirks & NO_DATA_INTERFACE)
1021fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter				data_interface = usb_ifnum_to_if(usb_dev, 0);
1022fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter			else
1023fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter				data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = intf;
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
1026a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1027a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1028a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -ENODEV;
1029a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			} else {
1030a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1031a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				combined_interfaces = 1;
1032a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				control_interface = data_interface = intf;
1033a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				goto look_for_collapsed_interface;
1034a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			}
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!control_interface || !data_interface) {
10406e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "no interfaces\n");
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10446e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface_num != call_interface_num)
10466e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1048a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (control_interface == data_interface) {
1049a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* some broken devices designed for windows work this way */
1050a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1051a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		combined_interfaces = 1;
1052a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* a popular other OS doesn't use it */
1053a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		quirks |= NO_CAP_LINE;
1054a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1055a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1056a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -EINVAL;
1057a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
1058a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukumlook_for_collapsed_interface:
1059a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		for (i = 0; i < 3; i++) {
1060a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			struct usb_endpoint_descriptor *ep;
1061a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			ep = &data_interface->cur_altsetting->endpoint[i].desc;
1062a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
1063a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (usb_endpoint_is_int_in(ep))
1064a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epctrl = ep;
1065a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_out(ep))
1066a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epwrite = ep;
1067a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_in(ep))
1068a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epread = ep;
1069a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else
1070a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -EINVAL;
1071a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
1072a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (!epctrl || !epread || !epwrite)
1073a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -ENODEV;
1074a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		else
1075a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			goto made_compressed_probe;
1076a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	}
1077a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe:
10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*workaround for switched interfaces */
10816e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (data_interface->cur_altsetting->desc.bInterfaceClass
10826e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						!= CDC_DATA_INTERFACE_TYPE) {
10836e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (control_interface->cur_altsetting->desc.bInterfaceClass
10846e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						== CDC_DATA_INTERFACE_TYPE) {
10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_interface *t;
10866e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
10876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Your device has switched interfaces.\n");
10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = control_interface;
10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = data_interface;
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = t;
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
109574da5d68a54d66667664fbe233ededab2376a070Alan Stern
109674da5d68a54d66667664fbe233ededab2376a070Alan Stern	/* Accept probe requests only for the control interface */
1097a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && intf != control_interface)
109874da5d68a54d66667664fbe233ededab2376a070Alan Stern		return -ENODEV;
10996e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
1100a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1101a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* valid in this context */
11026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "The data interface isn't available\n");
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epread = &data_interface->cur_altsetting->endpoint[0].desc;
11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* workaround for switched endpoints */
111645aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino	if (!usb_endpoint_dir_in(epread)) {
11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* descriptors are swapped */
11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_endpoint_descriptor *t;
11196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev,
11206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			"The data interface has switched endpoints\n");
11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = epread;
11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epread = epwrite;
11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epwrite = t;
11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1125a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukummade_compressed_probe:
1126a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&intf->dev, "interfaces are valid\n");
11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
11296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm == NULL) {
1130255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail;
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11347fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	minor = acm_alloc_minor(acm);
11357fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (minor == ACM_TTY_MINORS) {
11367fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		dev_err(&intf->dev, "no more free acm devices\n");
11377fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		kfree(acm);
11387fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		return -ENODEV;
11397fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	}
11407fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
114129cc88979a8818cd8c5019426e945aed118b400eKuninori Morimoto	ctrlsize = usb_endpoint_maxp(epctrl);
114229cc88979a8818cd8c5019426e945aed118b400eKuninori Morimoto	readsize = usb_endpoint_maxp(epread) *
11436e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(quirks == SINGLE_RX_URB ? 1 : 2);
1144a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	acm->combined_interfaces = combined_interfaces;
114529cc88979a8818cd8c5019426e945aed118b400eKuninori Morimoto	acm->writesize = usb_endpoint_maxp(epwrite) * 20;
11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->control = control_interface;
11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->data = data_interface;
11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->minor = minor;
11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = usb_dev;
11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_caps = ac_management_function;
1151a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (quirks & NO_CAP_LINE)
1152a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlsize = ctrlsize;
11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->readsize = readsize;
115586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->rx_buflimit = num_rx_buf;
1156c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&acm->work, acm_softint);
1157884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_init(&acm->write_lock);
115861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock_init(&acm->read_lock);
11591365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_init(&acm->mutex);
116061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
1161cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum	acm->is_int_ep = usb_endpoint_xfer_int(epread);
1162cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum	if (acm->is_int_ep)
1163cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum		acm->bInterval = epread->bInterval;
1164739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	tty_port_init(&acm->port);
1165739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	acm->port.ops = &acm_port_ops;
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1167997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buf) {
1169255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail2;
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_buffer = buf;
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (acm_write_buffers_alloc(acm) < 0) {
1175255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail4;
11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->ctrlurb) {
1181255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail5;
11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
118486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
1185088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		struct acm_rb *rb = &(acm->read_buffers[i]);
1186088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		struct urb *urb;
118761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
118874f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
118974f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold								&rb->dma);
119074f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		if (!rb->base) {
119174f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold			dev_err(&intf->dev, "out of memory "
119274f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold					"(read bufs usb_alloc_coherent)\n");
119374f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold			goto alloc_fail6;
119474f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		}
1195088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		rb->index = i;
1196088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		rb->instance = acm;
119774f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold
1198088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		urb = usb_alloc_urb(0, GFP_KERNEL);
1199088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		if (!urb) {
1200255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold			dev_err(&intf->dev,
12016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"out of memory (read urbs usb_alloc_urb)\n");
1202c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin			goto alloc_fail6;
120361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
1204088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1205088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		urb->transfer_dma = rb->dma;
1206088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		if (acm->is_int_ep) {
1207088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			usb_fill_int_urb(urb, acm->dev,
1208088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 acm->rx_endpoint,
1209088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 rb->base,
1210088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 acm->readsize,
1211088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 acm_read_bulk_callback, rb,
1212088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 acm->bInterval);
1213088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		} else {
1214088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			usb_fill_bulk_urb(urb, acm->dev,
1215088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					  acm->rx_endpoint,
1216088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					  rb->base,
1217088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					  acm->readsize,
1218088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					  acm_read_bulk_callback, rb);
1219088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		}
122061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
1221088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		acm->read_urbs[i] = urb;
1222088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		__set_bit(i, &acm->read_urbs_free);
122361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
12246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++) {
1225e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		struct acm_wb *snd = &(acm->wb[i]);
1226e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
12276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
12286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (snd->urb == NULL) {
1229255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold			dev_err(&intf->dev,
123059d7fec7c6908604862658a3679ac44c2c3eea44Johan Hovold				"out of memory (write urbs usb_alloc_urb)\n");
123174f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold			goto alloc_fail7;
1232e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		}
1233e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
12345186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		if (usb_endpoint_xfer_int(epwrite))
12355186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_int_urb(snd->urb, usb_dev,
12365186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
12375186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
12385186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		else
12395186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_bulk_urb(snd->urb, usb_dev,
12405186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
12415186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd);
1242e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1243e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->instance = acm;
12441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12466e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_set_intfdata(intf, acm);
1247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1249c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (i < 0)
125074f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		goto alloc_fail7;
1251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (cfd) { /* export the country data */
1253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1254c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (!acm->country_codes)
1255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_code_size = cfd->bLength - 4;
12576e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
12586e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox							cfd->bLength - 4);
1259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_rel_date = cfd->iCountryCodeRelDate;
1260c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1261c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1262c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1263c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1264e7c8e8605d0bafc705ff27f9da98a1668427cc0fJulia Lawall			acm->country_codes = NULL;
1265e7c8e8605d0bafc705ff27f9da98a1668427cc0fJulia Lawall			acm->country_code_size = 0;
1266c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1267c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1268c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
12696e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		i = device_create_file(&intf->dev,
12706e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						&dev_attr_iCountryCodeRelDate);
1271c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1272c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin			device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
1273c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1274e7c8e8605d0bafc705ff27f9da98a1668427cc0fJulia Lawall			acm->country_codes = NULL;
1275e7c8e8605d0bafc705ff27f9da98a1668427cc0fJulia Lawall			acm->country_code_size = 0;
1276c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1277c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1278c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
1279c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1280c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries:
12816e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_fill_int_urb(acm->ctrlurb, usb_dev,
1282a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1283a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1284a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 /* works around buggy devices */
1285a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 epctrl->bInterval ? epctrl->bInterval : 0xff);
12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
12881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_control(acm, acm->ctrlout);
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.dwDTERate = cpu_to_le32(9600);
12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.bDataBits = 8;
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_line(acm, &acm->line);
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_driver_claim_interface(&acm_driver, data_interface, acm);
1298672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	usb_set_intfdata(data_interface, acm);
12991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
130083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_get_intf(control_interface);
130183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1303c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return 0;
130474f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovoldalloc_fail7:
1305e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
1306e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
1307c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Linalloc_fail6:
130886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
1309088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		usb_free_urb(acm->read_urbs[i]);
131074f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold	acm_read_buffers_free(acm);
13111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_free_urb(acm->ctrlurb);
13121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5:
1313884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
13141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4:
1315997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
13161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2:
13177fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	acm_release_minor(acm);
13181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acm);
13191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail:
13201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOMEM;
13211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm)
13241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int i;
1326a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold
1327a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&acm->control->dev, "%s\n", __func__);
13281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_kill_urb(acm->ctrlurb);
13306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++)
1331e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_kill_urb(acm->wb[i].urb);
13321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
1333088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		usb_kill_urb(acm->read_urbs[i]);
13341365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	cancel_work_sync(&acm->work);
13361365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
13371365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf)
13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1340c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
134210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
13437fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	int i;
13447fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
13457fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	dev_dbg(&intf->dev, "%s\n", __func__);
13461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1347672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	/* sibling interface is already cleaning up */
1348672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	if (!acm)
134986067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum		return;
1350672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell
13517fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	mutex_lock(&acm->mutex);
13527fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	acm->disconnected = true;
13536e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm->country_codes) {
135474da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
135574da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_wCountryCodes);
135674da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
135774da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_iCountryCodeRelDate);
1358c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
135974da5d68a54d66667664fbe233ededab2376a070Alan Stern	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
136086067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->control, NULL);
136186067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->data, NULL);
13627fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	mutex_unlock(&acm->mutex);
13637fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
13647fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	tty = tty_port_tty_get(&acm->port);
13657fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (tty) {
13667fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		tty_vhangup(tty);
13677fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		tty_kref_put(tty);
13687fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	}
13691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13701365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	stop_data_traffic(acm);
13711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13727fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	usb_free_urb(acm->ctrlurb);
13737fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	for (i = 0; i < ACM_NW; i++)
13747fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		usb_free_urb(acm->wb[i].urb);
13757fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	for (i = 0; i < acm->rx_buflimit; i++)
13767fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen		usb_free_urb(acm->read_urbs[i]);
1377884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
13787fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
1379830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
13801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1381a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!acm->combined_interfaces)
1382a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		usb_driver_release_interface(&acm_driver, intf == acm->control ?
1383830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum					acm->data : acm->control);
13841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13857fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	tty_port_put(&acm->port);
13861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1388357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
13891365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message)
13901365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13911365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
139211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
139311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
13945b1b0b812a7b1a5b968c5d06d90d1cb88621b941Alan Stern	if (PMSG_IS_AUTO(message)) {
139511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		int b;
139611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
1397088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		spin_lock_irq(&acm->write_lock);
1398088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		b = acm->transmitting;
1399088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		spin_unlock_irq(&acm->write_lock);
140011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (b)
140111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			return -EBUSY;
140211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
140311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
140411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
140511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock(&acm->write_lock);
140611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count++;
140711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock(&acm->write_lock);
140811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
14091365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
141011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
14111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
14121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14137fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags))
14141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		stop_data_traffic(acm);
14151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return 0;
14171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
14181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf)
14201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
14211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
142297d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	struct acm_wb *wb;
14231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int rv = 0;
142411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
14251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
142611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
142711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->susp_count -= 1;
142811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count;
142911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
143011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
143111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
14321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
14331365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14347fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
14351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
143697d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum
143797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		spin_lock_irq(&acm->write_lock);
143897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		if (acm->delayed_wb) {
143997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			wb = acm->delayed_wb;
144097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			acm->delayed_wb = NULL;
144197d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			spin_unlock_irq(&acm->write_lock);
1442f0730924e9e32bb8935c60040a26d94179355088Oliver Neukum			acm_start_wb(acm, wb);
144397d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		} else {
144497d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			spin_unlock_irq(&acm->write_lock);
144597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		}
144697d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum
144797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		/*
144897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 * delayed error checking because we must
144997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 * do the write path at all cost
145097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 */
14511365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		if (rv < 0)
145211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			goto err_out;
14531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
1454088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		rv = acm_submit_read_urbs(acm, GFP_NOIO);
14551365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	}
14561365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14571365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out:
14581365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return rv;
14591365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
1460357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum
1461a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavrastatic int acm_reset_resume(struct usb_interface *intf)
1462a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra{
1463a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	struct acm *acm = usb_get_intfdata(intf);
1464a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	struct tty_struct *tty;
1465a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra
14667fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
1467a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		tty = tty_port_tty_get(&acm->port);
1468a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		if (tty) {
1469a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra			tty_hangup(tty);
1470a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra			tty_kref_put(tty);
1471a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		}
1472a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	}
14737fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen
1474a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	return acm_resume(intf);
1475a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra}
1476a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra
1477357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */
1478c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
1479c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor#define NOKIA_PCSUITE_ACM_INFO(x) \
1480c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1481c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1482c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_CDC_ACM_PROTO_VENDOR)
1483c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
14844035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray#define SAMSUNG_PCSUITE_ACM_INFO(x) \
14854035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
14864035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
14874035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_CDC_ACM_PROTO_VENDOR)
14884035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray
14891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
14901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure.
14911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14936ef4852b1326301f6e9657e99b2c3221be1a3a44Németh Mártonstatic const struct usb_device_id acm_ids[] = {
14941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* quirky and broken devices */
14951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
14961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1498b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1499b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1500b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	},
15010f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	{ USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
15020f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15030f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	},
15048753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
15058753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15068753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	},
150791a9c9214e34c364bf15406aadb922787ae7129bChris Malley	{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
150891a9c9214e34c364bf15406aadb922787ae7129bChris Malley	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
150991a9c9214e34c364bf15406aadb922787ae7129bChris Malley	},
15107abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	{ USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
15117abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	.driver_info = SINGLE_RX_URB,
15127abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	},
151386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
151486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
151586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	},
15163dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
15173dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
15183dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	},
15199be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
15209be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15219be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	},
15226149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	{ USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
15236149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15246149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	},
1525c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	{ USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1526c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1527c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	},
1528c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	{ USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1529c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1530c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	},
1531cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	{ USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1532cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1533cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	},
1534155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1535155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	},
15366abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	/* Motorola H24 HSPA module: */
15376abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d91) }, /* modem                                */
15386abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d92) }, /* modem           + diagnostics        */
15396abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d93) }, /* modem + AT port                      */
15406abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d95) }, /* modem + AT port + diagnostics        */
15416abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d96) }, /* modem                         + NMEA */
15426abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d97) }, /* modem           + diagnostics + NMEA */
15436abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d99) }, /* modem + AT port               + NMEA */
15446abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d9a) }, /* modem + AT port + diagnostics + NMEA */
15456abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa
1546c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	{ USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1547c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	.driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1548c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   data interface instead of
1549c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   communications interface.
1550c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   Maybe we should define a new
1551c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   quirk for this. */
1552c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	},
15531f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	{ USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
15541f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
15551f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	},
1556c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	{ USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1557c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1558c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	},
15599be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum
1560c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	/* Nokia S60 phones expose two ACM channels. The first is
1561c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * a modem and is picked up by the standard AT-command
1562c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * information below. The second is 'vendor-specific' but
1563c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * is treated as a serial device at the S60 end, so we want
1564c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * to expose it on Linux too. */
1565c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1566c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1567c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1568c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1569c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1570c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1571c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1572c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1573c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1574c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1575c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1576c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1577c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1578c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1579c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1580c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1581c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1582c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i  */
1583c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1584c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1585c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1586c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic &  */
1587c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1588c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1589c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1590c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1591c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1592c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1593c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1594c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1595c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1596c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB  */
1597c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1598c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1599c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1600c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1601c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1602c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1603c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1604c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3  */
1605c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1606c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1607c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
160883a4eae9aeed4a69e89e323a105e653ae06e7c1fPrzemo Firszt	{ NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
16094035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
16104035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
16114035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
16124035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
16134035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
16144035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
16154035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
16164035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
16174035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
16184035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
1619721d92fc6373dee15846216f9d178ec240ec0fd7Arvid Ephraim Picciani	{ NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
16204061fde2fa80f40cb27114f60500d38d0afcf350Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */
16214061fde2fa80f40cb27114f60500d38d0afcf350Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
16224035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
1623c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
162465e52f41fa944cef2e6d4222b8c54f46cc575214Denis Pershin	/* Support for Owen devices */
162565e52f41fa944cef2e6d4222b8c54f46cc575214Denis Pershin	{ USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */
162665e52f41fa944cef2e6d4222b8c54f46cc575214Denis Pershin
1627c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	/* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1628c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
16297c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby	/* Support Lego NXT using pbLua firmware */
1630ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby	{ USB_DEVICE(0x0694, 0xff00),
1631ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby	.driver_info = NOT_A_MODEM,
16327893afc035590383a14b176c1497cba984276ef4Otavio Salvador	},
16337c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby
1634fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	/* Support for Droids MuIn LCD */
1635fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	{ USB_DEVICE(0x04d8, 0x000b),
1636fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	.driver_info = NO_DATA_INTERFACE,
1637fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	},
1638fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter
16395b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes	/* control interfaces without any protocol set */
16405b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16415b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes		USB_CDC_PROTO_NONE) },
16425b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes
16431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* control interfaces with various AT-command sets */
16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_V25TER) },
16461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101) },
16481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
16501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_GSM) },
16521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16536e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		USB_CDC_ACM_PROTO_AT_3G) },
16541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_CDMA) },
16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
16581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16606e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DEVICE_TABLE(usb, acm_ids);
16611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = {
16631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"cdc_acm",
16641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	acm_probe,
16651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	acm_disconnect,
1666357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
16671365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.suspend =	acm_suspend,
16681365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.resume =	acm_resume,
1669a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	.reset_resume =	acm_reset_resume,
1670357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
16711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	acm_ids,
1672357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
16731365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.supports_autosuspend = 1,
1674357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
16751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
16781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures.
16791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
16801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1681b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = {
16827fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	.install =		acm_tty_install,
16831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			acm_tty_open,
16841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		acm_tty_close,
16857fb57a019f94ea0c1290c39b8da753be155af41cHavard Skinnemoen	.cleanup =		acm_tty_cleanup,
168610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	.hangup =		acm_tty_hangup,
16871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		acm_tty_write,
16881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		acm_tty_write_room,
16891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		acm_tty_ioctl,
16901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		acm_tty_throttle,
16911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		acm_tty_unthrottle,
16921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	acm_tty_chars_in_buffer,
16931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl =		acm_tty_break_ctl,
16941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios =		acm_tty_set_termios,
16951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmget =		acm_tty_tiocmget,
16961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmset =		acm_tty_tiocmset,
16971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
17001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit.
17011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
17021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void)
17041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
17061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
17071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm_tty_driver)
17081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
17091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->driver_name = "acm",
17101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->name = "ttyACM",
17111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->major = ACM_TTY_MAJOR,
17121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->minor_start = 0,
17131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
17141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
1715331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
17161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios = tty_std_termios;
17176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
17186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox								HUPCL | CLOCAL;
17191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(acm_tty_driver, &acm_ops);
17201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = tty_register_driver(acm_tty_driver);
17221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
17231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
17241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
17251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&acm_driver);
17281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
17291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_unregister_driver(acm_tty_driver);
17301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
17311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
17321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1734a2c7b9353e8f782590852052fe2948692020147eJohan Hovold	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
17351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
17371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void)
17401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister(&acm_driver);
17421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_unregister_driver(acm_tty_driver);
17431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	put_tty_driver(acm_tty_driver);
17441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init);
17471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit);
17481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17496e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_AUTHOR(DRIVER_AUTHOR);
17506e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DESCRIPTION(DRIVER_DESC);
17511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
1752e766aeb882b41355d8732cf49aa9412baef852c5Scott James RemnantMODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);
1753