cdc-acm.c revision 4061fde2fa80f40cb27114f60500d38d0afcf350
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * cdc-acm.c 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de> 5a2531293dbb7608fa672ff28efe3ab4027917a2fPavel Machek * Copyright (c) 1999 Pavel Machek <pavel@ucw.cz> 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999 Johannes Erdfelt <johannes@erdfelt.com> 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2004 Oliver Neukum <oliver@neukum.name> 961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek * Copyright (c) 2005 David Kubicek <dave@awk.cz> 10088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold * Copyright (c) 2011 Johan Hovold <jhovold@gmail.com> 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB Abstract Control Model driver for USB modems and ISDN adapters 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Sponsored by SuSE 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version. 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful, 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details. 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG 32e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell#undef VERBOSE_DEBUG 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h> 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h> 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h> 397af25b4b34a2439020d78da765a3bed0ff73f25cOliver Neukum#include <linux/serial.h> 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h> 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h> 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 434186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven#include <linux/mutex.h> 4410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#include <linux/uaccess.h> 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h> 46a8c28f2389942bab376e39351d27525499630248David Brownell#include <linux/usb/cdc.h> 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h> 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h> 4961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek#include <linux/list.h> 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cdc-acm.h" 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 53e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 54088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek, Johan Hovold" 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver; 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *acm_tty_driver; 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acm *acm_table[ACM_TTY_MINORS]; 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 614186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Venstatic DEFINE_MUTEX(open_mutex); 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox#define ACM_READY(acm) (acm && acm->dev && acm->port.count) 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 65739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Coxstatic const struct tty_port_operations acm_port_ops = { 66739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox}; 67739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Functions for ACM control messages. 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 726e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_ctrl_msg(struct acm *acm, int request, int value, 736e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox void *buf, int len) 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds request, USB_RT_ACM, value, 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->control->altsetting[0].desc.bInterfaceNumber, 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf, len, 5000); 79a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_dbg(&acm->control->dev, 80a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold "%s - rq 0x%02x, val %#x, len %#x, result %d\n", 81a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold __func__, request, value, len, retval); 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval < 0 ? retval : 0; 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* devices aren't required to support these requests. 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cdc acm descriptor tells whether they do... 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_control(acm, control) \ 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0) 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_set_line(acm, line) \ 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)) 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define acm_send_break(acm, ms) \ 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 96884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Write buffer management. 97884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * All of these assume proper locks taken by the caller. 98884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 99884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 100884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_alloc(struct acm *acm) 101884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 102884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i, wbn; 103884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 104884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 105e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf wbn = 0; 106884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum i = 0; 107884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum for (;;) { 108884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 109884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!wb->use) { 110884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->use = 1; 111884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return wbn; 112884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 11386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum wbn = (wbn + 1) % ACM_NW; 11486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum if (++i >= ACM_NW) 115884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -1; 116884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 117884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 118884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 119884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_wb_is_avail(struct acm *acm) 120884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 121884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i, n; 122e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell unsigned long flags; 123884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 12486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum n = ACM_NW; 125e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell spin_lock_irqsave(&acm->write_lock, flags); 1266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < ACM_NW; i++) 12786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum n -= acm->wb[i].use; 128e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell spin_unlock_irqrestore(&acm->write_lock, flags); 129884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return n; 130884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 131884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 132884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 133ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips * Finish write. Caller must hold acm->write_lock 134884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 135e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic void acm_write_done(struct acm *acm, struct acm_wb *wb) 136884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 137e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf wb->use = 0; 13811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->transmitting--; 13997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum usb_autopm_put_interface_async(acm->control); 140884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 141884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 142884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 143884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Poke write. 14411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * 14511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum * the caller is responsible for locking 146884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 14711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 14811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukumstatic int acm_start_wb(struct acm *acm, struct acm_wb *wb) 14911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum{ 15011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int rc; 15111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 15211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->transmitting++; 15311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 15411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->transfer_buffer = wb->buf; 15511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->transfer_dma = wb->dmah; 15611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->transfer_buffer_length = wb->len; 15711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum wb->urb->dev = acm->dev; 15811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 1596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox rc = usb_submit_urb(wb->urb, GFP_ATOMIC); 1606e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (rc < 0) { 161a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_err(&acm->data->dev, 162a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold "%s - usb_submit_urb(write bulk) failed: %d\n", 163a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold __func__, rc); 16411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm_write_done(acm, wb); 16511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 16611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return rc; 16711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum} 16811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 169e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engrafstatic int acm_write_start(struct acm *acm, int wbn) 170884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 171884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 172934da4635c2d05cef474e5243ef05df95b2ad264David Brownell struct acm_wb *wb = &acm->wb[wbn]; 173884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int rc; 174884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 175884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 176884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!acm->dev) { 177934da4635c2d05cef474e5243ef05df95b2ad264David Brownell wb->use = 0; 178884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 179884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -ENODEV; 180884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 181884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 1825e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__, 183a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold acm->susp_count); 18497d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum usb_autopm_get_interface_async(acm->control); 18511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (acm->susp_count) { 18697d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum if (!acm->delayed_wb) 18797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum acm->delayed_wb = wb; 18897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum else 18997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum usb_autopm_put_interface_async(acm->control); 19011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 19111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return 0; /* A white lie */ 19211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 19311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_mark_last_busy(acm->dev); 19411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 19511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum rc = acm_start_wb(acm, wb); 196884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 197884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 198884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return rc; 19911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 200884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 201c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum/* 202c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum * attributes exported through sysfs 203c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum */ 204c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_caps 205c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 206c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 207c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 208c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 209c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 210c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return sprintf(buf, "%d", acm->ctrl_caps); 211c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 212c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); 213c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 214c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_codes 215c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 216c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 217c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 218c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 219c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 220c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum memcpy(buf, acm->country_codes, acm->country_code_size); 221c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return acm->country_code_size; 222c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 223c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 224c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); 225c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 226c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic ssize_t show_country_rel_date 227c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum(struct device *dev, struct device_attribute *attr, char *buf) 228c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum{ 229c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_interface *intf = to_usb_interface(dev); 230c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 231c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 232c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return sprintf(buf, "%d", acm->country_rel_date); 233c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum} 234884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 235c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumstatic DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); 236884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Interrupt handlers for various ACM device responses 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* control interface reports status changes with "interrupt" transfers */ 2417d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_ctrl_irq(struct urb *urb) 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = urb->context; 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_notification *dr = urb->transfer_buffer; 24510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct tty_struct *tty; 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char *data; 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int newctrl; 248185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman int retval; 249185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman int status = urb->status; 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 251185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman switch (status) { 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0: 2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* success */ 2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ECONNRESET: 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ENOENT: 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case -ESHUTDOWN: 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* this urb is terminated, clean up */ 259a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_dbg(&acm->control->dev, 260a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold "%s - urb shutting down with status: %d\n", 261a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold __func__, status); 2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 264a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_dbg(&acm->control->dev, 265a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold "%s - nonzero urb status received: %d\n", 266a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold __func__, status); 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto exit; 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto exit; 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2737e7797e7f6f7bfab73fca02c65e40eaa5bb9000cJohan Hovold usb_mark_last_busy(acm->dev); 2747e7797e7f6f7bfab73fca02c65e40eaa5bb9000cJohan Hovold 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data = (unsigned char *)(dr + 1); 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (dr->bNotificationType) { 2776e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_NOTIFY_NETWORK_CONNECTION: 278a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_dbg(&acm->control->dev, "%s - network connection: %d\n", 279a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold __func__, dr->wValue); 2806e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2826e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_NOTIFY_SERIAL_STATE: 2836e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox tty = tty_port_tty_get(&acm->port); 2846e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox newctrl = get_unaligned_le16(data); 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2866e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (tty) { 2876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (!acm->clocal && 2886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { 289a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_dbg(&acm->control->dev, 290a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold "%s - calling hangup\n", __func__); 2916e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox tty_hangup(tty); 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2936e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox tty_kref_put(tty); 2946e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox } 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2966e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin = newctrl; 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 298a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_dbg(&acm->control->dev, 299a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold "%s - input control lines: dcd%c dsr%c break%c " 300a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold "ring%c framing%c parity%c overrun%c\n", 301a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold __func__, 3026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', 3036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', 3046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', 3056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_RI ? '+' : '-', 3066e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', 3076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', 3086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox default: 312a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_dbg(&acm->control->dev, 313a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold "%s - unknown notification %d received: index %d " 314a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold "len %d data0 %d data1 %d\n", 315a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold __func__, 3166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dr->bNotificationType, dr->wIndex, 3176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dr->wLength, data[0], data[1]); 3186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit: 3216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox retval = usb_submit_urb(urb, GFP_ATOMIC); 322185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman if (retval) 3231d9846e505febb71255b098910ace741433312b7Johan Hovold dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n", 3241d9846e505febb71255b098910ace741433312b7Johan Hovold __func__, retval); 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 327088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovoldstatic int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags) 3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 329088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold int res; 330185d40587d22fe604962fb53c0c9a9f1670feb66Greg Kroah-Hartman 331088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (!test_and_clear_bit(index, &acm->read_urbs_free)) 332088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold return 0; 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 334088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index); 335088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold 336088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold res = usb_submit_urb(acm->read_urbs[index], mem_flags); 337088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (res) { 338088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (res != -EPERM) { 339088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold dev_err(&acm->data->dev, 340088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold "%s - usb_submit_urb failed: %d\n", 341088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold __func__, res); 342088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold } 343088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold set_bit(index, &acm->read_urbs_free); 344088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold return res; 34511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 347088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold return 0; 348088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold} 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 350088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovoldstatic int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags) 351088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold{ 352088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold int res; 353088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold int i; 35461a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 355088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold for (i = 0; i < acm->rx_buflimit; ++i) { 356088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold res = acm_submit_read_urb(acm, i, mem_flags); 357088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (res) 358088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold return res; 35986478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum } 360088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold 361088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold return 0; 3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 364088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovoldstatic void acm_process_read_urb(struct acm *acm, struct urb *urb) 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 36610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct tty_struct *tty; 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 368088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (!urb->actual_length) 369ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum return; 37061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 37110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty = tty_port_tty_get(&acm->port); 372088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (!tty) 373088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold return; 37410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 375088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length); 376088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold tty_flip_buffer_push(tty); 37761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 37810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_kref_put(tty); 379088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold} 38010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 381088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovoldstatic void acm_read_bulk_callback(struct urb *urb) 382088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold{ 383088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold struct acm_rb *rb = urb->context; 384088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold struct acm *acm = rb->instance; 385088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold unsigned long flags; 38661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 387088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__, 388088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold rb->index, urb->actual_length); 389088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold set_bit(rb->index, &acm->read_urbs_free); 39061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 391088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (!acm->dev) { 392088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__); 393088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold return; 394088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold } 395088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold usb_mark_last_busy(acm->dev); 39661a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 397088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (urb->status) { 398088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", 399088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold __func__, urb->status); 400088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold return; 40161a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 402088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm_process_read_urb(acm, urb); 403088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold 404088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold /* throttle device if requested by tty */ 40511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irqsave(&acm->read_lock, flags); 406088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm->throttled = acm->throttle_req; 407088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (!acm->throttled && !acm->susp_count) { 408088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold spin_unlock_irqrestore(&acm->read_lock, flags); 409088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm_submit_read_urb(acm, rb->index, GFP_ATOMIC); 410088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold } else { 411088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold spin_unlock_irqrestore(&acm->read_lock, flags); 412088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold } 4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* data interface wrote those outgoing bytes */ 4167d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void acm_write_bulk(struct urb *urb) 4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 418cdc97792289179974af6dda781c855696358d307Ming Lei struct acm_wb *wb = urb->context; 419e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell struct acm *acm = wb->instance; 420ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips unsigned long flags; 4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4224fa4626cd43679dc62a73ee3e347665e761abc9cJohan Hovold if (urb->status || (urb->actual_length != urb->transfer_buffer_length)) 4234fa4626cd43679dc62a73ee3e347665e761abc9cJohan Hovold dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n", 4241d9846e505febb71255b098910ace741433312b7Johan Hovold __func__, 425e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell urb->actual_length, 426e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell urb->transfer_buffer_length, 427e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell urb->status); 4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 429ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips spin_lock_irqsave(&acm->write_lock, flags); 430e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf acm_write_done(acm, wb); 431ad0b65efd12d020b046cde8d6f474e37bb98dd73Brandon Philips spin_unlock_irqrestore(&acm->write_lock, flags); 432884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (ACM_READY(acm)) 433884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum schedule_work(&acm->work); 4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 436c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void acm_softint(struct work_struct *work) 4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 438c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells struct acm *acm = container_of(work, struct acm, work); 43910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct tty_struct *tty; 440e5fbab51b4219fbd1dab28666affe38a920b5f7eDavid Brownell 4411d9846e505febb71255b098910ace741433312b7Johan Hovold dev_vdbg(&acm->data->dev, "%s\n", __func__); 4421d9846e505febb71255b098910ace741433312b7Johan Hovold 4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 44510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty = tty_port_tty_get(&acm->port); 44615e5bee33ffc11d0e5c6f819a65e7881c5c407beJohan Hovold if (!tty) 44715e5bee33ffc11d0e5c6f819a65e7881c5c407beJohan Hovold return; 44810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_wakeup(tty); 44910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_kref_put(tty); 4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY handlers 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_open(struct tty_struct *tty, struct file *filp) 4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm; 45942dd2aa6496a2e87e496aac5494d2e1d6096c85bThadeu Lima de Souza Cascardo int rv = -ENODEV; 4604186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven 4614186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_lock(&open_mutex); 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm = acm_table[tty->index]; 4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm || !acm->dev) 4652b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum goto out; 4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rv = 0; 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 469a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_dbg(&acm->control->dev, "%s\n", __func__); 470a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold 47128d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91David Engraf set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); 47210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty->driver_data = acm; 47410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_tty_set(&acm->port, tty); 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 47694409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum if (usb_autopm_get_interface(acm->control) < 0) 47794409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum goto early_bail; 47811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum else 47911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->control->needs_remote_wakeup = 1; 4801365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 4811365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 48210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->port.count++) { 4832b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum mutex_unlock(&acm->mutex); 4841365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_autopm_put_interface(acm->control); 4852b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum goto out; 48610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } 4871365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->dev = acm->dev; 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { 490a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_err(&acm->control->dev, 491a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold "%s - usb_submit_urb(ctrl irq) failed\n", __func__); 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto bail_out; 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 495ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && 496ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum (acm->ctrl_caps & USB_CDC_CAP_LINE)) 49794d4c8919de3ae9e2e029ed121adfed43803bb5dJohan Hovold goto bail_out; 49810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 49911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum usb_autopm_put_interface(acm->control); 5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 501088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (acm_submit_read_urbs(acm, GFP_KERNEL)) 502088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold goto bail_out; 503ca79b7b4158cbf32625793a1fc1d59ac46d44197Oliver Neukum 5047af25b4b34a2439020d78da765a3bed0ff73f25cOliver Neukum set_bit(ASYNCB_INITIALIZED, &acm->port.flags); 50510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox rv = tty_port_block_til_ready(&acm->port, tty, filp); 5062b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum 5071365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 5082b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukumout: 50994409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum mutex_unlock(&open_mutex); 5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rv; 5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail_out: 51310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox acm->port.count--; 5141365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 5152b626dc134d38d0001b98acf8c7293b6bc5ee86dOliver Neukum usb_autopm_put_interface(acm->control); 51694409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukumearly_bail: 51794409cc1e507b157f8442dad80ff5e560c3205e5Oliver Neukum mutex_unlock(&open_mutex); 51810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_tty_set(&acm->port, NULL); 5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 52283ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dkstatic void acm_tty_unregister(struct acm *acm) 52383ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk{ 524dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold int i; 52561a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 52683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk tty_unregister_device(acm_tty_driver, acm->minor); 52783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_put_intf(acm->control); 52883ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_table[acm->minor] = NULL; 52983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_free_urb(acm->ctrlurb); 530e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf for (i = 0; i < ACM_NW; i++) 531e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_free_urb(acm->wb[i].urb); 532dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold for (i = 0; i < acm->rx_buflimit; i++) 533088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold usb_free_urb(acm->read_urbs[i]); 534c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 53583ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk kfree(acm); 53683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk} 53783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk 5384e608671674b62e97166f903830d5553e37970e8Arnd Bergmannstatic void acm_port_down(struct acm *acm) 53910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{ 540dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold int i; 541dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold 54210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox mutex_lock(&open_mutex); 54310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->dev) { 54410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_autopm_get_interface(acm->control); 54510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox acm_set_control(acm, acm->ctrlout = 0); 54610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_kill_urb(acm->ctrlurb); 54710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox for (i = 0; i < ACM_NW; i++) 54810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_kill_urb(acm->wb[i].urb); 549dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold for (i = 0; i < acm->rx_buflimit; i++) 550088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold usb_kill_urb(acm->read_urbs[i]); 55110077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox acm->control->needs_remote_wakeup = 0; 55210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox usb_autopm_put_interface(acm->control); 55310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } 55410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox mutex_unlock(&open_mutex); 55510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox} 55610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 55710077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic void acm_tty_hangup(struct tty_struct *tty) 55810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox{ 55910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct acm *acm = tty->driver_data; 56010077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_hangup(&acm->port); 5614e608671674b62e97166f903830d5553e37970e8Arnd Bergmann acm_port_down(acm); 56210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox} 56310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox 5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_close(struct tty_struct *tty, struct file *filp) 5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 56810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox /* Perform the closing process and see if we need to do the hardware 56910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox shutdown */ 570051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra if (!acm) 5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 572051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra if (tty_port_close_start(&acm->port, tty, filp) == 0) { 573051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra mutex_lock(&open_mutex); 574051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra if (!acm->dev) { 575051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra tty_port_tty_set(&acm->port, NULL); 576051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra acm_tty_unregister(acm); 577051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra tty->driver_data = NULL; 578051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra } 579051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra mutex_unlock(&open_mutex); 580051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra return; 581051522bb47797f7168a617a0752d7ddc1a2f6f24Francesco Lavra } 5824e608671674b62e97166f903830d5553e37970e8Arnd Bergmann acm_port_down(acm); 58310077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_close_end(&acm->port, tty); 58410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_port_tty_set(&acm->port, NULL); 5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic int acm_tty_write(struct tty_struct *tty, 5886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox const unsigned char *buf, int count) 5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int stat; 592884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum unsigned long flags; 593884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int wbn; 594884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 595884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!count) 5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6015e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count); 602a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold 603884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_irqsave(&acm->write_lock, flags); 6046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox wbn = acm_wb_alloc(acm); 6056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (wbn < 0) { 606884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 607884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 608884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 609884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb = &acm->wb[wbn]; 6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 611884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum count = (count > acm->writesize) ? acm->writesize : count; 6125e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3Johan Hovold dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count); 613884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum memcpy(wb->buf, buf, count); 614884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->len = count; 615884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_unlock_irqrestore(&acm->write_lock, flags); 6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox stat = acm_write_start(acm, wbn); 6186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (stat < 0) 6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return stat; 6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return count; 6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_write_room(struct tty_struct *tty) 6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 628884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum /* 629884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * Do not let the line discipline to know that we have a reserve, 630884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * or it might get too enthusiastic. 631884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 632934da4635c2d05cef474e5243ef05df95b2ad264David Brownell return acm_wb_is_avail(acm) ? acm->writesize : 0; 6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int acm_tty_chars_in_buffer(struct tty_struct *tty) 6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 63923198fda7182969b619613a555f8645fdc3dc334Alan Cox return 0; 640884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum /* 641884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum * This is inaccurate (overcounts), but it works. 642884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum */ 64386478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize; 6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_throttle(struct tty_struct *tty) 6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 649088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold 6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 652088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold 653088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold spin_lock_irq(&acm->read_lock); 654088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm->throttle_req = 1; 655088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold spin_unlock_irq(&acm->read_lock); 6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_tty_unthrottle(struct tty_struct *tty) 6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 661088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold unsigned int was_throttled; 662088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold 6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 665088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold 666088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold spin_lock_irq(&acm->read_lock); 667088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold was_throttled = acm->throttled; 668088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm->throttled = 0; 669088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm->throttle_req = 0; 670088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold spin_unlock_irq(&acm->read_lock); 671088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold 672088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (was_throttled) 673088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm_submit_read_urbs(acm, GFP_KERNEL); 6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6769e98966c7bb94355689478bc84cc3e0c190f977eAlan Coxstatic int acm_tty_break_ctl(struct tty_struct *tty, int state) 6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6799e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox int retval; 6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6819e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox return -EINVAL; 6829e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox retval = acm_send_break(acm, state ? 0xffff : 0); 6839e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox if (retval < 0) 684a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_dbg(&acm->control->dev, "%s - send break failed\n", 685a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold __func__); 6869e98966c7bb94355689478bc84cc3e0c190f977eAlan Cox return retval; 6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 68960b33c133ca0b7c0b6072c87234b63fee6e80558Alan Coxstatic int acm_tty_tiocmget(struct tty_struct *tty) 6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | 6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | 6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | 6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | 7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | 7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds TIOCM_CTS; 7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 70420b9d17715017ae4dd4ec87fabc36d33b9de708eAlan Coxstatic int acm_tty_tiocmset(struct tty_struct *tty, 7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int set, unsigned int clear) 7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int newctrl; 7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = acm->ctrlout; 7146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | 7156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); 7166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | 7176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0); 7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl = (newctrl & ~clear) | set; 7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acm->ctrlout == newctrl) 7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return acm_set_control(acm, acm->ctrlout = newctrl); 7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7266caa76b7786891b42b66a0e61e2c2fff2c884620Alan Coxstatic int acm_tty_ioctl(struct tty_struct *tty, 7276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox unsigned int cmd, unsigned long arg) 7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOIOCTLCMD; 7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7374c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u32 acm_tty_speed[] = { 7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0, 50, 75, 110, 134, 150, 200, 300, 600, 7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1200, 1800, 2400, 4800, 9600, 19200, 38400, 7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 57600, 115200, 230400, 460800, 500000, 576000, 7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 921600, 1000000, 1152000, 1500000, 2000000, 7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2500000, 3000000, 3500000, 4000000 7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7454c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u8 acm_tty_size[] = { 7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5, 6, 7, 8 7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7496e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Coxstatic void acm_tty_set_termios(struct tty_struct *tty, 7506e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox struct ktermios *termios_old) 7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm = tty->driver_data; 753606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox struct ktermios *termios = tty->termios; 7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_line_coding newline; 7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int newctrl = acm->ctrlout; 7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ACM_READY(acm)) 7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7609b80fee149a875a6292b2556ab2c64dc7ab7d6f5Alan Cox newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty)); 7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0; 7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bParityType = termios->c_cflag & PARENB ? 7636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (termios->c_cflag & PARODD ? 1 : 2) + 7646e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (termios->c_cflag & CMSPAR ? 2 : 0) : 0; 7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; 7666e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox /* FIXME: Needs to clear unsupported bits in the termios */ 7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->clocal = ((termios->c_cflag & CLOCAL) != 0); 7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!newline.dwDTERate) { 7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.dwDTERate = acm->line.dwDTERate; 7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newctrl &= ~ACM_CTRL_DTR; 7726e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox } else 7736e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox newctrl |= ACM_CTRL_DTR; 7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (newctrl != acm->ctrlout) 7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout = newctrl); 7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (memcmp(&acm->line, &newline, sizeof newline)) { 7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(&acm->line, &newline, sizeof newline); 780a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n", 781a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold __func__, 782a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold le32_to_cpu(newline.dwDTERate), 7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bCharFormat, newline.bParityType, 7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newline.bDataBits); 7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_line(acm, &acm->line); 7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB probe and disconnect routines. 7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 793830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum/* Little helpers: write/read buffers free */ 794884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic void acm_write_buffers_free(struct acm *acm) 795884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 796884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i; 797884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 798a496c64f1363ec4d67ebdc1e1f619ad6372a574cOliver Neukum struct usb_device *usb_dev = interface_to_usbdev(acm->control); 799884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 8006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) 801997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah); 802884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 803884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 804830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukumstatic void acm_read_buffers_free(struct acm *acm) 805830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum{ 806830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum struct usb_device *usb_dev = interface_to_usbdev(acm->control); 807dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold int i; 808830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum 809dab54c9f1e26f47a3313300bc1f4dc0eecb47375Johan Hovold for (i = 0; i < acm->rx_buflimit; i++) 810997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack usb_free_coherent(usb_dev, acm->readsize, 811088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm->read_buffers[i].base, acm->read_buffers[i].dma); 812830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum} 813830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum 814884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum/* Little helper: write buffers allocate */ 815884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukumstatic int acm_write_buffers_alloc(struct acm *acm) 816884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum{ 817884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum int i; 818884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum struct acm_wb *wb; 819884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 82086478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { 821997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL, 822884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum &wb->dmah); 823884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (!wb->buf) { 824884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum while (i != 0) { 825884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum --i; 826884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum --wb; 827997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack usb_free_coherent(acm->dev, acm->writesize, 828884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum wb->buf, wb->dmah); 829884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 830884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return -ENOMEM; 831884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 832884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum } 833884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum return 0; 834884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum} 835884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum 83610077d4a6674f535abdbe25cdecb1202af7948f1Alan Coxstatic int acm_probe(struct usb_interface *intf, 83710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox const struct usb_device_id *id) 8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_cdc_union_desc *union_header = NULL; 840c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct usb_cdc_country_functional_desc *cfd = NULL; 841c6dbf554bc8a79c9caab3dbf891a33c19068f646David Brownell unsigned char *buffer = intf->altsetting->extra; 8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int buflen = intf->altsetting->extralen; 8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *control_interface; 8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *data_interface; 845a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum struct usb_endpoint_descriptor *epctrl = NULL; 846a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum struct usb_endpoint_descriptor *epread = NULL; 847a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum struct usb_endpoint_descriptor *epwrite = NULL; 8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_device *usb_dev = interface_to_usbdev(intf); 8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acm *acm; 8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int minor; 8516e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox int ctrlsize, readsize; 8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 *buf; 8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 ac_management_function = 0; 8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 call_management_function = 0; 8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int call_interface_num = -1; 856fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter int data_interface_num = -1; 8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long quirks; 85886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum int num_rx_buf; 85961a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek int i; 860a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum int combined_interfaces = 0; 8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 86286478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* normal quirks */ 8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds quirks = (unsigned long)id->driver_info; 86486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR; 86586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum 86686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum /* handle quirks deadly to normal probing*/ 8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (quirks == NO_UNION_NORMAL) { 8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, 1); 8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = usb_ifnum_to_if(usb_dev, 0); 8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto skip_normal_probe; 8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8726e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox 8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* normal probing*/ 8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buffer) { 8759908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "Weird descriptor references\n"); 8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buflen) { 880577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray if (intf->cur_altsetting->endpoint && 881577045c0a76e34294f902a7d5d60e90b04d094d0Toby Gray intf->cur_altsetting->endpoint->extralen && 8826e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox intf->cur_altsetting->endpoint->extra) { 8836e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 8846e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "Seeking extra descriptors on endpoint\n"); 8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen = intf->cur_altsetting->endpoint->extralen; 8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer = intf->cur_altsetting->endpoint->extra; 8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 8889908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, 8899908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman "Zero length descriptor references\n"); 8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (buflen > 0) { 8956e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (buffer[1] != USB_DT_CS_INTERFACE) { 8969908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "skipping garbage\n"); 8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto next_desc; 8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9006e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox switch (buffer[2]) { 9016e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_UNION_TYPE: /* we've found it */ 9026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (union_header) { 9036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_err(&intf->dev, "More than one " 9046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "union descriptor, skipping ...\n"); 9056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox goto next_desc; 9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox union_header = (struct usb_cdc_union_desc *)buffer; 9086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 9096e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ 9106e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox cfd = (struct usb_cdc_country_functional_desc *)buffer; 9116e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 9126e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_HEADER_TYPE: /* maybe check version */ 9136e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; /* for now we ignore it */ 9146e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_ACM_TYPE: 9156e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox ac_management_function = buffer[3]; 9166e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 9176e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox case USB_CDC_CALL_MANAGEMENT_TYPE: 9186e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox call_management_function = buffer[3]; 9196e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox call_interface_num = buffer[4]; 920ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3) 9216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n"); 9226e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 9236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox default: 9246e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox /* there are LOTS more CDC descriptors that 9256e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox * could legitimately be found here. 9266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox */ 9276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "Ignoring descriptor: " 9286e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "type %02x, length %d\n", 9296e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox buffer[2], buffer[0]); 9306e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox break; 9316e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox } 9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnext_desc: 9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen -= buffer[0]; 9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer += buffer[0]; 9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!union_header) { 9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (call_interface_num > 0) { 9396e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n"); 940fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter /* quirks for Droids MuIn LCD */ 941fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter if (quirks & NO_DATA_INTERFACE) 942fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter data_interface = usb_ifnum_to_if(usb_dev, 0); 943fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter else 944fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); 9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = intf; 9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 947a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (intf->cur_altsetting->desc.bNumEndpoints != 3) { 948a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum dev_dbg(&intf->dev,"No union descriptor, giving up\n"); 949a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum return -ENODEV; 950a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } else { 951a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n"); 952a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum combined_interfaces = 1; 953a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum control_interface = data_interface = intf; 954a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum goto look_for_collapsed_interface; 955a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } 9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); 9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); 9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!control_interface || !data_interface) { 9616e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "no interfaces\n"); 9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9656e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox 9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface_num != call_interface_num) 9676e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n"); 9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 969a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (control_interface == data_interface) { 970a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum /* some broken devices designed for windows work this way */ 971a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum dev_warn(&intf->dev,"Control and data interfaces are not separated!\n"); 972a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum combined_interfaces = 1; 973a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum /* a popular other OS doesn't use it */ 974a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum quirks |= NO_CAP_LINE; 975a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) { 976a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum dev_err(&intf->dev, "This needs exactly 3 endpoints\n"); 977a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum return -EINVAL; 978a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } 979a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukumlook_for_collapsed_interface: 980a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum for (i = 0; i < 3; i++) { 981a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum struct usb_endpoint_descriptor *ep; 982a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum ep = &data_interface->cur_altsetting->endpoint[i].desc; 983a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum 984a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (usb_endpoint_is_int_in(ep)) 985a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum epctrl = ep; 986a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum else if (usb_endpoint_is_bulk_out(ep)) 987a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum epwrite = ep; 988a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum else if (usb_endpoint_is_bulk_in(ep)) 989a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum epread = ep; 990a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum else 991a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum return -EINVAL; 992a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } 993a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (!epctrl || !epread || !epwrite) 994a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum return -ENODEV; 995a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum else 996a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum goto made_compressed_probe; 997a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum } 998a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum 9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip_normal_probe: 10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /*workaround for switched interfaces */ 10026e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (data_interface->cur_altsetting->desc.bInterfaceClass 10036e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox != CDC_DATA_INTERFACE_TYPE) { 10046e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (control_interface->cur_altsetting->desc.bInterfaceClass 10056e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox == CDC_DATA_INTERFACE_TYPE) { 10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_interface *t; 10076e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 10086e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "Your device has switched interfaces.\n"); 10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t = control_interface; 10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds control_interface = data_interface; 10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data_interface = t; 10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 101674da5d68a54d66667664fbe233ededab2376a070Alan Stern 101774da5d68a54d66667664fbe233ededab2376a070Alan Stern /* Accept probe requests only for the control interface */ 1018a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (!combined_interfaces && intf != control_interface) 101974da5d68a54d66667664fbe233ededab2376a070Alan Stern return -ENODEV; 10206e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox 1021a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (!combined_interfaces && usb_interface_claimed(data_interface)) { 1022a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum /* valid in this context */ 10236e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, "The data interface isn't available\n"); 10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EBUSY; 10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data_interface->cur_altsetting->desc.bNumEndpoints < 2) 10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epctrl = &control_interface->cur_altsetting->endpoint[0].desc; 10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epread = &data_interface->cur_altsetting->endpoint[0].desc; 10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epwrite = &data_interface->cur_altsetting->endpoint[1].desc; 10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* workaround for switched endpoints */ 103745aea704d12d05f06b3f82974aa1438460f42398Luiz Fernando N. Capitulino if (!usb_endpoint_dir_in(epread)) { 10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* descriptors are swapped */ 10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_endpoint_descriptor *t; 10406e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox dev_dbg(&intf->dev, 10416e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "The data interface has switched endpoints\n"); 10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t = epread; 10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epread = epwrite; 10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds epwrite = t; 10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1046a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukummade_compressed_probe: 1047a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_dbg(&intf->dev, "interfaces are valid\n"); 10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); 10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (minor == ACM_TTY_MINORS) { 10519908a32e94de2141463e104c9924279ed3509447Greg Kroah-Hartman dev_err(&intf->dev, "no more free acm devices\n"); 10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10556e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm = kzalloc(sizeof(struct acm), GFP_KERNEL); 10566e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (acm == NULL) { 1057255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold dev_err(&intf->dev, "out of memory (acm kzalloc)\n"); 10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail; 10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); 10626e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox readsize = le16_to_cpu(epread->wMaxPacketSize) * 10636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox (quirks == SINGLE_RX_URB ? 1 : 2); 1064a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum acm->combined_interfaces = combined_interfaces; 1065e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; 10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->control = control_interface; 10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->data = data_interface; 10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->minor = minor; 10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->dev = usb_dev; 10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_caps = ac_management_function; 1071a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (quirks & NO_CAP_LINE) 1072a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum acm->ctrl_caps &= ~USB_CDC_CAP_LINE; 10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlsize = ctrlsize; 10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->readsize = readsize; 107586478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum acm->rx_buflimit = num_rx_buf; 1076c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells INIT_WORK(&acm->work, acm_softint); 1077884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum spin_lock_init(&acm->write_lock); 107861a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek spin_lock_init(&acm->read_lock); 10791365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_init(&acm->mutex); 108061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); 1081cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum acm->is_int_ep = usb_endpoint_xfer_int(epread); 1082cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum if (acm->is_int_ep) 1083cf7fdd57f978d40ceb9a0f58a25f5cf9c84d6f33Oliver Neukum acm->bInterval = epread->bInterval; 1084739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox tty_port_init(&acm->port); 1085739e0285cbb162c8ddd0061fda581ee54a34c19aAlan Cox acm->port.ops = &acm_port_ops; 10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1087997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); 10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buf) { 1089255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n"); 10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail2; 10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrl_buffer = buf; 10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1094884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum if (acm_write_buffers_alloc(acm) < 0) { 1095255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold dev_err(&intf->dev, "out of memory (write buffer alloc)\n"); 10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail4; 10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); 11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm->ctrlurb) { 1101255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); 11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto alloc_fail5; 11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 110486478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) { 1105088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold struct acm_rb *rb = &(acm->read_buffers[i]); 1106088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold struct urb *urb; 110761a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 110874f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL, 110974f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold &rb->dma); 111074f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold if (!rb->base) { 111174f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold dev_err(&intf->dev, "out of memory " 111274f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold "(read bufs usb_alloc_coherent)\n"); 111374f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold goto alloc_fail6; 111474f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold } 1115088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold rb->index = i; 1116088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold rb->instance = acm; 111774f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold 1118088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold urb = usb_alloc_urb(0, GFP_KERNEL); 1119088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (!urb) { 1120255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold dev_err(&intf->dev, 11216e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox "out of memory (read urbs usb_alloc_urb)\n"); 1122c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin goto alloc_fail6; 112361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 1124088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 1125088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold urb->transfer_dma = rb->dma; 1126088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold if (acm->is_int_ep) { 1127088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold usb_fill_int_urb(urb, acm->dev, 1128088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm->rx_endpoint, 1129088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold rb->base, 1130088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm->readsize, 1131088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm_read_bulk_callback, rb, 1132088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm->bInterval); 1133088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold } else { 1134088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold usb_fill_bulk_urb(urb, acm->dev, 1135088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm->rx_endpoint, 1136088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold rb->base, 1137088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm->readsize, 1138088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm_read_bulk_callback, rb); 1139088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold } 114061a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek 1141088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold acm->read_urbs[i] = urb; 1142088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold __set_bit(i, &acm->read_urbs_free); 114361a87adf2e7b410da8e41799c61c21a7b8c8b001David Kubicek } 11446e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < ACM_NW; i++) { 1145e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf struct acm_wb *snd = &(acm->wb[i]); 1146e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf 11476e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox snd->urb = usb_alloc_urb(0, GFP_KERNEL); 11486e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (snd->urb == NULL) { 1149255ab56c5ae30165d506b837f6576944a02ecdf0Johan Hovold dev_err(&intf->dev, 115059d7fec7c6908604862658a3679ac44c2c3eea44Johan Hovold "out of memory (write urbs usb_alloc_urb)\n"); 115174f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold goto alloc_fail7; 1152e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf } 1153e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf 11545186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev if (usb_endpoint_xfer_int(epwrite)) 11555186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev usb_fill_int_urb(snd->urb, usb_dev, 11565186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), 11575186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval); 11585186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev else 11595186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev usb_fill_bulk_urb(snd->urb, usb_dev, 11605186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), 11615186ffee2320942c3dc9745f7930e0eb15329ca6Arseniy Lartsev NULL, acm->writesize, acm_write_bulk, snd); 1162e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 1163e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf snd->instance = acm; 11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11666e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox usb_set_intfdata(intf, acm); 1167c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1168c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); 1169c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) 117074f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold goto alloc_fail7; 1171c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1172c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (cfd) { /* export the country data */ 1173c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); 1174c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (!acm->country_codes) 1175c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1176c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_code_size = cfd->bLength - 4; 11776e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, 11786e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox cfd->bLength - 4); 1179c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum acm->country_rel_date = cfd->iCountryCodeRelDate; 1180c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1181c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); 1182c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) { 1183c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 1184c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1185c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1186c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 11876e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox i = device_create_file(&intf->dev, 11886e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox &dev_attr_iCountryCodeRelDate); 1189c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum if (i < 0) { 1190c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Lin device_remove_file(&intf->dev, &dev_attr_wCountryCodes); 1191c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum kfree(acm->country_codes); 1192c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum goto skip_countries; 1193c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1194c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 1195c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum 1196c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukumskip_countries: 11976e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox usb_fill_int_urb(acm->ctrlurb, usb_dev, 1198a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), 1199a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, 1200a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum /* works around buggy devices */ 1201a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum epctrl->bInterval ? epctrl->bInterval : 0xff); 12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->ctrlurb->transfer_dma = acm->ctrl_dma; 12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); 12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_control(acm, acm->ctrlout); 12081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->line.dwDTERate = cpu_to_le32(9600); 12101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->line.bDataBits = 8; 12111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_set_line(acm, &acm->line); 12121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_driver_claim_interface(&acm_driver, data_interface, acm); 1214672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell usb_set_intfdata(data_interface, acm); 12151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 121683ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk usb_get_intf(control_interface); 121783ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk tty_register_device(acm_tty_driver, minor, &control_interface->dev); 12181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_table[minor] = acm; 12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1221c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum return 0; 122274f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovoldalloc_fail7: 1223e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf for (i = 0; i < ACM_NW; i++) 1224e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_free_urb(acm->wb[i].urb); 1225c2572b78aa0447244a38e555ebb1b3b48a0088a5Axel Linalloc_fail6: 122686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum for (i = 0; i < num_rx_buf; i++) 1227088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold usb_free_urb(acm->read_urbs[i]); 122874f5e1babde76149c2bb35ca5dbf4d0b9b38f161Johan Hovold acm_read_buffers_free(acm); 12291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_free_urb(acm->ctrlurb); 12301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail5: 1231884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_buffers_free(acm); 12321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail4: 1233997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); 12341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail2: 12351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(acm); 12361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsalloc_fail: 12371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 12381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 12391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12401365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic void stop_data_traffic(struct acm *acm) 12411365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 12421365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum int i; 1243a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold 1244a5cc7ef92f69a88a1984cc3e09f6c19656efeb2eJohan Hovold dev_dbg(&acm->control->dev, "%s\n", __func__); 12451365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 12461365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum usb_kill_urb(acm->ctrlurb); 12476e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox for (i = 0; i < ACM_NW; i++) 1248e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4David Engraf usb_kill_urb(acm->wb[i].urb); 12491365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum for (i = 0; i < acm->rx_buflimit; i++) 1250088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold usb_kill_urb(acm->read_urbs[i]); 12511365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 12521365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum cancel_work_sync(&acm->work); 12531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 12541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void acm_disconnect(struct usb_interface *intf) 12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1257c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum struct acm *acm = usb_get_intfdata(intf); 12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct usb_device *usb_dev = interface_to_usbdev(intf); 125910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox struct tty_struct *tty; 12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1261672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell /* sibling interface is already cleaning up */ 1262672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell if (!acm) 126386067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum return; 1264672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell 1265672c4e1843c54227ff1bdf1fdd96f9c45c56aa85David Brownell mutex_lock(&open_mutex); 12666e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox if (acm->country_codes) { 126774da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, 126874da5d68a54d66667664fbe233ededab2376a070Alan Stern &dev_attr_wCountryCodes); 126974da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, 127074da5d68a54d66667664fbe233ededab2376a070Alan Stern &dev_attr_iCountryCodeRelDate); 1271c4cabd28c73116716dcfd0d5f91414b48c0cf5ceOliver Neukum } 127274da5d68a54d66667664fbe233ededab2376a070Alan Stern device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); 12731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm->dev = NULL; 127486067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_set_intfdata(acm->control, NULL); 127586067eead5a6c6fa413ef5cb59f7129f5ed80292Oliver Neukum usb_set_intfdata(acm->data, NULL); 12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12771365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum stop_data_traffic(acm); 12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1279884b600f63dc7c646f415a5d8f356df1f66ff6f2Oliver Neukum acm_write_buffers_free(acm); 1280997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, 1281997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack acm->ctrl_dma); 1282830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum acm_read_buffers_free(acm); 12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1284a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum if (!acm->combined_interfaces) 1285a2bfb4a346d2c2e25f84b35c6044ff53296be1eeOliver Neukum usb_driver_release_interface(&acm_driver, intf == acm->control ? 1286830f4021a8d5ce97c6bed267132e5e90fb166192Oliver Neukum acm->data : acm->control); 12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 128810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->port.count == 0) { 128983ef344a7539aa55a787790bc036f0bf3466e191brian@murphy.dk acm_tty_unregister(acm); 12904186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12944186ecf8ad16dd05759a09594de6a87e48759ba6Arjan van de Ven mutex_unlock(&open_mutex); 129510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty = tty_port_tty_get(&acm->port); 129610077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (tty) { 129710077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_hangup(tty); 129810077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox tty_kref_put(tty); 129910077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox } 13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 13011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1302357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM 13031365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_suspend(struct usb_interface *intf, pm_message_t message) 13041365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 13051365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum struct acm *acm = usb_get_intfdata(intf); 130611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int cnt; 130711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 130865bfd2967c906ca322a4bb69a285fe0de8916ac6Alan Stern if (message.event & PM_EVENT_AUTO) { 130911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int b; 131011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 1311088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold spin_lock_irq(&acm->write_lock); 1312088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold b = acm->transmitting; 1313088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold spin_unlock_irq(&acm->write_lock); 131411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (b) 131511ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum return -EBUSY; 131611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum } 131711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 131811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irq(&acm->read_lock); 131911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock(&acm->write_lock); 132011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum cnt = acm->susp_count++; 132111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock(&acm->write_lock); 132211ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irq(&acm->read_lock); 13231365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 132411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (cnt) 13251365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 13261365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum /* 13271365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum we treat opened interfaces differently, 13281365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum we must guard against open 13291365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum */ 13301365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 13311365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 133210077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->port.count) 13331365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum stop_data_traffic(acm); 13341365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13351365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 13361365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 13371365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 13381365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13391365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumstatic int acm_resume(struct usb_interface *intf) 13401365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum{ 13411365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum struct acm *acm = usb_get_intfdata(intf); 134297d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum struct acm_wb *wb; 13431365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum int rv = 0; 134411ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum int cnt; 13451365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 134611ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_lock_irq(&acm->read_lock); 134711ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum acm->susp_count -= 1; 134811ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum cnt = acm->susp_count; 134911ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum spin_unlock_irq(&acm->read_lock); 135011ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum 135111ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum if (cnt) 13521365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return 0; 13531365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13541365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_lock(&acm->mutex); 135510077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox if (acm->port.count) { 13561365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); 135797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum 135897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum spin_lock_irq(&acm->write_lock); 135997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum if (acm->delayed_wb) { 136097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum wb = acm->delayed_wb; 136197d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum acm->delayed_wb = NULL; 136297d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum spin_unlock_irq(&acm->write_lock); 1363f0730924e9e32bb8935c60040a26d94179355088Oliver Neukum acm_start_wb(acm, wb); 136497d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum } else { 136597d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum spin_unlock_irq(&acm->write_lock); 136697d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum } 136797d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum 136897d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum /* 136997d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum * delayed error checking because we must 137097d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum * do the write path at all cost 137197d35f95552c9a0ee4777a7f04431a9fd1260478Oliver Neukum */ 13721365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum if (rv < 0) 137311ea859d64b69a747d6b060b9ed1520eab1161feOliver Neukum goto err_out; 13741365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 1375088c64f812847b3623b03d167ed329f90f3e38a4Johan Hovold rv = acm_submit_read_urbs(acm, GFP_NOIO); 13761365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum } 13771365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum 13781365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukumerr_out: 13791365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum mutex_unlock(&acm->mutex); 13801365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum return rv; 13811365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum} 1382357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum 1383a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavrastatic int acm_reset_resume(struct usb_interface *intf) 1384a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra{ 1385a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra struct acm *acm = usb_get_intfdata(intf); 1386a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra struct tty_struct *tty; 1387a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra 1388a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra mutex_lock(&acm->mutex); 1389a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra if (acm->port.count) { 1390a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra tty = tty_port_tty_get(&acm->port); 1391a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra if (tty) { 1392a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra tty_hangup(tty); 1393a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra tty_kref_put(tty); 1394a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra } 1395a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra } 1396a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra mutex_unlock(&acm->mutex); 1397a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra return acm_resume(intf); 1398a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra} 1399a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra 1400357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif /* CONFIG_PM */ 1401c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor 1402c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor#define NOKIA_PCSUITE_ACM_INFO(x) \ 1403c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \ 1404c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \ 1405c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor USB_CDC_ACM_PROTO_VENDOR) 1406c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor 14074035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray#define SAMSUNG_PCSUITE_ACM_INFO(x) \ 14084035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \ 14094035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \ 14104035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray USB_CDC_ACM_PROTO_VENDOR) 14114035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray 14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 14131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB driver structure. 14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 14151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14166ef4852b1326301f6e9657e99b2c3221be1a3a44Németh Mártonstatic const struct usb_device_id acm_ids[] = { 14171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* quirky and broken devices */ 14181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */ 14191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 14201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds }, 1421b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ 1422b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1423b0e2a705bffbfb70d9bed8b5f9094901f28d9563Andrey Arapov }, 14240f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */ 14250f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 14260f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42dAndrew Lunn }, 14278753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */ 14288753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 14298753e65e34a7b02f8473e7c6ce1cf7e08db4c6e3Masahito Omote }, 143091a9c9214e34c364bf15406aadb922787ae7129bChris Malley { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */ 143191a9c9214e34c364bf15406aadb922787ae7129bChris Malley .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 143291a9c9214e34c364bf15406aadb922787ae7129bChris Malley }, 14337abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox { USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */ 14347abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox .driver_info = SINGLE_RX_URB, 14357abcf20b8f32dd679b162b33c07e427c67d4a1fbAlan Cox }, 143686478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */ 143786478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum .driver_info = SINGLE_RX_URB, /* firmware bug */ 143886478944eba887f149e151bacc023ae4b2d23ea6Oliver Neukum }, 14393dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */ 14403dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum .driver_info = SINGLE_RX_URB, /* firmware bug */ 14413dd2ae81f70f191f5b6751d18fdfe61dbafda7e8Oliver Neukum }, 14429be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */ 14439be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 14449be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum }, 14456149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */ 14466149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 14476149ed5e3a6207595bd7362af7724d64f44af216Iain McFarlane }, 1448c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */ 1449c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1450c8fd2c37b99c55c8d24888e0ed9d5f4f73458c9cEric Sandeen }, 1451c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */ 1452c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1453c89c60e9d6b306fb6963030abb3bd07cc3de66b2Alan Cox }, 1454cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */ 1455cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ 1456cab98a0a349829b145d924c0649a2d30cd6a9e3dXiao Kaijian }, 1457155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */ 1458155df65ae11dfc322214c6f887185929c809df1bDmitriy Taychenachev }, 1459c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */ 1460c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on 1461c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter data interface instead of 1462c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter communications interface. 1463c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter Maybe we should define a new 1464c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter quirk for this. */ 1465c332b4e1bfd56fe9028d8ef9708cb06179dd1a23Adam Richter }, 14661f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */ 14671f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ 14681f17c5026ce27d0449903d34f9fca461a45fe1cbKir Kolyshkin }, 1469c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */ 1470c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ 1471c3baa19b0a9b711b02cec81d9fea33b7b9628957Russ Nelson }, 14729be8456c00c5bd603b933e6e9d82041e8b32c401Oliver Neukum 1473c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor /* Nokia S60 phones expose two ACM channels. The first is 1474c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor * a modem and is picked up by the standard AT-command 1475c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor * information below. The second is 'vendor-specific' but 1476c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor * is treated as a serial device at the S60 end, so we want 1477c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor * to expose it on Linux too. */ 1478c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */ 1479c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */ 1480c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */ 1481c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */ 1482c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */ 1483c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */ 1484c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */ 1485c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */ 1486c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */ 1487c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */ 1488c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */ 1489c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */ 1490c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */ 1491c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */ 1492c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */ 1493c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */ 1494c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */ 1495c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */ 1496c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */ 1497c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */ 1498c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */ 1499c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */ 1500c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */ 1501c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */ 1502c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */ 1503c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */ 1504c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */ 1505c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */ 1506c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */ 1507c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */ 1508c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */ 1509c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */ 1510c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */ 1511c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */ 1512c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */ 1513c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */ 1514c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */ 1515c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */ 1516c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */ 1517c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */ 1518c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */ 1519c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */ 1520c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */ 152183a4eae9aeed4a69e89e323a105e653ae06e7c1fPrzemo Firszt { NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */ 15224035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */ 15234035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */ 15244035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */ 15254035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */ 15264035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */ 15274035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */ 15284035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */ 15294035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */ 15304035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */ 15314035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */ 1532721d92fc6373dee15846216f9d178ec240ec0fd7Arvid Ephraim Picciani { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */ 15334061fde2fa80f40cb27114f60500d38d0afcf350Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */ 15344061fde2fa80f40cb27114f60500d38d0afcf350Toby Gray { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */ 15354035e45632c2a8bb4edae83c20447051bd9a9604Toby Gray { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */ 1536c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor 1537c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */ 1538c1479a92cf0a7792298d364e44a781550621cb58Adrian Taylor 15397c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby /* Support Lego NXT using pbLua firmware */ 1540ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby { USB_DEVICE(0x0694, 0xff00), 1541ce126644aa10bf1d8f1c1929b65adab026095761Julian Calaby .driver_info = NOT_A_MODEM, 15427893afc035590383a14b176c1497cba984276ef4Otavio Salvador }, 15437c5d8c394a077a686cfa646cd85dc159a2a940ccJulian Calaby 1544fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter /* Support for Droids MuIn LCD */ 1545fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter { USB_DEVICE(0x04d8, 0x000b), 1546fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter .driver_info = NO_DATA_INTERFACE, 1547fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter }, 1548fd5054c169d29747a44b4e1419ff47f57ae82dbcErik Slagter 15495b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes /* control interfaces without any protocol set */ 15505b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15515b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes USB_CDC_PROTO_NONE) }, 15525b239f0aebd4dd6f85b13decf5e18e86e35d57f0Philippe Corbes 15531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* control interfaces with various AT-command sets */ 15541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_V25TER) }, 15561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_PCCA101) }, 15581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) }, 15601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_GSM) }, 15621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15636e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox USB_CDC_ACM_PROTO_AT_3G) }, 15641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 15651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds USB_CDC_ACM_PROTO_AT_CDMA) }, 15661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { } 15681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 15691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15706e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DEVICE_TABLE(usb, acm_ids); 15711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver acm_driver = { 15731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = "cdc_acm", 15741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .probe = acm_probe, 15751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .disconnect = acm_disconnect, 1576357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM 15771365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .suspend = acm_suspend, 15781365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .resume = acm_resume, 1579a91b0c502285fd0c569fae1222fdd945ef739233Francesco Lavra .reset_resume = acm_reset_resume, 1580357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif 15811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .id_table = acm_ids, 1582357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#ifdef CONFIG_PM 15831365baf7249bb2d05e774e7681237b8e86f5007aOliver Neukum .supports_autosuspend = 1, 1584357585892e56f7c7bec4a9c8dfaf90257c8756c6Oliver Neukum#endif 15851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 15861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 15881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TTY driver structures. 15891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 15901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1591b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations acm_ops = { 15921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .open = acm_tty_open, 15931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .close = acm_tty_close, 159410077d4a6674f535abdbe25cdecb1202af7948f1Alan Cox .hangup = acm_tty_hangup, 15951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write = acm_tty_write, 15961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write_room = acm_tty_write_room, 15971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .ioctl = acm_tty_ioctl, 15981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .throttle = acm_tty_throttle, 15991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .unthrottle = acm_tty_unthrottle, 16001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .chars_in_buffer = acm_tty_chars_in_buffer, 16011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .break_ctl = acm_tty_break_ctl, 16021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .set_termios = acm_tty_set_termios, 16031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .tiocmget = acm_tty_tiocmget, 16041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .tiocmset = acm_tty_tiocmset, 16051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 16061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 16081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Init / exit. 16091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init acm_init(void) 16121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 16131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval; 16141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS); 16151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acm_tty_driver) 16161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 16171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->owner = THIS_MODULE, 16181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->driver_name = "acm", 16191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->name = "ttyACM", 16201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->major = ACM_TTY_MAJOR, 16211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->minor_start = 0, 16221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, 16231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->subtype = SERIAL_TYPE_NORMAL, 1624331b831983f9d706f4a40d08a996d5c2c7a6ea7bGreg Kroah-Hartman acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; 16251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acm_tty_driver->init_termios = tty_std_termios; 16266e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | 16276e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan Cox HUPCL | CLOCAL; 16281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_set_operations(acm_tty_driver, &acm_ops); 16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = tty_register_driver(acm_tty_driver); 16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) { 16321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 16331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 16341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 16351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = usb_register(&acm_driver); 16371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retval) { 16381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_unregister_driver(acm_tty_driver); 16391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 16401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 16411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 16421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1643a2c7b9353e8f782590852052fe2948692020147eJohan Hovold printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n"); 16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 16461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 16471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit acm_exit(void) 16491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 16501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds usb_deregister(&acm_driver); 16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty_unregister_driver(acm_tty_driver); 16521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds put_tty_driver(acm_tty_driver); 16531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 16541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acm_init); 16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acm_exit); 16571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16586e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_AUTHOR(DRIVER_AUTHOR); 16596e47e069eb4dffa88ad91ddfc3fd85f32c35654bAlan CoxMODULE_DESCRIPTION(DRIVER_DESC); 16601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 1661e766aeb882b41355d8732cf49aa9412baef852c5Scott James RemnantMODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR); 1662