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