11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * USB Serial Converter Generic functions 31da177e4SLinus Torvalds * 4659597b7SJohan Hovold * Copyright (C) 2010 - 2013 Johan Hovold (jhovold@gmail.com) 51da177e4SLinus Torvalds * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 81da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License version 91da177e4SLinus Torvalds * 2 as published by the Free Software Foundation. 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds #include <linux/kernel.h> 131da177e4SLinus Torvalds #include <linux/errno.h> 141da177e4SLinus Torvalds #include <linux/slab.h> 157f0ae3a8SRandy Dunlap #include <linux/sysrq.h> 161da177e4SLinus Torvalds #include <linux/tty.h> 171da177e4SLinus Torvalds #include <linux/tty_flip.h> 181da177e4SLinus Torvalds #include <linux/module.h> 191da177e4SLinus Torvalds #include <linux/moduleparam.h> 201da177e4SLinus Torvalds #include <linux/usb.h> 21a969888cSGreg Kroah-Hartman #include <linux/usb/serial.h> 22ae64387aSAlan Cox #include <linux/uaccess.h> 238e8dce06SDavid VomLehn #include <linux/kfifo.h> 241f87158eSAlan Stern #include <linux/serial.h> 25d9b1b787SJohannes Hölzl 261da177e4SLinus Torvalds #ifdef CONFIG_USB_SERIAL_GENERIC 27b46d60fcSDavid Brownell 281da177e4SLinus Torvalds static __u16 vendor = 0x05f9; 291da177e4SLinus Torvalds static __u16 product = 0xffff; 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds module_param(vendor, ushort, 0); 321da177e4SLinus Torvalds MODULE_PARM_DESC(vendor, "User specified USB idVendor"); 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds module_param(product, ushort, 0); 351da177e4SLinus Torvalds MODULE_PARM_DESC(product, "User specified USB idProduct"); 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */ 381da177e4SLinus Torvalds 39ea65370dSGreg Kroah-Hartman struct usb_serial_driver usb_serial_generic_device = { 4018fcac35SGreg Kroah-Hartman .driver = { 411da177e4SLinus Torvalds .owner = THIS_MODULE, 42269bda1cSGreg Kroah-Hartman .name = "generic", 4318fcac35SGreg Kroah-Hartman }, 441da177e4SLinus Torvalds .id_table = generic_device_ids, 451da177e4SLinus Torvalds .num_ports = 1, 46253ca923SJoris van Rantwijk .throttle = usb_serial_generic_throttle, 47253ca923SJoris van Rantwijk .unthrottle = usb_serial_generic_unthrottle, 48ec22559eSOliver Neukum .resume = usb_serial_generic_resume, 491da177e4SLinus Torvalds }; 501da177e4SLinus Torvalds 51765e0ba6SAlan Stern static struct usb_serial_driver * const serial_drivers[] = { 52765e0ba6SAlan Stern &usb_serial_generic_device, NULL 53765e0ba6SAlan Stern }; 54765e0ba6SAlan Stern 551da177e4SLinus Torvalds #endif 561da177e4SLinus Torvalds 573033bc8dSGreg Kroah-Hartman int usb_serial_generic_register(void) 581da177e4SLinus Torvalds { 591da177e4SLinus Torvalds int retval = 0; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds #ifdef CONFIG_USB_SERIAL_GENERIC 621da177e4SLinus Torvalds generic_device_ids[0].idVendor = vendor; 631da177e4SLinus Torvalds generic_device_ids[0].idProduct = product; 64ae64387aSAlan Cox generic_device_ids[0].match_flags = 65ae64387aSAlan Cox USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT; 661da177e4SLinus Torvalds 670b84704aSAlan Stern retval = usb_serial_register_drivers(serial_drivers, 680b84704aSAlan Stern "usbserial_generic", generic_device_ids); 691da177e4SLinus Torvalds #endif 701da177e4SLinus Torvalds return retval; 711da177e4SLinus Torvalds } 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds void usb_serial_generic_deregister(void) 741da177e4SLinus Torvalds { 751da177e4SLinus Torvalds #ifdef CONFIG_USB_SERIAL_GENERIC 7668e24113SGreg Kroah-Hartman usb_serial_deregister_drivers(serial_drivers); 771da177e4SLinus Torvalds #endif 781da177e4SLinus Torvalds } 791da177e4SLinus Torvalds 80a509a7e4SAlan Cox int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port) 811da177e4SLinus Torvalds { 821da177e4SLinus Torvalds int result = 0; 83253ca923SJoris van Rantwijk unsigned long flags; 841da177e4SLinus Torvalds 85253ca923SJoris van Rantwijk spin_lock_irqsave(&port->lock, flags); 86253ca923SJoris van Rantwijk port->throttled = 0; 87253ca923SJoris van Rantwijk port->throttle_req = 0; 88253ca923SJoris van Rantwijk spin_unlock_irqrestore(&port->lock, flags); 89253ca923SJoris van Rantwijk 9041bd72f9SJohan Hovold if (port->bulk_in_size) 91d83b4053SJohan Hovold result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds return result; 941da177e4SLinus Torvalds } 95815ddc99SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(usb_serial_generic_open); 961da177e4SLinus Torvalds 97f8f0ad86SJohan Hovold void usb_serial_generic_close(struct usb_serial_port *port) 981da177e4SLinus Torvalds { 99ec3ee508SJohan Hovold unsigned long flags; 10027c7acf2SJohan Hovold int i; 1011da177e4SLinus Torvalds 102ec3ee508SJohan Hovold if (port->bulk_out_size) { 10327c7acf2SJohan Hovold for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) 10427c7acf2SJohan Hovold usb_kill_urb(port->write_urbs[i]); 105ec3ee508SJohan Hovold 106ec3ee508SJohan Hovold spin_lock_irqsave(&port->lock, flags); 107ec3ee508SJohan Hovold kfifo_reset_out(&port->write_fifo); 108ec3ee508SJohan Hovold spin_unlock_irqrestore(&port->lock, flags); 109ec3ee508SJohan Hovold } 110d83b4053SJohan Hovold if (port->bulk_in_size) { 111d83b4053SJohan Hovold for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) 112d83b4053SJohan Hovold usb_kill_urb(port->read_urbs[i]); 113d83b4053SJohan Hovold } 1141da177e4SLinus Torvalds } 115f26788daSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_close); 1161da177e4SLinus Torvalds 117eaa3bcb0SJohan Hovold int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port, 118c23e5fc1SJohan Hovold void *dest, size_t size) 119eaa3bcb0SJohan Hovold { 120c23e5fc1SJohan Hovold return kfifo_out_locked(&port->write_fifo, dest, size, &port->lock); 121715b1dc0SJason Wessel } 122715b1dc0SJason Wessel 1238e8dce06SDavid VomLehn /** 12492ad2479SJohan Hovold * usb_serial_generic_write_start - start writing buffered data 12592ad2479SJohan Hovold * @port: usb-serial port 126818f6036SJohan Hovold * @mem_flags: flags to use for memory allocations 1278e8dce06SDavid VomLehn * 12892ad2479SJohan Hovold * Serialised using USB_SERIAL_WRITE_BUSY flag. 12992ad2479SJohan Hovold * 13092ad2479SJohan Hovold * Return: Zero on success or if busy, otherwise a negative errno value. 1318e8dce06SDavid VomLehn */ 132706cd17eSJohan Hovold int usb_serial_generic_write_start(struct usb_serial_port *port, 133818f6036SJohan Hovold gfp_t mem_flags) 1341da177e4SLinus Torvalds { 13527c7acf2SJohan Hovold struct urb *urb; 13627c7acf2SJohan Hovold int count, result; 137acd2a847SJiri Kosina unsigned long flags; 13827c7acf2SJohan Hovold int i; 139715b1dc0SJason Wessel 14027c7acf2SJohan Hovold if (test_and_set_bit_lock(USB_SERIAL_WRITE_BUSY, &port->flags)) 14127c7acf2SJohan Hovold return 0; 14227c7acf2SJohan Hovold retry: 143acd2a847SJiri Kosina spin_lock_irqsave(&port->lock, flags); 14427c7acf2SJohan Hovold if (!port->write_urbs_free || !kfifo_len(&port->write_fifo)) { 14527c7acf2SJohan Hovold clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); 146acd2a847SJiri Kosina spin_unlock_irqrestore(&port->lock, flags); 1478e8dce06SDavid VomLehn return 0; 14850a5f70cSJohan Hovold } 14927c7acf2SJohan Hovold i = (int)find_first_bit(&port->write_urbs_free, 15027c7acf2SJohan Hovold ARRAY_SIZE(port->write_urbs)); 15150a5f70cSJohan Hovold spin_unlock_irqrestore(&port->lock, flags); 1521da177e4SLinus Torvalds 15327c7acf2SJohan Hovold urb = port->write_urbs[i]; 154eaa3bcb0SJohan Hovold count = port->serial->type->prepare_write_buffer(port, 155c23e5fc1SJohan Hovold urb->transfer_buffer, 156c23e5fc1SJohan Hovold port->bulk_out_size); 15727c7acf2SJohan Hovold urb->transfer_buffer_length = count; 15859d33f2fSGreg Kroah-Hartman usb_serial_debug_data(&port->dev, __func__, count, urb->transfer_buffer); 159b58af406SJohan Hovold spin_lock_irqsave(&port->lock, flags); 160b58af406SJohan Hovold port->tx_bytes += count; 161b58af406SJohan Hovold spin_unlock_irqrestore(&port->lock, flags); 162b58af406SJohan Hovold 163b58af406SJohan Hovold clear_bit(i, &port->write_urbs_free); 164818f6036SJohan Hovold result = usb_submit_urb(urb, mem_flags); 165507ca9bcSGreg Kroah-Hartman if (result) { 166f1475a00SJohan Hovold dev_err_console(port, "%s - error submitting urb: %d\n", 167ae64387aSAlan Cox __func__, result); 168b58af406SJohan Hovold set_bit(i, &port->write_urbs_free); 169b58af406SJohan Hovold spin_lock_irqsave(&port->lock, flags); 170b58af406SJohan Hovold port->tx_bytes -= count; 171b58af406SJohan Hovold spin_unlock_irqrestore(&port->lock, flags); 172b58af406SJohan Hovold 17327c7acf2SJohan Hovold clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); 1741da177e4SLinus Torvalds return result; 1751da177e4SLinus Torvalds } 17627c7acf2SJohan Hovold 1776f648546SJohan Hovold goto retry; /* try sending off another urb */ 17830af7fb5SJohan Hovold } 179706cd17eSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_write_start); 18030af7fb5SJohan Hovold 1818e8dce06SDavid VomLehn /** 18292ad2479SJohan Hovold * usb_serial_generic_write - generic write function 18392ad2479SJohan Hovold * @tty: tty for the port 18492ad2479SJohan Hovold * @port: usb-serial port 18592ad2479SJohan Hovold * @buf: data to write 18692ad2479SJohan Hovold * @count: number of bytes to write 1878e8dce06SDavid VomLehn * 18892ad2479SJohan Hovold * Return: The number of characters buffered, which may be anything from 18992ad2479SJohan Hovold * zero to @count, or a negative errno value. 1908e8dce06SDavid VomLehn */ 1918e8dce06SDavid VomLehn int usb_serial_generic_write(struct tty_struct *tty, 1928e8dce06SDavid VomLehn struct usb_serial_port *port, const unsigned char *buf, int count) 1938e8dce06SDavid VomLehn { 1948e8dce06SDavid VomLehn int result; 1958e8dce06SDavid VomLehn 196eb8878a8SJohan Hovold if (!port->bulk_out_size) 197eb8878a8SJohan Hovold return -ENODEV; 198eb8878a8SJohan Hovold 1991a1405e2SJohan Hovold if (!count) 200507ca9bcSGreg Kroah-Hartman return 0; 2018e8dce06SDavid VomLehn 202119eecc8SStefani Seibold count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock); 203043e3f83SJohan Hovold result = usb_serial_generic_write_start(port, GFP_ATOMIC); 204c23e5fc1SJohan Hovold if (result) 2058e8dce06SDavid VomLehn return result; 206c23e5fc1SJohan Hovold 207c23e5fc1SJohan Hovold return count; 2088e8dce06SDavid VomLehn } 20998fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_generic_write); 2101da177e4SLinus Torvalds 21195da310eSAlan Cox int usb_serial_generic_write_room(struct tty_struct *tty) 2121da177e4SLinus Torvalds { 21395da310eSAlan Cox struct usb_serial_port *port = tty->driver_data; 214715b1dc0SJason Wessel unsigned long flags; 21525d514caSJohan Hovold int room; 2161da177e4SLinus Torvalds 217eb8878a8SJohan Hovold if (!port->bulk_out_size) 218eb8878a8SJohan Hovold return 0; 219eb8878a8SJohan Hovold 220715b1dc0SJason Wessel spin_lock_irqsave(&port->lock, flags); 221119eecc8SStefani Seibold room = kfifo_avail(&port->write_fifo); 222715b1dc0SJason Wessel spin_unlock_irqrestore(&port->lock, flags); 2231da177e4SLinus Torvalds 224689c2781SGreg Kroah-Hartman dev_dbg(&port->dev, "%s - returns %d\n", __func__, room); 225a5b6f60cSAlan Cox return room; 2261da177e4SLinus Torvalds } 2271da177e4SLinus Torvalds 22895da310eSAlan Cox int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) 2291da177e4SLinus Torvalds { 23095da310eSAlan Cox struct usb_serial_port *port = tty->driver_data; 231715b1dc0SJason Wessel unsigned long flags; 232eb8878a8SJohan Hovold int chars; 2331da177e4SLinus Torvalds 234eb8878a8SJohan Hovold if (!port->bulk_out_size) 235eb8878a8SJohan Hovold return 0; 236eb8878a8SJohan Hovold 237715b1dc0SJason Wessel spin_lock_irqsave(&port->lock, flags); 23825d514caSJohan Hovold chars = kfifo_len(&port->write_fifo) + port->tx_bytes; 23925719e6bSStefani Seibold spin_unlock_irqrestore(&port->lock, flags); 2401da177e4SLinus Torvalds 241689c2781SGreg Kroah-Hartman dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars); 24295da310eSAlan Cox return chars; 2431da177e4SLinus Torvalds } 244428d9988SJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer); 2451da177e4SLinus Torvalds 246dcf01050SJohan Hovold void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) 247dcf01050SJohan Hovold { 248dcf01050SJohan Hovold struct usb_serial_port *port = tty->driver_data; 249dcf01050SJohan Hovold unsigned int bps; 250dcf01050SJohan Hovold unsigned long period; 251dcf01050SJohan Hovold unsigned long expire; 252dcf01050SJohan Hovold 253dcf01050SJohan Hovold bps = tty_get_baud_rate(tty); 254dcf01050SJohan Hovold if (!bps) 255dcf01050SJohan Hovold bps = 9600; /* B0 */ 256dcf01050SJohan Hovold /* 257dcf01050SJohan Hovold * Use a poll-period of roughly the time it takes to send one 258dcf01050SJohan Hovold * character or at least one jiffy. 259dcf01050SJohan Hovold */ 260dcf01050SJohan Hovold period = max_t(unsigned long, (10 * HZ / bps), 1); 261dcf01050SJohan Hovold period = min_t(unsigned long, period, timeout); 262dcf01050SJohan Hovold 263dcf01050SJohan Hovold dev_dbg(&port->dev, "%s - timeout = %u ms, period = %u ms\n", 264dcf01050SJohan Hovold __func__, jiffies_to_msecs(timeout), 265dcf01050SJohan Hovold jiffies_to_msecs(period)); 266dcf01050SJohan Hovold expire = jiffies + timeout; 267dcf01050SJohan Hovold while (!port->serial->type->tx_empty(port)) { 268dcf01050SJohan Hovold schedule_timeout_interruptible(period); 269dcf01050SJohan Hovold if (signal_pending(current)) 270dcf01050SJohan Hovold break; 271dcf01050SJohan Hovold if (time_after(jiffies, expire)) 272dcf01050SJohan Hovold break; 273dcf01050SJohan Hovold } 274dcf01050SJohan Hovold } 275dcf01050SJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_wait_until_sent); 276dcf01050SJohan Hovold 277d83b4053SJohan Hovold static int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, 278d83b4053SJohan Hovold int index, gfp_t mem_flags) 279d83b4053SJohan Hovold { 280d83b4053SJohan Hovold int res; 281d83b4053SJohan Hovold 282d83b4053SJohan Hovold if (!test_and_clear_bit(index, &port->read_urbs_free)) 283d83b4053SJohan Hovold return 0; 284d83b4053SJohan Hovold 28549bd196dSJohan Hovold dev_dbg(&port->dev, "%s - urb %d\n", __func__, index); 286d83b4053SJohan Hovold 287d83b4053SJohan Hovold res = usb_submit_urb(port->read_urbs[index], mem_flags); 288d83b4053SJohan Hovold if (res) { 289d83b4053SJohan Hovold if (res != -EPERM) { 290d83b4053SJohan Hovold dev_err(&port->dev, 291d83b4053SJohan Hovold "%s - usb_submit_urb failed: %d\n", 292d83b4053SJohan Hovold __func__, res); 293d83b4053SJohan Hovold } 294d83b4053SJohan Hovold set_bit(index, &port->read_urbs_free); 295d83b4053SJohan Hovold return res; 296d83b4053SJohan Hovold } 297d83b4053SJohan Hovold 298d83b4053SJohan Hovold return 0; 299d83b4053SJohan Hovold } 300d83b4053SJohan Hovold 301d83b4053SJohan Hovold int usb_serial_generic_submit_read_urbs(struct usb_serial_port *port, 30298fcb5f7SJason Wessel gfp_t mem_flags) 3031da177e4SLinus Torvalds { 304d83b4053SJohan Hovold int res; 305d83b4053SJohan Hovold int i; 3061da177e4SLinus Torvalds 307d83b4053SJohan Hovold for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) { 308d83b4053SJohan Hovold res = usb_serial_generic_submit_read_urb(port, i, mem_flags); 309d83b4053SJohan Hovold if (res) 310d83b4053SJohan Hovold goto err; 3111da177e4SLinus Torvalds } 312d83b4053SJohan Hovold 313d83b4053SJohan Hovold return 0; 314d83b4053SJohan Hovold err: 315d83b4053SJohan Hovold for (; i >= 0; --i) 316d83b4053SJohan Hovold usb_kill_urb(port->read_urbs[i]); 317d83b4053SJohan Hovold 318d83b4053SJohan Hovold return res; 3190ae14743SJohan Hovold } 320d83b4053SJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urbs); 321253ca923SJoris van Rantwijk 32223154320SJohan Hovold void usb_serial_generic_process_read_urb(struct urb *urb) 3231abdeeb1SOliver Neukum { 3240f3d5baeSJohan Hovold struct usb_serial_port *port = urb->context; 32598fcb5f7SJason Wessel char *ch = (char *)urb->transfer_buffer; 32698fcb5f7SJason Wessel int i; 32798fcb5f7SJason Wessel 32856a1df46SJohan Hovold if (!urb->actual_length) 32956a1df46SJohan Hovold return; 33092ad2479SJohan Hovold /* 33192ad2479SJohan Hovold * The per character mucking around with sysrq path it too slow for 33292ad2479SJohan Hovold * stuff like 3G modems, so shortcircuit it in the 99.9999999% of 33392ad2479SJohan Hovold * cases where the USB serial is not a console anyway. 33492ad2479SJohan Hovold */ 335ca0400d2SJohan Hovold if (!port->port.console || !port->sysrq) { 33605c7cd39SJiri Slaby tty_insert_flip_string(&port->port, ch, urb->actual_length); 337ca0400d2SJohan Hovold } else { 33898fcb5f7SJason Wessel for (i = 0; i < urb->actual_length; i++, ch++) { 3396ee9f4b4SDmitry Torokhov if (!usb_serial_handle_sysrq_char(port, *ch)) 34092a19f9cSJiri Slaby tty_insert_flip_char(&port->port, *ch, TTY_NORMAL); 34198fcb5f7SJason Wessel } 3424cd1de0aSAlan Cox } 3432e124b4aSJiri Slaby tty_flip_buffer_push(&port->port); 3441abdeeb1SOliver Neukum } 34523154320SJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_process_read_urb); 3461abdeeb1SOliver Neukum 347253ca923SJoris van Rantwijk void usb_serial_generic_read_bulk_callback(struct urb *urb) 348253ca923SJoris van Rantwijk { 349cdc97792SMing Lei struct usb_serial_port *port = urb->context; 350253ca923SJoris van Rantwijk unsigned char *data = urb->transfer_buffer; 351bfaeafcfSBorislav Petkov unsigned long flags; 352d83b4053SJohan Hovold int i; 353253ca923SJoris van Rantwijk 354d83b4053SJohan Hovold for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) { 355d83b4053SJohan Hovold if (urb == port->read_urbs[i]) 356d83b4053SJohan Hovold break; 357d83b4053SJohan Hovold } 358d83b4053SJohan Hovold set_bit(i, &port->read_urbs_free); 359253ca923SJoris van Rantwijk 36049bd196dSJohan Hovold dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i, 36149bd196dSJohan Hovold urb->actual_length); 362fc11efe2SJohan Hovold switch (urb->status) { 363fc11efe2SJohan Hovold case 0: 364fc11efe2SJohan Hovold break; 365fc11efe2SJohan Hovold case -ENOENT: 366fc11efe2SJohan Hovold case -ECONNRESET: 367fc11efe2SJohan Hovold case -ESHUTDOWN: 368fc11efe2SJohan Hovold dev_dbg(&port->dev, "%s - urb stopped: %d\n", 369689c2781SGreg Kroah-Hartman __func__, urb->status); 370253ca923SJoris van Rantwijk return; 371fc11efe2SJohan Hovold case -EPIPE: 372fc11efe2SJohan Hovold dev_err(&port->dev, "%s - urb stopped: %d\n", 373fc11efe2SJohan Hovold __func__, urb->status); 374fc11efe2SJohan Hovold return; 375fc11efe2SJohan Hovold default: 376aa8e2212SJeremiah Mahler dev_dbg(&port->dev, "%s - nonzero urb status: %d\n", 377fc11efe2SJohan Hovold __func__, urb->status); 378fc11efe2SJohan Hovold goto resubmit; 379253ca923SJoris van Rantwijk } 380253ca923SJoris van Rantwijk 38159d33f2fSGreg Kroah-Hartman usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); 38223154320SJohan Hovold port->serial->type->process_read_urb(urb); 383253ca923SJoris van Rantwijk 384fc11efe2SJohan Hovold resubmit: 385253ca923SJoris van Rantwijk /* Throttle the device if requested by tty */ 386bfaeafcfSBorislav Petkov spin_lock_irqsave(&port->lock, flags); 387ae64387aSAlan Cox port->throttled = port->throttle_req; 388ae64387aSAlan Cox if (!port->throttled) { 389bfaeafcfSBorislav Petkov spin_unlock_irqrestore(&port->lock, flags); 390d83b4053SJohan Hovold usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC); 391ca0400d2SJohan Hovold } else { 392b507cc97SPete Zaitcev spin_unlock_irqrestore(&port->lock, flags); 393b507cc97SPete Zaitcev } 394ca0400d2SJohan Hovold } 395166ffccfSLuiz Fernando N. Capitulino EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); 3961da177e4SLinus Torvalds 3977d12e780SDavid Howells void usb_serial_generic_write_bulk_callback(struct urb *urb) 3981da177e4SLinus Torvalds { 399715b1dc0SJason Wessel unsigned long flags; 400cdc97792SMing Lei struct usb_serial_port *port = urb->context; 40127c7acf2SJohan Hovold int i; 4021da177e4SLinus Torvalds 403ca0400d2SJohan Hovold for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) { 40427c7acf2SJohan Hovold if (port->write_urbs[i] == urb) 40527c7acf2SJohan Hovold break; 406ca0400d2SJohan Hovold } 40730af7fb5SJohan Hovold spin_lock_irqsave(&port->lock, flags); 40825d514caSJohan Hovold port->tx_bytes -= urb->transfer_buffer_length; 40927c7acf2SJohan Hovold set_bit(i, &port->write_urbs_free); 41030af7fb5SJohan Hovold spin_unlock_irqrestore(&port->lock, flags); 4118e8dce06SDavid VomLehn 412bd58c7bdSJohan Hovold switch (urb->status) { 413bd58c7bdSJohan Hovold case 0: 414bd58c7bdSJohan Hovold break; 415bd58c7bdSJohan Hovold case -ENOENT: 416bd58c7bdSJohan Hovold case -ECONNRESET: 417bd58c7bdSJohan Hovold case -ESHUTDOWN: 418bd58c7bdSJohan Hovold dev_dbg(&port->dev, "%s - urb stopped: %d\n", 419bd58c7bdSJohan Hovold __func__, urb->status); 420bd58c7bdSJohan Hovold return; 421bd58c7bdSJohan Hovold case -EPIPE: 422bd58c7bdSJohan Hovold dev_err_console(port, "%s - urb stopped: %d\n", 423bd58c7bdSJohan Hovold __func__, urb->status); 424bd58c7bdSJohan Hovold return; 425bd58c7bdSJohan Hovold default: 426bd58c7bdSJohan Hovold dev_err_console(port, "%s - nonzero urb status: %d\n", 427bd58c7bdSJohan Hovold __func__, urb->status); 428bd58c7bdSJohan Hovold goto resubmit; 4298e8dce06SDavid VomLehn } 43063136202SJohan Hovold 431bd58c7bdSJohan Hovold resubmit: 432bd58c7bdSJohan Hovold usb_serial_generic_write_start(port, GFP_ATOMIC); 433cf2c7481SPete Zaitcev usb_serial_port_softint(port); 4341da177e4SLinus Torvalds } 435bb833986SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); 4361da177e4SLinus Torvalds 43795da310eSAlan Cox void usb_serial_generic_throttle(struct tty_struct *tty) 438253ca923SJoris van Rantwijk { 43995da310eSAlan Cox struct usb_serial_port *port = tty->driver_data; 440253ca923SJoris van Rantwijk unsigned long flags; 441253ca923SJoris van Rantwijk 442253ca923SJoris van Rantwijk spin_lock_irqsave(&port->lock, flags); 443253ca923SJoris van Rantwijk port->throttle_req = 1; 444253ca923SJoris van Rantwijk spin_unlock_irqrestore(&port->lock, flags); 445253ca923SJoris van Rantwijk } 446f1e949acSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_throttle); 447253ca923SJoris van Rantwijk 44895da310eSAlan Cox void usb_serial_generic_unthrottle(struct tty_struct *tty) 449253ca923SJoris van Rantwijk { 45095da310eSAlan Cox struct usb_serial_port *port = tty->driver_data; 451253ca923SJoris van Rantwijk int was_throttled; 452253ca923SJoris van Rantwijk 4530f3d5baeSJohan Hovold spin_lock_irq(&port->lock); 454253ca923SJoris van Rantwijk was_throttled = port->throttled; 455253ca923SJoris van Rantwijk port->throttled = port->throttle_req = 0; 4560f3d5baeSJohan Hovold spin_unlock_irq(&port->lock); 457253ca923SJoris van Rantwijk 4580f3d5baeSJohan Hovold if (was_throttled) 459d83b4053SJohan Hovold usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); 460253ca923SJoris van Rantwijk } 461f1e949acSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle); 462253ca923SJoris van Rantwijk 463980373b7SJohan Hovold static bool usb_serial_generic_msr_changed(struct tty_struct *tty, 464980373b7SJohan Hovold unsigned long arg, struct async_icount *cprev) 465980373b7SJohan Hovold { 466980373b7SJohan Hovold struct usb_serial_port *port = tty->driver_data; 467980373b7SJohan Hovold struct async_icount cnow; 468980373b7SJohan Hovold unsigned long flags; 469980373b7SJohan Hovold bool ret; 470980373b7SJohan Hovold 471980373b7SJohan Hovold /* 472980373b7SJohan Hovold * Use tty-port initialised flag to detect all hangups including the 473980373b7SJohan Hovold * one generated at USB-device disconnect. 474980373b7SJohan Hovold */ 475980373b7SJohan Hovold if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) 476980373b7SJohan Hovold return true; 477980373b7SJohan Hovold 478980373b7SJohan Hovold spin_lock_irqsave(&port->lock, flags); 479980373b7SJohan Hovold cnow = port->icount; /* atomic copy*/ 480980373b7SJohan Hovold spin_unlock_irqrestore(&port->lock, flags); 481980373b7SJohan Hovold 482980373b7SJohan Hovold ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || 483980373b7SJohan Hovold ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || 484980373b7SJohan Hovold ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || 485980373b7SJohan Hovold ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); 486980373b7SJohan Hovold 487980373b7SJohan Hovold *cprev = cnow; 488980373b7SJohan Hovold 489980373b7SJohan Hovold return ret; 490980373b7SJohan Hovold } 491980373b7SJohan Hovold 492980373b7SJohan Hovold int usb_serial_generic_tiocmiwait(struct tty_struct *tty, unsigned long arg) 493980373b7SJohan Hovold { 494980373b7SJohan Hovold struct usb_serial_port *port = tty->driver_data; 495980373b7SJohan Hovold struct async_icount cnow; 496980373b7SJohan Hovold unsigned long flags; 497980373b7SJohan Hovold int ret; 498980373b7SJohan Hovold 499980373b7SJohan Hovold spin_lock_irqsave(&port->lock, flags); 500980373b7SJohan Hovold cnow = port->icount; /* atomic copy */ 501980373b7SJohan Hovold spin_unlock_irqrestore(&port->lock, flags); 502980373b7SJohan Hovold 503980373b7SJohan Hovold ret = wait_event_interruptible(port->port.delta_msr_wait, 504980373b7SJohan Hovold usb_serial_generic_msr_changed(tty, arg, &cnow)); 50591c4211cSJohan Hovold if (!ret && !test_bit(ASYNCB_INITIALIZED, &port->port.flags)) 506980373b7SJohan Hovold ret = -EIO; 507980373b7SJohan Hovold 508980373b7SJohan Hovold return ret; 509980373b7SJohan Hovold } 510980373b7SJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_tiocmiwait); 511980373b7SJohan Hovold 512befefcdaSJohan Hovold int usb_serial_generic_get_icount(struct tty_struct *tty, 513befefcdaSJohan Hovold struct serial_icounter_struct *icount) 514befefcdaSJohan Hovold { 515befefcdaSJohan Hovold struct usb_serial_port *port = tty->driver_data; 516befefcdaSJohan Hovold struct async_icount cnow; 517befefcdaSJohan Hovold unsigned long flags; 518befefcdaSJohan Hovold 519befefcdaSJohan Hovold spin_lock_irqsave(&port->lock, flags); 520befefcdaSJohan Hovold cnow = port->icount; /* atomic copy */ 521befefcdaSJohan Hovold spin_unlock_irqrestore(&port->lock, flags); 522befefcdaSJohan Hovold 523befefcdaSJohan Hovold icount->cts = cnow.cts; 524befefcdaSJohan Hovold icount->dsr = cnow.dsr; 525befefcdaSJohan Hovold icount->rng = cnow.rng; 526befefcdaSJohan Hovold icount->dcd = cnow.dcd; 527befefcdaSJohan Hovold icount->tx = cnow.tx; 528befefcdaSJohan Hovold icount->rx = cnow.rx; 529befefcdaSJohan Hovold icount->frame = cnow.frame; 530befefcdaSJohan Hovold icount->parity = cnow.parity; 531befefcdaSJohan Hovold icount->overrun = cnow.overrun; 532befefcdaSJohan Hovold icount->brk = cnow.brk; 533befefcdaSJohan Hovold icount->buf_overrun = cnow.buf_overrun; 534befefcdaSJohan Hovold 535befefcdaSJohan Hovold return 0; 536befefcdaSJohan Hovold } 537befefcdaSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_get_icount); 538befefcdaSJohan Hovold 5397f0ae3a8SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ 5406ee9f4b4SDmitry Torokhov int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch) 54198fcb5f7SJason Wessel { 542bd5afa9eSJason Wessel if (port->sysrq && port->port.console) { 54398fcb5f7SJason Wessel if (ch && time_before(jiffies, port->sysrq)) { 544f335397dSDmitry Torokhov handle_sysrq(ch); 54598fcb5f7SJason Wessel port->sysrq = 0; 54698fcb5f7SJason Wessel return 1; 54798fcb5f7SJason Wessel } 54898fcb5f7SJason Wessel port->sysrq = 0; 54998fcb5f7SJason Wessel } 55098fcb5f7SJason Wessel return 0; 55198fcb5f7SJason Wessel } 5527f0ae3a8SRandy Dunlap #else 5536ee9f4b4SDmitry Torokhov int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch) 5547f0ae3a8SRandy Dunlap { 5557f0ae3a8SRandy Dunlap return 0; 5567f0ae3a8SRandy Dunlap } 5577f0ae3a8SRandy Dunlap #endif 55898fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char); 55998fcb5f7SJason Wessel 56098fcb5f7SJason Wessel int usb_serial_handle_break(struct usb_serial_port *port) 56198fcb5f7SJason Wessel { 56298fcb5f7SJason Wessel if (!port->sysrq) { 56398fcb5f7SJason Wessel port->sysrq = jiffies + HZ*5; 56498fcb5f7SJason Wessel return 1; 56598fcb5f7SJason Wessel } 56698fcb5f7SJason Wessel port->sysrq = 0; 56798fcb5f7SJason Wessel return 0; 56898fcb5f7SJason Wessel } 56998fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_handle_break); 57098fcb5f7SJason Wessel 571d14fc1a7SLibor Pechacek /** 572d14fc1a7SLibor Pechacek * usb_serial_handle_dcd_change - handle a change of carrier detect state 57392ad2479SJohan Hovold * @port: usb-serial port 57492ad2479SJohan Hovold * @tty: tty for the port 575d14fc1a7SLibor Pechacek * @status: new carrier detect status, nonzero if active 576d14fc1a7SLibor Pechacek */ 577d14fc1a7SLibor Pechacek void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port, 578d14fc1a7SLibor Pechacek struct tty_struct *tty, unsigned int status) 579d14fc1a7SLibor Pechacek { 580d14fc1a7SLibor Pechacek struct tty_port *port = &usb_port->port; 581d14fc1a7SLibor Pechacek 58249bd196dSJohan Hovold dev_dbg(&usb_port->dev, "%s - status %d\n", __func__, status); 583d14fc1a7SLibor Pechacek 584833efc0eSPaul Chavent if (tty) { 585833efc0eSPaul Chavent struct tty_ldisc *ld = tty_ldisc_ref(tty); 586833efc0eSPaul Chavent 587833efc0eSPaul Chavent if (ld) { 588833efc0eSPaul Chavent if (ld->ops->dcd_change) 589833efc0eSPaul Chavent ld->ops->dcd_change(tty, status); 590833efc0eSPaul Chavent tty_ldisc_deref(ld); 591833efc0eSPaul Chavent } 592833efc0eSPaul Chavent } 593833efc0eSPaul Chavent 594d14fc1a7SLibor Pechacek if (status) 595d14fc1a7SLibor Pechacek wake_up_interruptible(&port->open_wait); 596d14fc1a7SLibor Pechacek else if (tty && !C_CLOCAL(tty)) 597d14fc1a7SLibor Pechacek tty_hangup(tty); 598d14fc1a7SLibor Pechacek } 599d14fc1a7SLibor Pechacek EXPORT_SYMBOL_GPL(usb_serial_handle_dcd_change); 600d14fc1a7SLibor Pechacek 6018e8dce06SDavid VomLehn int usb_serial_generic_resume(struct usb_serial *serial) 6028e8dce06SDavid VomLehn { 6038e8dce06SDavid VomLehn struct usb_serial_port *port; 6048e8dce06SDavid VomLehn int i, c = 0, r; 6058e8dce06SDavid VomLehn 6068e8dce06SDavid VomLehn for (i = 0; i < serial->num_ports; i++) { 6078e8dce06SDavid VomLehn port = serial->port[i]; 6081f87158eSAlan Stern if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) 6098e8dce06SDavid VomLehn continue; 6108e8dce06SDavid VomLehn 611d83b4053SJohan Hovold if (port->bulk_in_size) { 612d83b4053SJohan Hovold r = usb_serial_generic_submit_read_urbs(port, 613d83b4053SJohan Hovold GFP_NOIO); 6148e8dce06SDavid VomLehn if (r < 0) 6158e8dce06SDavid VomLehn c++; 6168e8dce06SDavid VomLehn } 6178e8dce06SDavid VomLehn 61827c7acf2SJohan Hovold if (port->bulk_out_size) { 619818f6036SJohan Hovold r = usb_serial_generic_write_start(port, GFP_NOIO); 6208e8dce06SDavid VomLehn if (r < 0) 6218e8dce06SDavid VomLehn c++; 6228e8dce06SDavid VomLehn } 6238e8dce06SDavid VomLehn } 6248e8dce06SDavid VomLehn 6258e8dce06SDavid VomLehn return c ? -EIO : 0; 6268e8dce06SDavid VomLehn } 6278e8dce06SDavid VomLehn EXPORT_SYMBOL_GPL(usb_serial_generic_resume); 628