cdc-acm.c revision 74f5e1babde76149c2bb35ca5dbf4d0b9b38f161
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>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB Abstract Control Model driver for USB modems and ISDN adapters
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Sponsored by SuSE
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG
31e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#undef VERBOSE_DEBUG
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
387af25b4b34a2439020d78da765a3bed0ff73f25cOliver Neukum#include <linux/serial.h>
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
424186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h>
4310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#include <linux/uaccess.h>
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
45a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h>
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h>
4861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h>
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h"
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
52e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
5361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver;
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver;
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS];
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
604186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex);
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#define ACM_READY(acm)	(acm && acm->dev && acm->port.count)
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
64739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Coxstatic const struct tty_port_operations acm_port_ops = {
65739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox};
66739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages.
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
716e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_ctrl_msg(struct acm *acm, int request, int value,
726e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox							void *buf, int len)
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		request, USB_RT_ACM, value,
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm->control->altsetting[0].desc.bInterfaceNumber,
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf, len, 5000);
78a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&acm->control->dev,
79a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - rq 0x%02x, val %#x, len %#x, result %d\n",
80a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__, request, value, len, retval);
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval < 0 ? retval : 0;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests.
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do...
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
95884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management.
96884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller.
97884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
98884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
99884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm)
100884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
101884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, wbn;
102884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
103884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
104e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wbn = 0;
105884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	i = 0;
106884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	for (;;) {
107884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		wb = &acm->wb[wbn];
108884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->use) {
109884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			wb->use = 1;
110884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return wbn;
111884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
11286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		wbn = (wbn + 1) % ACM_NW;
11386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		if (++i >= ACM_NW)
114884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -1;
115884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
116884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
117884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
118884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm)
119884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
120884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i, n;
121e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	unsigned long flags;
122884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
12386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	n = ACM_NW;
124e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	spin_lock_irqsave(&acm->write_lock, flags);
1256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++)
12686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		n -= acm->wb[i].use;
127e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	spin_unlock_irqrestore(&acm->write_lock, flags);
128884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return n;
129884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
130884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
131884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
132ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips * Finish write. Caller must hold acm->write_lock
133884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
134e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic void acm_write_done(struct acm *acm, struct acm_wb *wb)
135884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
136e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	wb->use = 0;
13711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting--;
13897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	usb_autopm_put_interface_async(acm->control);
139884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
140884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
141884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write.
14311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum *
14411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking
145884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */
14611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
14711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb)
14811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{
14911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int rc;
15011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
15111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->transmitting++;
15211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
15311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer = wb->buf;
15411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_dma = wb->dmah;
15511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->transfer_buffer_length = wb->len;
15611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	wb->urb->dev = acm->dev;
15711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
1586e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
1596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (rc < 0) {
160a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_err(&acm->data->dev,
161a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - usb_submit_urb(write bulk) failed: %d\n",
162a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__, rc);
16311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm_write_done(acm, wb);
16411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
16511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	return rc;
16611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum}
16711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
168e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn)
169884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
171934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	struct acm_wb *wb = &acm->wb[wbn];
172884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int rc;
173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
175884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (!acm->dev) {
176934da4635c2d05cef474e5243ef05df95b2ad264David Brownell		wb->use = 0;
177884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
178884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return -ENODEV;
179884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
180884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
1815e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
182a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold							acm->susp_count);
18397d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	usb_autopm_get_interface_async(acm->control);
18411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (acm->susp_count) {
18597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		if (!acm->delayed_wb)
18697d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			acm->delayed_wb = wb;
18797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		else
18897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			usb_autopm_put_interface_async(acm->control);
18911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
19011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		return 0;	/* A white lie */
19111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
19211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
19311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
19411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	rc = acm_start_wb(acm, wb);
195884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
196884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
197884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return rc;
19811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
199884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
200c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/*
201c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs
202c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */
203c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps
204c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
205c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
206c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
207c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
208c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
209c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->ctrl_caps);
210c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
211c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
212c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
213c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes
214c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
215c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
216c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
217c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
218c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
219c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	memcpy(buf, acm->country_codes, acm->country_code_size);
220c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return acm->country_code_size;
221c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
222c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
223c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
224c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
225c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date
226c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf)
227c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{
228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_interface *intf = to_usb_interface(dev);
229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
230c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
231c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return sprintf(buf, "%d", acm->country_rel_date);
232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum}
233884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
235884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/*
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */
2407d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb)
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = urb->context;
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_notification *dr = urb->transfer_buffer;
24410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl;
247185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int retval;
248185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	int status = urb->status;
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
250185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	switch (status) {
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* success */
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ECONNRESET:
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ENOENT:
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ESHUTDOWN:
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this urb is terminated, clean up */
258a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev,
259a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				"%s - urb shutting down with status: %d\n",
260a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				__func__, status);
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
263a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev,
264a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				"%s - nonzero urb status received: %d\n",
265a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				__func__, status);
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2727e7797e7f6f7bfab73fca02c65e40eaa5bb9000cJohan Hovold	usb_mark_last_busy(acm->dev);
2737e7797e7f6f7bfab73fca02c65e40eaa5bb9000cJohan Hovold
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = (unsigned char *)(dr + 1);
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (dr->bNotificationType) {
2766e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
277a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev, "%s - network connection: %d\n",
278a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold							__func__, dr->wValue);
2796e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		break;
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2816e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	case USB_CDC_NOTIFY_SERIAL_STATE:
2826e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		tty = tty_port_tty_get(&acm->port);
2836e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl = get_unaligned_le16(data);
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2856e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (tty) {
2866e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (!acm->clocal &&
2876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
288a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				dev_dbg(&acm->control->dev,
289a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold					"%s - calling hangup\n", __func__);
2906e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				tty_hangup(tty);
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			tty_kref_put(tty);
2936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		acm->ctrlin = newctrl;
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
297a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev,
298a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - input control lines: dcd%c dsr%c break%c "
299a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"ring%c framing%c parity%c overrun%c\n",
300a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__,
3016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
3026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
3036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
3046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
3056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
3066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
3076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	default:
311a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev,
312a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - unknown notification %d received: index %d "
313a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"len %d data0 %d data1 %d\n",
314a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__,
3156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dr->bNotificationType, dr->wIndex,
3166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dr->wLength, data[0], data[1]);
3176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		break;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
3206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	retval = usb_submit_urb(urb, GFP_ATOMIC);
321185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman	if (retval)
3221d9846e505febb71255b098910ace741433312b7Johan Hovold		dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
3231d9846e505febb71255b098910ace741433312b7Johan Hovold							__func__, retval);
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */
3277d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_read_bulk(struct urb *urb)
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
32961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
33061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv = urb->context;
33161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm *acm = rcv->instance;
33286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int status = urb->status;
333185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman
3345e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - status %d\n", __func__, status);
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
33611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (!ACM_READY(acm)) {
3371d9846e505febb71255b098910ace741433312b7Johan Hovold		dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
33911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
34011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_mark_last_busy(acm->dev);
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
34286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (status)
3431d9846e505febb71255b098910ace741433312b7Johan Hovold		dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
3441d9846e505febb71255b098910ace741433312b7Johan Hovold							__func__, status);
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
34661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = rcv->buffer;
34761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf->size = urb->actual_length;
34861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
34986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	if (likely(status == 0)) {
35086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
35111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->processing++;
35286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
35386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&buf->list, &acm->filled_read_bufs);
35486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
35586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	} else {
35686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* we drop the buffer due to an error */
35786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_lock(&acm->read_lock);
35886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add_tail(&rcv->list, &acm->spare_read_urbs);
35986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		list_add(&buf->list, &acm->spare_read_bufs);
36086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		spin_unlock(&acm->read_lock);
36186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		/* nevertheless the tasklet must be kicked unconditionally
36286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum		so the queue cannot dry up */
36386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	}
36411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (likely(!acm->susp_count))
36511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		tasklet_schedule(&acm->urb_task);
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm)
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = (void *)_acm;
37161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_rb *buf;
37210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
37361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	struct acm_ru *rcv;
374762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	unsigned long flags;
375ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	unsigned char throttled;
37611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
3775e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s\n", __func__);
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
37910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (!ACM_READY(acm)) {
380a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
381ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum		return;
38211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
383ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
384834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_lock_irqsave(&acm->throttle_lock, flags);
385ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	throttled = acm->throttle;
386834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum	spin_unlock_irqrestore(&acm->throttle_lock, flags);
38710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (throttled) {
388a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->data->dev, "%s - throttled\n", __func__);
38961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		return;
39011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
39161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
39210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
39310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
39461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer:
395762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
39661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	if (list_empty(&acm->filled_read_bufs)) {
397762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
39861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		goto urbs;
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
40061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	buf = list_entry(acm->filled_read_bufs.next,
40161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			 struct acm_rb, list);
40261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_del(&buf->list);
403762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
40461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
4055e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - processing buf 0x%p, size = %d\n",
406a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold						__func__, buf, buf->size);
40710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (tty) {
40810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		spin_lock_irqsave(&acm->throttle_lock, flags);
40910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		throttled = acm->throttle;
41010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		spin_unlock_irqrestore(&acm->throttle_lock, flags);
41110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		if (!throttled) {
41210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			tty_insert_flip_string(tty, buf->base, buf->size);
41310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			tty_flip_buffer_push(tty);
41410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		} else {
41510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			tty_kref_put(tty);
416a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			dev_dbg(&acm->data->dev, "%s - throttling noticed\n",
417a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold								__func__);
41810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			spin_lock_irqsave(&acm->read_lock, flags);
41910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			list_add(&buf->list, &acm->filled_read_bufs);
42010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			spin_unlock_irqrestore(&acm->read_lock, flags);
42110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			return;
42210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		}
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
425762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_lock_irqsave(&acm->read_lock, flags);
42661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	list_add(&buf->list, &acm->spare_read_bufs);
427762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski	spin_unlock_irqrestore(&acm->read_lock, flags);
42861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	goto next_buffer;
42961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
43061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs:
43110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_kref_put(tty);
43210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
43361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	while (!list_empty(&acm->spare_read_bufs)) {
434762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_lock_irqsave(&acm->read_lock, flags);
43561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		if (list_empty(&acm->spare_read_urbs)) {
43611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
437762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
43861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
43961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
44061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv = list_entry(acm->spare_read_urbs.next,
44161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_ru, list);
44261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&rcv->list);
443762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski		spin_unlock_irqrestore(&acm->read_lock, flags);
44461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
44561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		buf = list_entry(acm->spare_read_bufs.next,
44661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek				 struct acm_rb, list);
44761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_del(&buf->list);
44861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
44961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->buffer = buf;
45061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
451cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum		if (acm->is_int_ep)
4525186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_int_urb(rcv->urb, acm->dev,
4535186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 acm->rx_endpoint,
4545186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 buf->base,
4555186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					 acm->readsize,
456cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum					 acm_read_bulk, rcv, acm->bInterval);
4575186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		else
4585186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_bulk_urb(rcv->urb, acm->dev,
4595186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  acm->rx_endpoint,
4605186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  buf->base,
4615186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  acm->readsize,
4625186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev					  acm_read_bulk, rcv);
46361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_dma = buf->dma;
46461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
46561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
4666e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		/* This shouldn't kill the driver as unsuccessful URBs are
4676e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		   returned to the free-urbs-pool and resubmited ASAP */
46811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irqsave(&acm->read_lock, flags);
4696e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (acm->susp_count ||
4706e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
47161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&buf->list, &acm->spare_read_bufs);
47261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			list_add(&rcv->list, &acm->spare_read_urbs);
47311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			acm->processing = 0;
474762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski			spin_unlock_irqrestore(&acm->read_lock, flags);
47561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek			return;
47611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		} else {
47711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			spin_unlock_irqrestore(&acm->read_lock, flags);
4785e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold			dev_vdbg(&acm->data->dev,
479a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				"%s - sending urb 0x%p, rcv 0x%p, buf 0x%p\n",
480a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold				__func__, rcv->urb, rcv, buf);
48161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
48261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
48311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irqsave(&acm->read_lock, flags);
48411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->processing = 0;
48511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irqrestore(&acm->read_lock, flags);
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */
4897d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb)
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
491cdc97792289179974af6dda781c855696358d307Ming Lei	struct acm_wb *wb = urb->context;
492e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell	struct acm *acm = wb->instance;
493ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	unsigned long flags;
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4954fa4626cd43679dc62a73ee3e347665e761abc9cJohan Hovold	if (urb->status	|| (urb->actual_length != urb->transfer_buffer_length))
4964fa4626cd43679dc62a73ee3e347665e761abc9cJohan Hovold		dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
4971d9846e505febb71255b098910ace741433312b7Johan Hovold			__func__,
498e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->actual_length,
499e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->transfer_buffer_length,
500e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell			urb->status);
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
502ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_lock_irqsave(&acm->write_lock, flags);
503e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm_write_done(acm, wb);
504ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips	spin_unlock_irqrestore(&acm->write_lock, flags);
505884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (ACM_READY(acm))
506884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		schedule_work(&acm->work);
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
509c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work)
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
511c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct acm *acm = container_of(work, struct acm, work);
51210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
513e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell
5141d9846e505febb71255b098910ace741433312b7Johan Hovold	dev_vdbg(&acm->data->dev, "%s\n", __func__);
5151d9846e505febb71255b098910ace741433312b7Johan Hovold
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
51810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
51915e5bee33ffc11d0e5c6f819a65e7881c5c407beJohan Hovold	if (!tty)
52015e5bee33ffc11d0e5c6f819a65e7881c5c407beJohan Hovold		return;
52110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_wakeup(tty);
52210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_kref_put(tty);
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp)
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
53242dd2aa6496a2e87e496aac5494d2e1d6096c85bThadeu Lima de Souza Cascardo	int rv = -ENODEV;
53361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
5344186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven
5354186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_lock(&open_mutex);
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm = acm_table[tty->index];
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm || !acm->dev)
5392b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum		goto out;
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rv = 0;
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
543a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&acm->control->dev, "%s\n", __func__);
544a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold
54528d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91David Engraf	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
54610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty->driver_data = acm;
54810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, tty);
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
55094409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	if (usb_autopm_get_interface(acm->control) < 0)
55194409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum		goto early_bail;
55211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	else
55311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		acm->control->needs_remote_wakeup = 1;
5541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5551365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
55610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count++) {
5572b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum		mutex_unlock(&acm->mutex);
5581365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_autopm_put_interface(acm->control);
5592b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum		goto out;
56010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
5611365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->dev = acm->dev;
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
564a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_err(&acm->control->dev,
565a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			"%s - usb_submit_urb(ctrl irq) failed\n", __func__);
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto bail_out;
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
569ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
570ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	    (acm->ctrl_caps & USB_CDC_CAP_LINE))
57194d4c8919de3ae9e2e029ed121adfed43803bb5dJohan Hovold		goto bail_out;
57210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
57311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	usb_autopm_put_interface(acm->control);
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
57561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_urbs);
57661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->spare_read_bufs);
57761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	INIT_LIST_HEAD(&acm->filled_read_bufs);
5786e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
5796e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < acm->rx_buflimit; i++)
58061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
5816e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < acm->rx_buflimit; i++)
58261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
58361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
584ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum	acm->throttle = 0;
585ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum
5867af25b4b34a2439020d78da765a3bed0ff73f25cOliver Neukum	set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
58710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	rv = tty_port_block_til_ready(&acm->port, tty, filp);
58818a77b5d237a67d2c621a46f5271a3b51da1b380Henry Gebhardt	tasklet_schedule(&acm->urb_task);
5892b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum
5901365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
5912b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukumout:
59294409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rv;
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out:
59610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	acm->port.count--;
5971365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
5982b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum	usb_autopm_put_interface(acm->control);
59994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukumearly_bail:
60094409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum	mutex_unlock(&open_mutex);
60110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, NULL);
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EIO;
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
60583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm)
60683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{
607dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	int i;
60861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
60983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_unregister_device(acm_tty_driver, acm->minor);
61083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_put_intf(acm->control);
61183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	acm_table[acm->minor] = NULL;
61283ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_free_urb(acm->ctrlurb);
613e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
614e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
615dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	for (i = 0; i < acm->rx_buflimit; i++)
61661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
617c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	kfree(acm->country_codes);
61883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	kfree(acm);
61983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk}
62083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk
6214e608671674b62e97166f903830d5553e37970e8Arnd Bergmannstatic void acm_port_down(struct acm *acm)
62210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
623dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	int i;
624dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold
62510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	mutex_lock(&open_mutex);
62610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->dev) {
62710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_get_interface(acm->control);
62810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm_set_control(acm, acm->ctrlout = 0);
62910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_kill_urb(acm->ctrlurb);
63010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		for (i = 0; i < ACM_NW; i++)
63110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			usb_kill_urb(acm->wb[i].urb);
63223b80550e2aa61d0ba3af98b831b9195be0db9eeJohan Hovold		tasklet_disable(&acm->urb_task);
633dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold		for (i = 0; i < acm->rx_buflimit; i++)
63410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox			usb_kill_urb(acm->ru[i].urb);
63523b80550e2aa61d0ba3af98b831b9195be0db9eeJohan Hovold		tasklet_enable(&acm->urb_task);
63610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		acm->control->needs_remote_wakeup = 0;
63710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		usb_autopm_put_interface(acm->control);
63810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
63910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	mutex_unlock(&open_mutex);
64010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
64110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
64210077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_tty_hangup(struct tty_struct *tty)
64310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{
64410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct acm *acm = tty->driver_data;
64510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_hangup(&acm->port);
6464e608671674b62e97166f903830d5553e37970e8Arnd Bergmann	acm_port_down(acm);
64710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox}
64810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp)
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
65310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	/* Perform the closing process and see if we need to do the hardware
65410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	   shutdown */
655051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	if (!acm)
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
657051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	if (tty_port_close_start(&acm->port, tty, filp) == 0) {
658051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		mutex_lock(&open_mutex);
659051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		if (!acm->dev) {
660051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			tty_port_tty_set(&acm->port, NULL);
661051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			acm_tty_unregister(acm);
662051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra			tty->driver_data = NULL;
663051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		}
664051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		mutex_unlock(&open_mutex);
665051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra		return;
666051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra	}
6674e608671674b62e97166f903830d5553e37970e8Arnd Bergmann	acm_port_down(acm);
66810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_close_end(&acm->port, tty);
66910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty_port_tty_set(&acm->port, NULL);
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6726e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_write(struct tty_struct *tty,
6736e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					const unsigned char *buf, int count)
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int stat;
677884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	unsigned long flags;
678884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int wbn;
679884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
680884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!count)
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6865e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
687a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold
688884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_irqsave(&acm->write_lock, flags);
6896e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	wbn = acm_wb_alloc(acm);
6906e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (wbn < 0) {
691884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		spin_unlock_irqrestore(&acm->write_lock, flags);
692884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		return 0;
693884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
694884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb = &acm->wb[wbn];
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
696884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	count = (count > acm->writesize) ? acm->writesize : count;
6975e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold	dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
698884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	memcpy(wb->buf, buf, count);
699884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	wb->len = count;
700884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_unlock_irqrestore(&acm->write_lock, flags);
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	stat = acm_write_start(acm, wbn);
7036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (stat < 0)
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return stat;
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty)
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
713884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
714884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * Do not let the line discipline to know that we have a reserve,
715884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * or it might get too enthusiastic.
716884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
717934da4635c2d05cef474e5243ef05df95b2ad264David Brownell	return acm_wb_is_avail(acm) ? acm->writesize : 0;
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty)
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
72423198fda7182969b619613a555f8645fdc3dc334Alan Cox		return 0;
725884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	/*
726884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 * This is inaccurate (overcounts), but it works.
727884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	 */
72886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty)
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 1;
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty)
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&acm->throttle_lock);
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->throttle = 0;
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_bh(&acm->throttle_lock);
74961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	tasklet_schedule(&acm->urb_task);
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7529e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state)
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7559e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	int retval;
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7579e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox		return -EINVAL;
7589e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	retval = acm_send_break(acm, state ? 0xffff : 0);
7599e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	if (retval < 0)
760a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev, "%s - send break failed\n",
761a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold								__func__);
7629e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox	return retval;
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
76560b33c133ca0b7c0b6072c87234b63fee6e80558Alan Coxstatic int acm_tty_tiocmget(struct tty_struct *tty)
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TIOCM_CTS;
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
78020b9d17715017ae4dd4ec87fabc36d33b9de708eAlan Coxstatic int acm_tty_tiocmset(struct tty_struct *tty,
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    unsigned int set, unsigned int clear)
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int newctrl;
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = acm->ctrlout;
7906e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
7916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
7936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					(clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newctrl = (newctrl & ~clear) | set;
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (acm->ctrlout == newctrl)
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return acm_set_control(acm, acm->ctrlout = newctrl);
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8026caa76b7786891b42b66a0e61e2c2fff2c884620Alan Coxstatic int acm_tty_ioctl(struct tty_struct *tty,
8036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					unsigned int cmd, unsigned long arg)
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8134c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = {
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 50, 75, 110, 134, 150, 200, 300, 600,
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1200, 1800, 2400, 4800, 9600, 19200, 38400,
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	57600, 115200, 230400, 460800, 500000, 576000,
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	921600, 1000000, 1152000, 1500000, 2000000,
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	2500000, 3000000, 3500000, 4000000
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8214c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = {
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7, 8
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic void acm_tty_set_termios(struct tty_struct *tty,
8266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						struct ktermios *termios_old)
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm = tty->driver_data;
829606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox	struct ktermios *termios = tty->termios;
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_line_coding newline;
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int newctrl = acm->ctrlout;
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ACM_READY(acm))
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8369b80fee149a875a6292b2556ab2c64dc7ab7d6f5Alan Cox	newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bParityType = termios->c_cflag & PARENB ?
8396e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & PARODD ? 1 : 2) +
8406e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(termios->c_cflag & CMSPAR ? 2 : 0) : 0;
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
8426e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	/* FIXME: Needs to clear unsupported bits in the termios */
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!newline.dwDTERate) {
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newline.dwDTERate = acm->line.dwDTERate;
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		newctrl &= ~ACM_CTRL_DTR;
8486e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	} else
8496e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		newctrl |=  ACM_CTRL_DTR;
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (newctrl != acm->ctrlout)
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_control(acm, acm->ctrlout = newctrl);
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(&acm->line, &newline, sizeof newline)) {
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&acm->line, &newline, sizeof newline);
856a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold		dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
857a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			__func__,
858a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold			le32_to_cpu(newline.dwDTERate),
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bCharFormat, newline.bParityType,
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			newline.bDataBits);
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		acm_set_line(acm, &acm->line);
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines.
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
869830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */
870884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm)
871884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
872884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
873884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
874a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
875884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
8766e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
877997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
878884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
879884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
880830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm)
881830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{
882830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
883dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	int i;
884830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
885dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold	for (i = 0; i < acm->rx_buflimit; i++)
886997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		usb_free_coherent(usb_dev, acm->readsize,
887997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack				  acm->rb[i].base, acm->rb[i].dma);
888830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum}
889830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum
890884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */
891884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm)
892884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{
893884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	int i;
894884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	struct acm_wb *wb;
895884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
89686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
897997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
898884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		    &wb->dmah);
899884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		if (!wb->buf) {
900884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			while (i != 0) {
901884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--i;
902884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				--wb;
903997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack				usb_free_coherent(acm->dev, acm->writesize,
904884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum				    wb->buf, wb->dmah);
905884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			}
906884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum			return -ENOMEM;
907884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum		}
908884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	}
909884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	return 0;
910884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum}
911884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum
91210077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic int acm_probe(struct usb_interface *intf,
91310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		     const struct usb_device_id *id)
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_cdc_union_desc *union_header = NULL;
916c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct usb_cdc_country_functional_desc *cfd = NULL;
917c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell	unsigned char *buffer = intf->altsetting->extra;
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buflen = intf->altsetting->extralen;
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *control_interface;
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_interface *data_interface;
921a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epctrl = NULL;
922a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epread = NULL;
923a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	struct usb_endpoint_descriptor *epwrite = NULL;
9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct acm *acm;
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor;
9276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	int ctrlsize, readsize;
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *buf;
9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 ac_management_function = 0;
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 call_management_function = 0;
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int call_interface_num = -1;
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int data_interface_num;
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long quirks;
93486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	int num_rx_buf;
93561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	int i;
936a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	int combined_interfaces = 0;
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
93886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* normal quirks */
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	quirks = (unsigned long)id->driver_info;
94086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
94186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum
94286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	/* handle quirks deadly to normal probing*/
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (quirks == NO_UNION_NORMAL) {
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, 1);
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, 0);
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto skip_normal_probe;
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9486e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* normal probing*/
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
9519908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "Weird descriptor references\n");
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buflen) {
956577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray		if (intf->cur_altsetting->endpoint &&
957577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray				intf->cur_altsetting->endpoint->extralen &&
9586e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				intf->cur_altsetting->endpoint->extra) {
9596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
9606e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Seeking extra descriptors on endpoint\n");
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buflen = intf->cur_altsetting->endpoint->extralen;
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = intf->cur_altsetting->endpoint->extra;
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
9649908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev,
9659908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman				"Zero length descriptor references\n");
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (buflen > 0) {
9716e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (buffer[1] != USB_DT_CS_INTERFACE) {
9729908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman			dev_err(&intf->dev, "skipping garbage\n");
9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto next_desc;
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9766e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		switch (buffer[2]) {
9776e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_UNION_TYPE: /* we've found it */
9786e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			if (union_header) {
9796e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "More than one "
9806e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"union descriptor, skipping ...\n");
9816e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				goto next_desc;
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9836e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			union_header = (struct usb_cdc_union_desc *)buffer;
9846e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9856e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
9866e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			cfd = (struct usb_cdc_country_functional_desc *)buffer;
9876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_HEADER_TYPE: /* maybe check version */
9896e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break; /* for now we ignore it */
9906e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_ACM_TYPE:
9916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			ac_management_function = buffer[3];
9926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		case USB_CDC_CALL_MANAGEMENT_TYPE:
9946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_management_function = buffer[3];
9956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			call_interface_num = buffer[4];
996ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby			if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
9976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
9986e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
9996e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		default:
10006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			/* there are LOTS more CDC descriptors that
10016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 * could legitimately be found here.
10026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			 */
10036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "Ignoring descriptor: "
10046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					"type %02x, length %d\n",
10056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox					buffer[2], buffer[0]);
10066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			break;
10076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		}
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc:
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buflen -= buffer[0];
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer += buffer[0];
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!union_header) {
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (call_interface_num > 0) {
10156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = intf;
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
1019a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
1020a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_dbg(&intf->dev,"No union descriptor, giving up\n");
1021a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -ENODEV;
1022a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			} else {
1023a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
1024a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				combined_interfaces = 1;
1025a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				control_interface = data_interface = intf;
1026a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				goto look_for_collapsed_interface;
1027a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			}
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!control_interface || !data_interface) {
10336e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev, "no interfaces\n");
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10376e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface_num != call_interface_num)
10396e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1041a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (control_interface == data_interface) {
1042a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* some broken devices designed for windows work this way */
1043a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
1044a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		combined_interfaces = 1;
1045a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* a popular other OS doesn't use it */
1046a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		quirks |= NO_CAP_LINE;
1047a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
1048a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
1049a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -EINVAL;
1050a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
1051a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukumlook_for_collapsed_interface:
1052a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		for (i = 0; i < 3; i++) {
1053a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			struct usb_endpoint_descriptor *ep;
1054a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			ep = &data_interface->cur_altsetting->endpoint[i].desc;
1055a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
1056a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			if (usb_endpoint_is_int_in(ep))
1057a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epctrl = ep;
1058a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_out(ep))
1059a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epwrite = ep;
1060a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else if (usb_endpoint_is_bulk_in(ep))
1061a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				epread = ep;
1062a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			else
1063a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum				return -EINVAL;
1064a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		}
1065a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		if (!epctrl || !epread || !epwrite)
1066a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			return -ENODEV;
1067a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		else
1068a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			goto made_compressed_probe;
1069a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	}
1070a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum
10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe:
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*workaround for switched interfaces */
10746e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (data_interface->cur_altsetting->desc.bInterfaceClass
10756e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						!= CDC_DATA_INTERFACE_TYPE) {
10766e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (control_interface->cur_altsetting->desc.bInterfaceClass
10776e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						== CDC_DATA_INTERFACE_TYPE) {
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_interface *t;
10796e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			dev_dbg(&intf->dev,
10806e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"Your device has switched interfaces.\n");
10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = control_interface;
10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			control_interface = data_interface;
10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data_interface = t;
10841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
108874da5d68a54d66667664fbe233ededab2376a070Alan Stern
108974da5d68a54d66667664fbe233ededab2376a070Alan Stern	/* Accept probe requests only for the control interface */
1090a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && intf != control_interface)
109174da5d68a54d66667664fbe233ededab2376a070Alan Stern		return -ENODEV;
10926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox
1093a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!combined_interfaces && usb_interface_claimed(data_interface)) {
1094a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		/* valid in this context */
10956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev, "The data interface isn't available\n");
10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epread = &data_interface->cur_altsetting->endpoint[0].desc;
11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* workaround for switched endpoints */
110945aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino	if (!usb_endpoint_dir_in(epread)) {
11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* descriptors are swapped */
11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_endpoint_descriptor *t;
11126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		dev_dbg(&intf->dev,
11136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox			"The data interface has switched endpoints\n");
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = epread;
11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epread = epwrite;
11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		epwrite = t;
11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1118a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukummade_compressed_probe:
1119a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&intf->dev, "interfaces are valid\n");
11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (minor == ACM_TTY_MINORS) {
11239908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman		dev_err(&intf->dev, "no more free acm devices\n");
11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
11286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm == NULL) {
1129255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail;
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
11346e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	readsize = le16_to_cpu(epread->wMaxPacketSize) *
11356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				(quirks == SINGLE_RX_URB ? 1 : 2);
1136a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	acm->combined_interfaces = combined_interfaces;
1137e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->control = control_interface;
11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->data = data_interface;
11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->minor = minor;
11411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = usb_dev;
11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_caps = ac_management_function;
1143a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (quirks & NO_CAP_LINE)
1144a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlsize = ctrlsize;
11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->readsize = readsize;
114786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	acm->rx_buflimit = num_rx_buf;
114861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.func = acm_rx_tasklet;
114961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->urb_task.data = (unsigned long) acm;
1150c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	INIT_WORK(&acm->work, acm_softint);
11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&acm->throttle_lock);
1152884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	spin_lock_init(&acm->write_lock);
115361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	spin_lock_init(&acm->read_lock);
11541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_init(&acm->mutex);
115561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
1156cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum	acm->is_int_ep = usb_endpoint_xfer_int(epread);
1157cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum	if (acm->is_int_ep)
1158cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum		acm->bInterval = epread->bInterval;
1159739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	tty_port_init(&acm->port);
1160739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox	acm->port.ops = &acm_port_ops;
11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1162997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buf) {
1164255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail2;
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrl_buffer = buf;
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1169884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	if (acm_write_buffers_alloc(acm) < 0) {
1170255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail4;
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm->ctrlurb) {
1176255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold		dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto alloc_fail5;
11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
117986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++) {
118074f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		struct acm_rb *rb = &(acm->rb[i]);
118161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		struct acm_ru *rcv = &(acm->ru[i]);
118261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
118374f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
118474f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold								&rb->dma);
118574f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		if (!rb->base) {
118674f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold			dev_err(&intf->dev, "out of memory "
118774f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold					"(read bufs usb_alloc_coherent)\n");
118874f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold			goto alloc_fail6;
118974f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		}
119074f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold
11916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
11926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (rcv->urb == NULL) {
1193255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold			dev_err(&intf->dev,
11946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox				"out of memory (read urbs usb_alloc_urb)\n");
1195c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin			goto alloc_fail6;
119661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		}
119761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek
119861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
119961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		rcv->instance = acm;
120061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek	}
12016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++) {
1202e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		struct acm_wb *snd = &(acm->wb[i]);
1203e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
12046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
12056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		if (snd->urb == NULL) {
1206255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold			dev_err(&intf->dev,
120759d7fec7c6908604862658a3679ac44c2c3eea44Johan Hovold				"out of memory (write urbs usb_alloc_urb)\n");
120874f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold			goto alloc_fail7;
1209e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		}
1210e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf
12115186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		if (usb_endpoint_xfer_int(epwrite))
12125186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_int_urb(snd->urb, usb_dev,
12135186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
12145186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
12155186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev		else
12165186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev			usb_fill_bulk_urb(snd->urb, usb_dev,
12175186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
12185186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev				NULL, acm->writesize, acm_write_bulk, snd);
1219e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1220e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		snd->instance = acm;
12211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_set_intfdata(intf, acm);
1224c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1225c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1226c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (i < 0)
122774f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold		goto alloc_fail7;
1228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	if (cfd) { /* export the country data */
1230c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1231c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (!acm->country_codes)
1232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_code_size = cfd->bLength - 4;
12346e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
12356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox							cfd->bLength - 4);
1236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		acm->country_rel_date = cfd->iCountryCodeRelDate;
1237c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
12446e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		i = device_create_file(&intf->dev,
12456e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox						&dev_attr_iCountryCodeRelDate);
1246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		if (i < 0) {
1247c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin			device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
1248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			kfree(acm->country_codes);
1249c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum			goto skip_countries;
1250c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum		}
1251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
1252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum
1253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries:
12546e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	usb_fill_int_urb(acm->ctrlurb, usb_dev,
1255a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1256a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
1257a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 /* works around buggy devices */
1258a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum			 epctrl->bInterval ? epctrl->bInterval : 0xff);
12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_control(acm, acm->ctrlout);
12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.dwDTERate = cpu_to_le32(9600);
12671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->line.bDataBits = 8;
12681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_set_line(acm, &acm->line);
12691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_driver_claim_interface(&acm_driver, data_interface, acm);
1271672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	usb_set_intfdata(data_interface, acm);
12721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
127383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	usb_get_intf(control_interface);
127483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk	tty_register_device(acm_tty_driver, minor, &control_interface->dev);
12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_table[minor] = acm;
12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1278c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	return 0;
127974f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovoldalloc_fail7:
1280e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf	for (i = 0; i < ACM_NW; i++)
1281e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_free_urb(acm->wb[i].urb);
1282c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Linalloc_fail6:
128386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	for (i = 0; i < num_rx_buf; i++)
128461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek		usb_free_urb(acm->ru[i].urb);
128574f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold	acm_read_buffers_free(acm);
12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_free_urb(acm->ctrlurb);
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5:
1288884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4:
1290997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2:
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(acm);
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail:
12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOMEM;
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12971365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm)
12981365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
12991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int i;
1300a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold
1301a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold	dev_dbg(&acm->control->dev, "%s\n", __func__);
13021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_disable(&acm->urb_task);
13041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	usb_kill_urb(acm->ctrlurb);
13066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	for (i = 0; i < ACM_NW; i++)
1307e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf		usb_kill_urb(acm->wb[i].urb);
13081365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	for (i = 0; i < acm->rx_buflimit; i++)
13091365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		usb_kill_urb(acm->ru[i].urb);
13101365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	tasklet_enable(&acm->urb_task);
13121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	cancel_work_sync(&acm->work);
13141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
13151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf)
13171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1318c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
13191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device *usb_dev = interface_to_usbdev(intf);
132010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	struct tty_struct *tty;
13211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1322672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	/* sibling interface is already cleaning up */
1323672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	if (!acm)
132486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum		return;
1325672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell
1326672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell	mutex_lock(&open_mutex);
13276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	if (acm->country_codes) {
132874da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
132974da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_wCountryCodes);
133074da5d68a54d66667664fbe233ededab2376a070Alan Stern		device_remove_file(&acm->control->dev,
133174da5d68a54d66667664fbe233ededab2376a070Alan Stern				&dev_attr_iCountryCodeRelDate);
1332c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum	}
133374da5d68a54d66667664fbe233ededab2376a070Alan Stern	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm->dev = NULL;
133586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->control, NULL);
133686067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum	usb_set_intfdata(acm->data, NULL);
13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13381365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	stop_data_traffic(acm);
13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1340884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum	acm_write_buffers_free(acm);
1341997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
1342997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack			  acm->ctrl_dma);
1343830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum	acm_read_buffers_free(acm);
13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1345a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum	if (!acm->combined_interfaces)
1346a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum		usb_driver_release_interface(&acm_driver, intf == acm->control ?
1347830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum					acm->data : acm->control);
13481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
134910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count == 0) {
135083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk		acm_tty_unregister(acm);
13514186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven		mutex_unlock(&open_mutex);
13521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
13531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
13541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13554186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven	mutex_unlock(&open_mutex);
135610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	tty = tty_port_tty_get(&acm->port);
135710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (tty) {
135810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		tty_hangup(tty);
135910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox		tty_kref_put(tty);
136010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	}
13611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1363357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
13641365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message)
13651365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
13661365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
136711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
136811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
136965bfd2967c906ca322a4bb69a285fe0de8916ac6Alan Stern	if (message.event & PM_EVENT_AUTO) {
137011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		int b;
137111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
137211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock_irq(&acm->read_lock);
137311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_lock(&acm->write_lock);
137411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		b = acm->processing + acm->transmitting;
137511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock(&acm->write_lock);
137611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		spin_unlock_irq(&acm->read_lock);
137711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum		if (b)
137811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			return -EBUSY;
137911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	}
138011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
138111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
138211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock(&acm->write_lock);
138311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count++;
138411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock(&acm->write_lock);
138511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
13861365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
138711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
13881365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
13891365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	/*
13901365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we treat opened interfaces differently,
13911365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	we must guard against open
13921365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	*/
13931365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
13941365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
139510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count)
13961365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		stop_data_traffic(acm);
13971365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
13981365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
13991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return 0;
14001365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
14011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf)
14031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{
14041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	struct acm *acm = usb_get_intfdata(intf);
140597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum	struct acm_wb *wb;
14061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	int rv = 0;
140711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	int cnt;
14081365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
140911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_lock_irq(&acm->read_lock);
141011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	acm->susp_count -= 1;
141111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	cnt = acm->susp_count;
141211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	spin_unlock_irq(&acm->read_lock);
141311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum
141411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum	if (cnt)
14151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		return 0;
14161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_lock(&acm->mutex);
141810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	if (acm->port.count) {
14191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
142097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum
142197d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		spin_lock_irq(&acm->write_lock);
142297d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		if (acm->delayed_wb) {
142397d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			wb = acm->delayed_wb;
142497d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			acm->delayed_wb = NULL;
142597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			spin_unlock_irq(&acm->write_lock);
1426f0730924e9e32bb8935c60040a26d94179355088Oliver Neukum			acm_start_wb(acm, wb);
142797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		} else {
142897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum			spin_unlock_irq(&acm->write_lock);
142997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		}
143097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum
143197d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		/*
143297d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 * delayed error checking because we must
143397d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 * do the write path at all cost
143497d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum		 */
14351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		if (rv < 0)
143611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum			goto err_out;
14371365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14381365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum		tasklet_schedule(&acm->urb_task);
14391365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	}
14401365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum
14411365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out:
14421365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	mutex_unlock(&acm->mutex);
14431365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	return rv;
14441365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum}
1445357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum
1446a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavrastatic int acm_reset_resume(struct usb_interface *intf)
1447a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra{
1448a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	struct acm *acm = usb_get_intfdata(intf);
1449a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	struct tty_struct *tty;
1450a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra
1451a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	mutex_lock(&acm->mutex);
1452a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	if (acm->port.count) {
1453a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		tty = tty_port_tty_get(&acm->port);
1454a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		if (tty) {
1455a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra			tty_hangup(tty);
1456a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra			tty_kref_put(tty);
1457a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra		}
1458a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	}
1459a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	mutex_unlock(&acm->mutex);
1460a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	return acm_resume(intf);
1461a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra}
1462a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra
1463357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */
1464c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
1465c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor#define NOKIA_PCSUITE_ACM_INFO(x) \
1466c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
1467c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
1468c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor		USB_CDC_ACM_PROTO_VENDOR)
1469c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
14704035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray#define SAMSUNG_PCSUITE_ACM_INFO(x) \
14714035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
14724035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
14734035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray		USB_CDC_ACM_PROTO_VENDOR)
14744035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray
14751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
14761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure.
14771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14796ef4852b1326301f6e9657e99b2c3221be1a3a44Németh Mártonstatic const struct usb_device_id acm_ids[] = {
14801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* quirky and broken devices */
14811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
14821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1484b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1485b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1486b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov	},
14870f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	{ USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
14880f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14890f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn	},
14908753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
14918753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
14928753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote	},
149391a9c9214e34c364bf15406aadb922787ae7129bChris Malley	{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
149491a9c9214e34c364bf15406aadb922787ae7129bChris Malley	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
149591a9c9214e34c364bf15406aadb922787ae7129bChris Malley	},
14967abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	{ USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
14977abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	.driver_info = SINGLE_RX_URB,
14987abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox	},
149986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
150086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
150186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum	},
15023dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
15033dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	.driver_info = SINGLE_RX_URB, /* firmware bug */
15043dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum	},
15059be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
15069be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15079be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum	},
15086149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	{ USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
15096149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
15106149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane	},
1511c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	{ USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
1512c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1513c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen	},
1514c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	{ USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
1515c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1516c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox	},
1517cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	{ USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
1518cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1519cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian	},
1520155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
1521155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev	},
1522c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	{ USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
1523c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	.driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
1524c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   data interface instead of
1525c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   communications interface.
1526c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   Maybe we should define a new
1527c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter					   quirk for this. */
1528c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter	},
15291f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	{ USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
15301f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
15311f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin	},
1532c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	{ USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
1533c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
1534c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson	},
15359be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum
1536c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	/* Nokia S60 phones expose two ACM channels. The first is
1537c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * a modem and is picked up by the standard AT-command
1538c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * information below. The second is 'vendor-specific' but
1539c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * is treated as a serial device at the S60 end, so we want
1540c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	 * to expose it on Linux too. */
1541c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
1542c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
1543c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
1544c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
1545c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
1546c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
1547c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
1548c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
1549c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
1550c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
1551c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
1552c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
1553c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
1554c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
1555c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
1556c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
1557c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
1558c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i  */
1559c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
1560c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
1561c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
1562c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic &  */
1563c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
1564c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
1565c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
1566c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
1567c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
1568c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
1569c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
1570c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
1571c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
1572c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB  */
1573c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1574c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
1575c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
1576c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
1577c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
1578c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
1579c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
1580c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3  */
1581c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
1582c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
1583c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	{ NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
158483a4eae9aeed4a69e89e323a105e653ae06e7c1fPrzemo Firszt	{ NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
15854035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
15864035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
15874035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
15884035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
15894035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
15904035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
15914035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
15924035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
15934035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
15944035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
1595721d92fc6373dee15846216f9d178ec240ec0fd7Arvid Ephraim Picciani	{ NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
15964035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray	{ SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
1597c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
1598c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor	/* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
1599c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor
16007c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby	/* Support Lego NXT using pbLua firmware */
1601ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby	{ USB_DEVICE(0x0694, 0xff00),
1602ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby	.driver_info = NOT_A_MODEM,
16037893afc035590383a14b176c1497cba984276ef4Otavio Salvador	},
16047c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby
16055b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes	/* control interfaces without any protocol set */
16065b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16075b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes		USB_CDC_PROTO_NONE) },
16085b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes
16091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* control interfaces with various AT-command sets */
16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_V25TER) },
16121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101) },
16141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
16161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_GSM) },
16181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox		USB_CDC_ACM_PROTO_AT_3G) },
16201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
16211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		USB_CDC_ACM_PROTO_AT_CDMA) },
16221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }
16241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DEVICE_TABLE(usb, acm_ids);
16271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = {
16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"cdc_acm",
16301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	acm_probe,
16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	acm_disconnect,
1632357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
16331365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.suspend =	acm_suspend,
16341365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.resume =	acm_resume,
1635a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra	.reset_resume =	acm_reset_resume,
1636357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
16371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	acm_ids,
1638357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM
16391365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum	.supports_autosuspend = 1,
1640357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif
16411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures.
16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
16461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1647b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = {
16481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			acm_tty_open,
16491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		acm_tty_close,
165010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox	.hangup =		acm_tty_hangup,
16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		acm_tty_write,
16521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		acm_tty_write_room,
16531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		acm_tty_ioctl,
16541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		acm_tty_throttle,
16551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		acm_tty_unthrottle,
16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	acm_tty_chars_in_buffer,
16571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl =		acm_tty_break_ctl,
16581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios =		acm_tty_set_termios,
16591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmget =		acm_tty_tiocmget,
16601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.tiocmset =		acm_tty_tiocmset,
16611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
16641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit.
16651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
16661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void)
16681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
16701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
16711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!acm_tty_driver)
16721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
16731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->owner = THIS_MODULE,
16741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->driver_name = "acm",
16751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->name = "ttyACM",
16761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->major = ACM_TTY_MAJOR,
16771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->minor_start = 0,
16781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
16791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
1680331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
16811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	acm_tty_driver->init_termios = tty_std_termios;
16826e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
16836e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox								HUPCL | CLOCAL;
16841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(acm_tty_driver, &acm_ops);
16851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = tty_register_driver(acm_tty_driver);
16871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
16881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
16891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
16901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&acm_driver);
16931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
16941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_unregister_driver(acm_tty_driver);
16951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_tty_driver(acm_tty_driver);
16961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
16971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1699a2c7b9353e8f782590852052fe2948692020147eJohan Hovold	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
17001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
17021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void)
17051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister(&acm_driver);
17071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_unregister_driver(acm_tty_driver);
17081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	put_tty_driver(acm_tty_driver);
17091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init);
17121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit);
17131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_AUTHOR(DRIVER_AUTHOR);
17156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DESCRIPTION(DRIVER_DESC);
17161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
1717e766aeb882b41355d8732cf49aa9412baef852c5Scott James RemnantMODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);
1718