cdc-acm.c revision a8c28f2389942bab376e39351d27525499630248
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> 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/smp_lock.h> 634186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h> 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h> 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h> 66a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h> 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h> 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h> 6961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h> 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h" 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 7661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_VERSION "v0.25" 7761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver; 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver; 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS]; 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 844186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex); 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACM_READY(acm) (acm && acm->dev && acm->used) 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages. 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len) 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds request, USB_RT_ACM, value, 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->control->altsetting[0].desc.bInterfaceNumber, 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf, len, 5000); 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval); 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval < 0 ? retval : 0; 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests. 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do... 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \ 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0) 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \ 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)) 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \ 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 113884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management. 114884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller. 115884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 116884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 117884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm) 118884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 119884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i, wbn; 120884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 121884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 122884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wbn = acm->write_current; 123884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum i = 0; 124884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum for (;;) { 125884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 126884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!wb->use) { 127884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->use = 1; 128884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return wbn; 129884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 13086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum wbn = (wbn + 1) % ACM_NW; 13186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (++i >= ACM_NW) 132884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -1; 133884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 134884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 135884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 136884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_wb_free(struct acm *acm, int wbn) 137884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 138884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->wb[wbn].use = 0; 139884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 140884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 141884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm) 142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i, n; 144884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 14586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum n = ACM_NW; 14686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < ACM_NW; i++) { 14786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum n -= acm->wb[i].use; 148884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 149884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return n; 150884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 151884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 152884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic inline int acm_wb_is_used(struct acm *acm, int wbn) 153884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 154884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return acm->wb[wbn].use; 155884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 156884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 157884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 158884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Finish write. 159884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 160884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_done(struct acm *acm) 161884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 162884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 163884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int wbn; 164884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 165884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 166884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->write_ready = 1; 167884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wbn = acm->write_current; 168884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_wb_free(acm, wbn); 16986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum acm->write_current = (wbn + 1) % ACM_NW; 170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 171884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 172884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write. 175884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 176884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_start(struct acm *acm) 177884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 178884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 179884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int wbn; 180884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 181884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int rc; 182884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 183884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 184884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!acm->dev) { 185884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 186884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -ENODEV; 187884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 188884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 189884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!acm->write_ready) { 190884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 191884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; /* A white lie */ 192884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 193884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 194884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wbn = acm->write_current; 195884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!acm_wb_is_used(acm, wbn)) { 196884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 197884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 198884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 199884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 200884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 201884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->write_ready = 0; 202884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 203884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 204884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->writeurb->transfer_buffer = wb->buf; 205884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->writeurb->transfer_dma = wb->dmah; 206884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->writeurb->transfer_buffer_length = wb->len; 207884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->writeurb->dev = acm->dev; 208884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 209884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) { 210884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum dbg("usb_submit_urb(write bulk) failed: %d", rc); 211884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_done(acm); 212884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 213884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return rc; 214884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 215884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 216884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */ 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs) 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = urb->context; 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_notification *dr = urb->transfer_buffer; 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char *data; 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int newctrl; 2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int status; 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (urb->status) { 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0: 2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* success */ 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ECONNRESET: 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ENOENT: 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ESHUTDOWN: 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* this urb is terminated, clean up */ 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto exit; 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto exit; 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data = (unsigned char *)(dr + 1); 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (dr->bNotificationType) { 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_NOTIFY_NETWORK_CONNECTION: 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("%s network", dr->wValue ? "connected to" : "disconnected from"); 2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_NOTIFY_SERIAL_STATE: 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = le16_to_cpu(get_unaligned((__le16 *) data)); 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { 2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("calling hangup"); 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_hangup(acm->tty); 2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin = newctrl; 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c", 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-', 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("unknown notification %d received: index %d len %d data0 %d data1 %d", 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dr->bNotificationType, dr->wIndex, 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dr->wLength, data[0], data[1]); 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit: 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = usb_submit_urb (urb, GFP_ATOMIC); 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (status) 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err ("%s - usb_submit_urb failed with result %d", 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds __FUNCTION__, status); 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface returns incoming bytes, or we got unthrottled */ 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_read_bulk(struct urb *urb, struct pt_regs *regs) 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 29061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb *buf; 29161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv = urb->context; 29261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm *acm = rcv->instance; 29386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int status = urb->status; 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Entering acm_read_bulk with status %d\n", urb->status); 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 29986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (status) 30086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum dev_dbg(&acm->data->dev, "bulk rx status %d\n", status); 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 30261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = rcv->buffer; 30361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf->size = urb->actual_length; 30461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 30586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (likely(status == 0)) { 30686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_lock(&acm->read_lock); 30786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&rcv->list, &acm->spare_read_urbs); 30886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&buf->list, &acm->filled_read_bufs); 30986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_unlock(&acm->read_lock); 31086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum } else { 31186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* we drop the buffer due to an error */ 31286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_lock(&acm->read_lock); 31386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add_tail(&rcv->list, &acm->spare_read_urbs); 31486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum list_add(&buf->list, &acm->spare_read_bufs); 31586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum spin_unlock(&acm->read_lock); 31686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* nevertheless the tasklet must be kicked unconditionally 31786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum so the queue cannot dry up */ 31886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum } 31961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_schedule(&acm->urb_task); 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_rx_tasklet(unsigned long _acm) 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = (void *)_acm; 32561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb *buf; 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct tty_struct *tty = acm->tty; 32761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv; 32861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek //unsigned long flags; 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i = 0; 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Entering acm_rx_tasklet"); 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 33261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (!ACM_READY(acm) || acm->throttle) 33361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 33461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 33561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubiceknext_buffer: 33661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_lock(&acm->read_lock); 33761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (list_empty(&acm->filled_read_bufs)) { 33861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_unlock(&acm->read_lock); 33961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto urbs; 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 34161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = list_entry(acm->filled_read_bufs.next, 34261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb, list); 34361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&buf->list); 34461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_unlock(&acm->read_lock); 34561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 34661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d\n", buf, buf->size); 34761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 34833f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox tty_buffer_request_room(tty, buf->size); 34933f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox if (!acm->throttle) 35033f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox tty_insert_flip_string(tty, buf->base, buf->size); 35161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tty_flip_buffer_push(tty); 3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock(&acm->throttle_lock); 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->throttle) { 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Throtteling noticed"); 35661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek memmove(buf->base, buf->base + i, buf->size - i); 35761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf->size -= i; 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock(&acm->throttle_lock); 35961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_lock(&acm->read_lock); 36061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->filled_read_bufs); 36161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_unlock(&acm->read_lock); 3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock(&acm->throttle_lock); 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 36661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_lock(&acm->read_lock); 36761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->spare_read_bufs); 36861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_unlock(&acm->read_lock); 36961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto next_buffer; 37061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 37161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicekurbs: 37261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek while (!list_empty(&acm->spare_read_bufs)) { 37361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_lock(&acm->read_lock); 37461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (list_empty(&acm->spare_read_urbs)) { 37561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_unlock(&acm->read_lock); 37661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 37761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 37861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv = list_entry(acm->spare_read_urbs.next, 37961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru, list); 38061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&rcv->list); 38161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_unlock(&acm->read_lock); 38261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 38361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf = list_entry(acm->spare_read_bufs.next, 38461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb, list); 38561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_del(&buf->list); 38661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 38761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->buffer = buf; 38861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 38961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_fill_bulk_urb(rcv->urb, acm->dev, 39061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->rx_endpoint, 39161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek buf->base, 39261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->readsize, 39361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm_read_bulk, rcv); 39461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_dma = buf->dma; 39561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 39661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 39761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p\n", rcv->urb, rcv, buf); 39861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 39961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek /* This shouldn't kill the driver as unsuccessful URBs are returned to the 40061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek free-urbs-pool and resubmited ASAP */ 40161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { 40261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&buf->list, &acm->spare_read_bufs); 40361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_lock(&acm->read_lock); 40461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&rcv->list, &acm->spare_read_urbs); 40561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_unlock(&acm->read_lock); 40661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek return; 40761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 40861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */ 4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_write_bulk(struct urb *urb, struct pt_regs *regs) 4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = (struct acm *)urb->context; 4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 416884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum dbg("Entering acm_write_bulk with status %d\n", urb->status); 4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 418884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_done(acm); 419884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_start(acm); 420884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (ACM_READY(acm)) 421884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum schedule_work(&acm->work); 4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_softint(void *private) 4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = private; 4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Entering acm_softint.\n"); 4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_wakeup(acm->tty); 4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers 4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp) 4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm; 4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rv = -EINVAL; 44261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Entering acm_tty_open.\n"); 4444186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven 4454186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_lock(&open_mutex); 4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm = acm_table[tty->index]; 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm || !acm->dev) 4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto err_out; 4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rv = 0; 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty->driver_data = acm; 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->tty = tty; 4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 45661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek /* force low_latency on so that our tty_push actually forces the data through, 45761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek otherwise it is scheduled, and with high data rates data can get lost. */ 45861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tty->low_latency = 1; 4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->used++) { 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto done; 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->dev = acm->dev; 4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { 4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("usb_submit_urb(ctrl irq) failed"); 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto bail_out; 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS)) 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto full_bailout; 4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 47361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->spare_read_urbs); 47461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->spare_read_bufs); 47561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->filled_read_bufs); 47686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < acm->rx_buflimit; i++) { 47761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&(acm->ru[i].list), &acm->spare_read_urbs); 47861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 47986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < acm->rx_buflimit; i++) { 48061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek list_add(&(acm->rb[i].list), &acm->spare_read_bufs); 48161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 48261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 48361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_schedule(&acm->urb_task); 4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone: 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out: 4874186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rv; 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfull_bailout: 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_kill_urb(acm->ctrlurb); 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out: 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->used--; 4944186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 49883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm) 49983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{ 50086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int i,nr; 50161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 50286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum nr = acm->rx_buflimit; 50383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk tty_unregister_device(acm_tty_driver, acm->minor); 50483ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_put_intf(acm->control); 50583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_table[acm->minor] = NULL; 50683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_free_urb(acm->ctrlurb); 50783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_free_urb(acm->writeurb); 50886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < nr; i++) 50961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_free_urb(acm->ru[i].urb); 51083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk kfree(acm); 51183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk} 51283ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk 5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp) 5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 51686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int i,nr; 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm || !acm->used) 5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 52186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum nr = acm->rx_buflimit; 5224186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_lock(&open_mutex); 5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!--acm->used) { 5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->dev) { 5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout = 0); 5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_kill_urb(acm->ctrlurb); 5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_kill_urb(acm->writeurb); 52886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < nr; i++) 52961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_kill_urb(acm->ru[i].urb); 53083ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk } else 53183ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_tty_unregister(acm); 5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5334186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) 5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int stat; 540884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 541884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int wbn; 542884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 543884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Entering acm_tty_write to write %d bytes,\n", count); 5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!count) 5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 551884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 552884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if ((wbn = acm_wb_alloc(acm)) < 0) { 553884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 554884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_start(acm); 555884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 556884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 557884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 559884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum count = (count > acm->writesize) ? acm->writesize : count; 5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("Get %d bytes...", count); 561884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum memcpy(wb->buf, buf, count); 562884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->len = count; 563884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 565884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if ((stat = acm_write_start(acm)) < 0) 5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return stat; 5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return count; 5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty) 5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 575884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum /* 576884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Do not let the line discipline to know that we have a reserve, 577884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * or it might get too enthusiastic. 578884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 579884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0; 5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty) 5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 587884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum /* 588884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * This is inaccurate (overcounts), but it works. 589884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 59086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize; 5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty) 5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(&acm->throttle_lock); 5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->throttle = 1; 6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(&acm->throttle_lock); 6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty) 6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(&acm->throttle_lock); 6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->throttle = 0; 6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(&acm->throttle_lock); 61161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_schedule(&acm->urb_task); 6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_break_ctl(struct tty_struct *tty, int state) 6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm_send_break(acm, state ? 0xffff : 0)) 6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("send break failed"); 6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmget(struct tty_struct *tty, struct file *file) 6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | 6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | 6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | 6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | 6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | 6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds TIOCM_CTS; 6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_tiocmset(struct tty_struct *tty, struct file *file, 6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int set, unsigned int clear) 6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int newctrl; 6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = acm->ctrlout; 6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); 6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0); 6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = (newctrl & ~clear) | set; 6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->ctrlout == newctrl) 6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return acm_set_control(acm, acm->ctrlout = newctrl); 6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) 6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOIOCTLCMD; 6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6684c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = { 6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0, 50, 75, 110, 134, 150, 200, 300, 600, 6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1200, 1800, 2400, 4800, 9600, 19200, 38400, 6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 57600, 115200, 230400, 460800, 500000, 576000, 6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 921600, 1000000, 1152000, 1500000, 2000000, 6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2500000, 3000000, 3500000, 4000000 6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6764c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = { 6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5, 6, 7, 8 6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_old) 6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct termios *termios = tty->termios; 6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_line_coding newline; 6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int newctrl = acm->ctrlout; 6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.dwDTERate = cpu_to_le32p(acm_tty_speed + 6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0)); 6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0; 6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bParityType = termios->c_cflag & PARENB ? 6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; 6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; 6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->clocal = ((termios->c_cflag & CLOCAL) != 0); 6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!newline.dwDTERate) { 7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.dwDTERate = acm->line.dwDTERate; 7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl &= ~ACM_CTRL_DTR; 7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else newctrl |= ACM_CTRL_DTR; 7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (newctrl != acm->ctrlout) 7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout = newctrl); 7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (memcmp(&acm->line, &newline, sizeof newline)) { 7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(&acm->line, &newline, sizeof newline); 7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate), 7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bCharFormat, newline.bParityType, 7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bDataBits); 7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_line(acm, &acm->line); 7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines. 7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 720884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers free */ 721884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm) 722884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 723884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i; 724884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 725884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 72686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { 727884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah); 728884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 729884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 730884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 731884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */ 732884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm) 733884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 734884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i; 735884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 736884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 73786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { 738884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL, 739884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum &wb->dmah); 740884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!wb->buf) { 741884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum while (i != 0) { 742884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum --i; 743884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum --wb; 744884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum usb_buffer_free(acm->dev, acm->writesize, 745884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->buf, wb->dmah); 746884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 747884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -ENOMEM; 748884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 749884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 750884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 751884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 752884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_probe (struct usb_interface *intf, 7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds const struct usb_device_id *id) 7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_union_desc *union_header = NULL; 7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char *buffer = intf->altsetting->extra; 7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int buflen = intf->altsetting->extralen; 7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *control_interface; 7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *data_interface; 7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *epctrl; 7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *epread; 7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *epwrite; 7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_device *usb_dev = interface_to_usbdev(intf); 7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm; 7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int minor; 7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ctrlsize,readsize; 7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 *buf; 7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 ac_management_function = 0; 7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 call_management_function = 0; 7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int call_interface_num = -1; 7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int data_interface_num; 7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long quirks; 77486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int num_rx_buf; 77561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 77786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* normal quirks */ 7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds quirks = (unsigned long)id->driver_info; 77986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR; 78086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum 78186478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* handle quirks deadly to normal probing*/ 7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (quirks == NO_UNION_NORMAL) { 7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, 1); 7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = usb_ifnum_to_if(usb_dev, 0); 7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto skip_normal_probe; 7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* normal probing*/ 7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buffer) { 7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("Wierd descriptor references\n"); 7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buflen) { 7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) { 7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n"); 7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen = intf->cur_altsetting->endpoint->extralen; 7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer = intf->cur_altsetting->endpoint->extra; 7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("Zero length descriptor references\n"); 8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (buflen > 0) { 8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (buffer [1] != USB_DT_CS_INTERFACE) { 8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("skipping garbage\n"); 8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto next_desc; 8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (buffer [2]) { 8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_UNION_TYPE: /* we've found it */ 8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (union_header) { 8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("More than one union descriptor, skipping ..."); 8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto next_desc; 8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds union_header = (struct usb_cdc_union_desc *) 8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer; 8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */ 8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; /* for now we ignore it */ 8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_HEADER_TYPE: /* maybe check version */ 8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; /* for now we ignore it */ 8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_ACM_TYPE: 8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ac_management_function = buffer[3]; 8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case USB_CDC_CALL_MANAGEMENT_TYPE: 8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds call_management_function = buffer[3]; 8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds call_interface_num = buffer[4]; 8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((call_management_function & 3) != 3) 8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("This device cannot do calls on its own. It is no modem."); 8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]); 8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc: 8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen -= buffer[0]; 8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer += buffer[0]; 8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!union_header) { 8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (call_interface_num > 0) { 8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n"); 8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); 8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = intf; 8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_dbg(&intf->dev,"No union descriptor, giving up\n"); 8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); 8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); 8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!control_interface || !data_interface) { 8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_dbg(&intf->dev,"no interfaces\n"); 8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface_num != call_interface_num) 8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.\n"); 8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe: 8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /*workaround for switched interfaces */ 8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) { 8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) { 8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *t; 8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_dbg(&intf->dev,"Your device has switched interfaces.\n"); 8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t = control_interface; 8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = data_interface; 8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = t; 8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (usb_interface_claimed(data_interface)) { /* valid in this context */ 8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_dbg(&intf->dev,"The data interface isn't available\n"); 8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EBUSY; 8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface->cur_altsetting->desc.bNumEndpoints < 2) 8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epctrl = &control_interface->cur_altsetting->endpoint[0].desc; 8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epread = &data_interface->cur_altsetting->endpoint[0].desc; 8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epwrite = &data_interface->cur_altsetting->endpoint[1].desc; 8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* workaround for switched endpoints */ 8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((epread->bEndpointAddress & USB_DIR_IN) != USB_DIR_IN) { 8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* descriptors are swapped */ 8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *t; 8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_dbg(&intf->dev,"The data interface has switched endpoints\n"); 8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t = epread; 9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epread = epwrite; 9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epwrite = t; 9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("interfaces are valid"); 9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); 9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (minor == ACM_TTY_MINORS) { 9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err("no more free acm devices"); 9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 91246f116eab81b21c6ae8c4f169498c632b1f94bf1Oliver Neukum if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) { 91346f116eab81b21c6ae8c4f169498c632b1f94bf1Oliver Neukum dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n"); 9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail; 9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); 91886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2); 9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize); 9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->control = control_interface; 9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->data = data_interface; 9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->minor = minor; 9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->dev = usb_dev; 9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_caps = ac_management_function; 9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlsize = ctrlsize; 9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->readsize = readsize; 92786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum acm->rx_buflimit = num_rx_buf; 92861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->urb_task.func = acm_rx_tasklet; 92961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->urb_task.data = (unsigned long) acm; 9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds INIT_WORK(&acm->work, acm_softint, acm); 9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_init(&acm->throttle_lock); 932884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_init(&acm->write_lock); 93361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_lock_init(&acm->read_lock); 934884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm->write_ready = 1; 93561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); 9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); 9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buf) { 9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n"); 9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail2; 9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_buffer = buf; 9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 944884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (acm_write_buffers_alloc(acm) < 0) { 9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); 9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail4; 9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); 9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm->ctrlurb) { 9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); 9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail5; 9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 95486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) { 95561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_ru *rcv = &(acm->ru[i]); 95661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 95761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) { 95861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n"); 95961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto alloc_fail7; 96061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 96161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 96261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 96361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek rcv->instance = acm; 96461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 96586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) { 96661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek struct acm_rb *buf = &(acm->rb[i]); 96761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 96861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) { 96961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n"); 97061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek goto alloc_fail7; 97161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->writeurb = usb_alloc_urb(0, GFP_KERNEL); 9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm->writeurb) { 9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n"); 9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail7; 9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), 9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); 9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->transfer_dma = acm->ctrl_dma; 9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), 985884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum NULL, acm->writesize, acm_write_bulk, acm); 9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP; 9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); 9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout); 9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->line.dwDTERate = cpu_to_le32(9600); 9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->line.bDataBits = 8; 9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_line(acm, &acm->line); 9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_driver_claim_interface(&acm_driver, data_interface, acm); 9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 99883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_get_intf(control_interface); 99983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk tty_register_device(acm_tty_driver, minor, &control_interface->dev); 10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_table[minor] = acm; 10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_set_intfdata (intf, acm); 10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail7: 100686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) 100761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); 100886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) 100961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_free_urb(acm->ru[i].urb); 10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_free_urb(acm->ctrlurb); 10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5: 1012884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_buffers_free(acm); 10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4: 10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); 10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2: 10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(acm); 10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail: 10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf) 10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = usb_get_intfdata (intf); 10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_device *usb_dev = interface_to_usbdev(intf); 102561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm || !acm->dev) { 10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("disconnect on nonexisting interface"); 10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10324186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_lock(&open_mutex); 103386067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum if (!usb_get_intfdata(intf)) { 10344186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 103586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum return; 103686067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum } 10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->dev = NULL; 103886067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_set_intfdata(acm->control, NULL); 103986067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_set_intfdata(acm->data, NULL); 10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 104161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_disable(&acm->urb_task); 104261a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_kill_urb(acm->ctrlurb); 10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_kill_urb(acm->writeurb); 104586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < acm->rx_buflimit; i++) 104661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_kill_urb(acm->ru[i].urb); 104761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 104861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->filled_read_bufs); 104961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek INIT_LIST_HEAD(&acm->spare_read_bufs); 105061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 105161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek tasklet_enable(&acm->urb_task); 10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds flush_scheduled_work(); /* wait for acm_softint */ 10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1055884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_buffers_free(acm); 10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); 105786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < acm->rx_buflimit; i++) 105861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); 10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 106086067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf); 10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm->used) { 106383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_tty_unregister(acm); 10644186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10684186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->tty) 10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_hangup(acm->tty); 10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure. 10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id acm_ids[] = { 10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* quirky and broken devices */ 10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */ 10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds }, 10838753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */ 10848753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 10858753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote }, 108686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */ 108786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum .driver_info = SINGLE_RX_URB, /* firmware bug */ 108886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum }, 10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* control interfaces with various AT-command sets */ 10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_V25TER) }, 10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_PCCA101) }, 10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) }, 10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_GSM) }, 10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_3G ) }, 11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_CDMA) }, 11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */ 11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { } 11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE (usb, acm_ids); 11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = { 11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = "cdc_acm", 11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .probe = acm_probe, 11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .disconnect = acm_disconnect, 11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .id_table = acm_ids, 11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures. 11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_operations acm_ops = { 11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .open = acm_tty_open, 11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .close = acm_tty_close, 11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write = acm_tty_write, 11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write_room = acm_tty_write_room, 11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .ioctl = acm_tty_ioctl, 11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .throttle = acm_tty_throttle, 11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .unthrottle = acm_tty_unthrottle, 11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .chars_in_buffer = acm_tty_chars_in_buffer, 11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .break_ctl = acm_tty_break_ctl, 11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .set_termios = acm_tty_set_termios, 11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .tiocmget = acm_tty_tiocmget, 11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .tiocmset = acm_tty_tiocmset, 11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit. 11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void) 11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 11411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval; 11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS); 11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm_tty_driver) 11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->owner = THIS_MODULE, 11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->driver_name = "acm", 11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->name = "ttyACM", 11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->devfs_name = "usb/acm/", 11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->major = ACM_TTY_MAJOR, 11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->minor_start = 0, 11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, 11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->subtype = SERIAL_TYPE_NORMAL, 11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, 11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->init_termios = tty_std_termios; 11551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; 11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_set_operations(acm_tty_driver, &acm_ops); 11571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = tty_register_driver(acm_tty_driver); 11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) { 11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = usb_register(&acm_driver); 11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) { 11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_unregister_driver(acm_tty_driver); 11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds info(DRIVER_VERSION ":" DRIVER_DESC); 11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void) 11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_deregister(&acm_driver); 11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_unregister_driver(acm_tty_driver); 11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init); 11841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit); 11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR( DRIVER_AUTHOR ); 11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION( DRIVER_DESC ); 11881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1190