cdc-acm.c revision cab98a0a349829b145d924c0649a2d30cd6a9e3d
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * cdc-acm.c 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de> 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Pavel Machek <pavel@suse.cz> 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Johannes Erdfelt <johannes@erdfelt.com> 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2004 Oliver Neukum <oliver@neukum.name> 961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek * Copyright (c) 2005 David Kubicek <dave@awk.cz> 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB Abstract Control Model driver for USB modems and ISDN adapters 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Sponsored by SuSE 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ChangeLog: 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.9 - thorough cleaning, URBification, almost a rewrite 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.10 - some more cleanups 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.11 - fixed flow control, read error doesn't stop reads 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.13 - added termios, added hangup 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.14 - sized down struct acm 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.15 - fixed flow control again - characters could be lost 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.16 - added code for modems with swapped data and control interfaces 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.17 - added new style probing 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.18 - fixed new style probing for devices with more configurations 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan) 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.20 - switched to probing on interface (rather than device) class 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.21 - revert to probing on device for devices with multiple configs 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.22 - probe only the control interface. if usbcore doesn't choose the 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * config we want, sysadmin changes bConfigurationValue in sysfs. 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.23 - use softirq for rx processing, as needed by tty layer 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * v0.24 - change probe method to evaluate CDC union descriptor 3361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek * v0.25 - downstream tasks paralelized to maximize throughput 34e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf * v0.26 - multiple write urbs, writesize increased 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version. 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful, 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details. 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG 54e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#undef VERBOSE_DEBUG 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h> 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h> 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h> 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h> 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h> 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 644186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h> 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h> 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h> 67a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h> 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h> 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h> 7061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h> 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h" 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 74e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 75e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */ 76e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 80e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf#define DRIVER_VERSION "v0.26" 8161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver; 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver; 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS]; 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 884186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex); 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACM_READY(acm) (acm && acm->dev && acm->used) 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 92e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#ifdef VERBOSE_DEBUG 93e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define verbose 1 94e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#else 95e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#define verbose 0 96e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#endif 97e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages. 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len) 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds request, USB_RT_ACM, value, 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->control->altsetting[0].desc.bInterfaceNumber, 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf, len, 5000); 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval); 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval < 0 ? retval : 0; 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests. 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do... 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \ 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0) 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \ 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)) 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \ 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 123884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management. 124884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller. 125884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 126884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 127884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm) 128884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 129884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i, wbn; 130884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 131884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 132e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf wbn = 0; 133884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum i = 0; 134884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum for (;;) { 135884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 136884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!wb->use) { 137884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->use = 1; 138884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return wbn; 139884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 14086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum wbn = (wbn + 1) % ACM_NW; 14186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (++i >= ACM_NW) 142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -1; 143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 144884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 145884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 146884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm) 147884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 148884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i, n; 149e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell unsigned long flags; 150884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 15186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum n = ACM_NW; 152e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell spin_lock_irqsave(&acm->write_lock, flags); 15386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < ACM_NW; i++) { 15486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum n -= acm->wb[i].use; 155884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 156e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell spin_unlock_irqrestore(&acm->write_lock, flags); 157884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return n; 158884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 159884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 160884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 161ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips * Finish write. Caller must hold acm->write_lock 162884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 163e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic void acm_write_done(struct acm *acm, struct acm_wb *wb) 164884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 165e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf wb->use = 0; 16611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->transmitting--; 167884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 168884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 169884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write. 17111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * 17211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking 173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 17411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 17511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb) 17611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{ 17711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int rc; 17811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 17911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->transmitting++; 18011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 18111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->transfer_buffer = wb->buf; 18211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->transfer_dma = wb->dmah; 18311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->transfer_buffer_length = wb->len; 18411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->dev = acm->dev; 18511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 18611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) { 18711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("usb_submit_urb(write bulk) failed: %d", rc); 18811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm_write_done(acm, wb); 18911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 19011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return rc; 19111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum} 19211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 193e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn) 194884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 195884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 196934da4635c2d05cef474e5243ef05df95b2ad264David Brownell struct acm_wb *wb = &acm->wb[wbn]; 197884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int rc; 198884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 199884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 200884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!acm->dev) { 201934da4635c2d05cef474e5243ef05df95b2ad264David Brownell wb->use = 0; 202884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -ENODEV; 204884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 205884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 20611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("%s susp_count: %d", __func__, acm->susp_count); 20711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (acm->susp_count) { 20811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->delayed_wb = wb; 20911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum schedule_work(&acm->waker); 21011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 21111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return 0; /* A white lie */ 21211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 21311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_mark_last_busy(acm->dev); 21411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 21511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum rc = acm_start_wb(acm, wb); 216884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 217884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 218884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return rc; 21911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 220884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 221c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/* 222c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs 223c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */ 224c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps 225c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 226c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 227c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 230c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return sprintf(buf, "%d", acm->ctrl_caps); 231c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); 233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes 235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 237c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum memcpy(buf, acm->country_codes, acm->country_code_size); 241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return acm->country_code_size; 242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); 245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date 247c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 249c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 250c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 251c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 252c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return sprintf(buf, "%d", acm->country_rel_date); 253c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 254884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 255c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); 256884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */ 2617d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb) 2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = urb->context; 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_notification *dr = urb->transfer_buffer; 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char *data; 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int newctrl; 267185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman int retval; 268185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman int status = urb->status; 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 270185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman switch (status) { 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0: 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* success */ 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ECONNRESET: 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ENOENT: 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ESHUTDOWN: 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* this urb is terminated, clean up */ 278441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison dbg("%s - urb shutting down with status: %d", __func__, status); 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 281441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison dbg("%s - nonzero urb status received: %d", __func__, status); 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto exit; 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto exit; 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data = (unsigned char *)(dr + 1); 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (dr->bNotificationType) { 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_NOTIFY_NETWORK_CONNECTION: 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("%s network", dr->wValue ? "connected to" : "disconnected from"); 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_NOTIFY_SERIAL_STATE: 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 298a5abdeafedf722b0f3f357f4a23089a686b1b80dHarvey Harrison newctrl = get_unaligned_le16(data); 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("calling hangup"); 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_hangup(acm->tty); 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin = newctrl; 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c", 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-', 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("unknown notification %d received: index %d len %d data0 %d data1 %d", 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dr->bNotificationType, dr->wIndex, 3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dr->wLength, data[0], data[1]); 3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit: 32211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_mark_last_busy(acm->dev); 323185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman retval = usb_submit_urb (urb, GFP_ATOMIC); 324185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman if (retval) 3259908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with " 3269908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman "result %d", __func__, retval); 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */ 3307d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_read_bulk(struct urb *urb) 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 33261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb *buf; 33361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv = urb->context; 33461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm *acm = rcv->instance; 33586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int status = urb->status; 336185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman 337185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman dbg("Entering acm_read_bulk with status %d", status); 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 33911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (!ACM_READY(acm)) { 34011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dev_dbg(&acm->data->dev, "Aborting, acm not ready"); 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 34211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 34311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_mark_last_busy(acm->dev); 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 34586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (status) 346898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&acm->data->dev, "bulk rx status %d\n", status); 3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 34861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = rcv->buffer; 34961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf->size = urb->actual_length; 35061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 35186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (likely(status == 0)) { 35286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_lock(&acm->read_lock); 35311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->processing++; 35486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&rcv->list, &acm->spare_read_urbs); 35586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&buf->list, &acm->filled_read_bufs); 35686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_unlock(&acm->read_lock); 35786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum } else { 35886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* we drop the buffer due to an error */ 35986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_lock(&acm->read_lock); 36086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&rcv->list, &acm->spare_read_urbs); 36186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add(&buf->list, &acm->spare_read_bufs); 36286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_unlock(&acm->read_lock); 36386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* nevertheless the tasklet must be kicked unconditionally 36486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum so the queue cannot dry up */ 36586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum } 36611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (likely(!acm->susp_count)) 36711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum tasklet_schedule(&acm->urb_task); 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm) 3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = (void *)_acm; 37361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb *buf; 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct tty_struct *tty = acm->tty; 37561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv; 376762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski unsigned long flags; 377ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum unsigned char throttled; 37811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Entering acm_rx_tasklet"); 3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 381ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (!ACM_READY(acm)) 38211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum { 38311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("acm_rx_tasklet: ACM not ready"); 384ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum return; 38511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 386ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum 387834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum spin_lock_irqsave(&acm->throttle_lock, flags); 388ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum throttled = acm->throttle; 389834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum spin_unlock_irqrestore(&acm->throttle_lock, flags); 390ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (throttled) 39111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum { 39211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("acm_rx_tasklet: throttled"); 39361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 39411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 39561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 39661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer: 397762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 39861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (list_empty(&acm->filled_read_bufs)) { 399762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 40061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto urbs; 4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 40261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = list_entry(acm->filled_read_bufs.next, 40361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb, list); 40461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&buf->list); 405762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 40661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 4073dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size); 40861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 40933f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox tty_buffer_request_room(tty, buf->size); 410834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum spin_lock_irqsave(&acm->throttle_lock, flags); 411ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum throttled = acm->throttle; 412834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum spin_unlock_irqrestore(&acm->throttle_lock, flags); 413ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (!throttled) 41433f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox tty_insert_flip_string(tty, buf->base, buf->size); 41561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tty_flip_buffer_push(tty); 4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 417ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (throttled) { 418ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum dbg("Throttling noticed"); 419762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 42061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->filled_read_bufs); 421762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 425762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 42661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->spare_read_bufs); 427762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 42861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto next_buffer; 42961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 43061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs: 43161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek while (!list_empty(&acm->spare_read_bufs)) { 432762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 43361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (list_empty(&acm->spare_read_urbs)) { 43411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->processing = 0; 435762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 43661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 43761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 43861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv = list_entry(acm->spare_read_urbs.next, 43961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru, list); 44061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&rcv->list); 441762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 44261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 44361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = list_entry(acm->spare_read_bufs.next, 44461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb, list); 44561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&buf->list); 44661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 44761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->buffer = buf; 44861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 44961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_fill_bulk_urb(rcv->urb, acm->dev, 45061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->rx_endpoint, 45161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf->base, 45261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->readsize, 45361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm_read_bulk, rcv); 45461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_dma = buf->dma; 45561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 45661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 45761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek /* This shouldn't kill the driver as unsuccessful URBs are returned to the 45861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek free-urbs-pool and resubmited ASAP */ 45911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irqsave(&acm->read_lock, flags); 46011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (acm->susp_count || usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { 46161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->spare_read_bufs); 46261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&rcv->list, &acm->spare_read_urbs); 46311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->processing = 0; 464762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 46561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 46611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } else { 46711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irqrestore(&acm->read_lock, flags); 46811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); 46961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 47061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 47111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irqsave(&acm->read_lock, flags); 47211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->processing = 0; 47311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irqrestore(&acm->read_lock, flags); 4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */ 4777d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb) 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 479cdc97792289179974af6dda781c855696358d307Ming Lei struct acm_wb *wb = urb->context; 480e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell struct acm *acm = wb->instance; 481ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips unsigned long flags; 4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 483e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell if (verbose || urb->status 484e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell || (urb->actual_length != urb->transfer_buffer_length)) 485e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n", 486e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell urb->actual_length, 487e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell urb->transfer_buffer_length, 488e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell urb->status); 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 490ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips spin_lock_irqsave(&acm->write_lock, flags); 491e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf acm_write_done(acm, wb); 492ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips spin_unlock_irqrestore(&acm->write_lock, flags); 493884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (ACM_READY(acm)) 494884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum schedule_work(&acm->work); 495e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell else 496e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell wake_up_interruptible(&acm->drain_wait); 4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 499c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work) 5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 501c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells struct acm *acm = container_of(work, struct acm, work); 502e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 503e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell dev_vdbg(&acm->data->dev, "tx work\n"); 5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_wakeup(acm->tty); 5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 50911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic void acm_waker(struct work_struct *waker) 51011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{ 51111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum struct acm *acm = container_of(waker, struct acm, waker); 51211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int rv; 51311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 51411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum rv = usb_autopm_get_interface(acm->control); 51511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (rv < 0) { 5169908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__); 51711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return; 51811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 51911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (acm->delayed_wb) { 52011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm_start_wb(acm, acm->delayed_wb); 52111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->delayed_wb = NULL; 52211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 52311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_autopm_put_interface(acm->control); 52411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum} 52511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers 5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp) 5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm; 5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rv = -EINVAL; 53461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 5353dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("Entering acm_tty_open."); 5364186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven 5374186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_lock(&open_mutex); 5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm = acm_table[tty->index]; 5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm || !acm->dev) 5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto err_out; 5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rv = 0; 5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 54528d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91David Engraf set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); 5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty->driver_data = acm; 5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->tty = tty; 5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 54994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum if (usb_autopm_get_interface(acm->control) < 0) 55094409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum goto early_bail; 55111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum else 55211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->control->needs_remote_wakeup = 1; 5531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 5541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->used++) { 5561365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_autopm_put_interface(acm->control); 5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto done; 5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5601365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->dev = acm->dev; 5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { 5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("usb_submit_urb(ctrl irq) failed"); 5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto bail_out; 5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 567ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && 568ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum (acm->ctrl_caps & USB_CDC_CAP_LINE)) 5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto full_bailout; 57011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_autopm_put_interface(acm->control); 5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 57261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->spare_read_urbs); 57361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->spare_read_bufs); 57461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->filled_read_bufs); 57586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < acm->rx_buflimit; i++) { 57661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&(acm->ru[i].list), &acm->spare_read_urbs); 57761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 57886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < acm->rx_buflimit; i++) { 57961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&(acm->rb[i].list), &acm->spare_read_bufs); 58061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 58161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 582ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum acm->throttle = 0; 583ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum 58461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_schedule(&acm->urb_task); 5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone: 5871365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 58874573ee7096a4ffc2f098108d21c85801b9c7434Alexey Dobriyanerr_out: 58994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum mutex_unlock(&open_mutex); 5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rv; 5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout: 5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_kill_urb(acm->ctrlurb); 5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out: 5951365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_autopm_put_interface(acm->control); 5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->used--; 5971365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 59894409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukumearly_bail: 59994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum mutex_unlock(&open_mutex); 6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 60383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm) 60483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{ 60586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int i,nr; 60661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 60786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum nr = acm->rx_buflimit; 60883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk tty_unregister_device(acm_tty_driver, acm->minor); 60983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_put_intf(acm->control); 61083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_table[acm->minor] = NULL; 61183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_free_urb(acm->ctrlurb); 612e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf for (i = 0; i < ACM_NW; i++) 613e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_free_urb(acm->wb[i].urb); 61486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < nr; i++) 61561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_free_urb(acm->ru[i].urb); 616c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 61783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk kfree(acm); 61883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk} 61983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk 620e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownellstatic int acm_tty_chars_in_buffer(struct tty_struct *tty); 621e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp) 6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 62586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int i,nr; 6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm || !acm->used) 6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 63086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum nr = acm->rx_buflimit; 6314186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_lock(&open_mutex); 6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!--acm->used) { 6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->dev) { 63411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_autopm_get_interface(acm->control); 6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout = 0); 636e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 637e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell /* try letting the last writes drain naturally */ 638e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell wait_event_interruptible_timeout(acm->drain_wait, 639e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell (ACM_NW == acm_wb_is_avail(acm)) 640e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell || !acm->dev, 641e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell ACM_CLOSE_TIMEOUT * HZ); 642e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_kill_urb(acm->ctrlurb); 644e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf for (i = 0; i < ACM_NW; i++) 645e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_kill_urb(acm->wb[i].urb); 64686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < nr; i++) 64761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_kill_urb(acm->ru[i].urb); 64811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->control->needs_remote_wakeup = 0; 6491365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_autopm_put_interface(acm->control); 65083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk } else 65183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_tty_unregister(acm); 6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6534186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) 6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int stat; 660884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 661884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int wbn; 662884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 663884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 6643dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("Entering acm_tty_write to write %d bytes,", count); 6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!count) 6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 671884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 672884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if ((wbn = acm_wb_alloc(acm)) < 0) { 673884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 674884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 675884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 676884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 678884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum count = (count > acm->writesize) ? acm->writesize : count; 6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Get %d bytes...", count); 680884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum memcpy(wb->buf, buf, count); 681884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->len = count; 682884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 684e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf if ((stat = acm_write_start(acm, wbn)) < 0) 6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return stat; 6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return count; 6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty) 6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 694884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum /* 695884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Do not let the line discipline to know that we have a reserve, 696884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * or it might get too enthusiastic. 697884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 698934da4635c2d05cef474e5243ef05df95b2ad264David Brownell return acm_wb_is_avail(acm) ? acm->writesize : 0; 6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty) 7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 706884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum /* 707884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * This is inaccurate (overcounts), but it works. 708884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 70986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize; 7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty) 7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(&acm->throttle_lock); 7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->throttle = 1; 7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(&acm->throttle_lock); 7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty) 7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(&acm->throttle_lock); 7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->throttle = 0; 7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(&acm->throttle_lock); 73061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_schedule(&acm->urb_task); 7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7339e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state) 7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7369e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox int retval; 7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7389e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox return -EINVAL; 7399e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox retval = acm_send_break(acm, state ? 0xffff : 0); 7409e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox if (retval < 0) 7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("send break failed"); 7429e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox return retval; 7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmget(struct tty_struct *tty, struct file *file) 7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | 7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | 7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | 7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | 7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | 7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds TIOCM_CTS; 7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmset(struct tty_struct *tty, struct file *file, 7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int set, unsigned int clear) 7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int newctrl; 7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = acm->ctrlout; 7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); 7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0); 7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = (newctrl & ~clear) | set; 7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->ctrlout == newctrl) 7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return acm_set_control(acm, acm->ctrlout = newctrl); 7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) 7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOIOCTLCMD; 7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7904c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = { 7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0, 50, 75, 110, 134, 150, 200, 300, 600, 7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1200, 1800, 2400, 4800, 9600, 19200, 38400, 7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 57600, 115200, 230400, 460800, 500000, 576000, 7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 921600, 1000000, 1152000, 1500000, 2000000, 7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2500000, 3000000, 3500000, 4000000 7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7984c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = { 7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5, 6, 7, 8 8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 802606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Coxstatic void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old) 8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 805606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox struct ktermios *termios = tty->termios; 8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_line_coding newline; 8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int newctrl = acm->ctrlout; 8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.dwDTERate = cpu_to_le32p(acm_tty_speed + 8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0)); 8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0; 8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bParityType = termios->c_cflag & PARENB ? 8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; 8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; 8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->clocal = ((termios->c_cflag & CLOCAL) != 0); 8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!newline.dwDTERate) { 8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.dwDTERate = acm->line.dwDTERate; 8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl &= ~ACM_CTRL_DTR; 8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else newctrl |= ACM_CTRL_DTR; 8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (newctrl != acm->ctrlout) 8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout = newctrl); 8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (memcmp(&acm->line, &newline, sizeof newline)) { 8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(&acm->line, &newline, sizeof newline); 8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate), 8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bCharFormat, newline.bParityType, 8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bDataBits); 8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_line(acm, &acm->line); 8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines. 8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 842830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */ 843884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm) 844884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 845884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i; 846884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 847a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum struct usb_device *usb_dev = interface_to_usbdev(acm->control); 848884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 84986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { 850a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah); 851884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 852884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 853884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 854830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm) 855830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{ 856830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum struct usb_device *usb_dev = interface_to_usbdev(acm->control); 857830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum int i, n = acm->rx_buflimit; 858830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum 859830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum for (i = 0; i < n; i++) 860830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); 861830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum} 862830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum 863884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */ 864884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm) 865884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 866884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i; 867884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 868884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 86986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { 870884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL, 871884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum &wb->dmah); 872884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!wb->buf) { 873884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum while (i != 0) { 874884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum --i; 875884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum --wb; 876884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum usb_buffer_free(acm->dev, acm->writesize, 877884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->buf, wb->dmah); 878884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 879884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -ENOMEM; 880884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 881884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 882884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 883884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 884884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_probe (struct usb_interface *intf, 8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds const struct usb_device_id *id) 8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_union_desc *union_header = NULL; 889c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_cdc_country_functional_desc *cfd = NULL; 890c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell unsigned char *buffer = intf->altsetting->extra; 8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int buflen = intf->altsetting->extralen; 8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *control_interface; 8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *data_interface; 8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *epctrl; 8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *epread; 8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *epwrite; 8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_device *usb_dev = interface_to_usbdev(intf); 8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm; 8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int minor; 9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ctrlsize,readsize; 9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 *buf; 9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 ac_management_function = 0; 9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 call_management_function = 0; 9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int call_interface_num = -1; 9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int data_interface_num; 9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long quirks; 90786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int num_rx_buf; 90861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 91086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* normal quirks */ 9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds quirks = (unsigned long)id->driver_info; 91286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR; 91386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum 91486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* handle quirks deadly to normal probing*/ 9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (quirks == NO_UNION_NORMAL) { 9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, 1); 9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = usb_ifnum_to_if(usb_dev, 0); 9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto skip_normal_probe; 9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* normal probing*/ 9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buffer) { 9239908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "Weird descriptor references\n"); 9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buflen) { 9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) { 929898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n"); 9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen = intf->cur_altsetting->endpoint->extralen; 9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer = intf->cur_altsetting->endpoint->extra; 9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 9339908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, 9349908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman "Zero length descriptor references\n"); 9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (buflen > 0) { 9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (buffer [1] != USB_DT_CS_INTERFACE) { 9419908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "skipping garbage\n"); 9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto next_desc; 9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (buffer [2]) { 9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_UNION_TYPE: /* we've found it */ 9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (union_header) { 9489908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "More than one " 9499908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman "union descriptor, " 9509908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman "skipping ...\n"); 9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto next_desc; 9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds union_header = (struct usb_cdc_union_desc *) 9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer; 9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 956c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ 957c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum cfd = (struct usb_cdc_country_functional_desc *)buffer; 958c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum break; 9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_HEADER_TYPE: /* maybe check version */ 9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; /* for now we ignore it */ 9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_ACM_TYPE: 9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ac_management_function = buffer[3]; 9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_CALL_MANAGEMENT_TYPE: 9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds call_management_function = buffer[3]; 9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds call_interface_num = buffer[4]; 9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((call_management_function & 3) != 3) 9689908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "This device " 9699908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman "cannot do calls on its own. " 9709908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman "It is no modem.\n"); 9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 973c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell /* there are LOTS more CDC descriptors that 974c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell * could legitimately be found here. 975c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell */ 976c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell dev_dbg(&intf->dev, "Ignoring descriptor: " 977c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell "type %02x, length %d\n", 978c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell buffer[2], buffer[0]); 9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc: 9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen -= buffer[0]; 9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer += buffer[0]; 9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!union_header) { 9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (call_interface_num > 0) { 988898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n"); 9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); 9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = intf; 9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 992898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"No union descriptor, giving up\n"); 9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); 9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); 9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!control_interface || !data_interface) { 999898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"no interfaces\n"); 10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface_num != call_interface_num) 1005dc0d5c1e5c7532e800fff6e313cd4af44af99976Joe Perches dev_dbg(&intf->dev,"Separate call control interface. That is not fully supported.\n"); 10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe: 10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /*workaround for switched interfaces */ 10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) { 10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) { 10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *t; 1013898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"Your device has switched interfaces.\n"); 10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t = control_interface; 10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = data_interface; 10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = t; 10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 102274da5d68a54d66667664fbe233ededab2376a070Alan Stern 102374da5d68a54d66667664fbe233ededab2376a070Alan Stern /* Accept probe requests only for the control interface */ 102474da5d68a54d66667664fbe233ededab2376a070Alan Stern if (intf != control_interface) 102574da5d68a54d66667664fbe233ededab2376a070Alan Stern return -ENODEV; 10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (usb_interface_claimed(data_interface)) { /* valid in this context */ 1028898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"The data interface isn't available\n"); 10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EBUSY; 10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface->cur_altsetting->desc.bNumEndpoints < 2) 10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epctrl = &control_interface->cur_altsetting->endpoint[0].desc; 10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epread = &data_interface->cur_altsetting->endpoint[0].desc; 10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epwrite = &data_interface->cur_altsetting->endpoint[1].desc; 10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* workaround for switched endpoints */ 104245aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino if (!usb_endpoint_dir_in(epread)) { 10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* descriptors are swapped */ 10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *t; 1045898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"The data interface has switched endpoints\n"); 10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t = epread; 10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epread = epwrite; 10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epwrite = t; 10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("interfaces are valid"); 10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); 10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (minor == ACM_TTY_MINORS) { 10559908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "no more free acm devices\n"); 10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 105946f116eab81b21c6ae8c4f169498c632b1f94bf1Oliver Neukum if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) { 1060898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n"); 10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail; 10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); 106586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2); 1066e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; 10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->control = control_interface; 10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->data = data_interface; 10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->minor = minor; 10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->dev = usb_dev; 10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_caps = ac_management_function; 10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlsize = ctrlsize; 10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->readsize = readsize; 107486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum acm->rx_buflimit = num_rx_buf; 107561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->urb_task.func = acm_rx_tasklet; 107661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->urb_task.data = (unsigned long) acm; 1077c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells INIT_WORK(&acm->work, acm_softint); 107811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum INIT_WORK(&acm->waker, acm_waker); 1079e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell init_waitqueue_head(&acm->drain_wait); 10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_init(&acm->throttle_lock); 1081884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_init(&acm->write_lock); 108261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_lock_init(&acm->read_lock); 10831365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_init(&acm->mutex); 108461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); 10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); 10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buf) { 1088898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n"); 10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail2; 10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_buffer = buf; 10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1093884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (acm_write_buffers_alloc(acm) < 0) { 1094898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); 10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail4; 10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); 10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm->ctrlurb) { 1100898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); 11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail5; 11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 110386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) { 110461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv = &(acm->ru[i]); 110561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 110661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) { 1107898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n"); 110861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto alloc_fail7; 110961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 111061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 111161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 111261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->instance = acm; 111361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 111486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) { 1115672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell struct acm_rb *rb = &(acm->rb[i]); 111661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 1117672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell rb->base = usb_buffer_alloc(acm->dev, readsize, 1118672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell GFP_KERNEL, &rb->dma); 1119672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell if (!rb->base) { 1120898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n"); 112161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto alloc_fail7; 112261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1124e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf for(i = 0; i < ACM_NW; i++) 1125e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf { 1126e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf struct acm_wb *snd = &(acm->wb[i]); 1127e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf 1128e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) { 1129e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf dev_dbg(&intf->dev, "out of memory (write urbs usb_alloc_urb)"); 1130e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf goto alloc_fail7; 1131e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf } 1132e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf 1133e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), 1134e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf NULL, acm->writesize, acm_write_bulk, snd); 1135e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 1136e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf snd->instance = acm; 11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1139c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum usb_set_intfdata (intf, acm); 1140c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1141c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); 1142c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) 1143c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto alloc_fail8; 1144c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1145c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (cfd) { /* export the country data */ 1146c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); 1147c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (!acm->country_codes) 1148c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1149c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_code_size = cfd->bLength - 4; 1150c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4); 1151c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_rel_date = cfd->iCountryCodeRelDate; 1152c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1153c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); 1154c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) { 1155c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 1156c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1157c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1158c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1159c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate); 1160c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) { 1161c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 1162c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1163c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1164c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1165c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1166c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries: 11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), 11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); 11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->transfer_dma = acm->ctrl_dma; 11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); 11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout); 11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->line.dwDTERate = cpu_to_le32(9600); 11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->line.bDataBits = 8; 11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_line(acm, &acm->line); 11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_driver_claim_interface(&acm_driver, data_interface, acm); 1181672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell usb_set_intfdata(data_interface, acm); 11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 118383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_get_intf(control_interface); 118483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk tty_register_device(acm_tty_driver, minor, &control_interface->dev); 11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_table[minor] = acm; 11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1188c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return 0; 1189c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumalloc_fail8: 1190e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf for (i = 0; i < ACM_NW; i++) 1191e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_free_urb(acm->wb[i].urb); 11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7: 1193830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum acm_read_buffers_free(acm); 119486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) 119561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_free_urb(acm->ru[i].urb); 11961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_free_urb(acm->ctrlurb); 11971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5: 1198884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_buffers_free(acm); 11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4: 12001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); 12011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2: 12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(acm); 12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail: 12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm) 12081365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 12091365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum int i; 121011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum dbg("Entering stop_data_traffic"); 12111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 12121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum tasklet_disable(&acm->urb_task); 12131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 12141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_kill_urb(acm->ctrlurb); 1215e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf for(i = 0; i < ACM_NW; i++) 1216e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_kill_urb(acm->wb[i].urb); 12171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum for (i = 0; i < acm->rx_buflimit; i++) 12181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_kill_urb(acm->ru[i].urb); 12191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 12201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum tasklet_enable(&acm->urb_task); 12211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 12221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum cancel_work_sync(&acm->work); 122311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum cancel_work_sync(&acm->waker); 12241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 12251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 12261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf) 12271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 12291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_device *usb_dev = interface_to_usbdev(intf); 12301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1231672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell /* sibling interface is already cleaning up */ 1232672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell if (!acm) 123386067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum return; 1234672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell 1235672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell mutex_lock(&open_mutex); 1236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (acm->country_codes){ 123774da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, 123874da5d68a54d66667664fbe233ededab2376a070Alan Stern &dev_attr_wCountryCodes); 123974da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, 124074da5d68a54d66667664fbe233ededab2376a070Alan Stern &dev_attr_iCountryCodeRelDate); 1241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 124274da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); 12431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->dev = NULL; 124486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_set_intfdata(acm->control, NULL); 124586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_set_intfdata(acm->data, NULL); 12461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12471365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum stop_data_traffic(acm); 12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1249884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_buffers_free(acm); 12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); 1251830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum acm_read_buffers_free(acm); 12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1253830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum usb_driver_release_interface(&acm_driver, intf == acm->control ? 1254830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum acm->data : acm->control); 12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm->used) { 125783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_tty_unregister(acm); 12584186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12624186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->tty) 12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_hangup(acm->tty); 12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 12671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1268357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM 12691365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message) 12701365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 12711365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum struct acm *acm = usb_get_intfdata(intf); 127211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int cnt; 127311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 127465bfd2967c906ca322a4bb69a285fe0de8916ac6Alan Stern if (message.event & PM_EVENT_AUTO) { 127511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int b; 127611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 127711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irq(&acm->read_lock); 127811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock(&acm->write_lock); 127911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum b = acm->processing + acm->transmitting; 128011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock(&acm->write_lock); 128111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irq(&acm->read_lock); 128211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (b) 128311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return -EBUSY; 128411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 128511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 128611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irq(&acm->read_lock); 128711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock(&acm->write_lock); 128811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum cnt = acm->susp_count++; 128911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock(&acm->write_lock); 129011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irq(&acm->read_lock); 12911365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 129211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (cnt) 12931365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 12941365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum /* 12951365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum we treat opened interfaces differently, 12961365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum we must guard against open 12971365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum */ 12981365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 12991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13001365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum if (acm->used) 13011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum stop_data_traffic(acm); 13021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 13041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 13051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 13061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf) 13081365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 13091365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum struct acm *acm = usb_get_intfdata(intf); 13101365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum int rv = 0; 131111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int cnt; 13121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 131311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irq(&acm->read_lock); 131411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->susp_count -= 1; 131511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum cnt = acm->susp_count; 131611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irq(&acm->read_lock); 131711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 131811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (cnt) 13191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 13201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 13221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum if (acm->used) { 13231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); 13241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum if (rv < 0) 132511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum goto err_out; 13261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum tasklet_schedule(&acm->urb_task); 13281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum } 13291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out: 13311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 13321365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return rv; 13331365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 1334357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum 1335357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */ 13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure. 13381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id acm_ids[] = { 13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* quirky and broken devices */ 13421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */ 13431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds }, 1345b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ 1346b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1347b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov }, 13480f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */ 13490f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 13500f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn }, 13518753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */ 13528753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 13538753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote }, 135491a9c9214e34c364bf15406aadb922787ae7129bChris Malley { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */ 135591a9c9214e34c364bf15406aadb922787ae7129bChris Malley .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 135691a9c9214e34c364bf15406aadb922787ae7129bChris Malley }, 13577abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */ 13587abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox .driver_info = SINGLE_RX_URB, 13597abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox }, 136086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */ 136186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum .driver_info = SINGLE_RX_URB, /* firmware bug */ 136286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum }, 13633dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */ 13643dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum .driver_info = SINGLE_RX_URB, /* firmware bug */ 13653dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum }, 13669be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */ 13679be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 13689be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum }, 13696149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */ 13706149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 13716149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane }, 1372c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */ 1373c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1374c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen }, 1375c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */ 1376c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1377c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox }, 1378cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */ 1379cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1380cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian }, 1381155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */ 1382155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev }, 1383c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */ 1384c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on 1385c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter data interface instead of 1386c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter communications interface. 1387c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter Maybe we should define a new 1388c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter quirk for this. */ 1389c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter }, 13909be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum 13911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* control interfaces with various AT-command sets */ 13921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 13931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_V25TER) }, 13941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 13951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_PCCA101) }, 13961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 13971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) }, 13981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 13991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_GSM) }, 14001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 14011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_3G ) }, 14021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 14031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_CDMA) }, 14041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */ 14061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { } 14071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 14081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE (usb, acm_ids); 14101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = { 14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = "cdc_acm", 14131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .probe = acm_probe, 14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .disconnect = acm_disconnect, 1415357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM 14161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .suspend = acm_suspend, 14171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .resume = acm_resume, 1418357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif 14191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .id_table = acm_ids, 1420357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM 14211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .supports_autosuspend = 1, 1422357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif 14231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 14241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 14261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures. 14271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 14281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1429b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = { 14301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .open = acm_tty_open, 14311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .close = acm_tty_close, 14321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write = acm_tty_write, 14331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write_room = acm_tty_write_room, 14341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .ioctl = acm_tty_ioctl, 14351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .throttle = acm_tty_throttle, 14361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .unthrottle = acm_tty_unthrottle, 14371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .chars_in_buffer = acm_tty_chars_in_buffer, 14381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .break_ctl = acm_tty_break_ctl, 14391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .set_termios = acm_tty_set_termios, 14401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .tiocmget = acm_tty_tiocmget, 14411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .tiocmset = acm_tty_tiocmset, 14421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 14431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 14451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit. 14461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 14471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void) 14491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 14501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval; 14511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS); 14521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm_tty_driver) 14531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 14541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->owner = THIS_MODULE, 14551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->driver_name = "acm", 14561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->name = "ttyACM", 14571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->major = ACM_TTY_MAJOR, 14581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->minor_start = 0, 14591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, 14601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->subtype = SERIAL_TYPE_NORMAL, 1461331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; 14621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->init_termios = tty_std_termios; 14631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; 14641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_set_operations(acm_tty_driver, &acm_ops); 14651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = tty_register_driver(acm_tty_driver); 14671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) { 14681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 14691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 14701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 14711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = usb_register(&acm_driver); 14731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) { 14741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_unregister_driver(acm_tty_driver); 14751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 14761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 14771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 14781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14795909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" 14805909f6ea2bc7f785ceb1bed14c670946a536ff2dGreg Kroah-Hartman DRIVER_DESC "\n"); 14811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 14831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 14841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void) 14861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 14871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_deregister(&acm_driver); 14881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_unregister_driver(acm_tty_driver); 14891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 14901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 14911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init); 14931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit); 14941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR( DRIVER_AUTHOR ); 14961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION( DRIVER_DESC ); 14971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 1498e766aeb882b41355d8732cf49aa9412baef852c5Scott James RemnantMODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR); 1499