11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * USB Serial Converter Generic functions 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 71da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License version 81da177e4SLinus Torvalds * 2 as published by the Free Software Foundation. 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds #include <linux/kernel.h> 131da177e4SLinus Torvalds #include <linux/errno.h> 141da177e4SLinus Torvalds #include <linux/slab.h> 151da177e4SLinus Torvalds #include <linux/tty.h> 161da177e4SLinus Torvalds #include <linux/tty_flip.h> 171da177e4SLinus Torvalds #include <linux/module.h> 181da177e4SLinus Torvalds #include <linux/moduleparam.h> 191da177e4SLinus Torvalds #include <linux/usb.h> 20a969888cSGreg Kroah-Hartman #include <linux/usb/serial.h> 21ae64387aSAlan Cox #include <linux/uaccess.h> 228e8dce06SDavid VomLehn #include <linux/kfifo.h> 23d9b1b787SJohannes Hölzl 241da177e4SLinus Torvalds static int debug; 251da177e4SLinus Torvalds 261da177e4SLinus Torvalds #ifdef CONFIG_USB_SERIAL_GENERIC 27b46d60fcSDavid Brownell 28b46d60fcSDavid Brownell static int generic_probe(struct usb_interface *interface, 29b46d60fcSDavid Brownell const struct usb_device_id *id); 30b46d60fcSDavid Brownell 311da177e4SLinus Torvalds static __u16 vendor = 0x05f9; 321da177e4SLinus Torvalds static __u16 product = 0xffff; 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds module_param(vendor, ushort, 0); 351da177e4SLinus Torvalds MODULE_PARM_DESC(vendor, "User specified USB idVendor"); 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds module_param(product, ushort, 0); 381da177e4SLinus Torvalds MODULE_PARM_DESC(product, "User specified USB idProduct"); 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */ 411da177e4SLinus Torvalds 42d9b1b787SJohannes Hölzl /* we want to look at all devices, as the vendor/product id can change 43d9b1b787SJohannes Hölzl * depending on the command line argument */ 447d40d7e8SNémeth Márton static const struct usb_device_id generic_serial_ids[] = { 45d9b1b787SJohannes Hölzl {.driver_info = 42}, 46d9b1b787SJohannes Hölzl {} 47d9b1b787SJohannes Hölzl }; 48d9b1b787SJohannes Hölzl 49d9b1b787SJohannes Hölzl static struct usb_driver generic_driver = { 50d9b1b787SJohannes Hölzl .name = "usbserial_generic", 51d9b1b787SJohannes Hölzl .probe = generic_probe, 52d9b1b787SJohannes Hölzl .disconnect = usb_serial_disconnect, 53d9b1b787SJohannes Hölzl .id_table = generic_serial_ids, 54d9b1b787SJohannes Hölzl .no_dynamic_id = 1, 55d9b1b787SJohannes Hölzl }; 56d9b1b787SJohannes Hölzl 571da177e4SLinus Torvalds /* All of the device info needed for the Generic Serial Converter */ 58ea65370dSGreg Kroah-Hartman struct usb_serial_driver usb_serial_generic_device = { 5918fcac35SGreg Kroah-Hartman .driver = { 601da177e4SLinus Torvalds .owner = THIS_MODULE, 61269bda1cSGreg Kroah-Hartman .name = "generic", 6218fcac35SGreg Kroah-Hartman }, 631da177e4SLinus Torvalds .id_table = generic_device_ids, 64d9b1b787SJohannes Hölzl .usb_driver = &generic_driver, 651da177e4SLinus Torvalds .num_ports = 1, 66f9c99bb8SAlan Stern .disconnect = usb_serial_generic_disconnect, 67f9c99bb8SAlan Stern .release = usb_serial_generic_release, 68253ca923SJoris van Rantwijk .throttle = usb_serial_generic_throttle, 69253ca923SJoris van Rantwijk .unthrottle = usb_serial_generic_unthrottle, 70ec22559eSOliver Neukum .resume = usb_serial_generic_resume, 711da177e4SLinus Torvalds }; 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds static int generic_probe(struct usb_interface *interface, 741da177e4SLinus Torvalds const struct usb_device_id *id) 751da177e4SLinus Torvalds { 761da177e4SLinus Torvalds const struct usb_device_id *id_pattern; 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds id_pattern = usb_match_id(interface, generic_device_ids); 791da177e4SLinus Torvalds if (id_pattern != NULL) 801da177e4SLinus Torvalds return usb_serial_probe(interface, id); 811da177e4SLinus Torvalds return -ENODEV; 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds #endif 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds int usb_serial_generic_register(int _debug) 861da177e4SLinus Torvalds { 871da177e4SLinus Torvalds int retval = 0; 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds debug = _debug; 901da177e4SLinus Torvalds #ifdef CONFIG_USB_SERIAL_GENERIC 911da177e4SLinus Torvalds generic_device_ids[0].idVendor = vendor; 921da177e4SLinus Torvalds generic_device_ids[0].idProduct = product; 93ae64387aSAlan Cox generic_device_ids[0].match_flags = 94ae64387aSAlan Cox USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT; 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds /* register our generic driver with ourselves */ 971da177e4SLinus Torvalds retval = usb_serial_register(&usb_serial_generic_device); 981da177e4SLinus Torvalds if (retval) 991da177e4SLinus Torvalds goto exit; 1001da177e4SLinus Torvalds retval = usb_register(&generic_driver); 1011da177e4SLinus Torvalds if (retval) 1021da177e4SLinus Torvalds usb_serial_deregister(&usb_serial_generic_device); 1031da177e4SLinus Torvalds exit: 1041da177e4SLinus Torvalds #endif 1051da177e4SLinus Torvalds return retval; 1061da177e4SLinus Torvalds } 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds void usb_serial_generic_deregister(void) 1091da177e4SLinus Torvalds { 1101da177e4SLinus Torvalds #ifdef CONFIG_USB_SERIAL_GENERIC 1111da177e4SLinus Torvalds /* remove our generic driver */ 1121da177e4SLinus Torvalds usb_deregister(&generic_driver); 1131da177e4SLinus Torvalds usb_serial_deregister(&usb_serial_generic_device); 1141da177e4SLinus Torvalds #endif 1151da177e4SLinus Torvalds } 1161da177e4SLinus Torvalds 117a509a7e4SAlan Cox int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port) 1181da177e4SLinus Torvalds { 1191da177e4SLinus Torvalds struct usb_serial *serial = port->serial; 1201da177e4SLinus Torvalds int result = 0; 121253ca923SJoris van Rantwijk unsigned long flags; 1221da177e4SLinus Torvalds 123441b62c1SHarvey Harrison dbg("%s - port %d", __func__, port->number); 1241da177e4SLinus Torvalds 125253ca923SJoris van Rantwijk /* clear the throttle flags */ 126253ca923SJoris van Rantwijk spin_lock_irqsave(&port->lock, flags); 127253ca923SJoris van Rantwijk port->throttled = 0; 128253ca923SJoris van Rantwijk port->throttle_req = 0; 129253ca923SJoris van Rantwijk spin_unlock_irqrestore(&port->lock, flags); 130253ca923SJoris van Rantwijk 131253ca923SJoris van Rantwijk /* if we have a bulk endpoint, start reading from it */ 1321da177e4SLinus Torvalds if (serial->num_bulk_in) { 1331da177e4SLinus Torvalds /* Start reading from the device */ 1341da177e4SLinus Torvalds usb_fill_bulk_urb(port->read_urb, serial->dev, 135ae64387aSAlan Cox usb_rcvbulkpipe(serial->dev, 136ae64387aSAlan Cox port->bulk_in_endpointAddress), 1371da177e4SLinus Torvalds port->read_urb->transfer_buffer, 1381da177e4SLinus Torvalds port->read_urb->transfer_buffer_length, 1391da177e4SLinus Torvalds ((serial->type->read_bulk_callback) ? 1401da177e4SLinus Torvalds serial->type->read_bulk_callback : 1411da177e4SLinus Torvalds usb_serial_generic_read_bulk_callback), 1421da177e4SLinus Torvalds port); 1431da177e4SLinus Torvalds result = usb_submit_urb(port->read_urb, GFP_KERNEL); 1441da177e4SLinus Torvalds if (result) 145ae64387aSAlan Cox dev_err(&port->dev, 146ae64387aSAlan Cox "%s - failed resubmitting read urb, error %d\n", 147ae64387aSAlan Cox __func__, result); 1481da177e4SLinus Torvalds } 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds return result; 1511da177e4SLinus Torvalds } 152815ddc99SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(usb_serial_generic_open); 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds static void generic_cleanup(struct usb_serial_port *port) 1551da177e4SLinus Torvalds { 1561da177e4SLinus Torvalds struct usb_serial *serial = port->serial; 1571da177e4SLinus Torvalds 158441b62c1SHarvey Harrison dbg("%s - port %d", __func__, port->number); 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds if (serial->dev) { 1611da177e4SLinus Torvalds /* shutdown any bulk reads that might be going on */ 1621da177e4SLinus Torvalds if (serial->num_bulk_out) 1631da177e4SLinus Torvalds usb_kill_urb(port->write_urb); 1641da177e4SLinus Torvalds if (serial->num_bulk_in) 1651da177e4SLinus Torvalds usb_kill_urb(port->read_urb); 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds 169335f8514SAlan Cox void usb_serial_generic_close(struct usb_serial_port *port) 1701da177e4SLinus Torvalds { 171441b62c1SHarvey Harrison dbg("%s - port %d", __func__, port->number); 1721da177e4SLinus Torvalds generic_cleanup(port); 1731da177e4SLinus Torvalds } 1741da177e4SLinus Torvalds 175715b1dc0SJason Wessel static int usb_serial_multi_urb_write(struct tty_struct *tty, 176715b1dc0SJason Wessel struct usb_serial_port *port, const unsigned char *buf, int count) 177715b1dc0SJason Wessel { 178715b1dc0SJason Wessel unsigned long flags; 179715b1dc0SJason Wessel struct urb *urb; 180715b1dc0SJason Wessel unsigned char *buffer; 181715b1dc0SJason Wessel int status; 182715b1dc0SJason Wessel int towrite; 183715b1dc0SJason Wessel int bwrite = 0; 184715b1dc0SJason Wessel 185715b1dc0SJason Wessel dbg("%s - port %d", __func__, port->number); 186715b1dc0SJason Wessel 187715b1dc0SJason Wessel if (count == 0) 188715b1dc0SJason Wessel dbg("%s - write request of 0 bytes", __func__); 189715b1dc0SJason Wessel 190715b1dc0SJason Wessel while (count > 0) { 191715b1dc0SJason Wessel towrite = (count > port->bulk_out_size) ? 192715b1dc0SJason Wessel port->bulk_out_size : count; 193715b1dc0SJason Wessel spin_lock_irqsave(&port->lock, flags); 194715b1dc0SJason Wessel if (port->urbs_in_flight > 195715b1dc0SJason Wessel port->serial->type->max_in_flight_urbs) { 196715b1dc0SJason Wessel spin_unlock_irqrestore(&port->lock, flags); 197715b1dc0SJason Wessel dbg("%s - write limit hit\n", __func__); 198715b1dc0SJason Wessel return bwrite; 199715b1dc0SJason Wessel } 200715b1dc0SJason Wessel port->tx_bytes_flight += towrite; 201715b1dc0SJason Wessel port->urbs_in_flight++; 202715b1dc0SJason Wessel spin_unlock_irqrestore(&port->lock, flags); 203715b1dc0SJason Wessel 204715b1dc0SJason Wessel buffer = kmalloc(towrite, GFP_ATOMIC); 205715b1dc0SJason Wessel if (!buffer) { 206715b1dc0SJason Wessel dev_err(&port->dev, 207715b1dc0SJason Wessel "%s ran out of kernel memory for urb ...\n", __func__); 208715b1dc0SJason Wessel goto error_no_buffer; 209715b1dc0SJason Wessel } 210715b1dc0SJason Wessel 211715b1dc0SJason Wessel urb = usb_alloc_urb(0, GFP_ATOMIC); 212715b1dc0SJason Wessel if (!urb) { 213715b1dc0SJason Wessel dev_err(&port->dev, "%s - no more free urbs\n", 214715b1dc0SJason Wessel __func__); 215715b1dc0SJason Wessel goto error_no_urb; 216715b1dc0SJason Wessel } 217715b1dc0SJason Wessel 218715b1dc0SJason Wessel /* Copy data */ 219715b1dc0SJason Wessel memcpy(buffer, buf + bwrite, towrite); 220715b1dc0SJason Wessel usb_serial_debug_data(debug, &port->dev, __func__, 221715b1dc0SJason Wessel towrite, buffer); 222715b1dc0SJason Wessel /* fill the buffer and send it */ 223715b1dc0SJason Wessel usb_fill_bulk_urb(urb, port->serial->dev, 224715b1dc0SJason Wessel usb_sndbulkpipe(port->serial->dev, 225715b1dc0SJason Wessel port->bulk_out_endpointAddress), 226715b1dc0SJason Wessel buffer, towrite, 227715b1dc0SJason Wessel usb_serial_generic_write_bulk_callback, port); 228715b1dc0SJason Wessel 229715b1dc0SJason Wessel status = usb_submit_urb(urb, GFP_ATOMIC); 230715b1dc0SJason Wessel if (status) { 231715b1dc0SJason Wessel dev_err(&port->dev, 232715b1dc0SJason Wessel "%s - failed submitting write urb, error %d\n", 233715b1dc0SJason Wessel __func__, status); 234715b1dc0SJason Wessel goto error; 235715b1dc0SJason Wessel } 236715b1dc0SJason Wessel 237715b1dc0SJason Wessel /* This urb is the responsibility of the host driver now */ 238715b1dc0SJason Wessel usb_free_urb(urb); 239715b1dc0SJason Wessel dbg("%s write: %d", __func__, towrite); 240715b1dc0SJason Wessel count -= towrite; 241715b1dc0SJason Wessel bwrite += towrite; 242715b1dc0SJason Wessel } 243715b1dc0SJason Wessel return bwrite; 244715b1dc0SJason Wessel 245715b1dc0SJason Wessel error: 246715b1dc0SJason Wessel usb_free_urb(urb); 247715b1dc0SJason Wessel error_no_urb: 248715b1dc0SJason Wessel kfree(buffer); 249715b1dc0SJason Wessel error_no_buffer: 250715b1dc0SJason Wessel spin_lock_irqsave(&port->lock, flags); 251715b1dc0SJason Wessel port->urbs_in_flight--; 252715b1dc0SJason Wessel port->tx_bytes_flight -= towrite; 253715b1dc0SJason Wessel spin_unlock_irqrestore(&port->lock, flags); 254715b1dc0SJason Wessel return bwrite; 255715b1dc0SJason Wessel } 256715b1dc0SJason Wessel 2578e8dce06SDavid VomLehn /** 2588e8dce06SDavid VomLehn * usb_serial_generic_write_start - kick off an URB write 2598e8dce06SDavid VomLehn * @port: Pointer to the &struct usb_serial_port data 2608e8dce06SDavid VomLehn * 2618e8dce06SDavid VomLehn * Returns the number of bytes queued on success. This will be zero if there 2628e8dce06SDavid VomLehn * was nothing to send. Otherwise, it returns a negative errno value 2638e8dce06SDavid VomLehn */ 2648e8dce06SDavid VomLehn static int usb_serial_generic_write_start(struct usb_serial_port *port) 2651da177e4SLinus Torvalds { 2661da177e4SLinus Torvalds struct usb_serial *serial = port->serial; 2671da177e4SLinus Torvalds unsigned char *data; 2688e8dce06SDavid VomLehn int result; 2698e8dce06SDavid VomLehn int count; 270acd2a847SJiri Kosina unsigned long flags; 2718e8dce06SDavid VomLehn bool start_io; 272715b1dc0SJason Wessel 2738e8dce06SDavid VomLehn /* Atomically determine whether we can and need to start a USB 2748e8dce06SDavid VomLehn * operation. */ 275acd2a847SJiri Kosina spin_lock_irqsave(&port->lock, flags); 2768e8dce06SDavid VomLehn if (port->write_urb_busy) 2778e8dce06SDavid VomLehn start_io = false; 2788e8dce06SDavid VomLehn else { 279119eecc8SStefani Seibold start_io = (kfifo_len(&port->write_fifo) != 0); 2808e8dce06SDavid VomLehn port->write_urb_busy = start_io; 2811da177e4SLinus Torvalds } 282acd2a847SJiri Kosina spin_unlock_irqrestore(&port->lock, flags); 2831da177e4SLinus Torvalds 2848e8dce06SDavid VomLehn if (!start_io) 2858e8dce06SDavid VomLehn return 0; 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds data = port->write_urb->transfer_buffer; 288119eecc8SStefani Seibold count = kfifo_out_locked(&port->write_fifo, data, port->bulk_out_size, &port->lock); 289441b62c1SHarvey Harrison usb_serial_debug_data(debug, &port->dev, __func__, count, data); 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds /* set up our urb */ 2921da177e4SLinus Torvalds usb_fill_bulk_urb(port->write_urb, serial->dev, 2931da177e4SLinus Torvalds usb_sndbulkpipe(serial->dev, 2941da177e4SLinus Torvalds port->bulk_out_endpointAddress), 2951da177e4SLinus Torvalds port->write_urb->transfer_buffer, count, 2961da177e4SLinus Torvalds ((serial->type->write_bulk_callback) ? 2971da177e4SLinus Torvalds serial->type->write_bulk_callback : 298ae64387aSAlan Cox usb_serial_generic_write_bulk_callback), 299ae64387aSAlan Cox port); 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds /* send the data out the bulk port */ 3021da177e4SLinus Torvalds result = usb_submit_urb(port->write_urb, GFP_ATOMIC); 303507ca9bcSGreg Kroah-Hartman if (result) { 304ae64387aSAlan Cox dev_err(&port->dev, 305ae64387aSAlan Cox "%s - failed submitting write urb, error %d\n", 306ae64387aSAlan Cox __func__, result); 307ae64387aSAlan Cox /* don't have to grab the lock here, as we will 308ae64387aSAlan Cox retry if != 0 */ 309507ca9bcSGreg Kroah-Hartman port->write_urb_busy = 0; 310507ca9bcSGreg Kroah-Hartman } else 3111da177e4SLinus Torvalds result = count; 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds return result; 3141da177e4SLinus Torvalds } 3151da177e4SLinus Torvalds 3168e8dce06SDavid VomLehn /** 3178e8dce06SDavid VomLehn * usb_serial_generic_write - generic write function for serial USB devices 3188e8dce06SDavid VomLehn * @tty: Pointer to &struct tty_struct for the device 3198e8dce06SDavid VomLehn * @port: Pointer to the &usb_serial_port structure for the device 3208e8dce06SDavid VomLehn * @buf: Pointer to the data to write 3218e8dce06SDavid VomLehn * @count: Number of bytes to write 3228e8dce06SDavid VomLehn * 3238e8dce06SDavid VomLehn * Returns the number of characters actually written, which may be anything 3248e8dce06SDavid VomLehn * from zero to @count. If an error occurs, it returns the negative errno 3258e8dce06SDavid VomLehn * value. 3268e8dce06SDavid VomLehn */ 3278e8dce06SDavid VomLehn int usb_serial_generic_write(struct tty_struct *tty, 3288e8dce06SDavid VomLehn struct usb_serial_port *port, const unsigned char *buf, int count) 3298e8dce06SDavid VomLehn { 3308e8dce06SDavid VomLehn struct usb_serial *serial = port->serial; 3318e8dce06SDavid VomLehn int result; 3328e8dce06SDavid VomLehn 3338e8dce06SDavid VomLehn dbg("%s - port %d", __func__, port->number); 3348e8dce06SDavid VomLehn 3358e8dce06SDavid VomLehn if (count == 0) { 3368e8dce06SDavid VomLehn dbg("%s - write request of 0 bytes", __func__); 337507ca9bcSGreg Kroah-Hartman return 0; 3381da177e4SLinus Torvalds } 3398e8dce06SDavid VomLehn 3408e8dce06SDavid VomLehn /* only do something if we have a bulk out endpoint */ 3418e8dce06SDavid VomLehn if (!serial->num_bulk_out) 3428e8dce06SDavid VomLehn return 0; 3438e8dce06SDavid VomLehn 3448e8dce06SDavid VomLehn if (serial->type->max_in_flight_urbs) 3458e8dce06SDavid VomLehn return usb_serial_multi_urb_write(tty, port, 3468e8dce06SDavid VomLehn buf, count); 3478e8dce06SDavid VomLehn 348119eecc8SStefani Seibold count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock); 3498e8dce06SDavid VomLehn result = usb_serial_generic_write_start(port); 3508e8dce06SDavid VomLehn 3518e8dce06SDavid VomLehn if (result >= 0) 3528e8dce06SDavid VomLehn result = count; 3538e8dce06SDavid VomLehn 3548e8dce06SDavid VomLehn return result; 3558e8dce06SDavid VomLehn } 35698fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_generic_write); 3571da177e4SLinus Torvalds 35895da310eSAlan Cox int usb_serial_generic_write_room(struct tty_struct *tty) 3591da177e4SLinus Torvalds { 36095da310eSAlan Cox struct usb_serial_port *port = tty->driver_data; 3611da177e4SLinus Torvalds struct usb_serial *serial = port->serial; 362715b1dc0SJason Wessel unsigned long flags; 3631da177e4SLinus Torvalds int room = 0; 3641da177e4SLinus Torvalds 365441b62c1SHarvey Harrison dbg("%s - port %d", __func__, port->number); 366715b1dc0SJason Wessel spin_lock_irqsave(&port->lock, flags); 367715b1dc0SJason Wessel if (serial->type->max_in_flight_urbs) { 368715b1dc0SJason Wessel if (port->urbs_in_flight < serial->type->max_in_flight_urbs) 36998fcb5f7SJason Wessel room = port->bulk_out_size * 37098fcb5f7SJason Wessel (serial->type->max_in_flight_urbs - 37198fcb5f7SJason Wessel port->urbs_in_flight); 3728e8dce06SDavid VomLehn } else if (serial->num_bulk_out) 373119eecc8SStefani Seibold room = kfifo_avail(&port->write_fifo); 374715b1dc0SJason Wessel spin_unlock_irqrestore(&port->lock, flags); 3751da177e4SLinus Torvalds 376441b62c1SHarvey Harrison dbg("%s - returns %d", __func__, room); 377a5b6f60cSAlan Cox return room; 3781da177e4SLinus Torvalds } 3791da177e4SLinus Torvalds 38095da310eSAlan Cox int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) 3811da177e4SLinus Torvalds { 38295da310eSAlan Cox struct usb_serial_port *port = tty->driver_data; 3831da177e4SLinus Torvalds struct usb_serial *serial = port->serial; 3841da177e4SLinus Torvalds int chars = 0; 385715b1dc0SJason Wessel unsigned long flags; 3861da177e4SLinus Torvalds 387441b62c1SHarvey Harrison dbg("%s - port %d", __func__, port->number); 3881da177e4SLinus Torvalds 389715b1dc0SJason Wessel spin_lock_irqsave(&port->lock, flags); 39025719e6bSStefani Seibold if (serial->type->max_in_flight_urbs) 391715b1dc0SJason Wessel chars = port->tx_bytes_flight; 39225719e6bSStefani Seibold else if (serial->num_bulk_out) 393119eecc8SStefani Seibold chars = kfifo_len(&port->write_fifo); 39425719e6bSStefani Seibold spin_unlock_irqrestore(&port->lock, flags); 3951da177e4SLinus Torvalds 396441b62c1SHarvey Harrison dbg("%s - returns %d", __func__, chars); 39795da310eSAlan Cox return chars; 3981da177e4SLinus Torvalds } 3991da177e4SLinus Torvalds 4001abdeeb1SOliver Neukum 40198fcb5f7SJason Wessel void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port, 40298fcb5f7SJason Wessel gfp_t mem_flags) 4031da177e4SLinus Torvalds { 404253ca923SJoris van Rantwijk struct urb *urb = port->read_urb; 4051abdeeb1SOliver Neukum struct usb_serial *serial = port->serial; 4061da177e4SLinus Torvalds int result; 4071da177e4SLinus Torvalds 408253ca923SJoris van Rantwijk /* Continue reading from device */ 4091abdeeb1SOliver Neukum usb_fill_bulk_urb(urb, serial->dev, 4101da177e4SLinus Torvalds usb_rcvbulkpipe(serial->dev, 4111da177e4SLinus Torvalds port->bulk_in_endpointAddress), 4121abdeeb1SOliver Neukum urb->transfer_buffer, 4131abdeeb1SOliver Neukum urb->transfer_buffer_length, 4141da177e4SLinus Torvalds ((serial->type->read_bulk_callback) ? 4151da177e4SLinus Torvalds serial->type->read_bulk_callback : 4161da177e4SLinus Torvalds usb_serial_generic_read_bulk_callback), port); 4171abdeeb1SOliver Neukum result = usb_submit_urb(urb, mem_flags); 4181da177e4SLinus Torvalds if (result) 419ae64387aSAlan Cox dev_err(&port->dev, 420ae64387aSAlan Cox "%s - failed resubmitting read urb, error %d\n", 421ae64387aSAlan Cox __func__, result); 4221da177e4SLinus Torvalds } 42398fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_generic_resubmit_read_urb); 424253ca923SJoris van Rantwijk 4251abdeeb1SOliver Neukum /* Push data to tty layer and resubmit the bulk read URB */ 4261abdeeb1SOliver Neukum static void flush_and_resubmit_read_urb(struct usb_serial_port *port) 4271abdeeb1SOliver Neukum { 4281abdeeb1SOliver Neukum struct urb *urb = port->read_urb; 4294a90f09bSAlan Cox struct tty_struct *tty = tty_port_tty_get(&port->port); 43098fcb5f7SJason Wessel char *ch = (char *)urb->transfer_buffer; 43198fcb5f7SJason Wessel int i; 43298fcb5f7SJason Wessel 43398fcb5f7SJason Wessel if (!tty) 43498fcb5f7SJason Wessel goto done; 4351abdeeb1SOliver Neukum 4364cd1de0aSAlan Cox /* The per character mucking around with sysrq path it too slow for 4374cd1de0aSAlan Cox stuff like 3G modems, so shortcircuit it in the 99.9999999% of cases 4384cd1de0aSAlan Cox where the USB serial is not a console anyway */ 4394cd1de0aSAlan Cox if (!port->console || !port->sysrq) 4404cd1de0aSAlan Cox tty_insert_flip_string(tty, ch, urb->actual_length); 4414cd1de0aSAlan Cox else { 4421abdeeb1SOliver Neukum /* Push data to tty */ 44398fcb5f7SJason Wessel for (i = 0; i < urb->actual_length; i++, ch++) { 44424a15a62SAlan Cox if (!usb_serial_handle_sysrq_char(tty, port, *ch)) 44598fcb5f7SJason Wessel tty_insert_flip_char(tty, *ch, TTY_NORMAL); 44698fcb5f7SJason Wessel } 4474cd1de0aSAlan Cox } 448b507cc97SPete Zaitcev tty_flip_buffer_push(tty); 4494a90f09bSAlan Cox tty_kref_put(tty); 45098fcb5f7SJason Wessel done: 45198fcb5f7SJason Wessel usb_serial_generic_resubmit_read_urb(port, GFP_ATOMIC); 4521abdeeb1SOliver Neukum } 4531abdeeb1SOliver Neukum 454253ca923SJoris van Rantwijk void usb_serial_generic_read_bulk_callback(struct urb *urb) 455253ca923SJoris van Rantwijk { 456cdc97792SMing Lei struct usb_serial_port *port = urb->context; 457253ca923SJoris van Rantwijk unsigned char *data = urb->transfer_buffer; 458fbd27225SGreg Kroah-Hartman int status = urb->status; 459bfaeafcfSBorislav Petkov unsigned long flags; 460253ca923SJoris van Rantwijk 461441b62c1SHarvey Harrison dbg("%s - port %d", __func__, port->number); 462253ca923SJoris van Rantwijk 463fbd27225SGreg Kroah-Hartman if (unlikely(status != 0)) { 464fbd27225SGreg Kroah-Hartman dbg("%s - nonzero read bulk status received: %d", 465441b62c1SHarvey Harrison __func__, status); 466253ca923SJoris van Rantwijk return; 467253ca923SJoris van Rantwijk } 468253ca923SJoris van Rantwijk 469ae64387aSAlan Cox usb_serial_debug_data(debug, &port->dev, __func__, 470ae64387aSAlan Cox urb->actual_length, data); 471253ca923SJoris van Rantwijk 472253ca923SJoris van Rantwijk /* Throttle the device if requested by tty */ 473bfaeafcfSBorislav Petkov spin_lock_irqsave(&port->lock, flags); 474ae64387aSAlan Cox port->throttled = port->throttle_req; 475ae64387aSAlan Cox if (!port->throttled) { 476bfaeafcfSBorislav Petkov spin_unlock_irqrestore(&port->lock, flags); 477b507cc97SPete Zaitcev flush_and_resubmit_read_urb(port); 478ae64387aSAlan Cox } else 479b507cc97SPete Zaitcev spin_unlock_irqrestore(&port->lock, flags); 480b507cc97SPete Zaitcev } 481166ffccfSLuiz Fernando N. Capitulino EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); 4821da177e4SLinus Torvalds 4837d12e780SDavid Howells void usb_serial_generic_write_bulk_callback(struct urb *urb) 4841da177e4SLinus Torvalds { 485715b1dc0SJason Wessel unsigned long flags; 486cdc97792SMing Lei struct usb_serial_port *port = urb->context; 487fbd27225SGreg Kroah-Hartman int status = urb->status; 4881da177e4SLinus Torvalds 489441b62c1SHarvey Harrison dbg("%s - port %d", __func__, port->number); 4901da177e4SLinus Torvalds 491715b1dc0SJason Wessel if (port->serial->type->max_in_flight_urbs) { 49225915302SJohan Hovold kfree(urb->transfer_buffer); 49325915302SJohan Hovold 494715b1dc0SJason Wessel spin_lock_irqsave(&port->lock, flags); 495715b1dc0SJason Wessel --port->urbs_in_flight; 496715b1dc0SJason Wessel port->tx_bytes_flight -= urb->transfer_buffer_length; 497715b1dc0SJason Wessel if (port->urbs_in_flight < 0) 498715b1dc0SJason Wessel port->urbs_in_flight = 0; 499715b1dc0SJason Wessel spin_unlock_irqrestore(&port->lock, flags); 500715b1dc0SJason Wessel 501fbd27225SGreg Kroah-Hartman if (status) { 5028e8dce06SDavid VomLehn dbg("%s - nonzero multi-urb write bulk status " 5038e8dce06SDavid VomLehn "received: %d", __func__, status); 5041da177e4SLinus Torvalds return; 5051da177e4SLinus Torvalds } 5068e8dce06SDavid VomLehn } else { 5078e8dce06SDavid VomLehn port->write_urb_busy = 0; 5088e8dce06SDavid VomLehn 5098e8dce06SDavid VomLehn if (status) { 5108e8dce06SDavid VomLehn dbg("%s - nonzero multi-urb write bulk status " 5118e8dce06SDavid VomLehn "received: %d", __func__, status); 512119eecc8SStefani Seibold kfifo_reset_out(&port->write_fifo); 5138e8dce06SDavid VomLehn } else 5148e8dce06SDavid VomLehn usb_serial_generic_write_start(port); 5158e8dce06SDavid VomLehn } 5168e8dce06SDavid VomLehn 517cf2c7481SPete Zaitcev usb_serial_port_softint(port); 5181da177e4SLinus Torvalds } 519bb833986SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); 5201da177e4SLinus Torvalds 52195da310eSAlan Cox void usb_serial_generic_throttle(struct tty_struct *tty) 522253ca923SJoris van Rantwijk { 52395da310eSAlan Cox struct usb_serial_port *port = tty->driver_data; 524253ca923SJoris van Rantwijk unsigned long flags; 525253ca923SJoris van Rantwijk 526441b62c1SHarvey Harrison dbg("%s - port %d", __func__, port->number); 527253ca923SJoris van Rantwijk 528253ca923SJoris van Rantwijk /* Set the throttle request flag. It will be picked up 529253ca923SJoris van Rantwijk * by usb_serial_generic_read_bulk_callback(). */ 530253ca923SJoris van Rantwijk spin_lock_irqsave(&port->lock, flags); 531253ca923SJoris van Rantwijk port->throttle_req = 1; 532253ca923SJoris van Rantwijk spin_unlock_irqrestore(&port->lock, flags); 533253ca923SJoris van Rantwijk } 534253ca923SJoris van Rantwijk 53595da310eSAlan Cox void usb_serial_generic_unthrottle(struct tty_struct *tty) 536253ca923SJoris van Rantwijk { 53795da310eSAlan Cox struct usb_serial_port *port = tty->driver_data; 538253ca923SJoris van Rantwijk int was_throttled; 539253ca923SJoris van Rantwijk unsigned long flags; 540253ca923SJoris van Rantwijk 541441b62c1SHarvey Harrison dbg("%s - port %d", __func__, port->number); 542253ca923SJoris van Rantwijk 543253ca923SJoris van Rantwijk /* Clear the throttle flags */ 544253ca923SJoris van Rantwijk spin_lock_irqsave(&port->lock, flags); 545253ca923SJoris van Rantwijk was_throttled = port->throttled; 546253ca923SJoris van Rantwijk port->throttled = port->throttle_req = 0; 547253ca923SJoris van Rantwijk spin_unlock_irqrestore(&port->lock, flags); 548253ca923SJoris van Rantwijk 549253ca923SJoris van Rantwijk if (was_throttled) { 5501abdeeb1SOliver Neukum /* Resume reading from device */ 55163a96095SJoris van Rantwijk flush_and_resubmit_read_urb(port); 552253ca923SJoris van Rantwijk } 553253ca923SJoris van Rantwijk } 554253ca923SJoris van Rantwijk 55524a15a62SAlan Cox int usb_serial_handle_sysrq_char(struct tty_struct *tty, 55624a15a62SAlan Cox struct usb_serial_port *port, unsigned int ch) 55798fcb5f7SJason Wessel { 558568d422eSJason Wessel if (port->sysrq && port->console) { 55998fcb5f7SJason Wessel if (ch && time_before(jiffies, port->sysrq)) { 56024a15a62SAlan Cox handle_sysrq(ch, tty); 56198fcb5f7SJason Wessel port->sysrq = 0; 56298fcb5f7SJason Wessel return 1; 56398fcb5f7SJason Wessel } 56498fcb5f7SJason Wessel port->sysrq = 0; 56598fcb5f7SJason Wessel } 56698fcb5f7SJason Wessel return 0; 56798fcb5f7SJason Wessel } 56898fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char); 56998fcb5f7SJason Wessel 57098fcb5f7SJason Wessel int usb_serial_handle_break(struct usb_serial_port *port) 57198fcb5f7SJason Wessel { 57298fcb5f7SJason Wessel if (!port->sysrq) { 57398fcb5f7SJason Wessel port->sysrq = jiffies + HZ*5; 57498fcb5f7SJason Wessel return 1; 57598fcb5f7SJason Wessel } 57698fcb5f7SJason Wessel port->sysrq = 0; 57798fcb5f7SJason Wessel return 0; 57898fcb5f7SJason Wessel } 57998fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_handle_break); 58098fcb5f7SJason Wessel 5818e8dce06SDavid VomLehn int usb_serial_generic_resume(struct usb_serial *serial) 5828e8dce06SDavid VomLehn { 5838e8dce06SDavid VomLehn struct usb_serial_port *port; 5848e8dce06SDavid VomLehn int i, c = 0, r; 5858e8dce06SDavid VomLehn 5868e8dce06SDavid VomLehn for (i = 0; i < serial->num_ports; i++) { 5878e8dce06SDavid VomLehn port = serial->port[i]; 5888e8dce06SDavid VomLehn if (!port->port.count) 5898e8dce06SDavid VomLehn continue; 5908e8dce06SDavid VomLehn 5918e8dce06SDavid VomLehn if (port->read_urb) { 5928e8dce06SDavid VomLehn r = usb_submit_urb(port->read_urb, GFP_NOIO); 5938e8dce06SDavid VomLehn if (r < 0) 5948e8dce06SDavid VomLehn c++; 5958e8dce06SDavid VomLehn } 5968e8dce06SDavid VomLehn 5978e8dce06SDavid VomLehn if (port->write_urb) { 5988e8dce06SDavid VomLehn r = usb_serial_generic_write_start(port); 5998e8dce06SDavid VomLehn if (r < 0) 6008e8dce06SDavid VomLehn c++; 6018e8dce06SDavid VomLehn } 6028e8dce06SDavid VomLehn } 6038e8dce06SDavid VomLehn 6048e8dce06SDavid VomLehn return c ? -EIO : 0; 6058e8dce06SDavid VomLehn } 6068e8dce06SDavid VomLehn EXPORT_SYMBOL_GPL(usb_serial_generic_resume); 6078e8dce06SDavid VomLehn 608f9c99bb8SAlan Stern void usb_serial_generic_disconnect(struct usb_serial *serial) 6091da177e4SLinus Torvalds { 6101da177e4SLinus Torvalds int i; 6111da177e4SLinus Torvalds 612441b62c1SHarvey Harrison dbg("%s", __func__); 6131da177e4SLinus Torvalds 6141da177e4SLinus Torvalds /* stop reads and writes on all ports */ 615ae64387aSAlan Cox for (i = 0; i < serial->num_ports; ++i) 6161da177e4SLinus Torvalds generic_cleanup(serial->port[i]); 6171da177e4SLinus Torvalds } 6181da177e4SLinus Torvalds 619f9c99bb8SAlan Stern void usb_serial_generic_release(struct usb_serial *serial) 620f9c99bb8SAlan Stern { 621f9c99bb8SAlan Stern dbg("%s", __func__); 622f9c99bb8SAlan Stern } 623