cdc-acm.c revision 1365baf7249bb2d05e774e7681237b8e86f5007a
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 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version. 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful, 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details. 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h> 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h> 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h> 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h> 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h> 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 624186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h> 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h> 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h> 65a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h> 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h> 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h> 6861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h> 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h" 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 7561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_VERSION "v0.25" 7661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver; 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver; 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS]; 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 834186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex); 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACM_READY(acm) (acm && acm->dev && acm->used) 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages. 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len) 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds request, USB_RT_ACM, value, 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->control->altsetting[0].desc.bInterfaceNumber, 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf, len, 5000); 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval); 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval < 0 ? retval : 0; 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests. 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do... 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \ 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0) 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \ 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)) 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \ 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 112884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management. 113884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller. 114884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 115884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 116884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm) 117884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 118884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i, wbn; 119884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 120884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 121884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wbn = acm->write_current; 122884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum i = 0; 123884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum for (;;) { 124884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 125884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!wb->use) { 126884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->use = 1; 127884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return wbn; 128884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 12986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum wbn = (wbn + 1) % ACM_NW; 13086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (++i >= ACM_NW) 131884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -1; 132884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 133884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 134884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 135884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_wb_free(struct acm *acm, int wbn) 136884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 137884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->wb[wbn].use = 0; 138884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 139884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 140884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm) 141884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i, n; 143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 14486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum n = ACM_NW; 14586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < ACM_NW; i++) { 14686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum n -= acm->wb[i].use; 147884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 148884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return n; 149884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 150884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 151884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic inline int acm_wb_is_used(struct acm *acm, int wbn) 152884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 153884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return acm->wb[wbn].use; 154884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 155884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 156884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 157884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Finish write. 158884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 159884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_done(struct acm *acm) 160884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 161884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 162884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int wbn; 163884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 164884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 165884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->write_ready = 1; 166884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wbn = acm->write_current; 167884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_wb_free(acm, wbn); 16886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum acm->write_current = (wbn + 1) % ACM_NW; 169884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 171884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 172884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write. 174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 175884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_start(struct acm *acm) 176884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 177884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 178884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int wbn; 179884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 180884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int rc; 181884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 182884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 183884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!acm->dev) { 184884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 185884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -ENODEV; 186884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 187884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 188884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!acm->write_ready) { 189884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 190884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; /* A white lie */ 191884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 192884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 193884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wbn = acm->write_current; 194884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!acm_wb_is_used(acm, wbn)) { 195884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 196884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 197884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 198884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 199884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 200884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->write_ready = 0; 201884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 202884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->writeurb->transfer_buffer = wb->buf; 204884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->writeurb->transfer_dma = wb->dmah; 205884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->writeurb->transfer_buffer_length = wb->len; 206884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->writeurb->dev = acm->dev; 207884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 208884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) { 209884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum dbg("usb_submit_urb(write bulk) failed: %d", rc); 210884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_done(acm); 211884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 212884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return rc; 213884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 214c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/* 215c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs 216c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */ 217c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps 218c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 219c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 220c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 221c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 222c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 223c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return sprintf(buf, "%d", acm->ctrl_caps); 224c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 225c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); 226c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 227c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes 228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 230c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 231c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum memcpy(buf, acm->country_codes, acm->country_code_size); 234c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return acm->country_code_size; 235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 236c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 237c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); 238c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 239c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date 240c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 241c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 242c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 243c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 244c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 245c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return sprintf(buf, "%d", acm->country_rel_date); 246c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 247884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 248c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); 249884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */ 2547d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb) 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = urb->context; 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_notification *dr = urb->transfer_buffer; 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char *data; 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int newctrl; 260185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman int retval; 261185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman int status = urb->status; 2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 263185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman switch (status) { 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0: 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* success */ 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ECONNRESET: 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ENOENT: 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ESHUTDOWN: 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* this urb is terminated, clean up */ 271185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman dbg("%s - urb shutting down with status: %d", __FUNCTION__, status); 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 274185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman dbg("%s - nonzero urb status received: %d", __FUNCTION__, status); 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto exit; 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto exit; 2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data = (unsigned char *)(dr + 1); 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (dr->bNotificationType) { 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_NOTIFY_NETWORK_CONNECTION: 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("%s network", dr->wValue ? "connected to" : "disconnected from"); 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_NOTIFY_SERIAL_STATE: 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = le16_to_cpu(get_unaligned((__le16 *) data)); 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("calling hangup"); 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_hangup(acm->tty); 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin = newctrl; 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c", 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-', 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', 3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("unknown notification %d received: index %d len %d data0 %d data1 %d", 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dr->bNotificationType, dr->wIndex, 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dr->wLength, data[0], data[1]); 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit: 315185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman retval = usb_submit_urb (urb, GFP_ATOMIC); 316185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman if (retval) 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err ("%s - usb_submit_urb failed with result %d", 318185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman __FUNCTION__, retval); 3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */ 3227d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_read_bulk(struct urb *urb) 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 32461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb *buf; 32561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv = urb->context; 32661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm *acm = rcv->instance; 32786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int status = urb->status; 328185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman 329185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman dbg("Entering acm_read_bulk with status %d", status); 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 33486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (status) 335898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&acm->data->dev, "bulk rx status %d\n", status); 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 33761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = rcv->buffer; 33861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf->size = urb->actual_length; 33961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 34086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (likely(status == 0)) { 34186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_lock(&acm->read_lock); 34286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&rcv->list, &acm->spare_read_urbs); 34386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&buf->list, &acm->filled_read_bufs); 34486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_unlock(&acm->read_lock); 34586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum } else { 34686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* we drop the buffer due to an error */ 34786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_lock(&acm->read_lock); 34886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&rcv->list, &acm->spare_read_urbs); 34986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add(&buf->list, &acm->spare_read_bufs); 35086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_unlock(&acm->read_lock); 35186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* nevertheless the tasklet must be kicked unconditionally 35286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum so the queue cannot dry up */ 35386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum } 35461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_schedule(&acm->urb_task); 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm) 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = (void *)_acm; 36061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb *buf; 3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct tty_struct *tty = acm->tty; 36261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv; 363762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski unsigned long flags; 364ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum unsigned char throttled; 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Entering acm_rx_tasklet"); 3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 367ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (!ACM_READY(acm)) 368ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum return; 369ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum 370834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum spin_lock_irqsave(&acm->throttle_lock, flags); 371ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum throttled = acm->throttle; 372834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum spin_unlock_irqrestore(&acm->throttle_lock, flags); 373ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (throttled) 37461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 37561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 37661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer: 377762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 37861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (list_empty(&acm->filled_read_bufs)) { 379762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 38061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto urbs; 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 38261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = list_entry(acm->filled_read_bufs.next, 38361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb, list); 38461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&buf->list); 385762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 38661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 3873dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size); 38861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 38933f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox tty_buffer_request_room(tty, buf->size); 390834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum spin_lock_irqsave(&acm->throttle_lock, flags); 391ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum throttled = acm->throttle; 392834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7Oliver Neukum spin_unlock_irqrestore(&acm->throttle_lock, flags); 393ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (!throttled) 39433f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox tty_insert_flip_string(tty, buf->base, buf->size); 39561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tty_flip_buffer_push(tty); 3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 397ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (throttled) { 398ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum dbg("Throttling noticed"); 399762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 40061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->filled_read_bufs); 401762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 405762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 40661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->spare_read_bufs); 407762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 40861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto next_buffer; 40961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 41061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs: 41161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek while (!list_empty(&acm->spare_read_bufs)) { 412762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 41361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (list_empty(&acm->spare_read_urbs)) { 414762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 41561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 41661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 41761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv = list_entry(acm->spare_read_urbs.next, 41861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru, list); 41961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&rcv->list); 420762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 42161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 42261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = list_entry(acm->spare_read_bufs.next, 42361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb, list); 42461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&buf->list); 42561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 42661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->buffer = buf; 42761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 42861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_fill_bulk_urb(rcv->urb, acm->dev, 42961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->rx_endpoint, 43061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf->base, 43161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->readsize, 43261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm_read_bulk, rcv); 43361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_dma = buf->dma; 43461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 43561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 4363dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); 43761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 43861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek /* This shouldn't kill the driver as unsuccessful URBs are returned to the 43961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek free-urbs-pool and resubmited ASAP */ 44061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { 44161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->spare_read_bufs); 442762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_lock_irqsave(&acm->read_lock, flags); 44361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&rcv->list, &acm->spare_read_urbs); 444762f007b05446f5c63268fb2c28646f28959ee4bJarek Poplawski spin_unlock_irqrestore(&acm->read_lock, flags); 44561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 44661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 44761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */ 4517d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb) 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = (struct acm *)urb->context; 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4553dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("Entering acm_write_bulk with status %d", urb->status); 4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 457884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_done(acm); 458884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_start(acm); 459884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (ACM_READY(acm)) 460884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum schedule_work(&acm->work); 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 463c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work) 4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 465c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells struct acm *acm = container_of(work, struct acm, work); 4663dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("Entering acm_softint."); 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_wakeup(acm->tty); 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp) 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm; 4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rv = -EINVAL; 48161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 4823dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("Entering acm_tty_open."); 4834186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven 4844186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_lock(&open_mutex); 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm = acm_table[tty->index]; 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm || !acm->dev) 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto err_out; 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rv = 0; 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty->driver_data = acm; 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->tty = tty; 4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 49561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek /* force low_latency on so that our tty_push actually forces the data through, 49661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek otherwise it is scheduled, and with high data rates data can get lost. */ 49761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tty->low_latency = 1; 4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum if (usb_autopm_get_interface(acm->control)) { 5001365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&open_mutex); 5011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return -EIO; 5021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum } 5031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 5041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 5051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&open_mutex); 5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->used++) { 5071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_autopm_put_interface(acm->control); 5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto done; 5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->dev = acm->dev; 5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { 5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("usb_submit_urb(ctrl irq) failed"); 5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto bail_out; 5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 518ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && 519ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum (acm->ctrl_caps & USB_CDC_CAP_LINE)) 5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto full_bailout; 5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 52261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->spare_read_urbs); 52361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->spare_read_bufs); 52461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->filled_read_bufs); 52586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < acm->rx_buflimit; i++) { 52661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&(acm->ru[i].list), &acm->spare_read_urbs); 52761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 52886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < acm->rx_buflimit; i++) { 52961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&(acm->rb[i].list), &acm->spare_read_bufs); 53061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 53161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 532ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum acm->throttle = 0; 533ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum 53461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_schedule(&acm->urb_task); 5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone: 5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out: 5381365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rv; 5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout: 5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_kill_urb(acm->ctrlurb); 5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out: 5441365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_autopm_put_interface(acm->control); 5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->used--; 5461365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 55083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm) 55183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{ 55286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int i,nr; 55361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 55486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum nr = acm->rx_buflimit; 55583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk tty_unregister_device(acm_tty_driver, acm->minor); 55683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_put_intf(acm->control); 55783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_table[acm->minor] = NULL; 55883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_free_urb(acm->ctrlurb); 55983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_free_urb(acm->writeurb); 56086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < nr; i++) 56161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_free_urb(acm->ru[i].urb); 562c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 56383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk kfree(acm); 56483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk} 56583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk 5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp) 5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 56986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int i,nr; 5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm || !acm->used) 5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 57486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum nr = acm->rx_buflimit; 5754186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_lock(&open_mutex); 5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!--acm->used) { 5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->dev) { 5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout = 0); 5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_kill_urb(acm->ctrlurb); 5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_kill_urb(acm->writeurb); 58186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < nr; i++) 58261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_kill_urb(acm->ru[i].urb); 5831365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_autopm_put_interface(acm->control); 58483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk } else 58583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_tty_unregister(acm); 5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5874186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) 5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int stat; 594884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 595884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int wbn; 596884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 597884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 5983dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum dbg("Entering acm_tty_write to write %d bytes,", count); 5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!count) 6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 605884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 606884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if ((wbn = acm_wb_alloc(acm)) < 0) { 607884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 608884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_start(acm); 609884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 610884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 611884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 613884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum count = (count > acm->writesize) ? acm->writesize : count; 6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Get %d bytes...", count); 615884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum memcpy(wb->buf, buf, count); 616884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->len = count; 617884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 619884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if ((stat = acm_write_start(acm)) < 0) 6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return stat; 6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return count; 6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty) 6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 629884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum /* 630884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Do not let the line discipline to know that we have a reserve, 631884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * or it might get too enthusiastic. 632884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 633884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0; 6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty) 6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 641884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum /* 642884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * This is inaccurate (overcounts), but it works. 643884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 64486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize; 6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty) 6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(&acm->throttle_lock); 6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->throttle = 1; 6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(&acm->throttle_lock); 6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty) 6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(&acm->throttle_lock); 6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->throttle = 0; 6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(&acm->throttle_lock); 66561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_schedule(&acm->urb_task); 6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_break_ctl(struct tty_struct *tty, int state) 6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm_send_break(acm, state ? 0xffff : 0)) 6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("send break failed"); 6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmget(struct tty_struct *tty, struct file *file) 6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | 6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | 6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | 6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | 6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | 6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds TIOCM_CTS; 6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmset(struct tty_struct *tty, struct file *file, 6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int set, unsigned int clear) 6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int newctrl; 6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = acm->ctrlout; 7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); 7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0); 7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = (newctrl & ~clear) | set; 7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->ctrlout == newctrl) 7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return acm_set_control(acm, acm->ctrlout = newctrl); 7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) 7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOIOCTLCMD; 7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7224c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = { 7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0, 50, 75, 110, 134, 150, 200, 300, 600, 7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1200, 1800, 2400, 4800, 9600, 19200, 38400, 7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 57600, 115200, 230400, 460800, 500000, 576000, 7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 921600, 1000000, 1152000, 1500000, 2000000, 7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2500000, 3000000, 3500000, 4000000 7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7304c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = { 7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5, 6, 7, 8 7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 734606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Coxstatic void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old) 7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 737606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox struct ktermios *termios = tty->termios; 7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_line_coding newline; 7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int newctrl = acm->ctrlout; 7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.dwDTERate = cpu_to_le32p(acm_tty_speed + 7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0)); 7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0; 7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bParityType = termios->c_cflag & PARENB ? 7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; 7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; 7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->clocal = ((termios->c_cflag & CLOCAL) != 0); 7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!newline.dwDTERate) { 7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.dwDTERate = acm->line.dwDTERate; 7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl &= ~ACM_CTRL_DTR; 7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else newctrl |= ACM_CTRL_DTR; 7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (newctrl != acm->ctrlout) 7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout = newctrl); 7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (memcmp(&acm->line, &newline, sizeof newline)) { 7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(&acm->line, &newline, sizeof newline); 7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate), 7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bCharFormat, newline.bParityType, 7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bDataBits); 7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_line(acm, &acm->line); 7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines. 7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 774884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers free */ 775884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm) 776884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 777884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i; 778884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 779884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 78086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { 781884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah); 782884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 783884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 784884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 785884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */ 786884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm) 787884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 788884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i; 789884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 790884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 79186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { 792884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL, 793884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum &wb->dmah); 794884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!wb->buf) { 795884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum while (i != 0) { 796884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum --i; 797884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum --wb; 798884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum usb_buffer_free(acm->dev, acm->writesize, 799884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->buf, wb->dmah); 800884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 801884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -ENOMEM; 802884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 803884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 804884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 805884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 806884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_probe (struct usb_interface *intf, 8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds const struct usb_device_id *id) 8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_union_desc *union_header = NULL; 811c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_cdc_country_functional_desc *cfd = NULL; 8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char *buffer = intf->altsetting->extra; 8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int buflen = intf->altsetting->extralen; 8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *control_interface; 8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *data_interface; 8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *epctrl; 8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *epread; 8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *epwrite; 8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_device *usb_dev = interface_to_usbdev(intf); 8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm; 8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int minor; 8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ctrlsize,readsize; 8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 *buf; 8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 ac_management_function = 0; 8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 call_management_function = 0; 8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int call_interface_num = -1; 8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int data_interface_num; 8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long quirks; 82986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int num_rx_buf; 83061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 83286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* normal quirks */ 8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds quirks = (unsigned long)id->driver_info; 83486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR; 83586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum 83686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* handle quirks deadly to normal probing*/ 8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (quirks == NO_UNION_NORMAL) { 8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, 1); 8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = usb_ifnum_to_if(usb_dev, 0); 8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto skip_normal_probe; 8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* normal probing*/ 8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buffer) { 845898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches err("Weird descriptor references\n"); 8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buflen) { 8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) { 851898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n"); 8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen = intf->cur_altsetting->endpoint->extralen; 8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer = intf->cur_altsetting->endpoint->extra; 8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("Zero length descriptor references\n"); 8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (buflen > 0) { 8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (buffer [1] != USB_DT_CS_INTERFACE) { 8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("skipping garbage\n"); 8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto next_desc; 8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (buffer [2]) { 8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_UNION_TYPE: /* we've found it */ 8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (union_header) { 8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("More than one union descriptor, skipping ..."); 8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto next_desc; 8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds union_header = (struct usb_cdc_union_desc *) 8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer; 8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 875c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ 876c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum cfd = (struct usb_cdc_country_functional_desc *)buffer; 877c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum break; 8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_HEADER_TYPE: /* maybe check version */ 8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; /* for now we ignore it */ 8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_ACM_TYPE: 8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ac_management_function = buffer[3]; 8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_CALL_MANAGEMENT_TYPE: 8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds call_management_function = buffer[3]; 8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds call_interface_num = buffer[4]; 8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((call_management_function & 3) != 3) 8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("This device cannot do calls on its own. It is no modem."); 8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]); 8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc: 8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen -= buffer[0]; 8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer += buffer[0]; 8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!union_header) { 9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (call_interface_num > 0) { 901898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n"); 9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); 9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = intf; 9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 905898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"No union descriptor, giving up\n"); 9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); 9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); 9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!control_interface || !data_interface) { 912898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"no interfaces\n"); 9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface_num != call_interface_num) 918898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.\n"); 9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe: 9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /*workaround for switched interfaces */ 9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) { 9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) { 9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *t; 926898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"Your device has switched interfaces.\n"); 9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t = control_interface; 9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = data_interface; 9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = t; 9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 93574da5d68a54d66667664fbe233ededab2376a070Alan Stern 93674da5d68a54d66667664fbe233ededab2376a070Alan Stern /* Accept probe requests only for the control interface */ 93774da5d68a54d66667664fbe233ededab2376a070Alan Stern if (intf != control_interface) 93874da5d68a54d66667664fbe233ededab2376a070Alan Stern return -ENODEV; 9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (usb_interface_claimed(data_interface)) { /* valid in this context */ 941898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"The data interface isn't available\n"); 9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EBUSY; 9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface->cur_altsetting->desc.bNumEndpoints < 2) 9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epctrl = &control_interface->cur_altsetting->endpoint[0].desc; 9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epread = &data_interface->cur_altsetting->endpoint[0].desc; 9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epwrite = &data_interface->cur_altsetting->endpoint[1].desc; 9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* workaround for switched endpoints */ 95545aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino if (!usb_endpoint_dir_in(epread)) { 9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* descriptors are swapped */ 9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *t; 958898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev,"The data interface has switched endpoints\n"); 9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t = epread; 9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epread = epwrite; 9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epwrite = t; 9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("interfaces are valid"); 9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); 9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (minor == ACM_TTY_MINORS) { 9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("no more free acm devices"); 9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 97246f116eab81b21c6ae8c4f169498c632b1f94bf1Oliver Neukum if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) { 973898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n"); 9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail; 9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); 97886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2); 9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize); 9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->control = control_interface; 9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->data = data_interface; 9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->minor = minor; 9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->dev = usb_dev; 9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_caps = ac_management_function; 9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlsize = ctrlsize; 9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->readsize = readsize; 98786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum acm->rx_buflimit = num_rx_buf; 98861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->urb_task.func = acm_rx_tasklet; 98961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->urb_task.data = (unsigned long) acm; 990c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells INIT_WORK(&acm->work, acm_softint); 9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_init(&acm->throttle_lock); 992884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_init(&acm->write_lock); 99361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_lock_init(&acm->read_lock); 9941365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_init(&acm->mutex); 995884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->write_ready = 1; 99661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); 9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); 9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buf) { 1000898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n"); 10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail2; 10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_buffer = buf; 10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1005884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (acm_write_buffers_alloc(acm) < 0) { 1006898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); 10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail4; 10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); 10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm->ctrlurb) { 1012898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); 10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail5; 10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 101586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) { 101661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv = &(acm->ru[i]); 101761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 101861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) { 1019898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n"); 102061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto alloc_fail7; 102161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 102261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 102361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 102461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->instance = acm; 102561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 102686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) { 102761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb *buf = &(acm->rb[i]); 102861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 102961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) { 1030898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n"); 103161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto alloc_fail7; 103261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->writeurb = usb_alloc_urb(0, GFP_KERNEL); 10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm->writeurb) { 1036898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n"); 10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail7; 10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1040c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum usb_set_intfdata (intf, acm); 1041c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1042c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); 1043c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) 1044c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto alloc_fail8; 1045c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1046c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (cfd) { /* export the country data */ 1047c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); 1048c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (!acm->country_codes) 1049c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1050c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_code_size = cfd->bLength - 4; 1051c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4); 1052c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_rel_date = cfd->iCountryCodeRelDate; 1053c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1054c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); 1055c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) { 1056c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 1057c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1058c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1059c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1060c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate); 1061c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) { 1062c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 1063c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1064c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1065c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1066c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1067c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries: 10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), 10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); 10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->transfer_dma = acm->ctrl_dma; 10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), 1074884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum NULL, acm->writesize, acm_write_bulk, acm); 10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP; 10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); 10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout); 10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->line.dwDTERate = cpu_to_le32(9600); 10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->line.bDataBits = 8; 10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_line(acm, &acm->line); 10841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_driver_claim_interface(&acm_driver, data_interface, acm); 10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 108783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_get_intf(control_interface); 108883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk tty_register_device(acm_tty_driver, minor, &control_interface->dev); 10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_table[minor] = acm; 10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1092c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return 0; 1093c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumalloc_fail8: 1094c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum usb_free_urb(acm->writeurb); 10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7: 109686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) 109761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); 109886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) 109961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_free_urb(acm->ru[i].urb); 11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_free_urb(acm->ctrlurb); 11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5: 1102884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_buffers_free(acm); 11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4: 11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); 11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2: 11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(acm); 11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail: 11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm) 11121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 11131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum int i; 11141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 11151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum tasklet_disable(&acm->urb_task); 11161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 11171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_kill_urb(acm->ctrlurb); 11181365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_kill_urb(acm->writeurb); 11191365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum for (i = 0; i < acm->rx_buflimit; i++) 11201365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_kill_urb(acm->ru[i].urb); 11211365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 11221365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum INIT_LIST_HEAD(&acm->filled_read_bufs); 11231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum INIT_LIST_HEAD(&acm->spare_read_bufs); 11241365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 11251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum tasklet_enable(&acm->urb_task); 11261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 11271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum cancel_work_sync(&acm->work); 11281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 11291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf) 11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1132c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_device *usb_dev = interface_to_usbdev(intf); 113461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm || !acm->dev) { 11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("disconnect on nonexisting interface"); 11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11414186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_lock(&open_mutex); 114286067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum if (!usb_get_intfdata(intf)) { 11434186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 114486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum return; 114586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum } 1146c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (acm->country_codes){ 114774da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, 114874da5d68a54d66667664fbe233ededab2376a070Alan Stern &dev_attr_wCountryCodes); 114974da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, 115074da5d68a54d66667664fbe233ededab2376a070Alan Stern &dev_attr_iCountryCodeRelDate); 1151c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 115274da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); 11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->dev = NULL; 115486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_set_intfdata(acm->control, NULL); 115586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_set_intfdata(acm->data, NULL); 11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11571365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum stop_data_traffic(acm); 11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1159884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_buffers_free(acm); 11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); 116186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < acm->rx_buflimit; i++) 116261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); 11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 116486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf); 11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm->used) { 116783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_tty_unregister(acm); 11684186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11724186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->tty) 11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_hangup(acm->tty); 11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11781365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message) 11791365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 11801365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum struct acm *acm = usb_get_intfdata(intf); 11811365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 11821365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum if (acm->susp_count++) 11831365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 11841365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum /* 11851365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum we treat opened interfaces differently, 11861365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum we must guard against open 11871365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum */ 11881365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 11891365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 11901365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum if (acm->used) 11911365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum stop_data_traffic(acm); 11921365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 11931365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 11941365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 11951365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 11961365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 11971365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf) 11981365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 11991365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum struct acm *acm = usb_get_intfdata(intf); 12001365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum int rv = 0; 12011365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 12021365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum if (--acm->susp_count) 12031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 12041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 12051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 12061365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum if (acm->used) { 12071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); 12081365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum if (rv < 0) 12091365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum goto err_out; 12101365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 12111365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum tasklet_schedule(&acm->urb_task); 12121365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum } 12131365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 12141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out: 12151365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 12161365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return rv; 12171365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 12181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 12191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure. 12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 12211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id acm_ids[] = { 12231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* quirky and broken devices */ 12241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */ 12251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 12261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds }, 1227b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ 1228b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1229b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov }, 12308753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */ 12318753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 12328753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote }, 123391a9c9214e34c364bf15406aadb922787ae7129bChris Malley { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */ 123491a9c9214e34c364bf15406aadb922787ae7129bChris Malley .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 123591a9c9214e34c364bf15406aadb922787ae7129bChris Malley }, 123686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */ 123786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum .driver_info = SINGLE_RX_URB, /* firmware bug */ 123886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum }, 12393dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */ 12403dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum .driver_info = SINGLE_RX_URB, /* firmware bug */ 12413dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum }, 12429be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */ 12439be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 12449be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum }, 12459be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum 12461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* control interfaces with various AT-command sets */ 12471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_V25TER) }, 12491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_PCCA101) }, 12511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) }, 12531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 12541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_GSM) }, 12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_3G ) }, 12571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_CDMA) }, 12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */ 12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { } 12621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE (usb, acm_ids); 12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = { 12671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = "cdc_acm", 12681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .probe = acm_probe, 12691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .disconnect = acm_disconnect, 12701365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .suspend = acm_suspend, 12711365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .resume = acm_resume, 12721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .id_table = acm_ids, 12731365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .supports_autosuspend = 1, 12741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures. 12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 12791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1280b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = { 12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .open = acm_tty_open, 12821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .close = acm_tty_close, 12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write = acm_tty_write, 12841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write_room = acm_tty_write_room, 12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .ioctl = acm_tty_ioctl, 12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .throttle = acm_tty_throttle, 12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .unthrottle = acm_tty_unthrottle, 12881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .chars_in_buffer = acm_tty_chars_in_buffer, 12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .break_ctl = acm_tty_break_ctl, 12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .set_termios = acm_tty_set_termios, 12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .tiocmget = acm_tty_tiocmget, 12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .tiocmset = acm_tty_tiocmset, 12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit. 12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 12981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void) 13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 13011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval; 13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS); 13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm_tty_driver) 13041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->owner = THIS_MODULE, 13061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->driver_name = "acm", 13071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->name = "ttyACM", 13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->major = ACM_TTY_MAJOR, 13091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->minor_start = 0, 13101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, 13111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->subtype = SERIAL_TYPE_NORMAL, 1312331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; 13131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->init_termios = tty_std_termios; 13141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; 13151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_set_operations(acm_tty_driver, &acm_ops); 13161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = tty_register_driver(acm_tty_driver); 13181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) { 13191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 13201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 13211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 13221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = usb_register(&acm_driver); 13241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) { 13251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_unregister_driver(acm_tty_driver); 13261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 13271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 13281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 13291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds info(DRIVER_VERSION ":" DRIVER_DESC); 13311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 13331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void) 13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_deregister(&acm_driver); 13381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_unregister_driver(acm_tty_driver); 13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 13401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init); 13431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit); 13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR( DRIVER_AUTHOR ); 13461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION( DRIVER_DESC ); 13471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 13481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1349