cdc-acm.c revision 7893afc035590383a14b176c1497cba984276ef4
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * cdc-acm.c 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de> 5a2531293dbb7608fa672ff28efe3ab4027917a2fPavel Machek * Copyright (c) 1999 Pavel Machek <pavel@ucw.cz> 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Johannes Erdfelt <johannes@erdfelt.com> 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2004 Oliver Neukum <oliver@neukum.name> 961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek * Copyright (c) 2005 David Kubicek <dave@awk.cz> 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB Abstract Control Model driver for USB modems and ISDN adapters 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Sponsored by SuSE 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 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> 627af25b4b34a2439020d78da765a3bed0ff73f25cOliver Neukum#include <linux/serial.h> 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h> 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h> 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 664186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h> 6710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#include <linux/uaccess.h> 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h> 69a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h> 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h> 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h> 7261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h> 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h" 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 76e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 77e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */ 78e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 82e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf#define DRIVER_VERSION "v0.26" 8361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver; 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver; 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS]; 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 904186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex); 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#define ACM_READY(acm) (acm && acm->dev && acm->port.count) 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 94739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Coxstatic const struct tty_port_operations acm_port_ops = { 95739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox}; 96739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox 97e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#ifdef VERBOSE_DEBUG 98e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define verbose 1 99e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#else 100e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define verbose 0 101e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#endif 102e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages. 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_ctrl_msg(struct acm *acm, int request, int value, 1086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox void *buf, int len) 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds request, USB_RT_ACM, value, 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->control->altsetting[0].desc.bInterfaceNumber, 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf, len, 5000); 1146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", 1156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox request, value, len, retval); 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval < 0 ? retval : 0; 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests. 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do... 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \ 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0) 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \ 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)) 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \ 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 130884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management. 131884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller. 132884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 133884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 134884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm) 135884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 136884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i, wbn; 137884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 138884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 139e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf wbn = 0; 140884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum i = 0; 141884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum for (;;) { 142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!wb->use) { 144884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->use = 1; 145884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return wbn; 146884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 14786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum wbn = (wbn + 1) % ACM_NW; 14886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (++i >= ACM_NW) 149884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -1; 150884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 151884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 152884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 153884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm) 154884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 155884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i, n; 156e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell unsigned long flags; 157884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 15886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum n = ACM_NW; 159e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell spin_lock_irqsave(&acm->write_lock, flags); 1606e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < ACM_NW; i++) 16186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum n -= acm->wb[i].use; 162e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell spin_unlock_irqrestore(&acm->write_lock, flags); 163884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return n; 164884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 165884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 166884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 167ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips * Finish write. Caller must hold acm->write_lock 168884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 169e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic void acm_write_done(struct acm *acm, struct acm_wb *wb) 170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 171e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf wb->use = 0; 17211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->transmitting--; 17397d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum usb_autopm_put_interface_async(acm->control); 174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 175884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 176884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 177884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write. 17811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * 17911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking 180884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 18111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 18211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb) 18311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{ 18411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int rc; 18511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 18611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->transmitting++; 18711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 18811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->transfer_buffer = wb->buf; 18911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->transfer_dma = wb->dmah; 19011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->transfer_buffer_length = wb->len; 19111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->dev = acm->dev; 19211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 1936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox rc = usb_submit_urb(wb->urb, GFP_ATOMIC); 1946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (rc < 0) { 19511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("usb_submit_urb(write bulk) failed: %d", rc); 19611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm_write_done(acm, wb); 19711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 19811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return rc; 19911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum} 20011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 201e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn) 202884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 204934da4635c2d05cef474e5243ef05df95b2ad264David Brownell struct acm_wb *wb = &acm->wb[wbn]; 205884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int rc; 206884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 207884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 208884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!acm->dev) { 209934da4635c2d05cef474e5243ef05df95b2ad264David Brownell wb->use = 0; 210884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 211884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -ENODEV; 212884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 213884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 21411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("%s susp_count: %d", __func__, acm->susp_count); 21597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum usb_autopm_get_interface_async(acm->control); 21611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (acm->susp_count) { 21797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum if (!acm->delayed_wb) 21897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum acm->delayed_wb = wb; 21997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum else 22097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum usb_autopm_put_interface_async(acm->control); 22111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 22211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return 0; /* A white lie */ 22311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 22411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_mark_last_busy(acm->dev); 22511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 22611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum rc = acm_start_wb(acm, wb); 227884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 228884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 229884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return rc; 23011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 231884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/* 233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs 234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */ 235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps 236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 237c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return sprintf(buf, "%d", acm->ctrl_caps); 242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); 244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes 246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 249c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 250c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum memcpy(buf, acm->country_codes, acm->country_code_size); 252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return acm->country_code_size; 253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 254c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); 256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date 258c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 259c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 260c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 261c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 262c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 263c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return sprintf(buf, "%d", acm->country_rel_date); 264c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 265884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 266c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); 267884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */ 2727d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb) 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = urb->context; 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_notification *dr = urb->transfer_buffer; 27610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct tty_struct *tty; 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char *data; 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int newctrl; 279185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman int retval; 280185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman int status = urb->status; 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 282185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman switch (status) { 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0: 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* success */ 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ECONNRESET: 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ENOENT: 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ESHUTDOWN: 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* this urb is terminated, clean up */ 290441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison dbg("%s - urb shutting down with status: %d", __func__, status); 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 293441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison dbg("%s - nonzero urb status received: %d", __func__, status); 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto exit; 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto exit; 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data = (unsigned char *)(dr + 1); 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (dr->bNotificationType) { 3026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_NOTIFY_NETWORK_CONNECTION: 3036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dbg("%s network", dr->wValue ? 3046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "connected to" : "disconnected from"); 3056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_NOTIFY_SERIAL_STATE: 3086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox tty = tty_port_tty_get(&acm->port); 3096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox newctrl = get_unaligned_le16(data); 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (tty) { 3126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (!acm->clocal && 3136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { 3146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dbg("calling hangup"); 3156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox tty_hangup(tty); 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox tty_kref_put(tty); 3186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox } 3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin = newctrl; 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c", 3236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', 3246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', 3256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', 3266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_RI ? '+' : '-', 3276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', 3286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', 3296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3326e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox default: 3336e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dbg("unknown notification %d received: index %d len %d data0 %d data1 %d", 3346e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dr->bNotificationType, dr->wIndex, 3356e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dr->wLength, data[0], data[1]); 3366e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit: 33911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_mark_last_busy(acm->dev); 3406e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox retval = usb_submit_urb(urb, GFP_ATOMIC); 341185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman if (retval) 3429908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with " 3439908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman "result %d", __func__, retval); 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */ 3477d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_read_bulk(struct urb *urb) 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 34961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb *buf; 35061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv = urb->context; 35161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm *acm = rcv->instance; 35286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int status = urb->status; 353185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman 354185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman dbg("Entering acm_read_bulk with status %d", status); 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 35611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (!ACM_READY(acm)) { 35711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dev_dbg(&acm->data->dev, "Aborting, acm not ready"); 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 35911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 36011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_mark_last_busy(acm->dev); 3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 36286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (status) 363898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&acm->data->dev, "bulk rx status %d\n", status); 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 36561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = rcv->buffer; 36661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf->size = urb->actual_length; 36761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 36886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (likely(status == 0)) { 36986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_lock(&acm->read_lock); 37011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->processing++; 37186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&rcv->list, &acm->spare_read_urbs); 37286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&buf->list, &acm->filled_read_bufs); 37386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_unlock(&acm->read_lock); 37486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum } else { 37586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* we drop the buffer due to an error */ 37686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_lock(&acm->read_lock); 37786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&rcv->list, &acm->spare_read_urbs); 37886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add(&buf->list, &acm->spare_read_bufs); 37986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_unlock(&acm->read_lock); 38086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* nevertheless the tasklet must be kicked unconditionally 38186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum so the queue cannot dry up */ 38286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum } 38311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (likely(!acm->susp_count)) 38411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum tasklet_schedule(&acm->urb_task); 3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm) 3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = (void *)_acm; 39061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb *buf; 39110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct tty_struct *tty; 39261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv; 393762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski unsigned long flags; 394ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum unsigned char throttled; 39511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Entering acm_rx_tasklet"); 3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 39810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (!ACM_READY(acm)) { 39911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("acm_rx_tasklet: ACM not ready"); 400ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum return; 40111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 402ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum 403834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum spin_lock_irqsave(&acm->throttle_lock, flags); 404ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum throttled = acm->throttle; 405834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum spin_unlock_irqrestore(&acm->throttle_lock, flags); 40610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (throttled) { 40711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("acm_rx_tasklet: throttled"); 40861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 40911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 41061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 41110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty = tty_port_tty_get(&acm->port); 41210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 41361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer: 414762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 41561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (list_empty(&acm->filled_read_bufs)) { 416762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 41761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto urbs; 4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 41961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = list_entry(acm->filled_read_bufs.next, 42061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb, list); 42161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&buf->list); 422762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 42361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 4243dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size); 42561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 42610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (tty) { 42710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox spin_lock_irqsave(&acm->throttle_lock, flags); 42810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox throttled = acm->throttle; 42910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox spin_unlock_irqrestore(&acm->throttle_lock, flags); 43010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (!throttled) { 43110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_insert_flip_string(tty, buf->base, buf->size); 43210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_flip_buffer_push(tty); 43310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } else { 43410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_kref_put(tty); 43510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox dbg("Throttling noticed"); 43610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox spin_lock_irqsave(&acm->read_lock, flags); 43710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox list_add(&buf->list, &acm->filled_read_bufs); 43810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox spin_unlock_irqrestore(&acm->read_lock, flags); 43910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox return; 44010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } 4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 443762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 44461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->spare_read_bufs); 445762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 44661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto next_buffer; 44761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 44861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs: 44910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_kref_put(tty); 45010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 45161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek while (!list_empty(&acm->spare_read_bufs)) { 452762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 45361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (list_empty(&acm->spare_read_urbs)) { 45411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->processing = 0; 455762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 45661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 45761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 45861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv = list_entry(acm->spare_read_urbs.next, 45961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru, list); 46061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&rcv->list); 461762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 46261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 46361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = list_entry(acm->spare_read_bufs.next, 46461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb, list); 46561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&buf->list); 46661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 46761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->buffer = buf; 46861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 469cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum if (acm->is_int_ep) 4705186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev usb_fill_int_urb(rcv->urb, acm->dev, 4715186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev acm->rx_endpoint, 4725186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev buf->base, 4735186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev acm->readsize, 474cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum acm_read_bulk, rcv, acm->bInterval); 4755186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev else 4765186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev usb_fill_bulk_urb(rcv->urb, acm->dev, 4775186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev acm->rx_endpoint, 4785186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev buf->base, 4795186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev acm->readsize, 4805186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev acm_read_bulk, rcv); 48161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_dma = buf->dma; 48261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 48361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 4846e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox /* This shouldn't kill the driver as unsuccessful URBs are 4856e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox returned to the free-urbs-pool and resubmited ASAP */ 48611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irqsave(&acm->read_lock, flags); 4876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (acm->susp_count || 4886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { 48961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->spare_read_bufs); 49061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&rcv->list, &acm->spare_read_urbs); 49111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->processing = 0; 492762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 49361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 49411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } else { 49511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irqrestore(&acm->read_lock, flags); 49611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); 49761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 49861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 49911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irqsave(&acm->read_lock, flags); 50011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->processing = 0; 50111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irqrestore(&acm->read_lock, flags); 5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */ 5057d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb) 5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 507cdc97792289179974af6dda781c855696358d307Ming Lei struct acm_wb *wb = urb->context; 508e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell struct acm *acm = wb->instance; 509ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips unsigned long flags; 5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 511e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell if (verbose || urb->status 512e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell || (urb->actual_length != urb->transfer_buffer_length)) 513e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n", 514e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell urb->actual_length, 515e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell urb->transfer_buffer_length, 516e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell urb->status); 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 518ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips spin_lock_irqsave(&acm->write_lock, flags); 519e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf acm_write_done(acm, wb); 520ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips spin_unlock_irqrestore(&acm->write_lock, flags); 521884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (ACM_READY(acm)) 522884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum schedule_work(&acm->work); 523e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell else 524e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell wake_up_interruptible(&acm->drain_wait); 5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 527c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work) 5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 529c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells struct acm *acm = container_of(work, struct acm, work); 53010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct tty_struct *tty; 531e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 532e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell dev_vdbg(&acm->data->dev, "tx work\n"); 5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 53510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty = tty_port_tty_get(&acm->port); 53610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_wakeup(tty); 53710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_kref_put(tty); 5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers 5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp) 5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm; 54742dd2aa6496a2e87e496aac5494d2e1d6096c85bThadeu Lima de Souza Cascardo int rv = -ENODEV; 54861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 5493dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("Entering acm_tty_open."); 5504186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven 5514186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_lock(&open_mutex); 5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm = acm_table[tty->index]; 5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm || !acm->dev) 5552b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum goto out; 5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rv = 0; 5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 55928d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91David Engraf set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); 56010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty->driver_data = acm; 56210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_tty_set(&acm->port, tty); 5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 56494409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum if (usb_autopm_get_interface(acm->control) < 0) 56594409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum goto early_bail; 56611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum else 56711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->control->needs_remote_wakeup = 1; 5681365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 5691365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 57010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->port.count++) { 5712b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum mutex_unlock(&acm->mutex); 5721365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_autopm_put_interface(acm->control); 5732b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum goto out; 57410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } 5751365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->dev = acm->dev; 5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { 5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("usb_submit_urb(ctrl irq) failed"); 5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto bail_out; 5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 582ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && 583ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum (acm->ctrl_caps & USB_CDC_CAP_LINE)) 5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto full_bailout; 58510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 58611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_autopm_put_interface(acm->control); 5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 58861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->spare_read_urbs); 58961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->spare_read_bufs); 59061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->filled_read_bufs); 5916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox 5926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < acm->rx_buflimit; i++) 59361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&(acm->ru[i].list), &acm->spare_read_urbs); 5946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < acm->rx_buflimit; i++) 59561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&(acm->rb[i].list), &acm->spare_read_bufs); 59661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 597ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum acm->throttle = 0; 598ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum 5997af25b4b34a2439020d78da765a3bed0ff73f25cOliver Neukum set_bit(ASYNCB_INITIALIZED, &acm->port.flags); 60010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox rv = tty_port_block_til_ready(&acm->port, tty, filp); 60118a77b5d237a67d2c621a46f5271a3b51da1b380Henry Gebhardt tasklet_schedule(&acm->urb_task); 6022b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum 6031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 6042b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukumout: 60594409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum mutex_unlock(&open_mutex); 6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rv; 6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout: 6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_kill_urb(acm->ctrlurb); 6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out: 61110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox acm->port.count--; 6121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 6132b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum usb_autopm_put_interface(acm->control); 61494409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukumearly_bail: 61594409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum mutex_unlock(&open_mutex); 61610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_tty_set(&acm->port, NULL); 6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 62083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm) 62183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{ 62210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox int i, nr; 62361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 62486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum nr = acm->rx_buflimit; 62583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk tty_unregister_device(acm_tty_driver, acm->minor); 62683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_put_intf(acm->control); 62783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_table[acm->minor] = NULL; 62883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_free_urb(acm->ctrlurb); 629e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf for (i = 0; i < ACM_NW; i++) 630e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_free_urb(acm->wb[i].urb); 63186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < nr; i++) 63261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_free_urb(acm->ru[i].urb); 633c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 63483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk kfree(acm); 63583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk} 63683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk 637e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownellstatic int acm_tty_chars_in_buffer(struct tty_struct *tty); 638e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 6394e608671674b62e97166f903830d5553e37970e8Arnd Bergmannstatic void acm_port_down(struct acm *acm) 64010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{ 64110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox int i, nr = acm->rx_buflimit; 64210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox mutex_lock(&open_mutex); 64310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->dev) { 64410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_autopm_get_interface(acm->control); 64510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox acm_set_control(acm, acm->ctrlout = 0); 64610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_kill_urb(acm->ctrlurb); 64710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox for (i = 0; i < ACM_NW; i++) 64810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_kill_urb(acm->wb[i].urb); 64910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox for (i = 0; i < nr; i++) 65010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_kill_urb(acm->ru[i].urb); 65110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox acm->control->needs_remote_wakeup = 0; 65210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_autopm_put_interface(acm->control); 65310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } 65410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox mutex_unlock(&open_mutex); 65510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox} 65610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 65710077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_tty_hangup(struct tty_struct *tty) 65810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{ 65910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct acm *acm = tty->driver_data; 66010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_hangup(&acm->port); 6614e608671674b62e97166f903830d5553e37970e8Arnd Bergmann acm_port_down(acm); 66210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox} 66310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp) 6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 66810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox /* Perform the closing process and see if we need to do the hardware 66910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox shutdown */ 670051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra if (!acm) 6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 672051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra if (tty_port_close_start(&acm->port, tty, filp) == 0) { 673051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra mutex_lock(&open_mutex); 674051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra if (!acm->dev) { 675051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra tty_port_tty_set(&acm->port, NULL); 676051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra acm_tty_unregister(acm); 677051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra tty->driver_data = NULL; 678051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra } 679051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra mutex_unlock(&open_mutex); 680051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra return; 681051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra } 6824e608671674b62e97166f903830d5553e37970e8Arnd Bergmann acm_port_down(acm); 68310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_close_end(&acm->port, tty); 68410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_tty_set(&acm->port, NULL); 6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_write(struct tty_struct *tty, 6886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox const unsigned char *buf, int count) 6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int stat; 692884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 693884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int wbn; 694884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 695884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 6963dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("Entering acm_tty_write to write %d bytes,", count); 6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!count) 7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 703884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 7046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox wbn = acm_wb_alloc(acm); 7056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (wbn < 0) { 706884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 707884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 708884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 709884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 711884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum count = (count > acm->writesize) ? acm->writesize : count; 7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Get %d bytes...", count); 713884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum memcpy(wb->buf, buf, count); 714884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->len = count; 715884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox stat = acm_write_start(acm, wbn); 7186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (stat < 0) 7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return stat; 7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return count; 7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty) 7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 728884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum /* 729884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Do not let the line discipline to know that we have a reserve, 730884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * or it might get too enthusiastic. 731884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 732934da4635c2d05cef474e5243ef05df95b2ad264David Brownell return acm_wb_is_avail(acm) ? acm->writesize : 0; 7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty) 7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 73923198fda7182969b619613a555f8645fdc3dc334Alan Cox return 0; 740884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum /* 741884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * This is inaccurate (overcounts), but it works. 742884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 74386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize; 7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty) 7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(&acm->throttle_lock); 7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->throttle = 1; 7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(&acm->throttle_lock); 7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty) 7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(&acm->throttle_lock); 7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->throttle = 0; 7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(&acm->throttle_lock); 76461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_schedule(&acm->urb_task); 7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7679e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state) 7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7709e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox int retval; 7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7729e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox return -EINVAL; 7739e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox retval = acm_send_break(acm, state ? 0xffff : 0); 7749e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox if (retval < 0) 7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("send break failed"); 7769e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox return retval; 7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmget(struct tty_struct *tty, struct file *file) 7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | 7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | 7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | 7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | 7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | 7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds TIOCM_CTS; 7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmset(struct tty_struct *tty, struct file *file, 7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int set, unsigned int clear) 7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int newctrl; 7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = acm->ctrlout; 8046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | 8056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); 8066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | 8076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0); 8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = (newctrl & ~clear) | set; 8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->ctrlout == newctrl) 8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return acm_set_control(acm, acm->ctrlout = newctrl); 8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_ioctl(struct tty_struct *tty, struct file *file, 8176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox unsigned int cmd, unsigned long arg) 8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOIOCTLCMD; 8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8274c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = { 8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0, 50, 75, 110, 134, 150, 200, 300, 600, 8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1200, 1800, 2400, 4800, 9600, 19200, 38400, 8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 57600, 115200, 230400, 460800, 500000, 576000, 8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 921600, 1000000, 1152000, 1500000, 2000000, 8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2500000, 3000000, 3500000, 4000000 8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8354c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = { 8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5, 6, 7, 8 8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8396e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic void acm_tty_set_termios(struct tty_struct *tty, 8406e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox struct ktermios *termios_old) 8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 843606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox struct ktermios *termios = tty->termios; 8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_line_coding newline; 8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int newctrl = acm->ctrlout; 8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8509b80fee149a875a6292b2556ab2c64dc7ab7d6f5Alan Cox newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty)); 8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0; 8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bParityType = termios->c_cflag & PARENB ? 8536e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (termios->c_cflag & PARODD ? 1 : 2) + 8546e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (termios->c_cflag & CMSPAR ? 2 : 0) : 0; 8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; 8566e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox /* FIXME: Needs to clear unsupported bits in the termios */ 8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->clocal = ((termios->c_cflag & CLOCAL) != 0); 8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!newline.dwDTERate) { 8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.dwDTERate = acm->line.dwDTERate; 8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl &= ~ACM_CTRL_DTR; 8626e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox } else 8636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox newctrl |= ACM_CTRL_DTR; 8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (newctrl != acm->ctrlout) 8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout = newctrl); 8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (memcmp(&acm->line, &newline, sizeof newline)) { 8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(&acm->line, &newline, sizeof newline); 8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate), 8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bCharFormat, newline.bParityType, 8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bDataBits); 8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_line(acm, &acm->line); 8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines. 8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 881830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */ 882884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm) 883884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 884884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i; 885884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 886a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum struct usb_device *usb_dev = interface_to_usbdev(acm->control); 887884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 8886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) 889997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah); 890884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 891884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 892830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm) 893830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{ 894830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum struct usb_device *usb_dev = interface_to_usbdev(acm->control); 895830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum int i, n = acm->rx_buflimit; 896830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum 897830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum for (i = 0; i < n; i++) 898997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack usb_free_coherent(usb_dev, acm->readsize, 899997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack acm->rb[i].base, acm->rb[i].dma); 900830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum} 901830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum 902884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */ 903884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm) 904884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 905884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i; 906884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 907884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 90886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { 909997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL, 910884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum &wb->dmah); 911884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!wb->buf) { 912884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum while (i != 0) { 913884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum --i; 914884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum --wb; 915997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack usb_free_coherent(acm->dev, acm->writesize, 916884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->buf, wb->dmah); 917884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 918884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -ENOMEM; 919884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 920884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 921884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 922884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 923884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 92410077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic int acm_probe(struct usb_interface *intf, 92510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox const struct usb_device_id *id) 9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_union_desc *union_header = NULL; 928c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_cdc_country_functional_desc *cfd = NULL; 929c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell unsigned char *buffer = intf->altsetting->extra; 9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int buflen = intf->altsetting->extralen; 9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *control_interface; 9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *data_interface; 933a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum struct usb_endpoint_descriptor *epctrl = NULL; 934a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum struct usb_endpoint_descriptor *epread = NULL; 935a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum struct usb_endpoint_descriptor *epwrite = NULL; 9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_device *usb_dev = interface_to_usbdev(intf); 9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm; 9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int minor; 9396e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox int ctrlsize, readsize; 9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 *buf; 9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 ac_management_function = 0; 9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 call_management_function = 0; 9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int call_interface_num = -1; 9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int data_interface_num; 9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long quirks; 94686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int num_rx_buf; 94761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 948a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum int combined_interfaces = 0; 9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 95086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* normal quirks */ 9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds quirks = (unsigned long)id->driver_info; 95286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR; 95386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum 95486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* handle quirks deadly to normal probing*/ 9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (quirks == NO_UNION_NORMAL) { 9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, 1); 9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = usb_ifnum_to_if(usb_dev, 0); 9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto skip_normal_probe; 9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9606e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox 9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* normal probing*/ 9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buffer) { 9639908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "Weird descriptor references\n"); 9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buflen) { 968577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray if (intf->cur_altsetting->endpoint && 969577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray intf->cur_altsetting->endpoint->extralen && 9706e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox intf->cur_altsetting->endpoint->extra) { 9716e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 9726e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "Seeking extra descriptors on endpoint\n"); 9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen = intf->cur_altsetting->endpoint->extralen; 9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer = intf->cur_altsetting->endpoint->extra; 9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 9769908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, 9779908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman "Zero length descriptor references\n"); 9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (buflen > 0) { 9836e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (buffer[1] != USB_DT_CS_INTERFACE) { 9849908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "skipping garbage\n"); 9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto next_desc; 9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox switch (buffer[2]) { 9896e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_UNION_TYPE: /* we've found it */ 9906e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (union_header) { 9916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_err(&intf->dev, "More than one " 9926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "union descriptor, skipping ...\n"); 9936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox goto next_desc; 9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox union_header = (struct usb_cdc_union_desc *)buffer; 9966e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 9976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ 9986e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox cfd = (struct usb_cdc_country_functional_desc *)buffer; 9996e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 10006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_HEADER_TYPE: /* maybe check version */ 10016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; /* for now we ignore it */ 10026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_ACM_TYPE: 10036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox ac_management_function = buffer[3]; 10046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 10056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_CALL_MANAGEMENT_TYPE: 10066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox call_management_function = buffer[3]; 10076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox call_interface_num = buffer[4]; 1008ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3) 10096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n"); 10106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 10116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox default: 10126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox /* there are LOTS more CDC descriptors that 10136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox * could legitimately be found here. 10146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox */ 10156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "Ignoring descriptor: " 10166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "type %02x, length %d\n", 10176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox buffer[2], buffer[0]); 10186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 10196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox } 10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc: 10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen -= buffer[0]; 10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer += buffer[0]; 10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!union_header) { 10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (call_interface_num > 0) { 10276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n"); 10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); 10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = intf; 10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 1031a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (intf->cur_altsetting->desc.bNumEndpoints != 3) { 1032a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum dev_dbg(&intf->dev,"No union descriptor, giving up\n"); 1033a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum return -ENODEV; 1034a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } else { 1035a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n"); 1036a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum combined_interfaces = 1; 1037a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum control_interface = data_interface = intf; 1038a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum goto look_for_collapsed_interface; 1039a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } 10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); 10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); 10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!control_interface || !data_interface) { 10456e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "no interfaces\n"); 10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10496e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox 10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface_num != call_interface_num) 10516e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n"); 10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1053a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (control_interface == data_interface) { 1054a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum /* some broken devices designed for windows work this way */ 1055a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum dev_warn(&intf->dev,"Control and data interfaces are not separated!\n"); 1056a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum combined_interfaces = 1; 1057a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum /* a popular other OS doesn't use it */ 1058a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum quirks |= NO_CAP_LINE; 1059a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) { 1060a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum dev_err(&intf->dev, "This needs exactly 3 endpoints\n"); 1061a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum return -EINVAL; 1062a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } 1063a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukumlook_for_collapsed_interface: 1064a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum for (i = 0; i < 3; i++) { 1065a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum struct usb_endpoint_descriptor *ep; 1066a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum ep = &data_interface->cur_altsetting->endpoint[i].desc; 1067a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum 1068a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (usb_endpoint_is_int_in(ep)) 1069a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum epctrl = ep; 1070a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum else if (usb_endpoint_is_bulk_out(ep)) 1071a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum epwrite = ep; 1072a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum else if (usb_endpoint_is_bulk_in(ep)) 1073a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum epread = ep; 1074a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum else 1075a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum return -EINVAL; 1076a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } 1077a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (!epctrl || !epread || !epwrite) 1078a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum return -ENODEV; 1079a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum else 1080a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum goto made_compressed_probe; 1081a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } 1082a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum 10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe: 10841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /*workaround for switched interfaces */ 10866e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (data_interface->cur_altsetting->desc.bInterfaceClass 10876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox != CDC_DATA_INTERFACE_TYPE) { 10886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (control_interface->cur_altsetting->desc.bInterfaceClass 10896e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox == CDC_DATA_INTERFACE_TYPE) { 10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *t; 10916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 10926e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "Your device has switched interfaces.\n"); 10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t = control_interface; 10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = data_interface; 10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = t; 10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 110074da5d68a54d66667664fbe233ededab2376a070Alan Stern 110174da5d68a54d66667664fbe233ededab2376a070Alan Stern /* Accept probe requests only for the control interface */ 1102a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (!combined_interfaces && intf != control_interface) 110374da5d68a54d66667664fbe233ededab2376a070Alan Stern return -ENODEV; 11046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox 1105a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (!combined_interfaces && usb_interface_claimed(data_interface)) { 1106a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum /* valid in this context */ 11076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "The data interface isn't available\n"); 11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EBUSY; 11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface->cur_altsetting->desc.bNumEndpoints < 2) 11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epctrl = &control_interface->cur_altsetting->endpoint[0].desc; 11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epread = &data_interface->cur_altsetting->endpoint[0].desc; 11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epwrite = &data_interface->cur_altsetting->endpoint[1].desc; 11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* workaround for switched endpoints */ 112145aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino if (!usb_endpoint_dir_in(epread)) { 11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* descriptors are swapped */ 11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *t; 11246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 11256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "The data interface has switched endpoints\n"); 11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t = epread; 11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epread = epwrite; 11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epwrite = t; 11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1130a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukummade_compressed_probe: 11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("interfaces are valid"); 11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); 11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (minor == ACM_TTY_MINORS) { 11359908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "no more free acm devices\n"); 11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11396e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm = kzalloc(sizeof(struct acm), GFP_KERNEL); 11406e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (acm == NULL) { 1141898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n"); 11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail; 11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); 11466e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox readsize = le16_to_cpu(epread->wMaxPacketSize) * 11476e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (quirks == SINGLE_RX_URB ? 1 : 2); 1148a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum acm->combined_interfaces = combined_interfaces; 1149e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; 11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->control = control_interface; 11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->data = data_interface; 11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->minor = minor; 11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->dev = usb_dev; 11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_caps = ac_management_function; 1155a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (quirks & NO_CAP_LINE) 1156a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum acm->ctrl_caps &= ~USB_CDC_CAP_LINE; 11571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlsize = ctrlsize; 11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->readsize = readsize; 115986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum acm->rx_buflimit = num_rx_buf; 116061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->urb_task.func = acm_rx_tasklet; 116161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->urb_task.data = (unsigned long) acm; 1162c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells INIT_WORK(&acm->work, acm_softint); 1163e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell init_waitqueue_head(&acm->drain_wait); 11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_init(&acm->throttle_lock); 1165884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_init(&acm->write_lock); 116661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_lock_init(&acm->read_lock); 11671365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_init(&acm->mutex); 116861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); 1169cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum acm->is_int_ep = usb_endpoint_xfer_int(epread); 1170cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum if (acm->is_int_ep) 1171cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum acm->bInterval = epread->bInterval; 1172739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox tty_port_init(&acm->port); 1173739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox acm->port.ops = &acm_port_ops; 11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1175997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); 11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buf) { 1177898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n"); 11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail2; 11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_buffer = buf; 11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1182884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (acm_write_buffers_alloc(acm) < 0) { 1183898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); 11841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail4; 11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); 11881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm->ctrlurb) { 1189898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); 11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail5; 11911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 119286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) { 119361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv = &(acm->ru[i]); 119461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 11956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox rcv->urb = usb_alloc_urb(0, GFP_KERNEL); 11966e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (rcv->urb == NULL) { 11976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 11986e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "out of memory (read urbs usb_alloc_urb)\n"); 1199c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin goto alloc_fail6; 120061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 120161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 120261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 120361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->instance = acm; 120461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 120586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) { 1206672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell struct acm_rb *rb = &(acm->rb[i]); 120761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 1208997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack rb->base = usb_alloc_coherent(acm->dev, readsize, 1209672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell GFP_KERNEL, &rb->dma); 1210672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell if (!rb->base) { 12116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 1212997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack "out of memory (read bufs usb_alloc_coherent)\n"); 121361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto alloc_fail7; 121461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 12151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 12166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < ACM_NW; i++) { 1217e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf struct acm_wb *snd = &(acm->wb[i]); 1218e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf 12196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox snd->urb = usb_alloc_urb(0, GFP_KERNEL); 12206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (snd->urb == NULL) { 12216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 12226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "out of memory (write urbs usb_alloc_urb)"); 1223c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin goto alloc_fail8; 1224e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf } 1225e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf 12265186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev if (usb_endpoint_xfer_int(epwrite)) 12275186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev usb_fill_int_urb(snd->urb, usb_dev, 12285186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), 12295186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval); 12305186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev else 12315186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev usb_fill_bulk_urb(snd->urb, usb_dev, 12325186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), 12335186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev NULL, acm->writesize, acm_write_bulk, snd); 1234e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 1235e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf snd->instance = acm; 12361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 12371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12386e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox usb_set_intfdata(intf, acm); 1239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); 1241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) 1242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto alloc_fail8; 1243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (cfd) { /* export the country data */ 1245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); 1246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (!acm->country_codes) 1247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_code_size = cfd->bLength - 4; 12496e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, 12506e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox cfd->bLength - 4); 1251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_rel_date = cfd->iCountryCodeRelDate; 1252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); 1254c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) { 1255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 1256c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1258c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 12596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox i = device_create_file(&intf->dev, 12606e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox &dev_attr_iCountryCodeRelDate); 1261c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) { 1262c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin device_remove_file(&intf->dev, &dev_attr_wCountryCodes); 1263c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 1264c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1265c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1266c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1267c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1268c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries: 12696e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox usb_fill_int_urb(acm->ctrlurb, usb_dev, 1270a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), 1271a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, 1272a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum /* works around buggy devices */ 1273a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum epctrl->bInterval ? epctrl->bInterval : 0xff); 12741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->transfer_dma = acm->ctrl_dma; 12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); 12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout); 12801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->line.dwDTERate = cpu_to_le32(9600); 12821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->line.bDataBits = 8; 12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_line(acm, &acm->line); 12841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_driver_claim_interface(&acm_driver, data_interface, acm); 1286672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell usb_set_intfdata(data_interface, acm); 12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 128883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_get_intf(control_interface); 128983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk tty_register_device(acm_tty_driver, minor, &control_interface->dev); 12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_table[minor] = acm; 12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1293c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return 0; 1294c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumalloc_fail8: 1295e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf for (i = 0; i < ACM_NW; i++) 1296e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_free_urb(acm->wb[i].urb); 12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7: 1298830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum acm_read_buffers_free(acm); 1299c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Linalloc_fail6: 130086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) 130161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_free_urb(acm->ru[i].urb); 13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_free_urb(acm->ctrlurb); 13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5: 1304884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_buffers_free(acm); 13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4: 1306997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); 13071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2: 13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(acm); 13091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail: 13101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 13111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 13121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm) 13141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 13151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum int i; 131611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("Entering stop_data_traffic"); 13171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum tasklet_disable(&acm->urb_task); 13191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_kill_urb(acm->ctrlurb); 13216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < ACM_NW; i++) 1322e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_kill_urb(acm->wb[i].urb); 13231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum for (i = 0; i < acm->rx_buflimit; i++) 13241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_kill_urb(acm->ru[i].urb); 13251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum tasklet_enable(&acm->urb_task); 13271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum cancel_work_sync(&acm->work); 13291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 13301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf) 13321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1333c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_device *usb_dev = interface_to_usbdev(intf); 133510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct tty_struct *tty; 13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1337672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell /* sibling interface is already cleaning up */ 1338672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell if (!acm) 133986067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum return; 1340672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell 1341672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell mutex_lock(&open_mutex); 13426e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (acm->country_codes) { 134374da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, 134474da5d68a54d66667664fbe233ededab2376a070Alan Stern &dev_attr_wCountryCodes); 134574da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, 134674da5d68a54d66667664fbe233ededab2376a070Alan Stern &dev_attr_iCountryCodeRelDate); 1347c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 134874da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); 13491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->dev = NULL; 135086067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_set_intfdata(acm->control, NULL); 135186067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_set_intfdata(acm->data, NULL); 13521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum stop_data_traffic(acm); 13541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1355884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_buffers_free(acm); 1356997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, 1357997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack acm->ctrl_dma); 1358830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum acm_read_buffers_free(acm); 13591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1360a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (!acm->combined_interfaces) 1361a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum usb_driver_release_interface(&acm_driver, intf == acm->control ? 1362830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum acm->data : acm->control); 13631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 136410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->port.count == 0) { 136583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_tty_unregister(acm); 13664186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 13671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 13681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 13691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13704186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 137110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty = tty_port_tty_get(&acm->port); 137210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (tty) { 137310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_hangup(tty); 137410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_kref_put(tty); 137510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } 13761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 13771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1378357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM 13791365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message) 13801365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 13811365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum struct acm *acm = usb_get_intfdata(intf); 138211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int cnt; 138311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 138465bfd2967c906ca322a4bb69a285fe0de8916ac6Alan Stern if (message.event & PM_EVENT_AUTO) { 138511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int b; 138611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 138711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irq(&acm->read_lock); 138811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock(&acm->write_lock); 138911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum b = acm->processing + acm->transmitting; 139011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock(&acm->write_lock); 139111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irq(&acm->read_lock); 139211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (b) 139311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return -EBUSY; 139411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 139511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 139611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irq(&acm->read_lock); 139711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock(&acm->write_lock); 139811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum cnt = acm->susp_count++; 139911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock(&acm->write_lock); 140011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irq(&acm->read_lock); 14011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 140211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (cnt) 14031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 14041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum /* 14051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum we treat opened interfaces differently, 14061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum we must guard against open 14071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum */ 14081365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 14091365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 141010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->port.count) 14111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum stop_data_traffic(acm); 14121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 14131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 14141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 14151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 14161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 14171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf) 14181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 14191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum struct acm *acm = usb_get_intfdata(intf); 142097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum struct acm_wb *wb; 14211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum int rv = 0; 142211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int cnt; 14231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 142411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irq(&acm->read_lock); 142511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->susp_count -= 1; 142611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum cnt = acm->susp_count; 142711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irq(&acm->read_lock); 142811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 142911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (cnt) 14301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 14311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 14321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 143310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->port.count) { 14341365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); 143597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum 143697d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum spin_lock_irq(&acm->write_lock); 143797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum if (acm->delayed_wb) { 143897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum wb = acm->delayed_wb; 143997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum acm->delayed_wb = NULL; 144097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum spin_unlock_irq(&acm->write_lock); 1441f0730924e9e32bb8935c60040a26d94179355088Oliver Neukum acm_start_wb(acm, wb); 144297d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum } else { 144397d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum spin_unlock_irq(&acm->write_lock); 144497d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum } 144597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum 144697d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum /* 144797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum * delayed error checking because we must 144897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum * do the write path at all cost 144997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum */ 14501365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum if (rv < 0) 145111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum goto err_out; 14521365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 14531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum tasklet_schedule(&acm->urb_task); 14541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum } 14551365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 14561365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out: 14571365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 14581365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return rv; 14591365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 1460357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum 1461a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavrastatic int acm_reset_resume(struct usb_interface *intf) 1462a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra{ 1463a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra struct acm *acm = usb_get_intfdata(intf); 1464a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra struct tty_struct *tty; 1465a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra 1466a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra mutex_lock(&acm->mutex); 1467a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra if (acm->port.count) { 1468a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra tty = tty_port_tty_get(&acm->port); 1469a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra if (tty) { 1470a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra tty_hangup(tty); 1471a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra tty_kref_put(tty); 1472a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra } 1473a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra } 1474a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra mutex_unlock(&acm->mutex); 1475a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra return acm_resume(intf); 1476a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra} 1477a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra 1478357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */ 1479c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor 1480c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor#define NOKIA_PCSUITE_ACM_INFO(x) \ 1481c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \ 1482c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \ 1483c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor USB_CDC_ACM_PROTO_VENDOR) 1484c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor 14854035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray#define SAMSUNG_PCSUITE_ACM_INFO(x) \ 14864035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \ 14874035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \ 14884035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray USB_CDC_ACM_PROTO_VENDOR) 14894035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray 14901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 14911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure. 14921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 14931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14946ef4852b1326301f6e9657e99b2c3221be1a3a44Németh Mártonstatic const struct usb_device_id acm_ids[] = { 14951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* quirky and broken devices */ 14961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */ 14971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 14981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds }, 1499b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ 1500b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1501b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov }, 15020f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */ 15030f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 15040f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn }, 15058753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */ 15068753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 15078753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote }, 150891a9c9214e34c364bf15406aadb922787ae7129bChris Malley { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */ 150991a9c9214e34c364bf15406aadb922787ae7129bChris Malley .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 151091a9c9214e34c364bf15406aadb922787ae7129bChris Malley }, 15117abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */ 15127abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox .driver_info = SINGLE_RX_URB, 15137abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox }, 151486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */ 151586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum .driver_info = SINGLE_RX_URB, /* firmware bug */ 151686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum }, 15173dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */ 15183dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum .driver_info = SINGLE_RX_URB, /* firmware bug */ 15193dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum }, 15209be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */ 15219be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 15229be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum }, 15236149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */ 15246149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 15256149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane }, 1526c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */ 1527c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1528c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen }, 1529c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */ 1530c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1531c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox }, 1532cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */ 1533cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1534cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian }, 1535155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */ 1536155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev }, 1537c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */ 1538c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on 1539c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter data interface instead of 1540c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter communications interface. 1541c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter Maybe we should define a new 1542c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter quirk for this. */ 1543c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter }, 15441f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */ 15451f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ 15461f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin }, 1547c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */ 1548c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ 1549c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson }, 15509be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum 1551c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor /* Nokia S60 phones expose two ACM channels. The first is 1552c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor * a modem and is picked up by the standard AT-command 1553c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor * information below. The second is 'vendor-specific' but 1554c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor * is treated as a serial device at the S60 end, so we want 1555c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor * to expose it on Linux too. */ 1556c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */ 1557c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */ 1558c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */ 1559c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */ 1560c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */ 1561c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */ 1562c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */ 1563c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */ 1564c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */ 1565c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */ 1566c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */ 1567c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */ 1568c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */ 1569c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */ 1570c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */ 1571c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */ 1572c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */ 1573c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */ 1574c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */ 1575c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */ 1576c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */ 1577c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */ 1578c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */ 1579c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */ 1580c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */ 1581c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */ 1582c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */ 1583c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */ 1584c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */ 1585c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */ 1586c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */ 1587c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */ 1588c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */ 1589c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */ 1590c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */ 1591c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */ 1592c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */ 1593c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */ 1594c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */ 1595c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */ 1596c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */ 1597c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */ 1598c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */ 159983a4eae9aeed4a69e89e323a105e653ae06e7c1fPrzemo Firszt { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */ 16004035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */ 16014035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */ 16024035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */ 16034035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */ 16044035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */ 16054035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */ 16064035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */ 16074035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */ 16084035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */ 16094035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */ 16104035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */ 1611c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor 1612c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */ 1613c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor 16147c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby /* Support Lego NXT using pbLua firmware */ 1615ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby { USB_DEVICE(0x0694, 0xff00), 1616ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby .driver_info = NOT_A_MODEM, 16177893afc035590383a14b176c1497cba984276ef4Otavio Salvador }, 16187c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby 16195b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes /* control interfaces without any protocol set */ 16205b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 16215b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes USB_CDC_PROTO_NONE) }, 16225b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes 16231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* control interfaces with various AT-command sets */ 16241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 16251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_V25TER) }, 16261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 16271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_PCCA101) }, 16281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) }, 16301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_GSM) }, 16321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 16336e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox USB_CDC_ACM_PROTO_AT_3G) }, 16341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 16351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_CDMA) }, 16361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { } 16381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 16391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16406e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DEVICE_TABLE(usb, acm_ids); 16411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = { 16431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = "cdc_acm", 16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .probe = acm_probe, 16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .disconnect = acm_disconnect, 1646357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM 16471365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .suspend = acm_suspend, 16481365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .resume = acm_resume, 1649a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra .reset_resume = acm_reset_resume, 1650357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif 16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .id_table = acm_ids, 1652357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM 16531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .supports_autosuspend = 1, 1654357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif 16551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 16581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures. 16591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 16601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1661b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = { 16621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .open = acm_tty_open, 16631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .close = acm_tty_close, 166410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox .hangup = acm_tty_hangup, 16651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write = acm_tty_write, 16661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write_room = acm_tty_write_room, 16671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .ioctl = acm_tty_ioctl, 16681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .throttle = acm_tty_throttle, 16691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .unthrottle = acm_tty_unthrottle, 16701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .chars_in_buffer = acm_tty_chars_in_buffer, 16711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .break_ctl = acm_tty_break_ctl, 16721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .set_termios = acm_tty_set_termios, 16731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .tiocmget = acm_tty_tiocmget, 16741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .tiocmset = acm_tty_tiocmset, 16751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 16761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 16781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit. 16791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 16801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void) 16821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 16831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval; 16841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS); 16851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm_tty_driver) 16861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 16871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->owner = THIS_MODULE, 16881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->driver_name = "acm", 16891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->name = "ttyACM", 16901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->major = ACM_TTY_MAJOR, 16911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->minor_start = 0, 16921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, 16931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->subtype = SERIAL_TYPE_NORMAL, 1694331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; 16951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->init_termios = tty_std_termios; 16966e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | 16976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox HUPCL | CLOCAL; 16981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_set_operations(acm_tty_driver, &acm_ops); 16991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 17001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = tty_register_driver(acm_tty_driver); 17011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) { 17021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 17031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 17041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 17051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 17061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = usb_register(&acm_driver); 17071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) { 17081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_unregister_driver(acm_tty_driver); 17091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 17101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 17111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 17121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 17135909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" 17145909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman DRIVER_DESC "\n"); 17151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 17161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 17171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 17181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 17191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void) 17201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 17211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_deregister(&acm_driver); 17221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_unregister_driver(acm_tty_driver); 17231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 17241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 17251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 17261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init); 17271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit); 17281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 17296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_AUTHOR(DRIVER_AUTHOR); 17306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DESCRIPTION(DRIVER_DESC); 17311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 1732e766aeb882b41355d8732cf49aa9412baef852c5Scott James RemnantMODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR); 1733