162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * USB Serial Converter Generic functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 - 2013 Johan Hovold (jhovold@gmail.com) 662306a36Sopenharmony_ci * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/sched/signal.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/sysrq.h> 1462306a36Sopenharmony_ci#include <linux/tty.h> 1562306a36Sopenharmony_ci#include <linux/tty_flip.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/moduleparam.h> 1862306a36Sopenharmony_ci#include <linux/usb.h> 1962306a36Sopenharmony_ci#include <linux/usb/serial.h> 2062306a36Sopenharmony_ci#include <linux/uaccess.h> 2162306a36Sopenharmony_ci#include <linux/kfifo.h> 2262306a36Sopenharmony_ci#include <linux/serial.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_GENERIC 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic __u16 vendor = 0x05f9; 2762306a36Sopenharmony_cistatic __u16 product = 0xffff; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cimodule_param(vendor, ushort, 0); 3062306a36Sopenharmony_ciMODULE_PARM_DESC(vendor, "User specified USB idVendor"); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cimodule_param(product, ushort, 0); 3362306a36Sopenharmony_ciMODULE_PARM_DESC(product, "User specified USB idProduct"); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int usb_serial_generic_probe(struct usb_serial *serial, 3862306a36Sopenharmony_ci const struct usb_device_id *id) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct device *dev = &serial->interface->dev; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci dev_info(dev, "The \"generic\" usb-serial driver is only for testing and one-off prototypes.\n"); 4362306a36Sopenharmony_ci dev_info(dev, "Tell linux-usb@vger.kernel.org to add your device to a proper driver.\n"); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return 0; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int usb_serial_generic_calc_num_ports(struct usb_serial *serial, 4962306a36Sopenharmony_ci struct usb_serial_endpoints *epds) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct device *dev = &serial->interface->dev; 5262306a36Sopenharmony_ci int num_ports; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci num_ports = max(epds->num_bulk_in, epds->num_bulk_out); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (num_ports == 0) { 5762306a36Sopenharmony_ci dev_err(dev, "device has no bulk endpoints\n"); 5862306a36Sopenharmony_ci return -ENODEV; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return num_ports; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct usb_serial_driver usb_serial_generic_device = { 6562306a36Sopenharmony_ci .driver = { 6662306a36Sopenharmony_ci .owner = THIS_MODULE, 6762306a36Sopenharmony_ci .name = "generic", 6862306a36Sopenharmony_ci }, 6962306a36Sopenharmony_ci .id_table = generic_device_ids, 7062306a36Sopenharmony_ci .probe = usb_serial_generic_probe, 7162306a36Sopenharmony_ci .calc_num_ports = usb_serial_generic_calc_num_ports, 7262306a36Sopenharmony_ci .throttle = usb_serial_generic_throttle, 7362306a36Sopenharmony_ci .unthrottle = usb_serial_generic_unthrottle, 7462306a36Sopenharmony_ci .resume = usb_serial_generic_resume, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 7862306a36Sopenharmony_ci &usb_serial_generic_device, NULL 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#endif 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ciint usb_serial_generic_register(void) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci int retval = 0; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_GENERIC 8862306a36Sopenharmony_ci generic_device_ids[0].idVendor = vendor; 8962306a36Sopenharmony_ci generic_device_ids[0].idProduct = product; 9062306a36Sopenharmony_ci generic_device_ids[0].match_flags = 9162306a36Sopenharmony_ci USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci retval = usb_serial_register_drivers(serial_drivers, 9462306a36Sopenharmony_ci "usbserial_generic", generic_device_ids); 9562306a36Sopenharmony_ci#endif 9662306a36Sopenharmony_ci return retval; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_civoid usb_serial_generic_deregister(void) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_GENERIC 10262306a36Sopenharmony_ci usb_serial_deregister_drivers(serial_drivers); 10362306a36Sopenharmony_ci#endif 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ciint usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci int result = 0; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci clear_bit(USB_SERIAL_THROTTLED, &port->flags); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (port->bulk_in_size) 11362306a36Sopenharmony_ci result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return result; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_open); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_civoid usb_serial_generic_close(struct usb_serial_port *port) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci unsigned long flags; 12262306a36Sopenharmony_ci int i; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (port->bulk_out_size) { 12562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) 12662306a36Sopenharmony_ci usb_kill_urb(port->write_urbs[i]); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 12962306a36Sopenharmony_ci kfifo_reset_out(&port->write_fifo); 13062306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci if (port->bulk_in_size) { 13362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) 13462306a36Sopenharmony_ci usb_kill_urb(port->read_urbs[i]); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_close); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciint usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port, 14062306a36Sopenharmony_ci void *dest, size_t size) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci return kfifo_out_locked(&port->write_fifo, dest, size, &port->lock); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/** 14662306a36Sopenharmony_ci * usb_serial_generic_write_start - start writing buffered data 14762306a36Sopenharmony_ci * @port: usb-serial port 14862306a36Sopenharmony_ci * @mem_flags: flags to use for memory allocations 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * Serialised using USB_SERIAL_WRITE_BUSY flag. 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * Return: Zero on success or if busy, otherwise a negative errno value. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ciint usb_serial_generic_write_start(struct usb_serial_port *port, 15562306a36Sopenharmony_ci gfp_t mem_flags) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct urb *urb; 15862306a36Sopenharmony_ci int count, result; 15962306a36Sopenharmony_ci unsigned long flags; 16062306a36Sopenharmony_ci int i; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (test_and_set_bit_lock(USB_SERIAL_WRITE_BUSY, &port->flags)) 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ciretry: 16562306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 16662306a36Sopenharmony_ci if (!port->write_urbs_free || !kfifo_len(&port->write_fifo)) { 16762306a36Sopenharmony_ci clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); 16862306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci i = (int)find_first_bit(&port->write_urbs_free, 17262306a36Sopenharmony_ci ARRAY_SIZE(port->write_urbs)); 17362306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci urb = port->write_urbs[i]; 17662306a36Sopenharmony_ci count = port->serial->type->prepare_write_buffer(port, 17762306a36Sopenharmony_ci urb->transfer_buffer, 17862306a36Sopenharmony_ci port->bulk_out_size); 17962306a36Sopenharmony_ci urb->transfer_buffer_length = count; 18062306a36Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, count, urb->transfer_buffer); 18162306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 18262306a36Sopenharmony_ci port->tx_bytes += count; 18362306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci clear_bit(i, &port->write_urbs_free); 18662306a36Sopenharmony_ci result = usb_submit_urb(urb, mem_flags); 18762306a36Sopenharmony_ci if (result) { 18862306a36Sopenharmony_ci dev_err_console(port, "%s - error submitting urb: %d\n", 18962306a36Sopenharmony_ci __func__, result); 19062306a36Sopenharmony_ci set_bit(i, &port->write_urbs_free); 19162306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 19262306a36Sopenharmony_ci port->tx_bytes -= count; 19362306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); 19662306a36Sopenharmony_ci return result; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci goto retry; /* try sending off another urb */ 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_write_start); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci/** 20462306a36Sopenharmony_ci * usb_serial_generic_write - generic write function 20562306a36Sopenharmony_ci * @tty: tty for the port 20662306a36Sopenharmony_ci * @port: usb-serial port 20762306a36Sopenharmony_ci * @buf: data to write 20862306a36Sopenharmony_ci * @count: number of bytes to write 20962306a36Sopenharmony_ci * 21062306a36Sopenharmony_ci * Return: The number of characters buffered, which may be anything from 21162306a36Sopenharmony_ci * zero to @count, or a negative errno value. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ciint usb_serial_generic_write(struct tty_struct *tty, 21462306a36Sopenharmony_ci struct usb_serial_port *port, const unsigned char *buf, int count) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci int result; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (!port->bulk_out_size) 21962306a36Sopenharmony_ci return -ENODEV; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (!count) 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock); 22562306a36Sopenharmony_ci result = usb_serial_generic_write_start(port, GFP_ATOMIC); 22662306a36Sopenharmony_ci if (result) 22762306a36Sopenharmony_ci return result; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return count; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_write); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ciunsigned int usb_serial_generic_write_room(struct tty_struct *tty) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 23662306a36Sopenharmony_ci unsigned long flags; 23762306a36Sopenharmony_ci unsigned int room; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!port->bulk_out_size) 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 24362306a36Sopenharmony_ci room = kfifo_avail(&port->write_fifo); 24462306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %u\n", __func__, room); 24762306a36Sopenharmony_ci return room; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ciunsigned int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 25362306a36Sopenharmony_ci unsigned long flags; 25462306a36Sopenharmony_ci unsigned int chars; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (!port->bulk_out_size) 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 26062306a36Sopenharmony_ci chars = kfifo_len(&port->write_fifo) + port->tx_bytes; 26162306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars); 26462306a36Sopenharmony_ci return chars; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_civoid usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 27162306a36Sopenharmony_ci unsigned int bps; 27262306a36Sopenharmony_ci unsigned long period; 27362306a36Sopenharmony_ci unsigned long expire; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci bps = tty_get_baud_rate(tty); 27662306a36Sopenharmony_ci if (!bps) 27762306a36Sopenharmony_ci bps = 9600; /* B0 */ 27862306a36Sopenharmony_ci /* 27962306a36Sopenharmony_ci * Use a poll-period of roughly the time it takes to send one 28062306a36Sopenharmony_ci * character or at least one jiffy. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci period = max_t(unsigned long, (10 * HZ / bps), 1); 28362306a36Sopenharmony_ci if (timeout) 28462306a36Sopenharmony_ci period = min_t(unsigned long, period, timeout); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - timeout = %u ms, period = %u ms\n", 28762306a36Sopenharmony_ci __func__, jiffies_to_msecs(timeout), 28862306a36Sopenharmony_ci jiffies_to_msecs(period)); 28962306a36Sopenharmony_ci expire = jiffies + timeout; 29062306a36Sopenharmony_ci while (!port->serial->type->tx_empty(port)) { 29162306a36Sopenharmony_ci schedule_timeout_interruptible(period); 29262306a36Sopenharmony_ci if (signal_pending(current)) 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci if (timeout && time_after(jiffies, expire)) 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_wait_until_sent); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, 30162306a36Sopenharmony_ci int index, gfp_t mem_flags) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci int res; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (!test_and_clear_bit(index, &port->read_urbs_free)) 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - urb %d\n", __func__, index); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci res = usb_submit_urb(port->read_urbs[index], mem_flags); 31162306a36Sopenharmony_ci if (res) { 31262306a36Sopenharmony_ci if (res != -EPERM && res != -ENODEV) { 31362306a36Sopenharmony_ci dev_err(&port->dev, 31462306a36Sopenharmony_ci "%s - usb_submit_urb failed: %d\n", 31562306a36Sopenharmony_ci __func__, res); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci set_bit(index, &port->read_urbs_free); 31862306a36Sopenharmony_ci return res; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ciint usb_serial_generic_submit_read_urbs(struct usb_serial_port *port, 32562306a36Sopenharmony_ci gfp_t mem_flags) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci int res; 32862306a36Sopenharmony_ci int i; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) { 33162306a36Sopenharmony_ci res = usb_serial_generic_submit_read_urb(port, i, mem_flags); 33262306a36Sopenharmony_ci if (res) 33362306a36Sopenharmony_ci goto err; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_cierr: 33862306a36Sopenharmony_ci for (; i >= 0; --i) 33962306a36Sopenharmony_ci usb_kill_urb(port->read_urbs[i]); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return res; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urbs); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_civoid usb_serial_generic_process_read_urb(struct urb *urb) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 34862306a36Sopenharmony_ci char *ch = urb->transfer_buffer; 34962306a36Sopenharmony_ci int i; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (!urb->actual_length) 35262306a36Sopenharmony_ci return; 35362306a36Sopenharmony_ci /* 35462306a36Sopenharmony_ci * The per character mucking around with sysrq path it too slow for 35562306a36Sopenharmony_ci * stuff like 3G modems, so shortcircuit it in the 99.9999999% of 35662306a36Sopenharmony_ci * cases where the USB serial is not a console anyway. 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ci if (port->sysrq) { 35962306a36Sopenharmony_ci for (i = 0; i < urb->actual_length; i++, ch++) { 36062306a36Sopenharmony_ci if (!usb_serial_handle_sysrq_char(port, *ch)) 36162306a36Sopenharmony_ci tty_insert_flip_char(&port->port, *ch, TTY_NORMAL); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci } else { 36462306a36Sopenharmony_ci tty_insert_flip_string(&port->port, ch, urb->actual_length); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci tty_flip_buffer_push(&port->port); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_process_read_urb); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_civoid usb_serial_generic_read_bulk_callback(struct urb *urb) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 37362306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 37462306a36Sopenharmony_ci bool stopped = false; 37562306a36Sopenharmony_ci int status = urb->status; 37662306a36Sopenharmony_ci int i; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) { 37962306a36Sopenharmony_ci if (urb == port->read_urbs[i]) 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i, 38462306a36Sopenharmony_ci urb->actual_length); 38562306a36Sopenharmony_ci switch (status) { 38662306a36Sopenharmony_ci case 0: 38762306a36Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, urb->actual_length, 38862306a36Sopenharmony_ci data); 38962306a36Sopenharmony_ci port->serial->type->process_read_urb(urb); 39062306a36Sopenharmony_ci break; 39162306a36Sopenharmony_ci case -ENOENT: 39262306a36Sopenharmony_ci case -ECONNRESET: 39362306a36Sopenharmony_ci case -ESHUTDOWN: 39462306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - urb stopped: %d\n", 39562306a36Sopenharmony_ci __func__, status); 39662306a36Sopenharmony_ci stopped = true; 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci case -EPIPE: 39962306a36Sopenharmony_ci dev_err(&port->dev, "%s - urb stopped: %d\n", 40062306a36Sopenharmony_ci __func__, status); 40162306a36Sopenharmony_ci stopped = true; 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci default: 40462306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - nonzero urb status: %d\n", 40562306a36Sopenharmony_ci __func__, status); 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* 41062306a36Sopenharmony_ci * Make sure URB processing is done before marking as free to avoid 41162306a36Sopenharmony_ci * racing with unthrottle() on another CPU. Matches the barriers 41262306a36Sopenharmony_ci * implied by the test_and_clear_bit() in 41362306a36Sopenharmony_ci * usb_serial_generic_submit_read_urb(). 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_ci smp_mb__before_atomic(); 41662306a36Sopenharmony_ci set_bit(i, &port->read_urbs_free); 41762306a36Sopenharmony_ci /* 41862306a36Sopenharmony_ci * Make sure URB is marked as free before checking the throttled flag 41962306a36Sopenharmony_ci * to avoid racing with unthrottle() on another CPU. Matches the 42062306a36Sopenharmony_ci * smp_mb__after_atomic() in unthrottle(). 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci smp_mb__after_atomic(); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (stopped) 42562306a36Sopenharmony_ci return; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (test_bit(USB_SERIAL_THROTTLED, &port->flags)) 42862306a36Sopenharmony_ci return; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_civoid usb_serial_generic_write_bulk_callback(struct urb *urb) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci unsigned long flags; 43762306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 43862306a36Sopenharmony_ci int status = urb->status; 43962306a36Sopenharmony_ci int i; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) { 44262306a36Sopenharmony_ci if (port->write_urbs[i] == urb) 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 44662306a36Sopenharmony_ci port->tx_bytes -= urb->transfer_buffer_length; 44762306a36Sopenharmony_ci set_bit(i, &port->write_urbs_free); 44862306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci switch (status) { 45162306a36Sopenharmony_ci case 0: 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci case -ENOENT: 45462306a36Sopenharmony_ci case -ECONNRESET: 45562306a36Sopenharmony_ci case -ESHUTDOWN: 45662306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - urb stopped: %d\n", 45762306a36Sopenharmony_ci __func__, status); 45862306a36Sopenharmony_ci return; 45962306a36Sopenharmony_ci case -EPIPE: 46062306a36Sopenharmony_ci dev_err_console(port, "%s - urb stopped: %d\n", 46162306a36Sopenharmony_ci __func__, status); 46262306a36Sopenharmony_ci return; 46362306a36Sopenharmony_ci default: 46462306a36Sopenharmony_ci dev_err_console(port, "%s - nonzero urb status: %d\n", 46562306a36Sopenharmony_ci __func__, status); 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci usb_serial_generic_write_start(port, GFP_ATOMIC); 47062306a36Sopenharmony_ci usb_serial_port_softint(port); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_civoid usb_serial_generic_throttle(struct tty_struct *tty) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci set_bit(USB_SERIAL_THROTTLED, &port->flags); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_throttle); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_civoid usb_serial_generic_unthrottle(struct tty_struct *tty) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci clear_bit(USB_SERIAL_THROTTLED, &port->flags); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* 48962306a36Sopenharmony_ci * Matches the smp_mb__after_atomic() in 49062306a36Sopenharmony_ci * usb_serial_generic_read_bulk_callback(). 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_ci smp_mb__after_atomic(); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic bool usb_serial_generic_msr_changed(struct tty_struct *tty, 49962306a36Sopenharmony_ci unsigned long arg, struct async_icount *cprev) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 50262306a36Sopenharmony_ci struct async_icount cnow; 50362306a36Sopenharmony_ci unsigned long flags; 50462306a36Sopenharmony_ci bool ret; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* 50762306a36Sopenharmony_ci * Use tty-port initialised flag to detect all hangups including the 50862306a36Sopenharmony_ci * one generated at USB-device disconnect. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci if (!tty_port_initialized(&port->port)) 51162306a36Sopenharmony_ci return true; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 51462306a36Sopenharmony_ci cnow = port->icount; /* atomic copy*/ 51562306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || 51862306a36Sopenharmony_ci ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || 51962306a36Sopenharmony_ci ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || 52062306a36Sopenharmony_ci ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci *cprev = cnow; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return ret; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ciint usb_serial_generic_tiocmiwait(struct tty_struct *tty, unsigned long arg) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 53062306a36Sopenharmony_ci struct async_icount cnow; 53162306a36Sopenharmony_ci unsigned long flags; 53262306a36Sopenharmony_ci int ret; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 53562306a36Sopenharmony_ci cnow = port->icount; /* atomic copy */ 53662306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci ret = wait_event_interruptible(port->port.delta_msr_wait, 53962306a36Sopenharmony_ci usb_serial_generic_msr_changed(tty, arg, &cnow)); 54062306a36Sopenharmony_ci if (!ret && !tty_port_initialized(&port->port)) 54162306a36Sopenharmony_ci ret = -EIO; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return ret; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_tiocmiwait); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ciint usb_serial_generic_get_icount(struct tty_struct *tty, 54862306a36Sopenharmony_ci struct serial_icounter_struct *icount) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 55162306a36Sopenharmony_ci struct async_icount cnow; 55262306a36Sopenharmony_ci unsigned long flags; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 55562306a36Sopenharmony_ci cnow = port->icount; /* atomic copy */ 55662306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci icount->cts = cnow.cts; 55962306a36Sopenharmony_ci icount->dsr = cnow.dsr; 56062306a36Sopenharmony_ci icount->rng = cnow.rng; 56162306a36Sopenharmony_ci icount->dcd = cnow.dcd; 56262306a36Sopenharmony_ci icount->tx = cnow.tx; 56362306a36Sopenharmony_ci icount->rx = cnow.rx; 56462306a36Sopenharmony_ci icount->frame = cnow.frame; 56562306a36Sopenharmony_ci icount->parity = cnow.parity; 56662306a36Sopenharmony_ci icount->overrun = cnow.overrun; 56762306a36Sopenharmony_ci icount->brk = cnow.brk; 56862306a36Sopenharmony_ci icount->buf_overrun = cnow.buf_overrun; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_get_icount); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci#if defined(CONFIG_USB_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) 57562306a36Sopenharmony_ciint usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci if (port->sysrq) { 57862306a36Sopenharmony_ci if (ch && time_before(jiffies, port->sysrq)) { 57962306a36Sopenharmony_ci handle_sysrq(ch); 58062306a36Sopenharmony_ci port->sysrq = 0; 58162306a36Sopenharmony_ci return 1; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci port->sysrq = 0; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ciint usb_serial_handle_break(struct usb_serial_port *port) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci if (!port->port.console) 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (!port->sysrq) { 59562306a36Sopenharmony_ci port->sysrq = jiffies + HZ*5; 59662306a36Sopenharmony_ci return 1; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci port->sysrq = 0; 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_handle_break); 60262306a36Sopenharmony_ci#endif 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci/** 60562306a36Sopenharmony_ci * usb_serial_handle_dcd_change - handle a change of carrier detect state 60662306a36Sopenharmony_ci * @port: usb-serial port 60762306a36Sopenharmony_ci * @tty: tty for the port 60862306a36Sopenharmony_ci * @status: new carrier detect status, nonzero if active 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_civoid usb_serial_handle_dcd_change(struct usb_serial_port *port, 61162306a36Sopenharmony_ci struct tty_struct *tty, unsigned int status) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - status %d\n", __func__, status); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (tty) { 61662306a36Sopenharmony_ci struct tty_ldisc *ld = tty_ldisc_ref(tty); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (ld) { 61962306a36Sopenharmony_ci if (ld->ops->dcd_change) 62062306a36Sopenharmony_ci ld->ops->dcd_change(tty, status); 62162306a36Sopenharmony_ci tty_ldisc_deref(ld); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (status) 62662306a36Sopenharmony_ci wake_up_interruptible(&port->port.open_wait); 62762306a36Sopenharmony_ci else if (tty && !C_CLOCAL(tty)) 62862306a36Sopenharmony_ci tty_hangup(tty); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_handle_dcd_change); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ciint usb_serial_generic_resume(struct usb_serial *serial) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct usb_serial_port *port; 63562306a36Sopenharmony_ci int i, c = 0, r; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci for (i = 0; i < serial->num_ports; i++) { 63862306a36Sopenharmony_ci port = serial->port[i]; 63962306a36Sopenharmony_ci if (!tty_port_initialized(&port->port)) 64062306a36Sopenharmony_ci continue; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (port->bulk_in_size) { 64362306a36Sopenharmony_ci r = usb_serial_generic_submit_read_urbs(port, 64462306a36Sopenharmony_ci GFP_NOIO); 64562306a36Sopenharmony_ci if (r < 0) 64662306a36Sopenharmony_ci c++; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (port->bulk_out_size) { 65062306a36Sopenharmony_ci r = usb_serial_generic_write_start(port, GFP_NOIO); 65162306a36Sopenharmony_ci if (r < 0) 65262306a36Sopenharmony_ci c++; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci return c ? -EIO : 0; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_resume); 659