cdc-acm.c revision 922b13565b6a826a925f9f91f053dc9cb0d6210e
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * cdc-acm.c 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de> 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Pavel Machek <pavel@suse.cz> 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Johannes Erdfelt <johannes@erdfelt.com> 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2004 Oliver Neukum <oliver@neukum.name> 961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek * Copyright (c) 2005 David Kubicek <dave@awk.cz> 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB Abstract Control Model driver for USB modems and ISDN adapters 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Sponsored by SuSE 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ChangeLog: 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.9 - thorough cleaning, URBification, almost a rewrite 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.10 - some more cleanups 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.11 - fixed flow control, read error doesn't stop reads 196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox * v0.12 - added TIOCM ioctls, added break handling, made struct acm 206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox * kmalloced 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.13 - added termios, added hangup 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.14 - sized down struct acm 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.15 - fixed flow control again - characters could be lost 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.16 - added code for modems with swapped data and control interfaces 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.17 - added new style probing 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.18 - fixed new style probing for devices with more configurations 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan) 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.20 - switched to probing on interface (rather than device) class 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.21 - revert to probing on device for devices with multiple configs 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.22 - probe only the control interface. if usbcore doesn't choose the 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * config we want, sysadmin changes bConfigurationValue in sysfs. 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.23 - use softirq for rx processing, as needed by tty layer 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.24 - change probe method to evaluate CDC union descriptor 3461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek * v0.25 - downstream tasks paralelized to maximize throughput 35e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf * v0.26 - multiple write urbs, writesize increased 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version. 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful, 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details. 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG 55e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#undef VERBOSE_DEBUG 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h> 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h> 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h> 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h> 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h> 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 654186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h> 6610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#include <linux/uaccess.h> 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h> 68a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h> 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h> 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h> 7161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h> 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h" 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 75e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 76e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */ 77e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 81e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf#define DRIVER_VERSION "v0.26" 8261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver; 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver; 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS]; 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 894186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex); 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#define ACM_READY(acm) (acm && acm->dev && acm->port.count) 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 93739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Coxstatic const struct tty_port_operations acm_port_ops = { 94739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox}; 95739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox 96e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#ifdef VERBOSE_DEBUG 97e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define verbose 1 98e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#else 99e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define verbose 0 100e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#endif 101e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages. 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_ctrl_msg(struct acm *acm, int request, int value, 1076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox void *buf, int len) 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds request, USB_RT_ACM, value, 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->control->altsetting[0].desc.bInterfaceNumber, 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf, len, 5000); 1136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", 1146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox request, value, len, retval); 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval < 0 ? retval : 0; 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests. 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do... 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \ 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0) 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \ 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)) 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \ 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 129884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management. 130884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller. 131884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 132884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 133884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm) 134884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 135884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i, wbn; 136884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 137884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 138e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf wbn = 0; 139884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum i = 0; 140884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum for (;;) { 141884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!wb->use) { 143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->use = 1; 144884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return wbn; 145884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 14686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum wbn = (wbn + 1) % ACM_NW; 14786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (++i >= ACM_NW) 148884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -1; 149884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 150884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 151884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 152884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm) 153884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 154884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i, n; 155e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell unsigned long flags; 156884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 15786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum n = ACM_NW; 158e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell spin_lock_irqsave(&acm->write_lock, flags); 1596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < ACM_NW; i++) 16086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum n -= acm->wb[i].use; 161e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell spin_unlock_irqrestore(&acm->write_lock, flags); 162884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return n; 163884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 164884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 165884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 166ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips * Finish write. Caller must hold acm->write_lock 167884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 168e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic void acm_write_done(struct acm *acm, struct acm_wb *wb) 169884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 170e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf wb->use = 0; 17111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->transmitting--; 172884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 175884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write. 17611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * 17711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking 178884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 17911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 18011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb) 18111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{ 18211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int rc; 18311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 18411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->transmitting++; 18511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 18611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->transfer_buffer = wb->buf; 18711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->transfer_dma = wb->dmah; 18811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->transfer_buffer_length = wb->len; 18911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->dev = acm->dev; 19011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 1916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox rc = usb_submit_urb(wb->urb, GFP_ATOMIC); 1926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (rc < 0) { 19311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("usb_submit_urb(write bulk) failed: %d", rc); 19411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm_write_done(acm, wb); 19511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 19611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return rc; 19711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum} 19811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 199e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn) 200884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 201884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 202934da4635c2d05cef474e5243ef05df95b2ad264David Brownell struct acm_wb *wb = &acm->wb[wbn]; 203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int rc; 204884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 205884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 206884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!acm->dev) { 207934da4635c2d05cef474e5243ef05df95b2ad264David Brownell wb->use = 0; 208884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 209884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -ENODEV; 210884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 211884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 21211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("%s susp_count: %d", __func__, acm->susp_count); 21311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (acm->susp_count) { 21411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->delayed_wb = wb; 21511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum schedule_work(&acm->waker); 21611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 21711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return 0; /* A white lie */ 21811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 21911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_mark_last_busy(acm->dev); 22011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 22111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum rc = acm_start_wb(acm, wb); 222884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 223884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 224884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return rc; 22511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 226884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 227c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/* 228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs 229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */ 230c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps 231c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return sprintf(buf, "%d", acm->ctrl_caps); 237c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); 239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes 241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum memcpy(buf, acm->country_codes, acm->country_code_size); 247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return acm->country_code_size; 248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 249c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 250c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); 251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date 253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 254c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 258c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return sprintf(buf, "%d", acm->country_rel_date); 259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 260884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 261c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); 262884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */ 2677d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb) 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = urb->context; 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_notification *dr = urb->transfer_buffer; 27110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct tty_struct *tty; 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char *data; 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int newctrl; 274185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman int retval; 275185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman int status = urb->status; 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 277185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman switch (status) { 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0: 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* success */ 2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ECONNRESET: 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ENOENT: 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ESHUTDOWN: 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* this urb is terminated, clean up */ 285441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison dbg("%s - urb shutting down with status: %d", __func__, status); 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 288441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison dbg("%s - nonzero urb status received: %d", __func__, status); 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto exit; 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto exit; 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data = (unsigned char *)(dr + 1); 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (dr->bNotificationType) { 2976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_NOTIFY_NETWORK_CONNECTION: 2986e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dbg("%s network", dr->wValue ? 2996e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "connected to" : "disconnected from"); 3006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_NOTIFY_SERIAL_STATE: 3036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox tty = tty_port_tty_get(&acm->port); 3046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox newctrl = get_unaligned_le16(data); 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (tty) { 3076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (!acm->clocal && 3086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { 3096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dbg("calling hangup"); 3106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox tty_hangup(tty); 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox tty_kref_put(tty); 3136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox } 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin = newctrl; 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c", 3186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', 3196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', 3206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', 3216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_RI ? '+' : '-', 3226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', 3236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', 3246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox default: 3286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dbg("unknown notification %d received: index %d len %d data0 %d data1 %d", 3296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dr->bNotificationType, dr->wIndex, 3306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dr->wLength, data[0], data[1]); 3316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit: 33411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_mark_last_busy(acm->dev); 3356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox retval = usb_submit_urb(urb, GFP_ATOMIC); 336185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman if (retval) 3379908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with " 3389908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman "result %d", __func__, retval); 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */ 3427d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_read_bulk(struct urb *urb) 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 34461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb *buf; 34561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv = urb->context; 34661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm *acm = rcv->instance; 34786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int status = urb->status; 348185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman 349185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman dbg("Entering acm_read_bulk with status %d", status); 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 35111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (!ACM_READY(acm)) { 35211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dev_dbg(&acm->data->dev, "Aborting, acm not ready"); 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 35411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 35511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_mark_last_busy(acm->dev); 3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 35786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (status) 358898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&acm->data->dev, "bulk rx status %d\n", status); 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 36061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = rcv->buffer; 36161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf->size = urb->actual_length; 36261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 36386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (likely(status == 0)) { 36486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_lock(&acm->read_lock); 36511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->processing++; 36686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&rcv->list, &acm->spare_read_urbs); 36786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&buf->list, &acm->filled_read_bufs); 36886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_unlock(&acm->read_lock); 36986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum } else { 37086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* we drop the buffer due to an error */ 37186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_lock(&acm->read_lock); 37286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&rcv->list, &acm->spare_read_urbs); 37386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add(&buf->list, &acm->spare_read_bufs); 37486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_unlock(&acm->read_lock); 37586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* nevertheless the tasklet must be kicked unconditionally 37686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum so the queue cannot dry up */ 37786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum } 37811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (likely(!acm->susp_count)) 37911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum tasklet_schedule(&acm->urb_task); 3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm) 3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = (void *)_acm; 38561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb *buf; 38610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct tty_struct *tty; 38761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv; 388762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski unsigned long flags; 389ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum unsigned char throttled; 39011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Entering acm_rx_tasklet"); 3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 39310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (!ACM_READY(acm)) { 39411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("acm_rx_tasklet: ACM not ready"); 395ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum return; 39611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 397ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum 398834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum spin_lock_irqsave(&acm->throttle_lock, flags); 399ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum throttled = acm->throttle; 400834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum spin_unlock_irqrestore(&acm->throttle_lock, flags); 40110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (throttled) { 40211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("acm_rx_tasklet: throttled"); 40361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 40411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 40561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 40610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty = tty_port_tty_get(&acm->port); 40710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 40861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer: 409762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 41061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (list_empty(&acm->filled_read_bufs)) { 411762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 41261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto urbs; 4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 41461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = list_entry(acm->filled_read_bufs.next, 41561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb, list); 41661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&buf->list); 417762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 41861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 4193dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size); 42061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 42110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (tty) { 42210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox spin_lock_irqsave(&acm->throttle_lock, flags); 42310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox throttled = acm->throttle; 42410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox spin_unlock_irqrestore(&acm->throttle_lock, flags); 42510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (!throttled) { 42610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_buffer_request_room(tty, buf->size); 42710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_insert_flip_string(tty, buf->base, buf->size); 42810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_flip_buffer_push(tty); 42910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } else { 43010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_kref_put(tty); 43110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox dbg("Throttling noticed"); 43210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox spin_lock_irqsave(&acm->read_lock, flags); 43310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox list_add(&buf->list, &acm->filled_read_bufs); 43410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox spin_unlock_irqrestore(&acm->read_lock, flags); 43510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox return; 43610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } 4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 439762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 44061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->spare_read_bufs); 441762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 44261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto next_buffer; 44361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 44461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs: 44510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_kref_put(tty); 44610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 44761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek while (!list_empty(&acm->spare_read_bufs)) { 448762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 44961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (list_empty(&acm->spare_read_urbs)) { 45011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->processing = 0; 451762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 45261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 45361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 45461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv = list_entry(acm->spare_read_urbs.next, 45561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru, list); 45661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&rcv->list); 457762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 45861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 45961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = list_entry(acm->spare_read_bufs.next, 46061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb, list); 46161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&buf->list); 46261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 46361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->buffer = buf; 46461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 46561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_fill_bulk_urb(rcv->urb, acm->dev, 46661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->rx_endpoint, 46761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf->base, 46861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->readsize, 46961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm_read_bulk, rcv); 47061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_dma = buf->dma; 47161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 47261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 4736e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox /* This shouldn't kill the driver as unsuccessful URBs are 4746e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox returned to the free-urbs-pool and resubmited ASAP */ 47511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irqsave(&acm->read_lock, flags); 4766e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (acm->susp_count || 4776e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { 47861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->spare_read_bufs); 47961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&rcv->list, &acm->spare_read_urbs); 48011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->processing = 0; 481762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 48261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 48311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } else { 48411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irqrestore(&acm->read_lock, flags); 48511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); 48661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 48761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 48811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irqsave(&acm->read_lock, flags); 48911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->processing = 0; 49011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irqrestore(&acm->read_lock, flags); 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */ 4947d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb) 4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 496cdc97792289179974af6dda781c855696358d307Ming Lei struct acm_wb *wb = urb->context; 497e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell struct acm *acm = wb->instance; 498ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips unsigned long flags; 4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 500e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell if (verbose || urb->status 501e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell || (urb->actual_length != urb->transfer_buffer_length)) 502e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n", 503e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell urb->actual_length, 504e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell urb->transfer_buffer_length, 505e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell urb->status); 5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 507ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips spin_lock_irqsave(&acm->write_lock, flags); 508e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf acm_write_done(acm, wb); 509ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips spin_unlock_irqrestore(&acm->write_lock, flags); 510884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (ACM_READY(acm)) 511884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum schedule_work(&acm->work); 512e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell else 513e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell wake_up_interruptible(&acm->drain_wait); 5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 516c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work) 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 518c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells struct acm *acm = container_of(work, struct acm, work); 51910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct tty_struct *tty; 520e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 521e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell dev_vdbg(&acm->data->dev, "tx work\n"); 5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 52410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty = tty_port_tty_get(&acm->port); 52510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_wakeup(tty); 52610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_kref_put(tty); 5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 52911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic void acm_waker(struct work_struct *waker) 53011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{ 53111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum struct acm *acm = container_of(waker, struct acm, waker); 53211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int rv; 53311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 53411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum rv = usb_autopm_get_interface(acm->control); 53511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (rv < 0) { 5369908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__); 53711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return; 53811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 53911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (acm->delayed_wb) { 54011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm_start_wb(acm, acm->delayed_wb); 54111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->delayed_wb = NULL; 54211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 54311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_autopm_put_interface(acm->control); 54411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum} 54511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers 5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp) 5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm; 55342dd2aa6496a2e87e496aac5494d2e1d6096c85bThadeu Lima de Souza Cascardo int rv = -ENODEV; 55461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 5553dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("Entering acm_tty_open."); 5564186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven 5574186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_lock(&open_mutex); 5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm = acm_table[tty->index]; 5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm || !acm->dev) 5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto err_out; 5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rv = 0; 5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 56528d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91David Engraf set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); 56610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty->driver_data = acm; 56810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_tty_set(&acm->port, tty); 5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 57094409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum if (usb_autopm_get_interface(acm->control) < 0) 57194409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum goto early_bail; 57211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum else 57311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->control->needs_remote_wakeup = 1; 5741365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 5751365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 57610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->port.count++) { 5771365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_autopm_put_interface(acm->control); 5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto done; 57910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } 5801365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->dev = acm->dev; 5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { 5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("usb_submit_urb(ctrl irq) failed"); 5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto bail_out; 5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 587ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && 588ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum (acm->ctrl_caps & USB_CDC_CAP_LINE)) 5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto full_bailout; 59010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 59111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_autopm_put_interface(acm->control); 5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 59361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->spare_read_urbs); 59461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->spare_read_bufs); 59561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->filled_read_bufs); 5966e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox 5976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < acm->rx_buflimit; i++) 59861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&(acm->ru[i].list), &acm->spare_read_urbs); 5996e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < acm->rx_buflimit; i++) 60061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&(acm->rb[i].list), &acm->spare_read_bufs); 60161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 602ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum acm->throttle = 0; 603ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum 60461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_schedule(&acm->urb_task); 60510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox rv = tty_port_block_til_ready(&acm->port, tty, filp); 6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone: 6071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 60874573ee7096a4ffc2f098108d21c85801b9c7434Alexey Dobriyanerr_out: 60994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum mutex_unlock(&open_mutex); 6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rv; 6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout: 6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_kill_urb(acm->ctrlurb); 6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out: 6151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_autopm_put_interface(acm->control); 61610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox acm->port.count--; 6171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 61894409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukumearly_bail: 61994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum mutex_unlock(&open_mutex); 62010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_tty_set(&acm->port, NULL); 6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 62483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm) 62583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{ 62610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox int i, nr; 62761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 62886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum nr = acm->rx_buflimit; 62983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk tty_unregister_device(acm_tty_driver, acm->minor); 63083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_put_intf(acm->control); 63183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_table[acm->minor] = NULL; 63283ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_free_urb(acm->ctrlurb); 633e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf for (i = 0; i < ACM_NW; i++) 634e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_free_urb(acm->wb[i].urb); 63586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < nr; i++) 63661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_free_urb(acm->ru[i].urb); 637c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 63883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk kfree(acm); 63983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk} 64083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk 641e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownellstatic int acm_tty_chars_in_buffer(struct tty_struct *tty); 642e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 64310077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_port_down(struct acm *acm, int drain) 64410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{ 64510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox int i, nr = acm->rx_buflimit; 64610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox mutex_lock(&open_mutex); 64710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->dev) { 64810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_autopm_get_interface(acm->control); 64910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox acm_set_control(acm, acm->ctrlout = 0); 65010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox /* try letting the last writes drain naturally */ 65110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (drain) { 65210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox wait_event_interruptible_timeout(acm->drain_wait, 65310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev, 65410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox ACM_CLOSE_TIMEOUT * HZ); 65510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } 65610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_kill_urb(acm->ctrlurb); 65710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox for (i = 0; i < ACM_NW; i++) 65810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_kill_urb(acm->wb[i].urb); 65910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox for (i = 0; i < nr; i++) 66010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_kill_urb(acm->ru[i].urb); 66110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox acm->control->needs_remote_wakeup = 0; 66210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_autopm_put_interface(acm->control); 66310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } 66410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox mutex_unlock(&open_mutex); 66510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox} 66610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 66710077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_tty_hangup(struct tty_struct *tty) 66810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{ 66910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct acm *acm = tty->driver_data; 67010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_hangup(&acm->port); 67110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox acm_port_down(acm, 0); 67210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox} 67310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp) 6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 67810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox /* Perform the closing process and see if we need to do the hardware 67910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox shutdown */ 680922b13565b6a826a925f9f91f053dc9cb0d6210eThadeu Lima de Souza Cascardo if (!acm || tty_port_close_start(&acm->port, tty, filp) == 0) 6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 68210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox acm_port_down(acm, 0); 68310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_close_end(&acm->port, tty); 6844186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_lock(&open_mutex); 68510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_tty_set(&acm->port, NULL); 68610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (!acm->dev) 68710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox acm_tty_unregister(acm); 6884186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_write(struct tty_struct *tty, 6926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox const unsigned char *buf, int count) 6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int stat; 696884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 697884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int wbn; 698884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 699884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 7003dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("Entering acm_tty_write to write %d bytes,", count); 7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!count) 7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 707884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 7086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox wbn = acm_wb_alloc(acm); 7096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (wbn < 0) { 710884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 711884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 712884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 713884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 715884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum count = (count > acm->writesize) ? acm->writesize : count; 7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Get %d bytes...", count); 717884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum memcpy(wb->buf, buf, count); 718884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->len = count; 719884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox stat = acm_write_start(acm, wbn); 7226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (stat < 0) 7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return stat; 7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return count; 7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty) 7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 732884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum /* 733884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Do not let the line discipline to know that we have a reserve, 734884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * or it might get too enthusiastic. 735884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 736934da4635c2d05cef474e5243ef05df95b2ad264David Brownell return acm_wb_is_avail(acm) ? acm->writesize : 0; 7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty) 7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 744884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum /* 745884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * This is inaccurate (overcounts), but it works. 746884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 74786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize; 7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty) 7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(&acm->throttle_lock); 7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->throttle = 1; 7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(&acm->throttle_lock); 7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty) 7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(&acm->throttle_lock); 7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->throttle = 0; 7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(&acm->throttle_lock); 76861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_schedule(&acm->urb_task); 7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7719e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state) 7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7749e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox int retval; 7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7769e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox return -EINVAL; 7779e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox retval = acm_send_break(acm, state ? 0xffff : 0); 7789e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox if (retval < 0) 7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("send break failed"); 7809e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox return retval; 7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmget(struct tty_struct *tty, struct file *file) 7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | 7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | 7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | 7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | 7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | 7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds TIOCM_CTS; 7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmset(struct tty_struct *tty, struct file *file, 7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int set, unsigned int clear) 8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int newctrl; 8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = acm->ctrlout; 8086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | 8096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); 8106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | 8116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0); 8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = (newctrl & ~clear) | set; 8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->ctrlout == newctrl) 8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return acm_set_control(acm, acm->ctrlout = newctrl); 8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_ioctl(struct tty_struct *tty, struct file *file, 8216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox unsigned int cmd, unsigned long arg) 8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOIOCTLCMD; 8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8314c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = { 8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0, 50, 75, 110, 134, 150, 200, 300, 600, 8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1200, 1800, 2400, 4800, 9600, 19200, 38400, 8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 57600, 115200, 230400, 460800, 500000, 576000, 8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 921600, 1000000, 1152000, 1500000, 2000000, 8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2500000, 3000000, 3500000, 4000000 8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8394c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = { 8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5, 6, 7, 8 8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8436e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic void acm_tty_set_termios(struct tty_struct *tty, 8446e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox struct ktermios *termios_old) 8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 847606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox struct ktermios *termios = tty->termios; 8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_line_coding newline; 8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int newctrl = acm->ctrlout; 8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8546e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox /* FIXME: Needs to support the tty_baud interface */ 8556e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox /* FIXME: Broken on sparc */ 8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.dwDTERate = cpu_to_le32p(acm_tty_speed + 8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0)); 8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0; 8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bParityType = termios->c_cflag & PARENB ? 8606e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (termios->c_cflag & PARODD ? 1 : 2) + 8616e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (termios->c_cflag & CMSPAR ? 2 : 0) : 0; 8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; 8636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox /* FIXME: Needs to clear unsupported bits in the termios */ 8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->clocal = ((termios->c_cflag & CLOCAL) != 0); 8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!newline.dwDTERate) { 8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.dwDTERate = acm->line.dwDTERate; 8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl &= ~ACM_CTRL_DTR; 8696e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox } else 8706e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox newctrl |= ACM_CTRL_DTR; 8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (newctrl != acm->ctrlout) 8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout = newctrl); 8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (memcmp(&acm->line, &newline, sizeof newline)) { 8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(&acm->line, &newline, sizeof newline); 8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate), 8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bCharFormat, newline.bParityType, 8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bDataBits); 8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_line(acm, &acm->line); 8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines. 8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 888830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */ 889884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm) 890884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 891884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i; 892884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 893a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum struct usb_device *usb_dev = interface_to_usbdev(acm->control); 894884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 8956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) 896a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah); 897884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 898884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 899830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm) 900830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{ 901830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum struct usb_device *usb_dev = interface_to_usbdev(acm->control); 902830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum int i, n = acm->rx_buflimit; 903830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum 904830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum for (i = 0; i < n; i++) 9056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox usb_buffer_free(usb_dev, acm->readsize, 9066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->rb[i].base, acm->rb[i].dma); 907830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum} 908830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum 909884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */ 910884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm) 911884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 912884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i; 913884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 914884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 91586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { 916884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL, 917884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum &wb->dmah); 918884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!wb->buf) { 919884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum while (i != 0) { 920884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum --i; 921884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum --wb; 922884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum usb_buffer_free(acm->dev, acm->writesize, 923884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->buf, wb->dmah); 924884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 925884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -ENOMEM; 926884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 927884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 928884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 929884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 930884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 93110077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic int acm_probe(struct usb_interface *intf, 93210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox const struct usb_device_id *id) 9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_union_desc *union_header = NULL; 935c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_cdc_country_functional_desc *cfd = NULL; 936c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell unsigned char *buffer = intf->altsetting->extra; 9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int buflen = intf->altsetting->extralen; 9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *control_interface; 9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *data_interface; 940a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum struct usb_endpoint_descriptor *epctrl = NULL; 941a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum struct usb_endpoint_descriptor *epread = NULL; 942a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum struct usb_endpoint_descriptor *epwrite = NULL; 9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_device *usb_dev = interface_to_usbdev(intf); 9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm; 9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int minor; 9466e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox int ctrlsize, readsize; 9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 *buf; 9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 ac_management_function = 0; 9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 call_management_function = 0; 9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int call_interface_num = -1; 9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int data_interface_num; 9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long quirks; 95386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int num_rx_buf; 95461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 955a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum int combined_interfaces = 0; 9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 95786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* normal quirks */ 9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds quirks = (unsigned long)id->driver_info; 95986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR; 96086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum 96186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* handle quirks deadly to normal probing*/ 9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (quirks == NO_UNION_NORMAL) { 9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, 1); 9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = usb_ifnum_to_if(usb_dev, 0); 9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto skip_normal_probe; 9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9676e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox 9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* normal probing*/ 9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buffer) { 9709908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "Weird descriptor references\n"); 9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buflen) { 9756e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (intf->cur_altsetting->endpoint->extralen && 9766e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox intf->cur_altsetting->endpoint->extra) { 9776e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 9786e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "Seeking extra descriptors on endpoint\n"); 9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen = intf->cur_altsetting->endpoint->extralen; 9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer = intf->cur_altsetting->endpoint->extra; 9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 9829908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, 9839908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman "Zero length descriptor references\n"); 9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (buflen > 0) { 9896e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (buffer[1] != USB_DT_CS_INTERFACE) { 9909908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "skipping garbage\n"); 9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto next_desc; 9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox switch (buffer[2]) { 9956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_UNION_TYPE: /* we've found it */ 9966e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (union_header) { 9976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_err(&intf->dev, "More than one " 9986e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "union descriptor, skipping ...\n"); 9996e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox goto next_desc; 10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox union_header = (struct usb_cdc_union_desc *)buffer; 10026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 10036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ 10046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox cfd = (struct usb_cdc_country_functional_desc *)buffer; 10056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 10066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_HEADER_TYPE: /* maybe check version */ 10076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; /* for now we ignore it */ 10086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_ACM_TYPE: 10096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox ac_management_function = buffer[3]; 10106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 10116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_CALL_MANAGEMENT_TYPE: 10126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox call_management_function = buffer[3]; 10136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox call_interface_num = buffer[4]; 10146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if ((call_management_function & 3) != 3) 10156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n"); 10166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 10176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox default: 10186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox /* there are LOTS more CDC descriptors that 10196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox * could legitimately be found here. 10206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox */ 10216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "Ignoring descriptor: " 10226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "type %02x, length %d\n", 10236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox buffer[2], buffer[0]); 10246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 10256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox } 10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc: 10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen -= buffer[0]; 10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer += buffer[0]; 10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!union_header) { 10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (call_interface_num > 0) { 10336e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n"); 10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); 10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = intf; 10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 1037a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (intf->cur_altsetting->desc.bNumEndpoints != 3) { 1038a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum dev_dbg(&intf->dev,"No union descriptor, giving up\n"); 1039a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum return -ENODEV; 1040a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } else { 1041a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n"); 1042a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum combined_interfaces = 1; 1043a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum control_interface = data_interface = intf; 1044a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum goto look_for_collapsed_interface; 1045a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } 10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); 10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); 10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!control_interface || !data_interface) { 10516e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "no interfaces\n"); 10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10556e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox 10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface_num != call_interface_num) 10576e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n"); 10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1059a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (control_interface == data_interface) { 1060a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum /* some broken devices designed for windows work this way */ 1061a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum dev_warn(&intf->dev,"Control and data interfaces are not separated!\n"); 1062a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum combined_interfaces = 1; 1063a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum /* a popular other OS doesn't use it */ 1064a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum quirks |= NO_CAP_LINE; 1065a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) { 1066a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum dev_err(&intf->dev, "This needs exactly 3 endpoints\n"); 1067a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum return -EINVAL; 1068a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } 1069a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukumlook_for_collapsed_interface: 1070a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum for (i = 0; i < 3; i++) { 1071a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum struct usb_endpoint_descriptor *ep; 1072a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum ep = &data_interface->cur_altsetting->endpoint[i].desc; 1073a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum 1074a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (usb_endpoint_is_int_in(ep)) 1075a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum epctrl = ep; 1076a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum else if (usb_endpoint_is_bulk_out(ep)) 1077a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum epwrite = ep; 1078a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum else if (usb_endpoint_is_bulk_in(ep)) 1079a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum epread = ep; 1080a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum else 1081a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum return -EINVAL; 1082a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } 1083a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (!epctrl || !epread || !epwrite) 1084a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum return -ENODEV; 1085a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum else 1086a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum goto made_compressed_probe; 1087a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } 1088a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum 10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe: 10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /*workaround for switched interfaces */ 10926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (data_interface->cur_altsetting->desc.bInterfaceClass 10936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox != CDC_DATA_INTERFACE_TYPE) { 10946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (control_interface->cur_altsetting->desc.bInterfaceClass 10956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox == CDC_DATA_INTERFACE_TYPE) { 10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *t; 10976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 10986e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "Your device has switched interfaces.\n"); 10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t = control_interface; 11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = data_interface; 11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = t; 11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 110674da5d68a54d66667664fbe233ededab2376a070Alan Stern 110774da5d68a54d66667664fbe233ededab2376a070Alan Stern /* Accept probe requests only for the control interface */ 1108a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (!combined_interfaces && intf != control_interface) 110974da5d68a54d66667664fbe233ededab2376a070Alan Stern return -ENODEV; 11106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox 1111a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (!combined_interfaces && usb_interface_claimed(data_interface)) { 1112a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum /* valid in this context */ 11136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "The data interface isn't available\n"); 11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EBUSY; 11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface->cur_altsetting->desc.bNumEndpoints < 2) 11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epctrl = &control_interface->cur_altsetting->endpoint[0].desc; 11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epread = &data_interface->cur_altsetting->endpoint[0].desc; 11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epwrite = &data_interface->cur_altsetting->endpoint[1].desc; 11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* workaround for switched endpoints */ 112745aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino if (!usb_endpoint_dir_in(epread)) { 11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* descriptors are swapped */ 11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *t; 11306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 11316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "The data interface has switched endpoints\n"); 11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t = epread; 11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epread = epwrite; 11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epwrite = t; 11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1136a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukummade_compressed_probe: 11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("interfaces are valid"); 11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); 11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (minor == ACM_TTY_MINORS) { 11419908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "no more free acm devices\n"); 11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11456e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm = kzalloc(sizeof(struct acm), GFP_KERNEL); 11466e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (acm == NULL) { 1147898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n"); 11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail; 11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); 11526e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox readsize = le16_to_cpu(epread->wMaxPacketSize) * 11536e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (quirks == SINGLE_RX_URB ? 1 : 2); 1154a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum acm->combined_interfaces = combined_interfaces; 1155e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; 11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->control = control_interface; 11571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->data = data_interface; 11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->minor = minor; 11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->dev = usb_dev; 11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_caps = ac_management_function; 1161a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (quirks & NO_CAP_LINE) 1162a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum acm->ctrl_caps &= ~USB_CDC_CAP_LINE; 11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlsize = ctrlsize; 11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->readsize = readsize; 116586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum acm->rx_buflimit = num_rx_buf; 116661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->urb_task.func = acm_rx_tasklet; 116761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->urb_task.data = (unsigned long) acm; 1168c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells INIT_WORK(&acm->work, acm_softint); 116911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum INIT_WORK(&acm->waker, acm_waker); 1170e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell init_waitqueue_head(&acm->drain_wait); 11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_init(&acm->throttle_lock); 1172884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_init(&acm->write_lock); 117361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_lock_init(&acm->read_lock); 11741365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_init(&acm->mutex); 117561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); 1176739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox tty_port_init(&acm->port); 1177739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox acm->port.ops = &acm_port_ops; 11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); 11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buf) { 1181898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n"); 11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail2; 11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_buffer = buf; 11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1186884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (acm_write_buffers_alloc(acm) < 0) { 1187898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); 11881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail4; 11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); 11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm->ctrlurb) { 1193898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); 11941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail5; 11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 119686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) { 119761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv = &(acm->ru[i]); 119861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 11996e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox rcv->urb = usb_alloc_urb(0, GFP_KERNEL); 12006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (rcv->urb == NULL) { 12016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 12026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "out of memory (read urbs usb_alloc_urb)\n"); 120361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto alloc_fail7; 120461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 120561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 120661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 120761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->instance = acm; 120861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 120986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) { 1210672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell struct acm_rb *rb = &(acm->rb[i]); 121161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 1212672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell rb->base = usb_buffer_alloc(acm->dev, readsize, 1213672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell GFP_KERNEL, &rb->dma); 1214672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell if (!rb->base) { 12156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 12166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "out of memory (read bufs usb_buffer_alloc)\n"); 121761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto alloc_fail7; 121861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 12191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 12206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < ACM_NW; i++) { 1221e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf struct acm_wb *snd = &(acm->wb[i]); 1222e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf 12236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox snd->urb = usb_alloc_urb(0, GFP_KERNEL); 12246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (snd->urb == NULL) { 12256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 12266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "out of memory (write urbs usb_alloc_urb)"); 1227e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf goto alloc_fail7; 1228e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf } 1229e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf 12306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox usb_fill_bulk_urb(snd->urb, usb_dev, 12316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), 12326e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox NULL, acm->writesize, acm_write_bulk, snd); 1233e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 1234e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf snd->instance = acm; 12351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 12361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12376e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox usb_set_intfdata(intf, acm); 1238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); 1240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) 1241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto alloc_fail8; 1242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (cfd) { /* export the country data */ 1244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); 1245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (!acm->country_codes) 1246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_code_size = cfd->bLength - 4; 12486e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, 12496e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox cfd->bLength - 4); 1250c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_rel_date = cfd->iCountryCodeRelDate; 1251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); 1253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) { 1254c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 1255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 12586e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox i = device_create_file(&intf->dev, 12596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox &dev_attr_iCountryCodeRelDate); 1260c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) { 1261c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 1262c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1263c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1264c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1265c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1266c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries: 12676e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox usb_fill_int_urb(acm->ctrlurb, usb_dev, 1268a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), 1269a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, 1270a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum /* works around buggy devices */ 1271a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum epctrl->bInterval ? epctrl->bInterval : 0xff); 12721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 12731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->transfer_dma = acm->ctrl_dma; 12741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); 12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout); 12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->line.dwDTERate = cpu_to_le32(9600); 12801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->line.bDataBits = 8; 12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_line(acm, &acm->line); 12821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_driver_claim_interface(&acm_driver, data_interface, acm); 1284672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell usb_set_intfdata(data_interface, acm); 12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 128683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_get_intf(control_interface); 128783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk tty_register_device(acm_tty_driver, minor, &control_interface->dev); 12881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_table[minor] = acm; 12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1291c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return 0; 1292c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumalloc_fail8: 1293e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf for (i = 0; i < ACM_NW; i++) 1294e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_free_urb(acm->wb[i].urb); 12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7: 1296830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum acm_read_buffers_free(acm); 129786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) 129861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_free_urb(acm->ru[i].urb); 12991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_free_urb(acm->ctrlurb); 13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5: 1301884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_buffers_free(acm); 13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4: 13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); 13041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2: 13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(acm); 13061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail: 13071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 13091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13101365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm) 13111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 13121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum int i; 131311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("Entering stop_data_traffic"); 13141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum tasklet_disable(&acm->urb_task); 13161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_kill_urb(acm->ctrlurb); 13186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < ACM_NW; i++) 1319e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_kill_urb(acm->wb[i].urb); 13201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum for (i = 0; i < acm->rx_buflimit; i++) 13211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_kill_urb(acm->ru[i].urb); 13221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum tasklet_enable(&acm->urb_task); 13241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum cancel_work_sync(&acm->work); 132611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum cancel_work_sync(&acm->waker); 13271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 13281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf) 13301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1331c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 13321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_device *usb_dev = interface_to_usbdev(intf); 133310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct tty_struct *tty; 13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1335672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell /* sibling interface is already cleaning up */ 1336672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell if (!acm) 133786067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum return; 1338672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell 1339672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell mutex_lock(&open_mutex); 13406e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (acm->country_codes) { 134174da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, 134274da5d68a54d66667664fbe233ededab2376a070Alan Stern &dev_attr_wCountryCodes); 134374da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, 134474da5d68a54d66667664fbe233ededab2376a070Alan Stern &dev_attr_iCountryCodeRelDate); 1345c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 134674da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); 13471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->dev = NULL; 134886067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_set_intfdata(acm->control, NULL); 134986067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_set_intfdata(acm->data, NULL); 13501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13511365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum stop_data_traffic(acm); 13521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1353884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_buffers_free(acm); 13546e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, 13556e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrl_dma); 1356830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum acm_read_buffers_free(acm); 13571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1358a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (!acm->combined_interfaces) 1359a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum usb_driver_release_interface(&acm_driver, intf == acm->control ? 1360830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum acm->data : acm->control); 13611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 136210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->port.count == 0) { 136383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_tty_unregister(acm); 13644186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 13651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 13661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 13671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13684186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 136910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty = tty_port_tty_get(&acm->port); 137010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (tty) { 137110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_hangup(tty); 137210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_kref_put(tty); 137310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } 13741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 13751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1376357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM 13771365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message) 13781365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 13791365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum struct acm *acm = usb_get_intfdata(intf); 138011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int cnt; 138111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 138265bfd2967c906ca322a4bb69a285fe0de8916ac6Alan Stern if (message.event & PM_EVENT_AUTO) { 138311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int b; 138411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 138511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irq(&acm->read_lock); 138611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock(&acm->write_lock); 138711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum b = acm->processing + acm->transmitting; 138811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock(&acm->write_lock); 138911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irq(&acm->read_lock); 139011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (b) 139111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return -EBUSY; 139211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 139311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 139411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irq(&acm->read_lock); 139511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock(&acm->write_lock); 139611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum cnt = acm->susp_count++; 139711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock(&acm->write_lock); 139811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irq(&acm->read_lock); 13991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 140011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (cnt) 14011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 14021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum /* 14031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum we treat opened interfaces differently, 14041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum we must guard against open 14051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum */ 14061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 14071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 140810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->port.count) 14091365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum stop_data_traffic(acm); 14101365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 14111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 14121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 14131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 14141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 14151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf) 14161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 14171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum struct acm *acm = usb_get_intfdata(intf); 14181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum int rv = 0; 141911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int cnt; 14201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 142111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irq(&acm->read_lock); 142211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->susp_count -= 1; 142311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum cnt = acm->susp_count; 142411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irq(&acm->read_lock); 142511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 142611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (cnt) 14271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 14281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 14291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 143010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->port.count) { 14311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); 14321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum if (rv < 0) 143311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum goto err_out; 14341365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 14351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum tasklet_schedule(&acm->urb_task); 14361365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum } 14371365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 14381365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out: 14391365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 14401365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return rv; 14411365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 1442357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum 1443357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */ 14441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 14451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure. 14461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 14471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id acm_ids[] = { 14491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* quirky and broken devices */ 14501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */ 14511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 14521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds }, 1453b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ 1454b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1455b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov }, 14560f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */ 14570f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 14580f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn }, 14598753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */ 14608753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 14618753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote }, 146291a9c9214e34c364bf15406aadb922787ae7129bChris Malley { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */ 146391a9c9214e34c364bf15406aadb922787ae7129bChris Malley .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 146491a9c9214e34c364bf15406aadb922787ae7129bChris Malley }, 14657abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */ 14667abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox .driver_info = SINGLE_RX_URB, 14677abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox }, 146886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */ 146986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum .driver_info = SINGLE_RX_URB, /* firmware bug */ 147086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum }, 14713dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */ 14723dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum .driver_info = SINGLE_RX_URB, /* firmware bug */ 14733dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum }, 14749be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */ 14759be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 14769be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum }, 14776149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */ 14786149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 14796149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane }, 1480c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */ 1481c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1482c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen }, 1483c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */ 1484c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1485c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox }, 1486cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */ 1487cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1488cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian }, 1489155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */ 1490155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev }, 1491c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */ 1492c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on 1493c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter data interface instead of 1494c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter communications interface. 1495c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter Maybe we should define a new 1496c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter quirk for this. */ 1497c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter }, 14981f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */ 14991f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ 15001f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin }, 15019be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum 15021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* control interfaces with various AT-command sets */ 15031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_V25TER) }, 15051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_PCCA101) }, 15071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) }, 15091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_GSM) }, 15111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox USB_CDC_ACM_PROTO_AT_3G) }, 15131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_CDMA) }, 15151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */ 15171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { } 15181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 15191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DEVICE_TABLE(usb, acm_ids); 15211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = { 15231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = "cdc_acm", 15241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .probe = acm_probe, 15251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .disconnect = acm_disconnect, 1526357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM 15271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .suspend = acm_suspend, 15281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .resume = acm_resume, 1529357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif 15301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .id_table = acm_ids, 1531357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM 15321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .supports_autosuspend = 1, 1533357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif 15341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 15351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 15371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures. 15381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 15391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1540b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = { 15411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .open = acm_tty_open, 15421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .close = acm_tty_close, 154310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox .hangup = acm_tty_hangup, 15441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write = acm_tty_write, 15451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write_room = acm_tty_write_room, 15461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .ioctl = acm_tty_ioctl, 15471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .throttle = acm_tty_throttle, 15481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .unthrottle = acm_tty_unthrottle, 15491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .chars_in_buffer = acm_tty_chars_in_buffer, 15501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .break_ctl = acm_tty_break_ctl, 15511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .set_termios = acm_tty_set_termios, 15521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .tiocmget = acm_tty_tiocmget, 15531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .tiocmset = acm_tty_tiocmset, 15541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 15551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 15571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit. 15581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 15591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void) 15611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 15621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval; 15631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS); 15641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm_tty_driver) 15651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 15661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->owner = THIS_MODULE, 15671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->driver_name = "acm", 15681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->name = "ttyACM", 15691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->major = ACM_TTY_MAJOR, 15701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->minor_start = 0, 15711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, 15721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->subtype = SERIAL_TYPE_NORMAL, 1573331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; 15741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->init_termios = tty_std_termios; 15756e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | 15766e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox HUPCL | CLOCAL; 15771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_set_operations(acm_tty_driver, &acm_ops); 15781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = tty_register_driver(acm_tty_driver); 15801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) { 15811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 15821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 15831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 15841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = usb_register(&acm_driver); 15861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) { 15871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_unregister_driver(acm_tty_driver); 15881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 15891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 15901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 15911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15925909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" 15935909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman DRIVER_DESC "\n"); 15941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 15961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 15971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void) 15991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 16001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_deregister(&acm_driver); 16011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_unregister_driver(acm_tty_driver); 16021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 16031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 16041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init); 16061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit); 16071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_AUTHOR(DRIVER_AUTHOR); 16096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DESCRIPTION(DRIVER_DESC); 16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 1611e766aeb882b41355d8732cf49aa9412baef852c5Scott James RemnantMODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR); 1612