cdc-acm.c revision 4061fde2fa80f40cb27114f60500d38d0afcf350
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>
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
434186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h>
4410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#include <linux/uaccess.h>
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
46a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h>
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h>
4961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h>
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h"
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
53e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
54088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek, Johan Hovold"
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver;
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver;
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS];
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
614186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex);
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#define ACM_READY(acm)	(acm && acm->dev && acm->port.count)
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
65739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Coxstatic const struct tty_port_operations acm_port_ops = {
66739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox};
67739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages.
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
726e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_ctrl_msg(struct acm *acm, int request, int value,
736e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox							void *buf, int len)
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		request, USB_RT_ACM, value,
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm->control->altsetting[0].desc.bInterfaceNumber,
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf, len, 5000);
79a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&acm->control->dev,
80a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - rq 0x%02x, val %#x, len %#x, result %d\n",
81a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__, request, value, len, retval);
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval < 0 ? retval : 0;
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests.
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do...
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
96884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management.
97884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller.
98884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
99884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
100884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm)
101884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
102884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, wbn;
103884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
104884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
105e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wbn = 0;
106884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	i = 0;
107884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	for (;;) {
108884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb = &acm->wb[wbn];
109884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->use) {
110884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			wb->use = 1;
111884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return wbn;
112884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
11386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		wbn = (wbn + 1) % ACM_NW;
11486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		if (++i >= ACM_NW)
115884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -1;
116884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
117884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
118884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
119884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm)
120884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
121884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, n;
122e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	unsigned long flags;
123884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
12486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	n = ACM_NW;
125e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	spin_lock_irqsave(&acm->write_lock, flags);
1266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++)
12786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		n -= acm->wb[i].use;
128e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	spin_unlock_irqrestore(&acm->write_lock, flags);
129884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return n;
130884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
131884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
132884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
133ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips * Finish write. Caller must hold acm->write_lock
134884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
135e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic void acm_write_done(struct acm *acm, struct acm_wb *wb)
136884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
137e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wb->use = 0;
13811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting--;
13997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	usb_autopm_put_interface_async(acm->control);
140884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
141884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write.
14411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum *
14511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking
146884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
14711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
14811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb)
14911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
15011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rc;
15111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
15211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting++;
15311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
15411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer = wb->buf;
15511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_dma = wb->dmah;
15611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer_length = wb->len;
15711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->dev = acm->dev;
15811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
1596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
1606e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (rc < 0) {
161a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_err(&acm->data->dev,
162a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - usb_submit_urb(write bulk) failed: %d\n",
163a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__, rc);
16411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_write_done(acm, wb);
16511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
16611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	return rc;
16711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
16811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
169e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn)
170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
171884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
172934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	struct acm_wb *wb = &acm->wb[wbn];
173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int rc;
174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
175884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
176884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->dev) {
177934da4635c2d05cef474e5243ef05df95b2ad264David Brownell		wb->use = 0;
178884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
179884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return -ENODEV;
180884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
181884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
1825e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
183a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold							acm->susp_count);
18497d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	usb_autopm_get_interface_async(acm->control);
18511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->susp_count) {
18697d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		if (!acm->delayed_wb)
18797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			acm->delayed_wb = wb;
18897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		else
18997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			usb_autopm_put_interface_async(acm->control);
19011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
19111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return 0;	/* A white lie */
19211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
19311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
19411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
19511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rc = acm_start_wb(acm, wb);
196884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
197884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
198884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return rc;
19911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
200884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
201c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/*
202c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs
203c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */
204c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps
205c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
206c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
207c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
208c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
209c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
210c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->ctrl_caps);
211c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
212c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
213c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
214c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes
215c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
216c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
217c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
218c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
219c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
220c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	memcpy(buf, acm->country_codes, acm->country_code_size);
221c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return acm->country_code_size;
222c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
223c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
224c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
225c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
226c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date
227c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
230c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
231c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->country_rel_date);
233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
234884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
236884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */
2417d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb)
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = urb->context;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_notification *dr = urb->transfer_buffer;
24510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data;
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl;
248185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int retval;
249185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int status = urb->status;
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
251185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	switch (status) {
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* success */
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ECONNRESET:
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ENOENT:
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ESHUTDOWN:
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this urb is terminated, clean up */
259a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev,
260a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				"%s - urb shutting down with status: %d\n",
261a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				__func__, status);
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
264a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev,
265a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				"%s - nonzero urb status received: %d\n",
266a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				__func__, status);
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2737e7797e7f6f7bfab73fca02c65e40eaa5bb9000cJohan Hovold	usb_mark_last_busy(acm->dev);
2747e7797e7f6f7bfab73fca02c65e40eaa5bb9000cJohan Hovold
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = (unsigned char *)(dr + 1);
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (dr->bNotificationType) {
2776e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
278a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev, "%s - network connection: %d\n",
279a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold							__func__, dr->wValue);
2806e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		break;
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2826e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	case USB_CDC_NOTIFY_SERIAL_STATE:
2836e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		tty = tty_port_tty_get(&acm->port);
2846e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl = get_unaligned_le16(data);
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2866e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (tty) {
2876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (!acm->clocal &&
2886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
289a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				dev_dbg(&acm->control->dev,
290a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold					"%s - calling hangup\n", __func__);
2916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				tty_hangup(tty);
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			tty_kref_put(tty);
2946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2966e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		acm->ctrlin = newctrl;
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
298a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev,
299a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - input control lines: dcd%c dsr%c break%c "
300a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"ring%c framing%c parity%c overrun%c\n",
301a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__,
3026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
3036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
3046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
3056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
3066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
3076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
3086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	default:
312a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev,
313a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - unknown notification %d received: index %d "
314a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"len %d data0 %d data1 %d\n",
315a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__,
3166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dr->bNotificationType, dr->wIndex,
3176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dr->wLength, data[0], data[1]);
3186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		break;
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
3216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	retval = usb_submit_urb(urb, GFP_ATOMIC);
322185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	if (retval)
3231d9846e505febb71255b098910ace741433312b7Johan Hovold		dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
3241d9846e505febb71255b098910ace741433312b7Johan Hovold							__func__, retval);
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
327088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovoldstatic int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
329088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	int res;
330185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman
331088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (!test_and_clear_bit(index, &acm->read_urbs_free))
332088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		return 0;
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
334088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
335088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
336088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	res = usb_submit_urb(acm->read_urbs[index], mem_flags);
337088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (res) {
338088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		if (res != -EPERM) {
339088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			dev_err(&acm->data->dev,
340088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					"%s - usb_submit_urb failed: %d\n",
341088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					__func__, res);
342088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		}
343088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		set_bit(index, &acm->read_urbs_free);
344088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		return res;
34511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
347088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	return 0;
348088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold}
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
350088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovoldstatic int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
351088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold{
352088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	int res;
353088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	int i;
35461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
355088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	for (i = 0; i < acm->rx_buflimit; ++i) {
356088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		res = acm_submit_read_urb(acm, i, mem_flags);
357088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		if (res)
358088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			return res;
35986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	}
360088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
361088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	return 0;
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
364088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovoldstatic void acm_process_read_urb(struct acm *acm, struct urb *urb)
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
36610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
368088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (!urb->actual_length)
369ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		return;
37061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
37110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
372088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (!tty)
373088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		return;
37410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
375088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
376088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	tty_flip_buffer_push(tty);
37761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
37810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_kref_put(tty);
379088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold}
38010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
381088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovoldstatic void acm_read_bulk_callback(struct urb *urb)
382088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold{
383088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	struct acm_rb *rb = urb->context;
384088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	struct acm *acm = rb->instance;
385088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	unsigned long flags;
38661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
387088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
388088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					rb->index, urb->actual_length);
389088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	set_bit(rb->index, &acm->read_urbs_free);
39061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
391088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (!acm->dev) {
392088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
393088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		return;
394088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	}
395088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	usb_mark_last_busy(acm->dev);
39661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
397088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (urb->status) {
398088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
399088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold							__func__, urb->status);
400088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		return;
40161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
402088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm_process_read_urb(acm, urb);
403088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
404088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	/* throttle device if requested by tty */
40511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irqsave(&acm->read_lock, flags);
406088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm->throttled = acm->throttle_req;
407088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (!acm->throttled && !acm->susp_count) {
408088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		spin_unlock_irqrestore(&acm->read_lock, flags);
409088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
410088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	} else {
411088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		spin_unlock_irqrestore(&acm->read_lock, flags);
412088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	}
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */
4167d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb)
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
418cdc97792289179974af6dda781c855696358d307Ming Lei	struct acm_wb *wb = urb->context;
419e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	struct acm *acm = wb->instance;
420ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	unsigned long flags;
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4224fa4626cd43679dc62a73ee3e347665e761abc9cJohan Hovold	if (urb->status	|| (urb->actual_length != urb->transfer_buffer_length))
4234fa4626cd43679dc62a73ee3e347665e761abc9cJohan Hovold		dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
4241d9846e505febb71255b098910ace741433312b7Johan Hovold			__func__,
425e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->actual_length,
426e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->transfer_buffer_length,
427e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->status);
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
429ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_lock_irqsave(&acm->write_lock, flags);
430e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm_write_done(acm, wb);
431ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_unlock_irqrestore(&acm->write_lock, flags);
432884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (ACM_READY(acm))
433884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		schedule_work(&acm->work);
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
436c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work)
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
438c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct acm *acm = container_of(work, struct acm, work);
43910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
440e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
4411d9846e505febb71255b098910ace741433312b7Johan Hovold	dev_vdbg(&acm->data->dev, "%s\n", __func__);
4421d9846e505febb71255b098910ace741433312b7Johan Hovold
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
44510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
44615e5bee33ffc11d0e5c6f819a65e7881c5c407beJohan Hovold	if (!tty)
44715e5bee33ffc11d0e5c6f819a65e7881c5c407beJohan Hovold		return;
44810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_wakeup(tty);
44910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_kref_put(tty);
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp)
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
45942dd2aa6496a2e87e496aac5494d2e1d6096c85bThadeu Lima de Souza Cascardo	int rv = -ENODEV;
4604186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven
4614186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm = acm_table[tty->index];
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->dev)
4652b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum		goto out;
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rv = 0;
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
469a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&acm->control->dev, "%s\n", __func__);
470a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold
47128d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91David Engraf	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
47210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty->driver_data = acm;
47410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, tty);
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
47694409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	if (usb_autopm_get_interface(acm->control) < 0)
47794409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum		goto early_bail;
47811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	else
47911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->control->needs_remote_wakeup = 1;
4801365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
4811365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
48210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count++) {
4832b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum		mutex_unlock(&acm->mutex);
4841365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_autopm_put_interface(acm->control);
4852b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum		goto out;
48610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
4871365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->dev = acm->dev;
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
490a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_err(&acm->control->dev,
491a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - usb_submit_urb(ctrl irq) failed\n", __func__);
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto bail_out;
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
495ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
496ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	    (acm->ctrl_caps & USB_CDC_CAP_LINE))
49794d4c8919de3ae9e2e029ed121adfed43803bb5dJohan Hovold		goto bail_out;
49810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
49911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
501088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (acm_submit_read_urbs(acm, GFP_KERNEL))
502088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		goto bail_out;
503ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
5047af25b4b34a2439020d78da765a3bed0ff73f25cOliver Neukum	set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
50510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	rv = tty_port_block_til_ready(&acm->port, tty, filp);
5062b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum
5071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
5082b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukumout:
50994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rv;
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out:
51310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	acm->port.count--;
5141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
5152b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum	usb_autopm_put_interface(acm->control);
51694409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukumearly_bail:
51794409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
51810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, NULL);
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EIO;
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
52283ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm)
52383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{
524dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	int i;
52561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
52683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_unregister_device(acm_tty_driver, acm->minor);
52783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_put_intf(acm->control);
52883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	acm_table[acm->minor] = NULL;
52983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_free_urb(acm->ctrlurb);
530e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
531e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
532dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	for (i = 0; i < acm->rx_buflimit; i++)
533088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		usb_free_urb(acm->read_urbs[i]);
534c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	kfree(acm->country_codes);
53583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	kfree(acm);
53683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk}
53783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk
5384e608671674b62e97166f903830d5553e37970e8Arnd Bergmannstatic void acm_port_down(struct acm *acm)
53910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
540dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	int i;
541dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold
54210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	mutex_lock(&open_mutex);
54310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->dev) {
54410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_get_interface(acm->control);
54510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm_set_control(acm, acm->ctrlout = 0);
54610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_kill_urb(acm->ctrlurb);
54710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		for (i = 0; i < ACM_NW; i++)
54810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			usb_kill_urb(acm->wb[i].urb);
549dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold		for (i = 0; i < acm->rx_buflimit; i++)
550088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			usb_kill_urb(acm->read_urbs[i]);
55110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm->control->needs_remote_wakeup = 0;
55210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_put_interface(acm->control);
55310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
55410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	mutex_unlock(&open_mutex);
55510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
55610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
55710077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_tty_hangup(struct tty_struct *tty)
55810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
55910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct acm *acm = tty->driver_data;
56010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_hangup(&acm->port);
5614e608671674b62e97166f903830d5553e37970e8Arnd Bergmann	acm_port_down(acm);
56210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
56310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp)
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
56810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	/* Perform the closing process and see if we need to do the hardware
56910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	   shutdown */
570051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	if (!acm)
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
572051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	if (tty_port_close_start(&acm->port, tty, filp) == 0) {
573051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		mutex_lock(&open_mutex);
574051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		if (!acm->dev) {
575051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			tty_port_tty_set(&acm->port, NULL);
576051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			acm_tty_unregister(acm);
577051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			tty->driver_data = NULL;
578051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		}
579051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		mutex_unlock(&open_mutex);
580051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		return;
581051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	}
5824e608671674b62e97166f903830d5553e37970e8Arnd Bergmann	acm_port_down(acm);
58310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_close_end(&acm->port, tty);
58410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, NULL);
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_write(struct tty_struct *tty,
5886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					const unsigned char *buf, int count)
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int stat;
592884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
593884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
594884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
595884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!count)
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6015e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
602a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold
603884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
6046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	wbn = acm_wb_alloc(acm);
6056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (wbn < 0) {
606884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
607884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
608884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
609884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
611884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	count = (count > acm->writesize) ? acm->writesize : count;
6125e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
613884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	memcpy(wb->buf, buf, count);
614884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb->len = count;
615884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	stat = acm_write_start(acm, wbn);
6186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (stat < 0)
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return stat;
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty)
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
628884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
629884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * Do not let the line discipline to know that we have a reserve,
630884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * or it might get too enthusiastic.
631884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
632934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	return acm_wb_is_avail(acm) ? acm->writesize : 0;
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty)
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
63923198fda7182969b619613a555f8645fdc3dc334Alan Cox		return 0;
640884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
641884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * This is inaccurate (overcounts), but it works.
642884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
64386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty)
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
649088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
652088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
653088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	spin_lock_irq(&acm->read_lock);
654088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm->throttle_req = 1;
655088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	spin_unlock_irq(&acm->read_lock);
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty)
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
661088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	unsigned int was_throttled;
662088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
665088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
666088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	spin_lock_irq(&acm->read_lock);
667088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	was_throttled = acm->throttled;
668088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm->throttled = 0;
669088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm->throttle_req = 0;
670088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	spin_unlock_irq(&acm->read_lock);
671088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
672088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (was_throttled)
673088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		acm_submit_read_urbs(acm, GFP_KERNEL);
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6769e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state)
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6799e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	int retval;
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6819e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox		return -EINVAL;
6829e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	retval = acm_send_break(acm, state ? 0xffff : 0);
6839e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	if (retval < 0)
684a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev, "%s - send break failed\n",
685a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold								__func__);
6869e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	return retval;
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
68960b33c133ca0b7c0b6072c87234b63fee6e80558Alan Coxstatic int acm_tty_tiocmget(struct tty_struct *tty)
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TIOCM_CTS;
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
70420b9d17715017ae4dd4ec87fabc36d33b9de708eAlan Coxstatic int acm_tty_tiocmset(struct tty_struct *tty,
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    unsigned int set, unsigned int clear)
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int newctrl;
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = acm->ctrlout;
7146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
7156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
7176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = (newctrl & ~clear) | set;
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->ctrlout == newctrl)
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return acm_set_control(acm, acm->ctrlout = newctrl);
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7266caa76b7786891b42b66a0e61e2c2fff2c884620Alan Coxstatic int acm_tty_ioctl(struct tty_struct *tty,
7276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					unsigned int cmd, unsigned long arg)
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7374c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = {
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 50, 75, 110, 134, 150, 200, 300, 600,
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1200, 1800, 2400, 4800, 9600, 19200, 38400,
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	57600, 115200, 230400, 460800, 500000, 576000,
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	921600, 1000000, 1152000, 1500000, 2000000,
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	2500000, 3000000, 3500000, 4000000
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7454c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = {
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7, 8
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7496e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic void acm_tty_set_termios(struct tty_struct *tty,
7506e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						struct ktermios *termios_old)
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
753606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox	struct ktermios *termios = tty->termios;
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_line_coding newline;
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl = acm->ctrlout;
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7609b80fee149a875a6292b2556ab2c64dc7ab7d6f5Alan Cox	newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bParityType = termios->c_cflag & PARENB ?
7636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & PARODD ? 1 : 2) +
7646e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & CMSPAR ? 2 : 0) : 0;
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
7666e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	/* FIXME: Needs to clear unsupported bits in the termios */
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!newline.dwDTERate) {
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newline.dwDTERate = acm->line.dwDTERate;
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newctrl &= ~ACM_CTRL_DTR;
7726e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	} else
7736e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl |=  ACM_CTRL_DTR;
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (newctrl != acm->ctrlout)
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_control(acm, acm->ctrlout = newctrl);
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(&acm->line, &newline, sizeof newline)) {
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&acm->line, &newline, sizeof newline);
780a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
781a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__,
782a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			le32_to_cpu(newline.dwDTERate),
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bCharFormat, newline.bParityType,
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bDataBits);
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_line(acm, &acm->line);
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines.
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
793830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */
794884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm)
795884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
796884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
797884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
798a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
799884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
8006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
801997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
802884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
803884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
804830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm)
805830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{
806830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
807dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	int i;
808830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
809dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	for (i = 0; i < acm->rx_buflimit; i++)
810997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		usb_free_coherent(usb_dev, acm->readsize,
811088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			  acm->read_buffers[i].base, acm->read_buffers[i].dma);
812830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum}
813830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
814884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */
815884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm)
816884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
817884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
818884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
819884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
82086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
821997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
822884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		    &wb->dmah);
823884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->buf) {
824884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			while (i != 0) {
825884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--i;
826884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--wb;
827997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack				usb_free_coherent(acm->dev, acm->writesize,
828884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				    wb->buf, wb->dmah);
829884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			}
830884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -ENOMEM;
831884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
832884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
833884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return 0;
834884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
835884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
83610077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic int acm_probe(struct usb_interface *intf,
83710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		     const struct usb_device_id *id)
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_union_desc *union_header = NULL;
840c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_cdc_country_functional_desc *cfd = NULL;
841c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell	unsigned char *buffer = intf->altsetting->extra;
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buflen = intf->altsetting->extralen;
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *control_interface;
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *data_interface;
845a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epctrl = NULL;
846a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epread = NULL;
847a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epwrite = NULL;
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor;
8516e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	int ctrlsize, readsize;
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *buf;
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 ac_management_function = 0;
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 call_management_function = 0;
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int call_interface_num = -1;
856fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	int data_interface_num = -1;
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long quirks;
85886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int num_rx_buf;
85961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
860a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	int combined_interfaces = 0;
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
86286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* normal quirks */
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	quirks = (unsigned long)id->driver_info;
86486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
86586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum
86686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* handle quirks deadly to normal probing*/
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (quirks == NO_UNION_NORMAL) {
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, 1);
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, 0);
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto skip_normal_probe;
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8726e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* normal probing*/
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
8759908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "Weird descriptor references\n");
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buflen) {
880577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray		if (intf->cur_altsetting->endpoint &&
881577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray				intf->cur_altsetting->endpoint->extralen &&
8826e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				intf->cur_altsetting->endpoint->extra) {
8836e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
8846e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Seeking extra descriptors on endpoint\n");
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buflen = intf->cur_altsetting->endpoint->extralen;
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = intf->cur_altsetting->endpoint->extra;
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
8889908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev,
8899908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman				"Zero length descriptor references\n");
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (buflen > 0) {
8956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (buffer[1] != USB_DT_CS_INTERFACE) {
8969908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev, "skipping garbage\n");
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto next_desc;
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		switch (buffer[2]) {
9016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_UNION_TYPE: /* we've found it */
9026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (union_header) {
9036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "More than one "
9046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"union descriptor, skipping ...\n");
9056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				goto next_desc;
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			union_header = (struct usb_cdc_union_desc *)buffer;
9086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
9106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			cfd = (struct usb_cdc_country_functional_desc *)buffer;
9116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_HEADER_TYPE: /* maybe check version */
9136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break; /* for now we ignore it */
9146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_ACM_TYPE:
9156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			ac_management_function = buffer[3];
9166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_CALL_MANAGEMENT_TYPE:
9186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_management_function = buffer[3];
9196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_interface_num = buffer[4];
920ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby			if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
9216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
9226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		default:
9246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			/* there are LOTS more CDC descriptors that
9256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 * could legitimately be found here.
9266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 */
9276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "Ignoring descriptor: "
9286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"type %02x, length %d\n",
9296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					buffer[2], buffer[0]);
9306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc:
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buflen -= buffer[0];
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer += buffer[0];
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!union_header) {
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (call_interface_num > 0) {
9396e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
940fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter			/* quirks for Droids MuIn LCD */
941fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter			if (quirks & NO_DATA_INTERFACE)
942fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter				data_interface = usb_ifnum_to_if(usb_dev, 0);
943fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter			else
944fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter				data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = intf;
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
947a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
948a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_dbg(&intf->dev,"No union descriptor, giving up\n");
949a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -ENODEV;
950a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			} else {
951a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
952a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				combined_interfaces = 1;
953a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				control_interface = data_interface = intf;
954a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				goto look_for_collapsed_interface;
955a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			}
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!control_interface || !data_interface) {
9616e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "no interfaces\n");
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9656e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface_num != call_interface_num)
9676e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
969a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (control_interface == data_interface) {
970a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* some broken devices designed for windows work this way */
971a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
972a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		combined_interfaces = 1;
973a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* a popular other OS doesn't use it */
974a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		quirks |= NO_CAP_LINE;
975a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
976a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
977a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -EINVAL;
978a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
979a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukumlook_for_collapsed_interface:
980a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		for (i = 0; i < 3; i++) {
981a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			struct usb_endpoint_descriptor *ep;
982a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			ep = &data_interface->cur_altsetting->endpoint[i].desc;
983a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
984a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (usb_endpoint_is_int_in(ep))
985a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epctrl = ep;
986a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_out(ep))
987a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epwrite = ep;
988a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_in(ep))
989a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epread = ep;
990a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else
991a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -EINVAL;
992a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
993a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (!epctrl || !epread || !epwrite)
994a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -ENODEV;
995a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		else
996a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			goto made_compressed_probe;
997a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	}
998a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe:
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*workaround for switched interfaces */
10026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (data_interface->cur_altsetting->desc.bInterfaceClass
10036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						!= CDC_DATA_INTERFACE_TYPE) {
10046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (control_interface->cur_altsetting->desc.bInterfaceClass
10056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						== CDC_DATA_INTERFACE_TYPE) {
10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_interface *t;
10076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
10086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Your device has switched interfaces.\n");
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = control_interface;
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = data_interface;
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = t;
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
101674da5d68a54d66667664fbe233ededab2376a070Alan Stern
101774da5d68a54d66667664fbe233ededab2376a070Alan Stern	/* Accept probe requests only for the control interface */
1018a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && intf != control_interface)
101974da5d68a54d66667664fbe233ededab2376a070Alan Stern		return -ENODEV;
10206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
1021a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1022a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* valid in this context */
10236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "The data interface isn't available\n");
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epread = &data_interface->cur_altsetting->endpoint[0].desc;
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* workaround for switched endpoints */
103745aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino	if (!usb_endpoint_dir_in(epread)) {
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* descriptors are swapped */
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_endpoint_descriptor *t;
10406e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev,
10416e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			"The data interface has switched endpoints\n");
10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = epread;
10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epread = epwrite;
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epwrite = t;
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1046a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukummade_compressed_probe:
1047a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&intf->dev, "interfaces are valid\n");
10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (minor == ACM_TTY_MINORS) {
10519908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "no more free acm devices\n");
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10556e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
10566e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm == NULL) {
1057255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail;
10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
10626e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	readsize = le16_to_cpu(epread->wMaxPacketSize) *
10636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(quirks == SINGLE_RX_URB ? 1 : 2);
1064a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	acm->combined_interfaces = combined_interfaces;
1065e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->control = control_interface;
10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->data = data_interface;
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->minor = minor;
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = usb_dev;
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_caps = ac_management_function;
1071a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (quirks & NO_CAP_LINE)
1072a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlsize = ctrlsize;
10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->readsize = readsize;
107586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->rx_buflimit = num_rx_buf;
1076c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&acm->work, acm_softint);
1077884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_init(&acm->write_lock);
107861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock_init(&acm->read_lock);
10791365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_init(&acm->mutex);
108061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
1081cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum	acm->is_int_ep = usb_endpoint_xfer_int(epread);
1082cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum	if (acm->is_int_ep)
1083cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum		acm->bInterval = epread->bInterval;
1084739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	tty_port_init(&acm->port);
1085739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	acm->port.ops = &acm_port_ops;
10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1087997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buf) {
1089255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail2;
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_buffer = buf;
10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1094884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (acm_write_buffers_alloc(acm) < 0) {
1095255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail4;
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->ctrlurb) {
1101255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail5;
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
110486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
1105088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		struct acm_rb *rb = &(acm->read_buffers[i]);
1106088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		struct urb *urb;
110761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
110874f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
110974f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold								&rb->dma);
111074f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		if (!rb->base) {
111174f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold			dev_err(&intf->dev, "out of memory "
111274f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold					"(read bufs usb_alloc_coherent)\n");
111374f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold			goto alloc_fail6;
111474f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		}
1115088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		rb->index = i;
1116088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		rb->instance = acm;
111774f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold
1118088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		urb = usb_alloc_urb(0, GFP_KERNEL);
1119088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		if (!urb) {
1120255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold			dev_err(&intf->dev,
11216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"out of memory (read urbs usb_alloc_urb)\n");
1122c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin			goto alloc_fail6;
112361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
1124088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1125088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		urb->transfer_dma = rb->dma;
1126088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		if (acm->is_int_ep) {
1127088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			usb_fill_int_urb(urb, acm->dev,
1128088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 acm->rx_endpoint,
1129088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 rb->base,
1130088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 acm->readsize,
1131088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 acm_read_bulk_callback, rb,
1132088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 acm->bInterval);
1133088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		} else {
1134088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			usb_fill_bulk_urb(urb, acm->dev,
1135088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					  acm->rx_endpoint,
1136088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					  rb->base,
1137088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					  acm->readsize,
1138088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					  acm_read_bulk_callback, rb);
1139088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		}
114061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
1141088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		acm->read_urbs[i] = urb;
1142088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		__set_bit(i, &acm->read_urbs_free);
114361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
11446e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++) {
1145e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		struct acm_wb *snd = &(acm->wb[i]);
1146e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
11476e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
11486e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (snd->urb == NULL) {
1149255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold			dev_err(&intf->dev,
115059d7fec7c6908604862658a3679ac44c2c3eea44Johan Hovold				"out of memory (write urbs usb_alloc_urb)\n");
115174f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold			goto alloc_fail7;
1152e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		}
1153e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
11545186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		if (usb_endpoint_xfer_int(epwrite))
11555186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_int_urb(snd->urb, usb_dev,
11565186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
11575186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
11585186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		else
11595186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_bulk_urb(snd->urb, usb_dev,
11605186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
11615186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd);
1162e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1163e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->instance = acm;
11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11666e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_set_intfdata(intf, acm);
1167c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1168c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1169c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (i < 0)
117074f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		goto alloc_fail7;
1171c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1172c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (cfd) { /* export the country data */
1173c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1174c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (!acm->country_codes)
1175c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1176c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_code_size = cfd->bLength - 4;
11776e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
11786e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox							cfd->bLength - 4);
1179c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_rel_date = cfd->iCountryCodeRelDate;
1180c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1181c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1182c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1183c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1184c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1185c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1186c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
11876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		i = device_create_file(&intf->dev,
11886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						&dev_attr_iCountryCodeRelDate);
1189c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1190c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin			device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
1191c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1192c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1193c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1194c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
1195c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1196c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries:
11976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_fill_int_urb(acm->ctrlurb, usb_dev,
1198a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1199a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1200a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 /* works around buggy devices */
1201a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 epctrl->bInterval ? epctrl->bInterval : 0xff);
12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_control(acm, acm->ctrlout);
12081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.dwDTERate = cpu_to_le32(9600);
12101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.bDataBits = 8;
12111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_line(acm, &acm->line);
12121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_driver_claim_interface(&acm_driver, data_interface, acm);
1214672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	usb_set_intfdata(data_interface, acm);
12151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
121683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_get_intf(control_interface);
121783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
12181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_table[minor] = acm;
12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1221c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return 0;
122274f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovoldalloc_fail7:
1223e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
1224e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
1225c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Linalloc_fail6:
122686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
1227088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		usb_free_urb(acm->read_urbs[i]);
122874f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold	acm_read_buffers_free(acm);
12291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_free_urb(acm->ctrlurb);
12301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5:
1231884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
12321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4:
1233997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
12341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2:
12351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acm);
12361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail:
12371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOMEM;
12381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12401365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm)
12411365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
12421365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int i;
1243a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold
1244a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&acm->control->dev, "%s\n", __func__);
12451365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12461365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_kill_urb(acm->ctrlurb);
12476e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++)
1248e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_kill_urb(acm->wb[i].urb);
12491365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
1250088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		usb_kill_urb(acm->read_urbs[i]);
12511365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12521365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	cancel_work_sync(&acm->work);
12531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
12541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf)
12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
125910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	/* sibling interface is already cleaning up */
1262672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	if (!acm)
126386067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum		return;
1264672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell
1265672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	mutex_lock(&open_mutex);
12666e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm->country_codes) {
126774da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
126874da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_wCountryCodes);
126974da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
127074da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_iCountryCodeRelDate);
1271c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
127274da5d68a54d66667664fbe233ededab2376a070Alan Stern	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
12731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = NULL;
127486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->control, NULL);
127586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->data, NULL);
12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12771365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	stop_data_traffic(acm);
12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1279884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
1280997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
1281997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack			  acm->ctrl_dma);
1282830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1284a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!acm->combined_interfaces)
1285a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		usb_driver_release_interface(&acm_driver, intf == acm->control ?
1286830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum					acm->data : acm->control);
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
128810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count == 0) {
128983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		acm_tty_unregister(acm);
12904186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12944186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
129510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
129610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (tty) {
129710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		tty_hangup(tty);
129810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		tty_kref_put(tty);
129910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1302357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
13031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message)
13041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
130611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
130711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
130865bfd2967c906ca322a4bb69a285fe0de8916ac6Alan Stern	if (message.event & PM_EVENT_AUTO) {
130911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		int b;
131011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
1311088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		spin_lock_irq(&acm->write_lock);
1312088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		b = acm->transmitting;
1313088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		spin_unlock_irq(&acm->write_lock);
131411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (b)
131511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			return -EBUSY;
131611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
131711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
131811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
131911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock(&acm->write_lock);
132011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count++;
132111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock(&acm->write_lock);
132211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
13231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
132411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
13251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
13261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	/*
13271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we treat opened interfaces differently,
13281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we must guard against open
13291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	*/
13301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
13311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
133210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count)
13331365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		stop_data_traffic(acm);
13341365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
13361365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return 0;
13371365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
13381365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13391365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf)
13401365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13411365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
134297d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	struct acm_wb *wb;
13431365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int rv = 0;
134411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
13451365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
134611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
134711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->susp_count -= 1;
134811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count;
134911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
135011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
135111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
13521365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
13531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
135510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count) {
13561365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
135797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum
135897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		spin_lock_irq(&acm->write_lock);
135997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		if (acm->delayed_wb) {
136097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			wb = acm->delayed_wb;
136197d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			acm->delayed_wb = NULL;
136297d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			spin_unlock_irq(&acm->write_lock);
1363f0730924e9e32bb8935c60040a26d94179355088Oliver Neukum			acm_start_wb(acm, wb);
136497d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		} else {
136597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			spin_unlock_irq(&acm->write_lock);
136697d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		}
136797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum
136897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		/*
136997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 * delayed error checking because we must
137097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 * do the write path at all cost
137197d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 */
13721365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		if (rv < 0)
137311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			goto err_out;
13741365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
1375088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		rv = acm_submit_read_urbs(acm, GFP_NOIO);
13761365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	}
13771365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13781365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out:
13791365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
13801365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return rv;
13811365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
1382357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum
1383a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavrastatic int acm_reset_resume(struct usb_interface *intf)
1384a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra{
1385a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	struct acm *acm = usb_get_intfdata(intf);
1386a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	struct tty_struct *tty;
1387a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra
1388a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	mutex_lock(&acm->mutex);
1389a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	if (acm->port.count) {
1390a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		tty = tty_port_tty_get(&acm->port);
1391a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		if (tty) {
1392a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra			tty_hangup(tty);
1393a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra			tty_kref_put(tty);
1394a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		}
1395a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	}
1396a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	mutex_unlock(&acm->mutex);
1397a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	return acm_resume(intf);
1398a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra}
1399a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra
1400357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */
1401c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
1402c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor#define NOKIA_PCSUITE_ACM_INFO(x) \
1403c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1404c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1405c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_CDC_ACM_PROTO_VENDOR)
1406c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
14074035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray#define SAMSUNG_PCSUITE_ACM_INFO(x) \
14084035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
14094035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
14104035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_CDC_ACM_PROTO_VENDOR)
14114035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray
14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
14131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure.
14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14166ef4852b1326301f6e9657e99b2c3221be1a3a44Németh Mártonstatic const struct usb_device_id acm_ids[] = {
14171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* quirky and broken devices */
14181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
14191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1421b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1422b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1423b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	},
14240f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	{ USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
14250f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14260f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	},
14278753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
14288753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14298753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	},
143091a9c9214e34c364bf15406aadb922787ae7129bChris Malley	{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
143191a9c9214e34c364bf15406aadb922787ae7129bChris Malley	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
143291a9c9214e34c364bf15406aadb922787ae7129bChris Malley	},
14337abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	{ USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
14347abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	.driver_info = SINGLE_RX_URB,
14357abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	},
143686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
143786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
143886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	},
14393dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
14403dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
14413dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	},
14429be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
14439be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14449be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	},
14456149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	{ USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
14466149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14476149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	},
1448c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	{ USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1449c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1450c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	},
1451c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	{ USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1452c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1453c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	},
1454cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	{ USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1455cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1456cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	},
1457155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1458155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	},
1459c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	{ USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1460c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	.driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1461c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   data interface instead of
1462c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   communications interface.
1463c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   Maybe we should define a new
1464c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   quirk for this. */
1465c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	},
14661f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	{ USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
14671f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
14681f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	},
1469c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	{ USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1470c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1471c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	},
14729be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum
1473c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	/* Nokia S60 phones expose two ACM channels. The first is
1474c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * a modem and is picked up by the standard AT-command
1475c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * information below. The second is 'vendor-specific' but
1476c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * is treated as a serial device at the S60 end, so we want
1477c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * to expose it on Linux too. */
1478c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1479c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1480c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1481c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1482c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1483c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1484c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1485c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1486c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1487c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1488c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1489c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1490c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1491c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1492c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1493c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1494c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1495c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i  */
1496c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1497c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1498c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1499c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic &  */
1500c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1501c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1502c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1503c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1504c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1505c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1506c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1507c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1508c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1509c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB  */
1510c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1511c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1512c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1513c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1514c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1515c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1516c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1517c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3  */
1518c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1519c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1520c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
152183a4eae9aeed4a69e89e323a105e653ae06e7c1fPrzemo Firszt	{ NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
15224035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
15234035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
15244035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
15254035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
15264035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
15274035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
15284035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
15294035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
15304035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
15314035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
1532721d92fc6373dee15846216f9d178ec240ec0fd7Arvid Ephraim Picciani	{ NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
15334061fde2fa80f40cb27114f60500d38d0afcf350Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */
15344061fde2fa80f40cb27114f60500d38d0afcf350Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
15354035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
1536c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
1537c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	/* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1538c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
15397c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby	/* Support Lego NXT using pbLua firmware */
1540ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby	{ USB_DEVICE(0x0694, 0xff00),
1541ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby	.driver_info = NOT_A_MODEM,
15427893afc035590383a14b176c1497cba984276ef4Otavio Salvador	},
15437c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby
1544fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	/* Support for Droids MuIn LCD */
1545fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	{ USB_DEVICE(0x04d8, 0x000b),
1546fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	.driver_info = NO_DATA_INTERFACE,
1547fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	},
1548fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter
15495b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes	/* control interfaces without any protocol set */
15505b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15515b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes		USB_CDC_PROTO_NONE) },
15525b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes
15531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* control interfaces with various AT-command sets */
15541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_V25TER) },
15561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101) },
15581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
15601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_GSM) },
15621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		USB_CDC_ACM_PROTO_AT_3G) },
15641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_CDMA) },
15661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
15681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
15691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15706e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DEVICE_TABLE(usb, acm_ids);
15711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = {
15731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"cdc_acm",
15741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	acm_probe,
15751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	acm_disconnect,
1576357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
15771365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.suspend =	acm_suspend,
15781365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.resume =	acm_resume,
1579a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	.reset_resume =	acm_reset_resume,
1580357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
15811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	acm_ids,
1582357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
15831365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.supports_autosuspend = 1,
1584357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
15851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
15861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
15881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures.
15891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
15901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1591b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = {
15921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			acm_tty_open,
15931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		acm_tty_close,
159410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	.hangup =		acm_tty_hangup,
15951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		acm_tty_write,
15961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		acm_tty_write_room,
15971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		acm_tty_ioctl,
15981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		acm_tty_throttle,
15991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		acm_tty_unthrottle,
16001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	acm_tty_chars_in_buffer,
16011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl =		acm_tty_break_ctl,
16021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios =		acm_tty_set_termios,
16031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmget =		acm_tty_tiocmget,
16041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmset =		acm_tty_tiocmset,
16051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
16081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit.
16091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void)
16121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
16141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
16151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm_tty_driver)
16161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
16171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->owner = THIS_MODULE,
16181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->driver_name = "acm",
16191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->name = "ttyACM",
16201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->major = ACM_TTY_MAJOR,
16211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->minor_start = 0,
16221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
16231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
1624331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
16251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios = tty_std_termios;
16266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
16276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox								HUPCL | CLOCAL;
16281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(acm_tty_driver, &acm_ops);
16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = tty_register_driver(acm_tty_driver);
16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
16321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
16331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
16341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&acm_driver);
16371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
16381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_unregister_driver(acm_tty_driver);
16391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
16401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
16411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1643a2c7b9353e8f782590852052fe2948692020147eJohan Hovold	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
16461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void)
16491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister(&acm_driver);
16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_unregister_driver(acm_tty_driver);
16521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	put_tty_driver(acm_tty_driver);
16531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init);
16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit);
16571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16586e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_AUTHOR(DRIVER_AUTHOR);
16596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DESCRIPTION(DRIVER_DESC);
16601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
1661e766aeb882b41355d8732cf49aa9412baef852c5Scott James RemnantMODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);
1662