18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * USB Serial Converter Generic functions 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 - 2013 Johan Hovold (jhovold@gmail.com) 68c2ecf20Sopenharmony_ci * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/sysrq.h> 148c2ecf20Sopenharmony_ci#include <linux/tty.h> 158c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 188c2ecf20Sopenharmony_ci#include <linux/usb.h> 198c2ecf20Sopenharmony_ci#include <linux/usb/serial.h> 208c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 218c2ecf20Sopenharmony_ci#include <linux/kfifo.h> 228c2ecf20Sopenharmony_ci#include <linux/serial.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_GENERIC 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic __u16 vendor = 0x05f9; 278c2ecf20Sopenharmony_cistatic __u16 product = 0xffff; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cimodule_param(vendor, ushort, 0); 308c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vendor, "User specified USB idVendor"); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cimodule_param(product, ushort, 0); 338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(product, "User specified USB idProduct"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int usb_serial_generic_probe(struct usb_serial *serial, 388c2ecf20Sopenharmony_ci const struct usb_device_id *id) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct device *dev = &serial->interface->dev; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci dev_info(dev, "The \"generic\" usb-serial driver is only for testing and one-off prototypes.\n"); 438c2ecf20Sopenharmony_ci dev_info(dev, "Tell linux-usb@vger.kernel.org to add your device to a proper driver.\n"); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return 0; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int usb_serial_generic_calc_num_ports(struct usb_serial *serial, 498c2ecf20Sopenharmony_ci struct usb_serial_endpoints *epds) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct device *dev = &serial->interface->dev; 528c2ecf20Sopenharmony_ci int num_ports; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci num_ports = max(epds->num_bulk_in, epds->num_bulk_out); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (num_ports == 0) { 578c2ecf20Sopenharmony_ci dev_err(dev, "device has no bulk endpoints\n"); 588c2ecf20Sopenharmony_ci return -ENODEV; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return num_ports; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic struct usb_serial_driver usb_serial_generic_device = { 658c2ecf20Sopenharmony_ci .driver = { 668c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 678c2ecf20Sopenharmony_ci .name = "generic", 688c2ecf20Sopenharmony_ci }, 698c2ecf20Sopenharmony_ci .id_table = generic_device_ids, 708c2ecf20Sopenharmony_ci .probe = usb_serial_generic_probe, 718c2ecf20Sopenharmony_ci .calc_num_ports = usb_serial_generic_calc_num_ports, 728c2ecf20Sopenharmony_ci .throttle = usb_serial_generic_throttle, 738c2ecf20Sopenharmony_ci .unthrottle = usb_serial_generic_unthrottle, 748c2ecf20Sopenharmony_ci .resume = usb_serial_generic_resume, 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 788c2ecf20Sopenharmony_ci &usb_serial_generic_device, NULL 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#endif 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ciint usb_serial_generic_register(void) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci int retval = 0; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_GENERIC 888c2ecf20Sopenharmony_ci generic_device_ids[0].idVendor = vendor; 898c2ecf20Sopenharmony_ci generic_device_ids[0].idProduct = product; 908c2ecf20Sopenharmony_ci generic_device_ids[0].match_flags = 918c2ecf20Sopenharmony_ci USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci retval = usb_serial_register_drivers(serial_drivers, 948c2ecf20Sopenharmony_ci "usbserial_generic", generic_device_ids); 958c2ecf20Sopenharmony_ci#endif 968c2ecf20Sopenharmony_ci return retval; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_civoid usb_serial_generic_deregister(void) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_GENERIC 1028c2ecf20Sopenharmony_ci usb_serial_deregister_drivers(serial_drivers); 1038c2ecf20Sopenharmony_ci#endif 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciint usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci int result = 0; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci clear_bit(USB_SERIAL_THROTTLED, &port->flags); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (port->bulk_in_size) 1138c2ecf20Sopenharmony_ci result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return result; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_open); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_civoid usb_serial_generic_close(struct usb_serial_port *port) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci unsigned long flags; 1228c2ecf20Sopenharmony_ci int i; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (port->bulk_out_size) { 1258c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) 1268c2ecf20Sopenharmony_ci usb_kill_urb(port->write_urbs[i]); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 1298c2ecf20Sopenharmony_ci kfifo_reset_out(&port->write_fifo); 1308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci if (port->bulk_in_size) { 1338c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) 1348c2ecf20Sopenharmony_ci usb_kill_urb(port->read_urbs[i]); 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_close); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciint usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port, 1408c2ecf20Sopenharmony_ci void *dest, size_t size) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci return kfifo_out_locked(&port->write_fifo, dest, size, &port->lock); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/** 1468c2ecf20Sopenharmony_ci * usb_serial_generic_write_start - start writing buffered data 1478c2ecf20Sopenharmony_ci * @port: usb-serial port 1488c2ecf20Sopenharmony_ci * @mem_flags: flags to use for memory allocations 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * Serialised using USB_SERIAL_WRITE_BUSY flag. 1518c2ecf20Sopenharmony_ci * 1528c2ecf20Sopenharmony_ci * Return: Zero on success or if busy, otherwise a negative errno value. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ciint usb_serial_generic_write_start(struct usb_serial_port *port, 1558c2ecf20Sopenharmony_ci gfp_t mem_flags) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct urb *urb; 1588c2ecf20Sopenharmony_ci int count, result; 1598c2ecf20Sopenharmony_ci unsigned long flags; 1608c2ecf20Sopenharmony_ci int i; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (test_and_set_bit_lock(USB_SERIAL_WRITE_BUSY, &port->flags)) 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ciretry: 1658c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 1668c2ecf20Sopenharmony_ci if (!port->write_urbs_free || !kfifo_len(&port->write_fifo)) { 1678c2ecf20Sopenharmony_ci clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); 1688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci i = (int)find_first_bit(&port->write_urbs_free, 1728c2ecf20Sopenharmony_ci ARRAY_SIZE(port->write_urbs)); 1738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci urb = port->write_urbs[i]; 1768c2ecf20Sopenharmony_ci count = port->serial->type->prepare_write_buffer(port, 1778c2ecf20Sopenharmony_ci urb->transfer_buffer, 1788c2ecf20Sopenharmony_ci port->bulk_out_size); 1798c2ecf20Sopenharmony_ci urb->transfer_buffer_length = count; 1808c2ecf20Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, count, urb->transfer_buffer); 1818c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 1828c2ecf20Sopenharmony_ci port->tx_bytes += count; 1838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci clear_bit(i, &port->write_urbs_free); 1868c2ecf20Sopenharmony_ci result = usb_submit_urb(urb, mem_flags); 1878c2ecf20Sopenharmony_ci if (result) { 1888c2ecf20Sopenharmony_ci dev_err_console(port, "%s - error submitting urb: %d\n", 1898c2ecf20Sopenharmony_ci __func__, result); 1908c2ecf20Sopenharmony_ci set_bit(i, &port->write_urbs_free); 1918c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 1928c2ecf20Sopenharmony_ci port->tx_bytes -= count; 1938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); 1968c2ecf20Sopenharmony_ci return result; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci goto retry; /* try sending off another urb */ 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_write_start); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/** 2048c2ecf20Sopenharmony_ci * usb_serial_generic_write - generic write function 2058c2ecf20Sopenharmony_ci * @tty: tty for the port 2068c2ecf20Sopenharmony_ci * @port: usb-serial port 2078c2ecf20Sopenharmony_ci * @buf: data to write 2088c2ecf20Sopenharmony_ci * @count: number of bytes to write 2098c2ecf20Sopenharmony_ci * 2108c2ecf20Sopenharmony_ci * Return: The number of characters buffered, which may be anything from 2118c2ecf20Sopenharmony_ci * zero to @count, or a negative errno value. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ciint usb_serial_generic_write(struct tty_struct *tty, 2148c2ecf20Sopenharmony_ci struct usb_serial_port *port, const unsigned char *buf, int count) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci int result; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (!port->bulk_out_size) 2198c2ecf20Sopenharmony_ci return -ENODEV; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!count) 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock); 2258c2ecf20Sopenharmony_ci result = usb_serial_generic_write_start(port, GFP_ATOMIC); 2268c2ecf20Sopenharmony_ci if (result) 2278c2ecf20Sopenharmony_ci return result; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return count; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_write); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ciint usb_serial_generic_write_room(struct tty_struct *tty) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 2368c2ecf20Sopenharmony_ci unsigned long flags; 2378c2ecf20Sopenharmony_ci int room; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (!port->bulk_out_size) 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 2438c2ecf20Sopenharmony_ci room = kfifo_avail(&port->write_fifo); 2448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %d\n", __func__, room); 2478c2ecf20Sopenharmony_ci return room; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ciint usb_serial_generic_chars_in_buffer(struct tty_struct *tty) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 2538c2ecf20Sopenharmony_ci unsigned long flags; 2548c2ecf20Sopenharmony_ci int chars; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (!port->bulk_out_size) 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 2608c2ecf20Sopenharmony_ci chars = kfifo_len(&port->write_fifo) + port->tx_bytes; 2618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars); 2648c2ecf20Sopenharmony_ci return chars; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_civoid usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 2718c2ecf20Sopenharmony_ci unsigned int bps; 2728c2ecf20Sopenharmony_ci unsigned long period; 2738c2ecf20Sopenharmony_ci unsigned long expire; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci bps = tty_get_baud_rate(tty); 2768c2ecf20Sopenharmony_ci if (!bps) 2778c2ecf20Sopenharmony_ci bps = 9600; /* B0 */ 2788c2ecf20Sopenharmony_ci /* 2798c2ecf20Sopenharmony_ci * Use a poll-period of roughly the time it takes to send one 2808c2ecf20Sopenharmony_ci * character or at least one jiffy. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ci period = max_t(unsigned long, (10 * HZ / bps), 1); 2838c2ecf20Sopenharmony_ci if (timeout) 2848c2ecf20Sopenharmony_ci period = min_t(unsigned long, period, timeout); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - timeout = %u ms, period = %u ms\n", 2878c2ecf20Sopenharmony_ci __func__, jiffies_to_msecs(timeout), 2888c2ecf20Sopenharmony_ci jiffies_to_msecs(period)); 2898c2ecf20Sopenharmony_ci expire = jiffies + timeout; 2908c2ecf20Sopenharmony_ci while (!port->serial->type->tx_empty(port)) { 2918c2ecf20Sopenharmony_ci schedule_timeout_interruptible(period); 2928c2ecf20Sopenharmony_ci if (signal_pending(current)) 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci if (timeout && time_after(jiffies, expire)) 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_wait_until_sent); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, 3018c2ecf20Sopenharmony_ci int index, gfp_t mem_flags) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci int res; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (!test_and_clear_bit(index, &port->read_urbs_free)) 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - urb %d\n", __func__, index); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci res = usb_submit_urb(port->read_urbs[index], mem_flags); 3118c2ecf20Sopenharmony_ci if (res) { 3128c2ecf20Sopenharmony_ci if (res != -EPERM && res != -ENODEV) { 3138c2ecf20Sopenharmony_ci dev_err(&port->dev, 3148c2ecf20Sopenharmony_ci "%s - usb_submit_urb failed: %d\n", 3158c2ecf20Sopenharmony_ci __func__, res); 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci set_bit(index, &port->read_urbs_free); 3188c2ecf20Sopenharmony_ci return res; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ciint usb_serial_generic_submit_read_urbs(struct usb_serial_port *port, 3258c2ecf20Sopenharmony_ci gfp_t mem_flags) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci int res; 3288c2ecf20Sopenharmony_ci int i; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) { 3318c2ecf20Sopenharmony_ci res = usb_serial_generic_submit_read_urb(port, i, mem_flags); 3328c2ecf20Sopenharmony_ci if (res) 3338c2ecf20Sopenharmony_ci goto err; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return 0; 3378c2ecf20Sopenharmony_cierr: 3388c2ecf20Sopenharmony_ci for (; i >= 0; --i) 3398c2ecf20Sopenharmony_ci usb_kill_urb(port->read_urbs[i]); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return res; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urbs); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_civoid usb_serial_generic_process_read_urb(struct urb *urb) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct usb_serial_port *port = urb->context; 3488c2ecf20Sopenharmony_ci char *ch = urb->transfer_buffer; 3498c2ecf20Sopenharmony_ci int i; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (!urb->actual_length) 3528c2ecf20Sopenharmony_ci return; 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * The per character mucking around with sysrq path it too slow for 3558c2ecf20Sopenharmony_ci * stuff like 3G modems, so shortcircuit it in the 99.9999999% of 3568c2ecf20Sopenharmony_ci * cases where the USB serial is not a console anyway. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci if (port->sysrq) { 3598c2ecf20Sopenharmony_ci for (i = 0; i < urb->actual_length; i++, ch++) { 3608c2ecf20Sopenharmony_ci if (!usb_serial_handle_sysrq_char(port, *ch)) 3618c2ecf20Sopenharmony_ci tty_insert_flip_char(&port->port, *ch, TTY_NORMAL); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci } else { 3648c2ecf20Sopenharmony_ci tty_insert_flip_string(&port->port, ch, urb->actual_length); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci tty_flip_buffer_push(&port->port); 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_process_read_urb); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_civoid usb_serial_generic_read_bulk_callback(struct urb *urb) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct usb_serial_port *port = urb->context; 3738c2ecf20Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 3748c2ecf20Sopenharmony_ci bool stopped = false; 3758c2ecf20Sopenharmony_ci int status = urb->status; 3768c2ecf20Sopenharmony_ci int i; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) { 3798c2ecf20Sopenharmony_ci if (urb == port->read_urbs[i]) 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i, 3848c2ecf20Sopenharmony_ci urb->actual_length); 3858c2ecf20Sopenharmony_ci switch (status) { 3868c2ecf20Sopenharmony_ci case 0: 3878c2ecf20Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, urb->actual_length, 3888c2ecf20Sopenharmony_ci data); 3898c2ecf20Sopenharmony_ci port->serial->type->process_read_urb(urb); 3908c2ecf20Sopenharmony_ci break; 3918c2ecf20Sopenharmony_ci case -ENOENT: 3928c2ecf20Sopenharmony_ci case -ECONNRESET: 3938c2ecf20Sopenharmony_ci case -ESHUTDOWN: 3948c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - urb stopped: %d\n", 3958c2ecf20Sopenharmony_ci __func__, status); 3968c2ecf20Sopenharmony_ci stopped = true; 3978c2ecf20Sopenharmony_ci break; 3988c2ecf20Sopenharmony_ci case -EPIPE: 3998c2ecf20Sopenharmony_ci dev_err(&port->dev, "%s - urb stopped: %d\n", 4008c2ecf20Sopenharmony_ci __func__, status); 4018c2ecf20Sopenharmony_ci stopped = true; 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci default: 4048c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - nonzero urb status: %d\n", 4058c2ecf20Sopenharmony_ci __func__, status); 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* 4108c2ecf20Sopenharmony_ci * Make sure URB processing is done before marking as free to avoid 4118c2ecf20Sopenharmony_ci * racing with unthrottle() on another CPU. Matches the barriers 4128c2ecf20Sopenharmony_ci * implied by the test_and_clear_bit() in 4138c2ecf20Sopenharmony_ci * usb_serial_generic_submit_read_urb(). 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ci smp_mb__before_atomic(); 4168c2ecf20Sopenharmony_ci set_bit(i, &port->read_urbs_free); 4178c2ecf20Sopenharmony_ci /* 4188c2ecf20Sopenharmony_ci * Make sure URB is marked as free before checking the throttled flag 4198c2ecf20Sopenharmony_ci * to avoid racing with unthrottle() on another CPU. Matches the 4208c2ecf20Sopenharmony_ci * smp_mb__after_atomic() in unthrottle(). 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_ci smp_mb__after_atomic(); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (stopped) 4258c2ecf20Sopenharmony_ci return; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (test_bit(USB_SERIAL_THROTTLED, &port->flags)) 4288c2ecf20Sopenharmony_ci return; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_civoid usb_serial_generic_write_bulk_callback(struct urb *urb) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci unsigned long flags; 4378c2ecf20Sopenharmony_ci struct usb_serial_port *port = urb->context; 4388c2ecf20Sopenharmony_ci int status = urb->status; 4398c2ecf20Sopenharmony_ci int i; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) { 4428c2ecf20Sopenharmony_ci if (port->write_urbs[i] == urb) 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 4468c2ecf20Sopenharmony_ci port->tx_bytes -= urb->transfer_buffer_length; 4478c2ecf20Sopenharmony_ci set_bit(i, &port->write_urbs_free); 4488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci switch (status) { 4518c2ecf20Sopenharmony_ci case 0: 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci case -ENOENT: 4548c2ecf20Sopenharmony_ci case -ECONNRESET: 4558c2ecf20Sopenharmony_ci case -ESHUTDOWN: 4568c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - urb stopped: %d\n", 4578c2ecf20Sopenharmony_ci __func__, status); 4588c2ecf20Sopenharmony_ci return; 4598c2ecf20Sopenharmony_ci case -EPIPE: 4608c2ecf20Sopenharmony_ci dev_err_console(port, "%s - urb stopped: %d\n", 4618c2ecf20Sopenharmony_ci __func__, status); 4628c2ecf20Sopenharmony_ci return; 4638c2ecf20Sopenharmony_ci default: 4648c2ecf20Sopenharmony_ci dev_err_console(port, "%s - nonzero urb status: %d\n", 4658c2ecf20Sopenharmony_ci __func__, status); 4668c2ecf20Sopenharmony_ci break; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci usb_serial_generic_write_start(port, GFP_ATOMIC); 4708c2ecf20Sopenharmony_ci usb_serial_port_softint(port); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_civoid usb_serial_generic_throttle(struct tty_struct *tty) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci set_bit(USB_SERIAL_THROTTLED, &port->flags); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_throttle); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_civoid usb_serial_generic_unthrottle(struct tty_struct *tty) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci clear_bit(USB_SERIAL_THROTTLED, &port->flags); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* 4898c2ecf20Sopenharmony_ci * Matches the smp_mb__after_atomic() in 4908c2ecf20Sopenharmony_ci * usb_serial_generic_read_bulk_callback(). 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_ci smp_mb__after_atomic(); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic bool usb_serial_generic_msr_changed(struct tty_struct *tty, 4998c2ecf20Sopenharmony_ci unsigned long arg, struct async_icount *cprev) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 5028c2ecf20Sopenharmony_ci struct async_icount cnow; 5038c2ecf20Sopenharmony_ci unsigned long flags; 5048c2ecf20Sopenharmony_ci bool ret; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* 5078c2ecf20Sopenharmony_ci * Use tty-port initialised flag to detect all hangups including the 5088c2ecf20Sopenharmony_ci * one generated at USB-device disconnect. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ci if (!tty_port_initialized(&port->port)) 5118c2ecf20Sopenharmony_ci return true; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 5148c2ecf20Sopenharmony_ci cnow = port->icount; /* atomic copy*/ 5158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || 5188c2ecf20Sopenharmony_ci ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || 5198c2ecf20Sopenharmony_ci ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || 5208c2ecf20Sopenharmony_ci ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci *cprev = cnow; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return ret; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ciint usb_serial_generic_tiocmiwait(struct tty_struct *tty, unsigned long arg) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 5308c2ecf20Sopenharmony_ci struct async_icount cnow; 5318c2ecf20Sopenharmony_ci unsigned long flags; 5328c2ecf20Sopenharmony_ci int ret; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 5358c2ecf20Sopenharmony_ci cnow = port->icount; /* atomic copy */ 5368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci ret = wait_event_interruptible(port->port.delta_msr_wait, 5398c2ecf20Sopenharmony_ci usb_serial_generic_msr_changed(tty, arg, &cnow)); 5408c2ecf20Sopenharmony_ci if (!ret && !tty_port_initialized(&port->port)) 5418c2ecf20Sopenharmony_ci ret = -EIO; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci return ret; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_tiocmiwait); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ciint usb_serial_generic_get_icount(struct tty_struct *tty, 5488c2ecf20Sopenharmony_ci struct serial_icounter_struct *icount) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 5518c2ecf20Sopenharmony_ci struct async_icount cnow; 5528c2ecf20Sopenharmony_ci unsigned long flags; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 5558c2ecf20Sopenharmony_ci cnow = port->icount; /* atomic copy */ 5568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci icount->cts = cnow.cts; 5598c2ecf20Sopenharmony_ci icount->dsr = cnow.dsr; 5608c2ecf20Sopenharmony_ci icount->rng = cnow.rng; 5618c2ecf20Sopenharmony_ci icount->dcd = cnow.dcd; 5628c2ecf20Sopenharmony_ci icount->tx = cnow.tx; 5638c2ecf20Sopenharmony_ci icount->rx = cnow.rx; 5648c2ecf20Sopenharmony_ci icount->frame = cnow.frame; 5658c2ecf20Sopenharmony_ci icount->parity = cnow.parity; 5668c2ecf20Sopenharmony_ci icount->overrun = cnow.overrun; 5678c2ecf20Sopenharmony_ci icount->brk = cnow.brk; 5688c2ecf20Sopenharmony_ci icount->buf_overrun = cnow.buf_overrun; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_get_icount); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci#if defined(CONFIG_USB_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) 5758c2ecf20Sopenharmony_ciint usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci if (port->sysrq) { 5788c2ecf20Sopenharmony_ci if (ch && time_before(jiffies, port->sysrq)) { 5798c2ecf20Sopenharmony_ci handle_sysrq(ch); 5808c2ecf20Sopenharmony_ci port->sysrq = 0; 5818c2ecf20Sopenharmony_ci return 1; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci port->sysrq = 0; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci return 0; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ciint usb_serial_handle_break(struct usb_serial_port *port) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci if (!port->port.console) 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (!port->sysrq) { 5958c2ecf20Sopenharmony_ci port->sysrq = jiffies + HZ*5; 5968c2ecf20Sopenharmony_ci return 1; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci port->sysrq = 0; 5998c2ecf20Sopenharmony_ci return 0; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_handle_break); 6028c2ecf20Sopenharmony_ci#endif 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci/** 6058c2ecf20Sopenharmony_ci * usb_serial_handle_dcd_change - handle a change of carrier detect state 6068c2ecf20Sopenharmony_ci * @port: usb-serial port 6078c2ecf20Sopenharmony_ci * @tty: tty for the port 6088c2ecf20Sopenharmony_ci * @status: new carrier detect status, nonzero if active 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_civoid usb_serial_handle_dcd_change(struct usb_serial_port *port, 6118c2ecf20Sopenharmony_ci struct tty_struct *tty, unsigned int status) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - status %d\n", __func__, status); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (tty) { 6168c2ecf20Sopenharmony_ci struct tty_ldisc *ld = tty_ldisc_ref(tty); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (ld) { 6198c2ecf20Sopenharmony_ci if (ld->ops->dcd_change) 6208c2ecf20Sopenharmony_ci ld->ops->dcd_change(tty, status); 6218c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (status) 6268c2ecf20Sopenharmony_ci wake_up_interruptible(&port->port.open_wait); 6278c2ecf20Sopenharmony_ci else if (tty && !C_CLOCAL(tty)) 6288c2ecf20Sopenharmony_ci tty_hangup(tty); 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_handle_dcd_change); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ciint usb_serial_generic_resume(struct usb_serial *serial) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct usb_serial_port *port; 6358c2ecf20Sopenharmony_ci int i, c = 0, r; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci for (i = 0; i < serial->num_ports; i++) { 6388c2ecf20Sopenharmony_ci port = serial->port[i]; 6398c2ecf20Sopenharmony_ci if (!tty_port_initialized(&port->port)) 6408c2ecf20Sopenharmony_ci continue; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (port->bulk_in_size) { 6438c2ecf20Sopenharmony_ci r = usb_serial_generic_submit_read_urbs(port, 6448c2ecf20Sopenharmony_ci GFP_NOIO); 6458c2ecf20Sopenharmony_ci if (r < 0) 6468c2ecf20Sopenharmony_ci c++; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (port->bulk_out_size) { 6508c2ecf20Sopenharmony_ci r = usb_serial_generic_write_start(port, GFP_NOIO); 6518c2ecf20Sopenharmony_ci if (r < 0) 6528c2ecf20Sopenharmony_ci c++; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci return c ? -EIO : 0; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_serial_generic_resume); 659