cdc-acm.c revision 6abff5dc4d5a2c90e597137ce8987e7fd439259b
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	if (acm->dev) {
54310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_get_interface(acm->control);
54410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm_set_control(acm, acm->ctrlout = 0);
54510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_kill_urb(acm->ctrlurb);
54610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		for (i = 0; i < ACM_NW; i++)
54710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			usb_kill_urb(acm->wb[i].urb);
548dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold		for (i = 0; i < acm->rx_buflimit; i++)
549088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			usb_kill_urb(acm->read_urbs[i]);
55010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm->control->needs_remote_wakeup = 0;
55110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_put_interface(acm->control);
55210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
55310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
55410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
55510077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_tty_hangup(struct tty_struct *tty)
55610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
55710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct acm *acm = tty->driver_data;
55810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_hangup(&acm->port);
5595dc2470c602da8851907ec18942cd876c3b4ecc1Havard Skinnemoen	mutex_lock(&open_mutex);
5604e608671674b62e97166f903830d5553e37970e8Arnd Bergmann	acm_port_down(acm);
5615dc2470c602da8851907ec18942cd876c3b4ecc1Havard Skinnemoen	mutex_unlock(&open_mutex);
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;
5725dc2470c602da8851907ec18942cd876c3b4ecc1Havard Skinnemoen
5735dc2470c602da8851907ec18942cd876c3b4ecc1Havard Skinnemoen	mutex_lock(&open_mutex);
574051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	if (tty_port_close_start(&acm->port, tty, filp) == 0) {
575051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		if (!acm->dev) {
576051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			tty_port_tty_set(&acm->port, NULL);
577051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			acm_tty_unregister(acm);
578051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			tty->driver_data = NULL;
579051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		}
580051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		mutex_unlock(&open_mutex);
581051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		return;
582051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	}
5834e608671674b62e97166f903830d5553e37970e8Arnd Bergmann	acm_port_down(acm);
58410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_close_end(&acm->port, tty);
58510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, NULL);
5865dc2470c602da8851907ec18942cd876c3b4ecc1Havard Skinnemoen	mutex_unlock(&open_mutex);
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5896e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_write(struct tty_struct *tty,
5906e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					const unsigned char *buf, int count)
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int stat;
594884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
595884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
596884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
597884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!count)
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6035e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
604a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold
605884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
6066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	wbn = acm_wb_alloc(acm);
6076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (wbn < 0) {
608884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
609884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
610884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
611884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
613884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	count = (count > acm->writesize) ? acm->writesize : count;
6145e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
615884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	memcpy(wb->buf, buf, count);
616884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb->len = count;
617884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	stat = acm_write_start(acm, wbn);
6206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (stat < 0)
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return stat;
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty)
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
630884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
631884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * Do not let the line discipline to know that we have a reserve,
632884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * or it might get too enthusiastic.
633884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
634934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	return acm_wb_is_avail(acm) ? acm->writesize : 0;
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty)
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
64123198fda7182969b619613a555f8645fdc3dc334Alan Cox		return 0;
642884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
643884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * This is inaccurate (overcounts), but it works.
644884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
64586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty)
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
651088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
654088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
655088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	spin_lock_irq(&acm->read_lock);
656088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm->throttle_req = 1;
657088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	spin_unlock_irq(&acm->read_lock);
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty)
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
663088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	unsigned int was_throttled;
664088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
667088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
668088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	spin_lock_irq(&acm->read_lock);
669088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	was_throttled = acm->throttled;
670088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm->throttled = 0;
671088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	acm->throttle_req = 0;
672088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	spin_unlock_irq(&acm->read_lock);
673088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold
674088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold	if (was_throttled)
675088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		acm_submit_read_urbs(acm, GFP_KERNEL);
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6789e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state)
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6819e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	int retval;
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6839e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox		return -EINVAL;
6849e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	retval = acm_send_break(acm, state ? 0xffff : 0);
6859e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	if (retval < 0)
686a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev, "%s - send break failed\n",
687a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold								__func__);
6889e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	return retval;
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
69160b33c133ca0b7c0b6072c87234b63fee6e80558Alan Coxstatic int acm_tty_tiocmget(struct tty_struct *tty)
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TIOCM_CTS;
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
70620b9d17715017ae4dd4ec87fabc36d33b9de708eAlan Coxstatic int acm_tty_tiocmset(struct tty_struct *tty,
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    unsigned int set, unsigned int clear)
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int newctrl;
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = acm->ctrlout;
7166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
7176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
7196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = (newctrl & ~clear) | set;
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->ctrlout == newctrl)
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return acm_set_control(acm, acm->ctrlout = newctrl);
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7286caa76b7786891b42b66a0e61e2c2fff2c884620Alan Coxstatic int acm_tty_ioctl(struct tty_struct *tty,
7296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					unsigned int cmd, unsigned long arg)
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7394c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = {
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 50, 75, 110, 134, 150, 200, 300, 600,
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1200, 1800, 2400, 4800, 9600, 19200, 38400,
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	57600, 115200, 230400, 460800, 500000, 576000,
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	921600, 1000000, 1152000, 1500000, 2000000,
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	2500000, 3000000, 3500000, 4000000
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7474c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = {
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7, 8
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7516e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic void acm_tty_set_termios(struct tty_struct *tty,
7526e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						struct ktermios *termios_old)
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
755606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox	struct ktermios *termios = tty->termios;
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_line_coding newline;
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl = acm->ctrlout;
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7629b80fee149a875a6292b2556ab2c64dc7ab7d6f5Alan Cox	newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bParityType = termios->c_cflag & PARENB ?
7656e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & PARODD ? 1 : 2) +
7666e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & CMSPAR ? 2 : 0) : 0;
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
7686e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	/* FIXME: Needs to clear unsupported bits in the termios */
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!newline.dwDTERate) {
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newline.dwDTERate = acm->line.dwDTERate;
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newctrl &= ~ACM_CTRL_DTR;
7746e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	} else
7756e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl |=  ACM_CTRL_DTR;
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (newctrl != acm->ctrlout)
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_control(acm, acm->ctrlout = newctrl);
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(&acm->line, &newline, sizeof newline)) {
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&acm->line, &newline, sizeof newline);
782a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
783a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__,
784a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			le32_to_cpu(newline.dwDTERate),
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bCharFormat, newline.bParityType,
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bDataBits);
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_line(acm, &acm->line);
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines.
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
795830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */
796884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm)
797884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
798884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
799884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
800a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
801884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
8026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
803997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
804884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
805884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
806830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm)
807830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{
808830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
809dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	int i;
810830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
811dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	for (i = 0; i < acm->rx_buflimit; i++)
812997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		usb_free_coherent(usb_dev, acm->readsize,
813088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			  acm->read_buffers[i].base, acm->read_buffers[i].dma);
814830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum}
815830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
816884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */
817884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm)
818884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
819884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
820884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
821884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
82286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
823997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
824884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		    &wb->dmah);
825884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->buf) {
826884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			while (i != 0) {
827884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--i;
828884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--wb;
829997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack				usb_free_coherent(acm->dev, acm->writesize,
830884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				    wb->buf, wb->dmah);
831884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			}
832884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -ENOMEM;
833884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
834884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
835884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return 0;
836884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
837884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
83810077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic int acm_probe(struct usb_interface *intf,
83910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		     const struct usb_device_id *id)
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_union_desc *union_header = NULL;
842c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_cdc_country_functional_desc *cfd = NULL;
843c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell	unsigned char *buffer = intf->altsetting->extra;
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buflen = intf->altsetting->extralen;
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *control_interface;
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *data_interface;
847a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epctrl = NULL;
848a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epread = NULL;
849a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epwrite = NULL;
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor;
8536e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	int ctrlsize, readsize;
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *buf;
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 ac_management_function = 0;
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 call_management_function = 0;
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int call_interface_num = -1;
858fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	int data_interface_num = -1;
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long quirks;
86086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int num_rx_buf;
86161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
862a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	int combined_interfaces = 0;
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
86486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* normal quirks */
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	quirks = (unsigned long)id->driver_info;
86686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
86786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum
86886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* handle quirks deadly to normal probing*/
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (quirks == NO_UNION_NORMAL) {
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, 1);
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, 0);
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto skip_normal_probe;
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8746e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* normal probing*/
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
8779908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "Weird descriptor references\n");
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buflen) {
882577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray		if (intf->cur_altsetting->endpoint &&
883577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray				intf->cur_altsetting->endpoint->extralen &&
8846e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				intf->cur_altsetting->endpoint->extra) {
8856e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
8866e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Seeking extra descriptors on endpoint\n");
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buflen = intf->cur_altsetting->endpoint->extralen;
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = intf->cur_altsetting->endpoint->extra;
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
8909908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev,
8919908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman				"Zero length descriptor references\n");
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (buflen > 0) {
8976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (buffer[1] != USB_DT_CS_INTERFACE) {
8989908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev, "skipping garbage\n");
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto next_desc;
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		switch (buffer[2]) {
9036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_UNION_TYPE: /* we've found it */
9046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (union_header) {
9056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "More than one "
9066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"union descriptor, skipping ...\n");
9076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				goto next_desc;
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			union_header = (struct usb_cdc_union_desc *)buffer;
9106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
9126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			cfd = (struct usb_cdc_country_functional_desc *)buffer;
9136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_HEADER_TYPE: /* maybe check version */
9156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break; /* for now we ignore it */
9166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_ACM_TYPE:
9176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			ac_management_function = buffer[3];
9186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_CALL_MANAGEMENT_TYPE:
9206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_management_function = buffer[3];
9216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_interface_num = buffer[4];
922ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby			if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
9236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
9246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		default:
9266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			/* there are LOTS more CDC descriptors that
9276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 * could legitimately be found here.
9286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 */
9296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "Ignoring descriptor: "
9306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"type %02x, length %d\n",
9316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					buffer[2], buffer[0]);
9326e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9336e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc:
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buflen -= buffer[0];
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer += buffer[0];
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!union_header) {
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (call_interface_num > 0) {
9416e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
942fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter			/* quirks for Droids MuIn LCD */
943fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter			if (quirks & NO_DATA_INTERFACE)
944fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter				data_interface = usb_ifnum_to_if(usb_dev, 0);
945fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter			else
946fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter				data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = intf;
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
949a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
950a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_dbg(&intf->dev,"No union descriptor, giving up\n");
951a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -ENODEV;
952a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			} else {
953a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
954a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				combined_interfaces = 1;
955a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				control_interface = data_interface = intf;
956a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				goto look_for_collapsed_interface;
957a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			}
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!control_interface || !data_interface) {
9636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "no interfaces\n");
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9676e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface_num != call_interface_num)
9696e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
971a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (control_interface == data_interface) {
972a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* some broken devices designed for windows work this way */
973a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
974a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		combined_interfaces = 1;
975a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* a popular other OS doesn't use it */
976a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		quirks |= NO_CAP_LINE;
977a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
978a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
979a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -EINVAL;
980a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
981a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukumlook_for_collapsed_interface:
982a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		for (i = 0; i < 3; i++) {
983a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			struct usb_endpoint_descriptor *ep;
984a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			ep = &data_interface->cur_altsetting->endpoint[i].desc;
985a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
986a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (usb_endpoint_is_int_in(ep))
987a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epctrl = ep;
988a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_out(ep))
989a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epwrite = ep;
990a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_in(ep))
991a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epread = ep;
992a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else
993a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -EINVAL;
994a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
995a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (!epctrl || !epread || !epwrite)
996a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -ENODEV;
997a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		else
998a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			goto made_compressed_probe;
999a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	}
1000a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe:
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*workaround for switched interfaces */
10046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (data_interface->cur_altsetting->desc.bInterfaceClass
10056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						!= CDC_DATA_INTERFACE_TYPE) {
10066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (control_interface->cur_altsetting->desc.bInterfaceClass
10076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						== CDC_DATA_INTERFACE_TYPE) {
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_interface *t;
10096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
10106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Your device has switched interfaces.\n");
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = control_interface;
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = data_interface;
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = t;
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
101874da5d68a54d66667664fbe233ededab2376a070Alan Stern
101974da5d68a54d66667664fbe233ededab2376a070Alan Stern	/* Accept probe requests only for the control interface */
1020a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && intf != control_interface)
102174da5d68a54d66667664fbe233ededab2376a070Alan Stern		return -ENODEV;
10226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
1023a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1024a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* valid in this context */
10256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "The data interface isn't available\n");
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epread = &data_interface->cur_altsetting->endpoint[0].desc;
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* workaround for switched endpoints */
103945aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino	if (!usb_endpoint_dir_in(epread)) {
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* descriptors are swapped */
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_endpoint_descriptor *t;
10426e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev,
10436e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			"The data interface has switched endpoints\n");
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = epread;
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epread = epwrite;
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epwrite = t;
10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1048a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukummade_compressed_probe:
1049a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&intf->dev, "interfaces are valid\n");
10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (minor == ACM_TTY_MINORS) {
10539908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "no more free acm devices\n");
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10576e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
10586e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm == NULL) {
1059255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail;
10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
106329cc88979a8818cd8c5019426e945aed118b400eKuninori Morimoto	ctrlsize = usb_endpoint_maxp(epctrl);
106429cc88979a8818cd8c5019426e945aed118b400eKuninori Morimoto	readsize = usb_endpoint_maxp(epread) *
10656e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(quirks == SINGLE_RX_URB ? 1 : 2);
1066a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	acm->combined_interfaces = combined_interfaces;
106729cc88979a8818cd8c5019426e945aed118b400eKuninori Morimoto	acm->writesize = usb_endpoint_maxp(epwrite) * 20;
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->control = control_interface;
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->data = data_interface;
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->minor = minor;
10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = usb_dev;
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_caps = ac_management_function;
1073a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (quirks & NO_CAP_LINE)
1074a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlsize = ctrlsize;
10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->readsize = readsize;
107786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->rx_buflimit = num_rx_buf;
1078c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&acm->work, acm_softint);
1079884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_init(&acm->write_lock);
108061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock_init(&acm->read_lock);
10811365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_init(&acm->mutex);
108261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
1083cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum	acm->is_int_ep = usb_endpoint_xfer_int(epread);
1084cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum	if (acm->is_int_ep)
1085cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum		acm->bInterval = epread->bInterval;
1086739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	tty_port_init(&acm->port);
1087739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	acm->port.ops = &acm_port_ops;
10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1089997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buf) {
1091255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail2;
10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_buffer = buf;
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1096884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (acm_write_buffers_alloc(acm) < 0) {
1097255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail4;
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->ctrlurb) {
1103255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail5;
11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
110686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
1107088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		struct acm_rb *rb = &(acm->read_buffers[i]);
1108088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		struct urb *urb;
110961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
111074f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
111174f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold								&rb->dma);
111274f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		if (!rb->base) {
111374f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold			dev_err(&intf->dev, "out of memory "
111474f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold					"(read bufs usb_alloc_coherent)\n");
111574f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold			goto alloc_fail6;
111674f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		}
1117088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		rb->index = i;
1118088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		rb->instance = acm;
111974f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold
1120088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		urb = usb_alloc_urb(0, GFP_KERNEL);
1121088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		if (!urb) {
1122255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold			dev_err(&intf->dev,
11236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"out of memory (read urbs usb_alloc_urb)\n");
1124c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin			goto alloc_fail6;
112561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
1126088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1127088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		urb->transfer_dma = rb->dma;
1128088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		if (acm->is_int_ep) {
1129088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			usb_fill_int_urb(urb, acm->dev,
1130088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 acm->rx_endpoint,
1131088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 rb->base,
1132088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 acm->readsize,
1133088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 acm_read_bulk_callback, rb,
1134088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					 acm->bInterval);
1135088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		} else {
1136088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold			usb_fill_bulk_urb(urb, acm->dev,
1137088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					  acm->rx_endpoint,
1138088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					  rb->base,
1139088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					  acm->readsize,
1140088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold					  acm_read_bulk_callback, rb);
1141088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		}
114261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
1143088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		acm->read_urbs[i] = urb;
1144088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		__set_bit(i, &acm->read_urbs_free);
114561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
11466e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++) {
1147e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		struct acm_wb *snd = &(acm->wb[i]);
1148e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
11496e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
11506e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (snd->urb == NULL) {
1151255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold			dev_err(&intf->dev,
115259d7fec7c6908604862658a3679ac44c2c3eea44Johan Hovold				"out of memory (write urbs usb_alloc_urb)\n");
115374f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold			goto alloc_fail7;
1154e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		}
1155e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
11565186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		if (usb_endpoint_xfer_int(epwrite))
11575186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_int_urb(snd->urb, usb_dev,
11585186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
11595186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
11605186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		else
11615186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_bulk_urb(snd->urb, usb_dev,
11625186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
11635186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd);
1164e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1165e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->instance = acm;
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11686e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_set_intfdata(intf, acm);
1169c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1170c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1171c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (i < 0)
117274f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		goto alloc_fail7;
1173c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1174c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (cfd) { /* export the country data */
1175c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1176c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (!acm->country_codes)
1177c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1178c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_code_size = cfd->bLength - 4;
11796e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
11806e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox							cfd->bLength - 4);
1181c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_rel_date = cfd->iCountryCodeRelDate;
1182c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1183c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1184c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1185c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1186c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1187c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1188c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
11896e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		i = device_create_file(&intf->dev,
11906e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						&dev_attr_iCountryCodeRelDate);
1191c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1192c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin			device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
1193c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1194c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1195c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1196c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
1197c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1198c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries:
11996e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_fill_int_urb(acm->ctrlurb, usb_dev,
1200a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1201a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1202a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 /* works around buggy devices */
1203a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 epctrl->bInterval ? epctrl->bInterval : 0xff);
12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
12081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_control(acm, acm->ctrlout);
12101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.dwDTERate = cpu_to_le32(9600);
12121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.bDataBits = 8;
12131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_line(acm, &acm->line);
12141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_driver_claim_interface(&acm_driver, data_interface, acm);
1216672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	usb_set_intfdata(data_interface, acm);
12171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
121883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_get_intf(control_interface);
121983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_table[minor] = acm;
12221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1223c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return 0;
122474f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovoldalloc_fail7:
1225e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
1226e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
1227c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Linalloc_fail6:
122886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
1229088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		usb_free_urb(acm->read_urbs[i]);
123074f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold	acm_read_buffers_free(acm);
12311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_free_urb(acm->ctrlurb);
12321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5:
1233884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
12341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4:
1235997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
12361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2:
12371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acm);
12381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail:
12391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOMEM;
12401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12421365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm)
12431365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
12441365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int i;
1245a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold
1246a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&acm->control->dev, "%s\n", __func__);
12471365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12481365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_kill_urb(acm->ctrlurb);
12496e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++)
1250e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_kill_urb(acm->wb[i].urb);
12511365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
1252088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		usb_kill_urb(acm->read_urbs[i]);
12531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	cancel_work_sync(&acm->work);
12551365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
12561365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
12571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf)
12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
126110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
12621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1263672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	/* sibling interface is already cleaning up */
1264672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	if (!acm)
126586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum		return;
1266672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell
1267672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	mutex_lock(&open_mutex);
12686e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm->country_codes) {
126974da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
127074da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_wCountryCodes);
127174da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
127274da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_iCountryCodeRelDate);
1273c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
127474da5d68a54d66667664fbe233ededab2376a070Alan Stern	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = NULL;
127686067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->control, NULL);
127786067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->data, NULL);
12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12791365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	stop_data_traffic(acm);
12801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1281884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
1282997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
1283997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack			  acm->ctrl_dma);
1284830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1286a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!acm->combined_interfaces)
1287a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		usb_driver_release_interface(&acm_driver, intf == acm->control ?
1288830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum					acm->data : acm->control);
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
129010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count == 0) {
129183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		acm_tty_unregister(acm);
12924186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12964186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
129710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
129810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (tty) {
129910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		tty_hangup(tty);
130010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		tty_kref_put(tty);
130110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1304357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
13051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message)
13061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
130811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
130911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
13105b1b0b812a7b1a5b968c5d06d90d1cb88621b941Alan Stern	if (PMSG_IS_AUTO(message)) {
131111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		int b;
131211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
1313088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		spin_lock_irq(&acm->write_lock);
1314088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		b = acm->transmitting;
1315088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		spin_unlock_irq(&acm->write_lock);
131611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (b)
131711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			return -EBUSY;
131811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
131911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
132011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
132111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock(&acm->write_lock);
132211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count++;
132311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock(&acm->write_lock);
132411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
13251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
132611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
13271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
13281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	/*
13291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we treat opened interfaces differently,
13301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we must guard against open
13311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	*/
13321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
13331365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
133410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count)
13351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		stop_data_traffic(acm);
13361365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13371365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
13381365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return 0;
13391365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
13401365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13411365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf)
13421365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13431365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
134497d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	struct acm_wb *wb;
13451365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int rv = 0;
134611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
13471365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
134811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
134911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->susp_count -= 1;
135011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count;
135111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
135211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
135311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
13541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
13551365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13561365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
135710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count) {
13581365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
135997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum
136097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		spin_lock_irq(&acm->write_lock);
136197d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		if (acm->delayed_wb) {
136297d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			wb = acm->delayed_wb;
136397d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			acm->delayed_wb = NULL;
136497d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			spin_unlock_irq(&acm->write_lock);
1365f0730924e9e32bb8935c60040a26d94179355088Oliver Neukum			acm_start_wb(acm, wb);
136697d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		} else {
136797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			spin_unlock_irq(&acm->write_lock);
136897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		}
136997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum
137097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		/*
137197d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 * delayed error checking because we must
137297d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 * do the write path at all cost
137397d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 */
13741365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		if (rv < 0)
137511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			goto err_out;
13761365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
1377088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold		rv = acm_submit_read_urbs(acm, GFP_NOIO);
13781365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	}
13791365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13801365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out:
13811365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
13821365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return rv;
13831365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
1384357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum
1385a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavrastatic int acm_reset_resume(struct usb_interface *intf)
1386a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra{
1387a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	struct acm *acm = usb_get_intfdata(intf);
1388a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	struct tty_struct *tty;
1389a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra
1390a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	mutex_lock(&acm->mutex);
1391a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	if (acm->port.count) {
1392a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		tty = tty_port_tty_get(&acm->port);
1393a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		if (tty) {
1394a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra			tty_hangup(tty);
1395a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra			tty_kref_put(tty);
1396a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		}
1397a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	}
1398a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	mutex_unlock(&acm->mutex);
1399a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	return acm_resume(intf);
1400a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra}
1401a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra
1402357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */
1403c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
1404c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor#define NOKIA_PCSUITE_ACM_INFO(x) \
1405c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1406c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1407c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_CDC_ACM_PROTO_VENDOR)
1408c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
14094035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray#define SAMSUNG_PCSUITE_ACM_INFO(x) \
14104035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
14114035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
14124035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_CDC_ACM_PROTO_VENDOR)
14134035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray
14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
14151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure.
14161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14186ef4852b1326301f6e9657e99b2c3221be1a3a44Németh Mártonstatic const struct usb_device_id acm_ids[] = {
14191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* quirky and broken devices */
14201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
14211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1423b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1424b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1425b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	},
14260f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	{ USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
14270f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14280f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	},
14298753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
14308753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14318753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	},
143291a9c9214e34c364bf15406aadb922787ae7129bChris Malley	{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
143391a9c9214e34c364bf15406aadb922787ae7129bChris Malley	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
143491a9c9214e34c364bf15406aadb922787ae7129bChris Malley	},
14357abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	{ USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
14367abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	.driver_info = SINGLE_RX_URB,
14377abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	},
143886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
143986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
144086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	},
14413dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
14423dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
14433dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	},
14449be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
14459be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14469be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	},
14476149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	{ USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
14486149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14496149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	},
1450c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	{ USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1451c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1452c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	},
1453c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	{ USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1454c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1455c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	},
1456cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	{ USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1457cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1458cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	},
1459155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1460155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	},
14616abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	/* Motorola H24 HSPA module: */
14626abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d91) }, /* modem                                */
14636abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d92) }, /* modem           + diagnostics        */
14646abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d93) }, /* modem + AT port                      */
14656abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d95) }, /* modem + AT port + diagnostics        */
14666abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d96) }, /* modem                         + NMEA */
14676abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d97) }, /* modem           + diagnostics + NMEA */
14686abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d99) }, /* modem + AT port               + NMEA */
14696abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa	{ USB_DEVICE(0x22b8, 0x2d9a) }, /* modem + AT port + diagnostics + NMEA */
14706abff5dc4d5a2c90e597137ce8987e7fd439259bKrzysztof Hałasa
1471c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	{ USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1472c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	.driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1473c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   data interface instead of
1474c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   communications interface.
1475c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   Maybe we should define a new
1476c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   quirk for this. */
1477c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	},
14781f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	{ USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
14791f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
14801f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	},
1481c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	{ USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1482c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1483c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	},
14849be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum
1485c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	/* Nokia S60 phones expose two ACM channels. The first is
1486c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * a modem and is picked up by the standard AT-command
1487c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * information below. The second is 'vendor-specific' but
1488c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * is treated as a serial device at the S60 end, so we want
1489c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * to expose it on Linux too. */
1490c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1491c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1492c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1493c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1494c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1495c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1496c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1497c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1498c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1499c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1500c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1501c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1502c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1503c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1504c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1505c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1506c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1507c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i  */
1508c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1509c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1510c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1511c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic &  */
1512c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1513c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1514c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1515c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1516c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1517c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1518c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1519c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1520c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1521c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB  */
1522c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1523c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1524c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1525c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1526c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1527c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1528c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1529c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3  */
1530c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1531c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1532c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
153383a4eae9aeed4a69e89e323a105e653ae06e7c1fPrzemo Firszt	{ NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
15344035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
15354035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
15364035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
15374035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
15384035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
15394035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
15404035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
15414035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
15424035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
15434035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
1544721d92fc6373dee15846216f9d178ec240ec0fd7Arvid Ephraim Picciani	{ NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
15454061fde2fa80f40cb27114f60500d38d0afcf350Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */
15464061fde2fa80f40cb27114f60500d38d0afcf350Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
15474035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
1548c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
154965e52f41fa944cef2e6d4222b8c54f46cc575214Denis Pershin	/* Support for Owen devices */
155065e52f41fa944cef2e6d4222b8c54f46cc575214Denis Pershin	{ USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */
155165e52f41fa944cef2e6d4222b8c54f46cc575214Denis Pershin
1552c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	/* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1553c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
15547c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby	/* Support Lego NXT using pbLua firmware */
1555ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby	{ USB_DEVICE(0x0694, 0xff00),
1556ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby	.driver_info = NOT_A_MODEM,
15577893afc035590383a14b176c1497cba984276ef4Otavio Salvador	},
15587c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby
1559fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	/* Support for Droids MuIn LCD */
1560fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	{ USB_DEVICE(0x04d8, 0x000b),
1561fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	.driver_info = NO_DATA_INTERFACE,
1562fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter	},
1563fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter
15645b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes	/* control interfaces without any protocol set */
15655b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15665b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes		USB_CDC_PROTO_NONE) },
15675b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes
15681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* control interfaces with various AT-command sets */
15691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_V25TER) },
15711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101) },
15731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
15751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_GSM) },
15771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15786e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		USB_CDC_ACM_PROTO_AT_3G) },
15791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
15801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_CDMA) },
15811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
15831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
15841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15856e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DEVICE_TABLE(usb, acm_ids);
15861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = {
15881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"cdc_acm",
15891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	acm_probe,
15901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	acm_disconnect,
1591357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
15921365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.suspend =	acm_suspend,
15931365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.resume =	acm_resume,
1594a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	.reset_resume =	acm_reset_resume,
1595357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
15961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	acm_ids,
1597357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
15981365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.supports_autosuspend = 1,
1599357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
16001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
16031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures.
16041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
16051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1606b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = {
16071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			acm_tty_open,
16081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		acm_tty_close,
160910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	.hangup =		acm_tty_hangup,
16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		acm_tty_write,
16111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		acm_tty_write_room,
16121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		acm_tty_ioctl,
16131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		acm_tty_throttle,
16141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		acm_tty_unthrottle,
16151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	acm_tty_chars_in_buffer,
16161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl =		acm_tty_break_ctl,
16171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios =		acm_tty_set_termios,
16181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmget =		acm_tty_tiocmget,
16191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmset =		acm_tty_tiocmset,
16201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
16231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit.
16241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
16251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void)
16271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
16301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm_tty_driver)
16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
16321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->owner = THIS_MODULE,
16331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->driver_name = "acm",
16341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->name = "ttyACM",
16351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->major = ACM_TTY_MAJOR,
16361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->minor_start = 0,
16371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
16381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
1639331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
16401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios = tty_std_termios;
16416e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
16426e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox								HUPCL | CLOCAL;
16431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(acm_tty_driver, &acm_ops);
16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = tty_register_driver(acm_tty_driver);
16461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
16471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
16481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
16491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&acm_driver);
16521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
16531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_unregister_driver(acm_tty_driver);
16541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
16551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1658a2c7b9353e8f782590852052fe2948692020147eJohan Hovold	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
16591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
16611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void)
16641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister(&acm_driver);
16661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_unregister_driver(acm_tty_driver);
16671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	put_tty_driver(acm_tty_driver);
16681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init);
16711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit);
16721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16736e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_AUTHOR(DRIVER_AUTHOR);
16746e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DESCRIPTION(DRIVER_DESC);
16751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
1676e766aeb882b41355d8732cf49aa9412baef852c5Scott James RemnantMODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);
1677