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