xref: /openbmc/linux/drivers/usb/serial/generic.c (revision 155591d3)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * USB Serial Converter Generic functions
41da177e4SLinus Torvalds  *
5659597b7SJohan Hovold  * Copyright (C) 2010 - 2013 Johan Hovold (jhovold@gmail.com)
61da177e4SLinus Torvalds  * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
71da177e4SLinus Torvalds  */
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds #include <linux/kernel.h>
10174cd4b1SIngo Molnar #include <linux/sched/signal.h>
111da177e4SLinus Torvalds #include <linux/errno.h>
121da177e4SLinus Torvalds #include <linux/slab.h>
137f0ae3a8SRandy Dunlap #include <linux/sysrq.h>
141da177e4SLinus Torvalds #include <linux/tty.h>
151da177e4SLinus Torvalds #include <linux/tty_flip.h>
161da177e4SLinus Torvalds #include <linux/module.h>
171da177e4SLinus Torvalds #include <linux/moduleparam.h>
181da177e4SLinus Torvalds #include <linux/usb.h>
19a969888cSGreg Kroah-Hartman #include <linux/usb/serial.h>
20ae64387aSAlan Cox #include <linux/uaccess.h>
218e8dce06SDavid VomLehn #include <linux/kfifo.h>
221f87158eSAlan Stern #include <linux/serial.h>
23d9b1b787SJohannes Hölzl 
241da177e4SLinus Torvalds #ifdef CONFIG_USB_SERIAL_GENERIC
25b46d60fcSDavid Brownell 
261da177e4SLinus Torvalds static __u16 vendor  = 0x05f9;
271da177e4SLinus Torvalds static __u16 product = 0xffff;
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds module_param(vendor, ushort, 0);
301da177e4SLinus Torvalds MODULE_PARM_DESC(vendor, "User specified USB idVendor");
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds module_param(product, ushort, 0);
331da177e4SLinus Torvalds MODULE_PARM_DESC(product, "User specified USB idProduct");
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
361da177e4SLinus Torvalds 
usb_serial_generic_probe(struct usb_serial * serial,const struct usb_device_id * id)37415d7b3aSJohan Hovold static int usb_serial_generic_probe(struct usb_serial *serial,
38415d7b3aSJohan Hovold 					const struct usb_device_id *id)
39415d7b3aSJohan Hovold {
40415d7b3aSJohan Hovold 	struct device *dev = &serial->interface->dev;
41415d7b3aSJohan Hovold 
42415d7b3aSJohan Hovold 	dev_info(dev, "The \"generic\" usb-serial driver is only for testing and one-off prototypes.\n");
43415d7b3aSJohan Hovold 	dev_info(dev, "Tell linux-usb@vger.kernel.org to add your device to a proper driver.\n");
44415d7b3aSJohan Hovold 
45415d7b3aSJohan Hovold 	return 0;
46415d7b3aSJohan Hovold }
47415d7b3aSJohan Hovold 
usb_serial_generic_calc_num_ports(struct usb_serial * serial,struct usb_serial_endpoints * epds)48a794499bSJohan Hovold static int usb_serial_generic_calc_num_ports(struct usb_serial *serial,
49a794499bSJohan Hovold 					struct usb_serial_endpoints *epds)
50a794499bSJohan Hovold {
51a794499bSJohan Hovold 	struct device *dev = &serial->interface->dev;
526538808cSJohan Hovold 	int num_ports;
536538808cSJohan Hovold 
546538808cSJohan Hovold 	num_ports = max(epds->num_bulk_in, epds->num_bulk_out);
55a794499bSJohan Hovold 
56a794499bSJohan Hovold 	if (num_ports == 0) {
576538808cSJohan Hovold 		dev_err(dev, "device has no bulk endpoints\n");
58a794499bSJohan Hovold 		return -ENODEV;
59a794499bSJohan Hovold 	}
60a794499bSJohan Hovold 
61a794499bSJohan Hovold 	return num_ports;
62a794499bSJohan Hovold }
63a794499bSJohan Hovold 
64a794499bSJohan Hovold static struct usb_serial_driver usb_serial_generic_device = {
6518fcac35SGreg Kroah-Hartman 	.driver = {
661da177e4SLinus Torvalds 		.owner =	THIS_MODULE,
67269bda1cSGreg Kroah-Hartman 		.name =		"generic",
6818fcac35SGreg Kroah-Hartman 	},
691da177e4SLinus Torvalds 	.id_table =		generic_device_ids,
70415d7b3aSJohan Hovold 	.probe =		usb_serial_generic_probe,
71a794499bSJohan Hovold 	.calc_num_ports =	usb_serial_generic_calc_num_ports,
72253ca923SJoris van Rantwijk 	.throttle =		usb_serial_generic_throttle,
73253ca923SJoris van Rantwijk 	.unthrottle =		usb_serial_generic_unthrottle,
74ec22559eSOliver Neukum 	.resume =		usb_serial_generic_resume,
751da177e4SLinus Torvalds };
761da177e4SLinus Torvalds 
77765e0ba6SAlan Stern static struct usb_serial_driver * const serial_drivers[] = {
78765e0ba6SAlan Stern 	&usb_serial_generic_device, NULL
79765e0ba6SAlan Stern };
80765e0ba6SAlan Stern 
811da177e4SLinus Torvalds #endif
821da177e4SLinus Torvalds 
usb_serial_generic_register(void)833033bc8dSGreg Kroah-Hartman int usb_serial_generic_register(void)
841da177e4SLinus Torvalds {
851da177e4SLinus Torvalds 	int retval = 0;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds #ifdef CONFIG_USB_SERIAL_GENERIC
881da177e4SLinus Torvalds 	generic_device_ids[0].idVendor = vendor;
891da177e4SLinus Torvalds 	generic_device_ids[0].idProduct = product;
90ae64387aSAlan Cox 	generic_device_ids[0].match_flags =
91ae64387aSAlan Cox 		USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
921da177e4SLinus Torvalds 
930b84704aSAlan Stern 	retval = usb_serial_register_drivers(serial_drivers,
940b84704aSAlan Stern 			"usbserial_generic", generic_device_ids);
951da177e4SLinus Torvalds #endif
961da177e4SLinus Torvalds 	return retval;
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds 
usb_serial_generic_deregister(void)991da177e4SLinus Torvalds void usb_serial_generic_deregister(void)
1001da177e4SLinus Torvalds {
1011da177e4SLinus Torvalds #ifdef CONFIG_USB_SERIAL_GENERIC
10268e24113SGreg Kroah-Hartman 	usb_serial_deregister_drivers(serial_drivers);
1031da177e4SLinus Torvalds #endif
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds 
usb_serial_generic_open(struct tty_struct * tty,struct usb_serial_port * port)106a509a7e4SAlan Cox int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port)
1071da177e4SLinus Torvalds {
1081da177e4SLinus Torvalds 	int result = 0;
1091da177e4SLinus Torvalds 
110a8d78d9fSJohan Hovold 	clear_bit(USB_SERIAL_THROTTLED, &port->flags);
111253ca923SJoris van Rantwijk 
11241bd72f9SJohan Hovold 	if (port->bulk_in_size)
113d83b4053SJohan Hovold 		result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 	return result;
1161da177e4SLinus Torvalds }
117815ddc99SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(usb_serial_generic_open);
1181da177e4SLinus Torvalds 
usb_serial_generic_close(struct usb_serial_port * port)119f8f0ad86SJohan Hovold void usb_serial_generic_close(struct usb_serial_port *port)
1201da177e4SLinus Torvalds {
121ec3ee508SJohan Hovold 	unsigned long flags;
12227c7acf2SJohan Hovold 	int i;
1231da177e4SLinus Torvalds 
124ec3ee508SJohan Hovold 	if (port->bulk_out_size) {
12527c7acf2SJohan Hovold 		for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
12627c7acf2SJohan Hovold 			usb_kill_urb(port->write_urbs[i]);
127ec3ee508SJohan Hovold 
128ec3ee508SJohan Hovold 		spin_lock_irqsave(&port->lock, flags);
129ec3ee508SJohan Hovold 		kfifo_reset_out(&port->write_fifo);
130ec3ee508SJohan Hovold 		spin_unlock_irqrestore(&port->lock, flags);
131ec3ee508SJohan Hovold 	}
132d83b4053SJohan Hovold 	if (port->bulk_in_size) {
133d83b4053SJohan Hovold 		for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i)
134d83b4053SJohan Hovold 			usb_kill_urb(port->read_urbs[i]);
135d83b4053SJohan Hovold 	}
1361da177e4SLinus Torvalds }
137f26788daSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_close);
1381da177e4SLinus Torvalds 
usb_serial_generic_prepare_write_buffer(struct usb_serial_port * port,void * dest,size_t size)139eaa3bcb0SJohan Hovold int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
140c23e5fc1SJohan Hovold 						void *dest, size_t size)
141eaa3bcb0SJohan Hovold {
142c23e5fc1SJohan Hovold 	return kfifo_out_locked(&port->write_fifo, dest, size, &port->lock);
143715b1dc0SJason Wessel }
144715b1dc0SJason Wessel 
1458e8dce06SDavid VomLehn /**
14692ad2479SJohan Hovold  * usb_serial_generic_write_start - start writing buffered data
14792ad2479SJohan Hovold  * @port: usb-serial port
148818f6036SJohan Hovold  * @mem_flags: flags to use for memory allocations
1498e8dce06SDavid VomLehn  *
15092ad2479SJohan Hovold  * Serialised using USB_SERIAL_WRITE_BUSY flag.
15192ad2479SJohan Hovold  *
15292ad2479SJohan Hovold  * Return: Zero on success or if busy, otherwise a negative errno value.
1538e8dce06SDavid VomLehn  */
usb_serial_generic_write_start(struct usb_serial_port * port,gfp_t mem_flags)154706cd17eSJohan Hovold int usb_serial_generic_write_start(struct usb_serial_port *port,
155818f6036SJohan Hovold 							gfp_t mem_flags)
1561da177e4SLinus Torvalds {
15727c7acf2SJohan Hovold 	struct urb *urb;
15827c7acf2SJohan Hovold 	int count, result;
159acd2a847SJiri Kosina 	unsigned long flags;
16027c7acf2SJohan Hovold 	int i;
161715b1dc0SJason Wessel 
16227c7acf2SJohan Hovold 	if (test_and_set_bit_lock(USB_SERIAL_WRITE_BUSY, &port->flags))
16327c7acf2SJohan Hovold 		return 0;
16427c7acf2SJohan Hovold retry:
165acd2a847SJiri Kosina 	spin_lock_irqsave(&port->lock, flags);
16627c7acf2SJohan Hovold 	if (!port->write_urbs_free || !kfifo_len(&port->write_fifo)) {
16727c7acf2SJohan Hovold 		clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
168acd2a847SJiri Kosina 		spin_unlock_irqrestore(&port->lock, flags);
1698e8dce06SDavid VomLehn 		return 0;
17050a5f70cSJohan Hovold 	}
17127c7acf2SJohan Hovold 	i = (int)find_first_bit(&port->write_urbs_free,
17227c7acf2SJohan Hovold 						ARRAY_SIZE(port->write_urbs));
17350a5f70cSJohan Hovold 	spin_unlock_irqrestore(&port->lock, flags);
1741da177e4SLinus Torvalds 
17527c7acf2SJohan Hovold 	urb = port->write_urbs[i];
176eaa3bcb0SJohan Hovold 	count = port->serial->type->prepare_write_buffer(port,
177c23e5fc1SJohan Hovold 						urb->transfer_buffer,
178c23e5fc1SJohan Hovold 						port->bulk_out_size);
17927c7acf2SJohan Hovold 	urb->transfer_buffer_length = count;
18059d33f2fSGreg Kroah-Hartman 	usb_serial_debug_data(&port->dev, __func__, count, urb->transfer_buffer);
181b58af406SJohan Hovold 	spin_lock_irqsave(&port->lock, flags);
182b58af406SJohan Hovold 	port->tx_bytes += count;
183b58af406SJohan Hovold 	spin_unlock_irqrestore(&port->lock, flags);
184b58af406SJohan Hovold 
185b58af406SJohan Hovold 	clear_bit(i, &port->write_urbs_free);
186818f6036SJohan Hovold 	result = usb_submit_urb(urb, mem_flags);
187507ca9bcSGreg Kroah-Hartman 	if (result) {
188f1475a00SJohan Hovold 		dev_err_console(port, "%s - error submitting urb: %d\n",
189ae64387aSAlan Cox 						__func__, result);
190b58af406SJohan Hovold 		set_bit(i, &port->write_urbs_free);
191b58af406SJohan Hovold 		spin_lock_irqsave(&port->lock, flags);
192b58af406SJohan Hovold 		port->tx_bytes -= count;
193b58af406SJohan Hovold 		spin_unlock_irqrestore(&port->lock, flags);
194b58af406SJohan Hovold 
19527c7acf2SJohan Hovold 		clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
1961da177e4SLinus Torvalds 		return result;
1971da177e4SLinus Torvalds 	}
19827c7acf2SJohan Hovold 
1996f648546SJohan Hovold 	goto retry;	/* try sending off another urb */
20030af7fb5SJohan Hovold }
201706cd17eSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_write_start);
20230af7fb5SJohan Hovold 
2038e8dce06SDavid VomLehn /**
20492ad2479SJohan Hovold  * usb_serial_generic_write - generic write function
20592ad2479SJohan Hovold  * @tty: tty for the port
20692ad2479SJohan Hovold  * @port: usb-serial port
20792ad2479SJohan Hovold  * @buf: data to write
20892ad2479SJohan Hovold  * @count: number of bytes to write
2098e8dce06SDavid VomLehn  *
21092ad2479SJohan Hovold  * Return: The number of characters buffered, which may be anything from
21192ad2479SJohan Hovold  * zero to @count, or a negative errno value.
2128e8dce06SDavid VomLehn  */
usb_serial_generic_write(struct tty_struct * tty,struct usb_serial_port * port,const unsigned char * buf,int count)2138e8dce06SDavid VomLehn int usb_serial_generic_write(struct tty_struct *tty,
2148e8dce06SDavid VomLehn 	struct usb_serial_port *port, const unsigned char *buf, int count)
2158e8dce06SDavid VomLehn {
2168e8dce06SDavid VomLehn 	int result;
2178e8dce06SDavid VomLehn 
218eb8878a8SJohan Hovold 	if (!port->bulk_out_size)
219eb8878a8SJohan Hovold 		return -ENODEV;
220eb8878a8SJohan Hovold 
2211a1405e2SJohan Hovold 	if (!count)
222507ca9bcSGreg Kroah-Hartman 		return 0;
2238e8dce06SDavid VomLehn 
224119eecc8SStefani Seibold 	count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock);
225043e3f83SJohan Hovold 	result = usb_serial_generic_write_start(port, GFP_ATOMIC);
226c23e5fc1SJohan Hovold 	if (result)
2278e8dce06SDavid VomLehn 		return result;
228c23e5fc1SJohan Hovold 
229c23e5fc1SJohan Hovold 	return count;
2308e8dce06SDavid VomLehn }
23198fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_generic_write);
2321da177e4SLinus Torvalds 
usb_serial_generic_write_room(struct tty_struct * tty)23394cc7aeaSJiri Slaby unsigned int usb_serial_generic_write_room(struct tty_struct *tty)
2341da177e4SLinus Torvalds {
23595da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
236715b1dc0SJason Wessel 	unsigned long flags;
23794cc7aeaSJiri Slaby 	unsigned int room;
2381da177e4SLinus Torvalds 
239eb8878a8SJohan Hovold 	if (!port->bulk_out_size)
240eb8878a8SJohan Hovold 		return 0;
241eb8878a8SJohan Hovold 
242715b1dc0SJason Wessel 	spin_lock_irqsave(&port->lock, flags);
243119eecc8SStefani Seibold 	room = kfifo_avail(&port->write_fifo);
244715b1dc0SJason Wessel 	spin_unlock_irqrestore(&port->lock, flags);
2451da177e4SLinus Torvalds 
24694cc7aeaSJiri Slaby 	dev_dbg(&port->dev, "%s - returns %u\n", __func__, room);
247a5b6f60cSAlan Cox 	return room;
2481da177e4SLinus Torvalds }
2491da177e4SLinus Torvalds 
usb_serial_generic_chars_in_buffer(struct tty_struct * tty)250*155591d3SJiri Slaby unsigned int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
2511da177e4SLinus Torvalds {
25295da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
253715b1dc0SJason Wessel 	unsigned long flags;
254*155591d3SJiri Slaby 	unsigned int chars;
2551da177e4SLinus Torvalds 
256eb8878a8SJohan Hovold 	if (!port->bulk_out_size)
257eb8878a8SJohan Hovold 		return 0;
258eb8878a8SJohan Hovold 
259715b1dc0SJason Wessel 	spin_lock_irqsave(&port->lock, flags);
26025d514caSJohan Hovold 	chars = kfifo_len(&port->write_fifo) + port->tx_bytes;
26125719e6bSStefani Seibold 	spin_unlock_irqrestore(&port->lock, flags);
2621da177e4SLinus Torvalds 
263*155591d3SJiri Slaby 	dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars);
26495da310eSAlan Cox 	return chars;
2651da177e4SLinus Torvalds }
266428d9988SJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer);
2671da177e4SLinus Torvalds 
usb_serial_generic_wait_until_sent(struct tty_struct * tty,long timeout)268dcf01050SJohan Hovold void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout)
269dcf01050SJohan Hovold {
270dcf01050SJohan Hovold 	struct usb_serial_port *port = tty->driver_data;
271dcf01050SJohan Hovold 	unsigned int bps;
272dcf01050SJohan Hovold 	unsigned long period;
273dcf01050SJohan Hovold 	unsigned long expire;
274dcf01050SJohan Hovold 
275dcf01050SJohan Hovold 	bps = tty_get_baud_rate(tty);
276dcf01050SJohan Hovold 	if (!bps)
277dcf01050SJohan Hovold 		bps = 9600;	/* B0 */
278dcf01050SJohan Hovold 	/*
279dcf01050SJohan Hovold 	 * Use a poll-period of roughly the time it takes to send one
280dcf01050SJohan Hovold 	 * character or at least one jiffy.
281dcf01050SJohan Hovold 	 */
282dcf01050SJohan Hovold 	period = max_t(unsigned long, (10 * HZ / bps), 1);
283f528bf4fSJohan Hovold 	if (timeout)
284dcf01050SJohan Hovold 		period = min_t(unsigned long, period, timeout);
285dcf01050SJohan Hovold 
286dcf01050SJohan Hovold 	dev_dbg(&port->dev, "%s - timeout = %u ms, period = %u ms\n",
287dcf01050SJohan Hovold 					__func__, jiffies_to_msecs(timeout),
288dcf01050SJohan Hovold 					jiffies_to_msecs(period));
289dcf01050SJohan Hovold 	expire = jiffies + timeout;
290dcf01050SJohan Hovold 	while (!port->serial->type->tx_empty(port)) {
291dcf01050SJohan Hovold 		schedule_timeout_interruptible(period);
292dcf01050SJohan Hovold 		if (signal_pending(current))
293dcf01050SJohan Hovold 			break;
294f528bf4fSJohan Hovold 		if (timeout && time_after(jiffies, expire))
295dcf01050SJohan Hovold 			break;
296dcf01050SJohan Hovold 	}
297dcf01050SJohan Hovold }
298dcf01050SJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_wait_until_sent);
299dcf01050SJohan Hovold 
usb_serial_generic_submit_read_urb(struct usb_serial_port * port,int index,gfp_t mem_flags)300d83b4053SJohan Hovold static int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
301d83b4053SJohan Hovold 						int index, gfp_t mem_flags)
302d83b4053SJohan Hovold {
303d83b4053SJohan Hovold 	int res;
304d83b4053SJohan Hovold 
305d83b4053SJohan Hovold 	if (!test_and_clear_bit(index, &port->read_urbs_free))
306d83b4053SJohan Hovold 		return 0;
307d83b4053SJohan Hovold 
30849bd196dSJohan Hovold 	dev_dbg(&port->dev, "%s - urb %d\n", __func__, index);
309d83b4053SJohan Hovold 
310d83b4053SJohan Hovold 	res = usb_submit_urb(port->read_urbs[index], mem_flags);
311d83b4053SJohan Hovold 	if (res) {
31204f9c6e6SJeremiah Mahler 		if (res != -EPERM && res != -ENODEV) {
313d83b4053SJohan Hovold 			dev_err(&port->dev,
314d83b4053SJohan Hovold 					"%s - usb_submit_urb failed: %d\n",
315d83b4053SJohan Hovold 					__func__, res);
316d83b4053SJohan Hovold 		}
317d83b4053SJohan Hovold 		set_bit(index, &port->read_urbs_free);
318d83b4053SJohan Hovold 		return res;
319d83b4053SJohan Hovold 	}
320d83b4053SJohan Hovold 
321d83b4053SJohan Hovold 	return 0;
322d83b4053SJohan Hovold }
323d83b4053SJohan Hovold 
usb_serial_generic_submit_read_urbs(struct usb_serial_port * port,gfp_t mem_flags)324d83b4053SJohan Hovold int usb_serial_generic_submit_read_urbs(struct usb_serial_port *port,
32598fcb5f7SJason Wessel 					gfp_t mem_flags)
3261da177e4SLinus Torvalds {
327d83b4053SJohan Hovold 	int res;
328d83b4053SJohan Hovold 	int i;
3291da177e4SLinus Torvalds 
330d83b4053SJohan Hovold 	for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
331d83b4053SJohan Hovold 		res = usb_serial_generic_submit_read_urb(port, i, mem_flags);
332d83b4053SJohan Hovold 		if (res)
333d83b4053SJohan Hovold 			goto err;
3341da177e4SLinus Torvalds 	}
335d83b4053SJohan Hovold 
336d83b4053SJohan Hovold 	return 0;
337d83b4053SJohan Hovold err:
338d83b4053SJohan Hovold 	for (; i >= 0; --i)
339d83b4053SJohan Hovold 		usb_kill_urb(port->read_urbs[i]);
340d83b4053SJohan Hovold 
341d83b4053SJohan Hovold 	return res;
3420ae14743SJohan Hovold }
343d83b4053SJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urbs);
344253ca923SJoris van Rantwijk 
usb_serial_generic_process_read_urb(struct urb * urb)34523154320SJohan Hovold void usb_serial_generic_process_read_urb(struct urb *urb)
3461abdeeb1SOliver Neukum {
3470f3d5baeSJohan Hovold 	struct usb_serial_port *port = urb->context;
348eb0c68eaSJohan Hovold 	char *ch = urb->transfer_buffer;
34998fcb5f7SJason Wessel 	int i;
35098fcb5f7SJason Wessel 
35156a1df46SJohan Hovold 	if (!urb->actual_length)
35256a1df46SJohan Hovold 		return;
35392ad2479SJohan Hovold 	/*
35492ad2479SJohan Hovold 	 * The per character mucking around with sysrq path it too slow for
35592ad2479SJohan Hovold 	 * stuff like 3G modems, so shortcircuit it in the 99.9999999% of
35692ad2479SJohan Hovold 	 * cases where the USB serial is not a console anyway.
35792ad2479SJohan Hovold 	 */
35837ae2315SJohan Hovold 	if (port->sysrq) {
35998fcb5f7SJason Wessel 		for (i = 0; i < urb->actual_length; i++, ch++) {
3606ee9f4b4SDmitry Torokhov 			if (!usb_serial_handle_sysrq_char(port, *ch))
36192a19f9cSJiri Slaby 				tty_insert_flip_char(&port->port, *ch, TTY_NORMAL);
36298fcb5f7SJason Wessel 		}
36337ae2315SJohan Hovold 	} else {
36437ae2315SJohan Hovold 		tty_insert_flip_string(&port->port, ch, urb->actual_length);
3654cd1de0aSAlan Cox 	}
3662e124b4aSJiri Slaby 	tty_flip_buffer_push(&port->port);
3671abdeeb1SOliver Neukum }
36823154320SJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_process_read_urb);
3691abdeeb1SOliver Neukum 
usb_serial_generic_read_bulk_callback(struct urb * urb)370253ca923SJoris van Rantwijk void usb_serial_generic_read_bulk_callback(struct urb *urb)
371253ca923SJoris van Rantwijk {
372cdc97792SMing Lei 	struct usb_serial_port *port = urb->context;
373253ca923SJoris van Rantwijk 	unsigned char *data = urb->transfer_buffer;
3743f5edd58SJohan Hovold 	bool stopped = false;
3753161da97SOliver Neukum 	int status = urb->status;
376d83b4053SJohan Hovold 	int i;
377253ca923SJoris van Rantwijk 
378d83b4053SJohan Hovold 	for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
379d83b4053SJohan Hovold 		if (urb == port->read_urbs[i])
380d83b4053SJohan Hovold 			break;
381d83b4053SJohan Hovold 	}
382253ca923SJoris van Rantwijk 
38349bd196dSJohan Hovold 	dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i,
38449bd196dSJohan Hovold 							urb->actual_length);
3853161da97SOliver Neukum 	switch (status) {
386fc11efe2SJohan Hovold 	case 0:
3873f5edd58SJohan Hovold 		usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
3883f5edd58SJohan Hovold 							data);
3893f5edd58SJohan Hovold 		port->serial->type->process_read_urb(urb);
390fc11efe2SJohan Hovold 		break;
391fc11efe2SJohan Hovold 	case -ENOENT:
392fc11efe2SJohan Hovold 	case -ECONNRESET:
393fc11efe2SJohan Hovold 	case -ESHUTDOWN:
394fc11efe2SJohan Hovold 		dev_dbg(&port->dev, "%s - urb stopped: %d\n",
3953161da97SOliver Neukum 							__func__, status);
3963f5edd58SJohan Hovold 		stopped = true;
3973f5edd58SJohan Hovold 		break;
398fc11efe2SJohan Hovold 	case -EPIPE:
399fc11efe2SJohan Hovold 		dev_err(&port->dev, "%s - urb stopped: %d\n",
4003161da97SOliver Neukum 							__func__, status);
4013f5edd58SJohan Hovold 		stopped = true;
4023f5edd58SJohan Hovold 		break;
403fc11efe2SJohan Hovold 	default:
404aa8e2212SJeremiah Mahler 		dev_dbg(&port->dev, "%s - nonzero urb status: %d\n",
4053161da97SOliver Neukum 							__func__, status);
4063f5edd58SJohan Hovold 		break;
407253ca923SJoris van Rantwijk 	}
408253ca923SJoris van Rantwijk 
4093f5edd58SJohan Hovold 	/*
4103f5edd58SJohan Hovold 	 * Make sure URB processing is done before marking as free to avoid
4113f5edd58SJohan Hovold 	 * racing with unthrottle() on another CPU. Matches the barriers
4123f5edd58SJohan Hovold 	 * implied by the test_and_clear_bit() in
4133f5edd58SJohan Hovold 	 * usb_serial_generic_submit_read_urb().
4143f5edd58SJohan Hovold 	 */
4153f5edd58SJohan Hovold 	smp_mb__before_atomic();
4163f5edd58SJohan Hovold 	set_bit(i, &port->read_urbs_free);
4173f5edd58SJohan Hovold 	/*
4183f5edd58SJohan Hovold 	 * Make sure URB is marked as free before checking the throttled flag
4193f5edd58SJohan Hovold 	 * to avoid racing with unthrottle() on another CPU. Matches the
42032553441SJohan Hovold 	 * smp_mb__after_atomic() in unthrottle().
4213f5edd58SJohan Hovold 	 */
4223f5edd58SJohan Hovold 	smp_mb__after_atomic();
423253ca923SJoris van Rantwijk 
4243f5edd58SJohan Hovold 	if (stopped)
4253f5edd58SJohan Hovold 		return;
4263f5edd58SJohan Hovold 
427a8d78d9fSJohan Hovold 	if (test_bit(USB_SERIAL_THROTTLED, &port->flags))
428a8d78d9fSJohan Hovold 		return;
429a8d78d9fSJohan Hovold 
430d83b4053SJohan Hovold 	usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC);
431ca0400d2SJohan Hovold }
432166ffccfSLuiz Fernando N. Capitulino EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);
4331da177e4SLinus Torvalds 
usb_serial_generic_write_bulk_callback(struct urb * urb)4347d12e780SDavid Howells void usb_serial_generic_write_bulk_callback(struct urb *urb)
4351da177e4SLinus Torvalds {
436715b1dc0SJason Wessel 	unsigned long flags;
437cdc97792SMing Lei 	struct usb_serial_port *port = urb->context;
4383161da97SOliver Neukum 	int status = urb->status;
43927c7acf2SJohan Hovold 	int i;
4401da177e4SLinus Torvalds 
441ca0400d2SJohan Hovold 	for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) {
44227c7acf2SJohan Hovold 		if (port->write_urbs[i] == urb)
44327c7acf2SJohan Hovold 			break;
444ca0400d2SJohan Hovold 	}
44530af7fb5SJohan Hovold 	spin_lock_irqsave(&port->lock, flags);
44625d514caSJohan Hovold 	port->tx_bytes -= urb->transfer_buffer_length;
44727c7acf2SJohan Hovold 	set_bit(i, &port->write_urbs_free);
44830af7fb5SJohan Hovold 	spin_unlock_irqrestore(&port->lock, flags);
4498e8dce06SDavid VomLehn 
4503161da97SOliver Neukum 	switch (status) {
451bd58c7bdSJohan Hovold 	case 0:
452bd58c7bdSJohan Hovold 		break;
453bd58c7bdSJohan Hovold 	case -ENOENT:
454bd58c7bdSJohan Hovold 	case -ECONNRESET:
455bd58c7bdSJohan Hovold 	case -ESHUTDOWN:
456bd58c7bdSJohan Hovold 		dev_dbg(&port->dev, "%s - urb stopped: %d\n",
4573161da97SOliver Neukum 							__func__, status);
458bd58c7bdSJohan Hovold 		return;
459bd58c7bdSJohan Hovold 	case -EPIPE:
460bd58c7bdSJohan Hovold 		dev_err_console(port, "%s - urb stopped: %d\n",
4613161da97SOliver Neukum 							__func__, status);
462bd58c7bdSJohan Hovold 		return;
463bd58c7bdSJohan Hovold 	default:
464bd58c7bdSJohan Hovold 		dev_err_console(port, "%s - nonzero urb status: %d\n",
4653161da97SOliver Neukum 							__func__, status);
4665b67b10aSJohan Hovold 		break;
4678e8dce06SDavid VomLehn 	}
46863136202SJohan Hovold 
469bd58c7bdSJohan Hovold 	usb_serial_generic_write_start(port, GFP_ATOMIC);
470cf2c7481SPete Zaitcev 	usb_serial_port_softint(port);
4711da177e4SLinus Torvalds }
472bb833986SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback);
4731da177e4SLinus Torvalds 
usb_serial_generic_throttle(struct tty_struct * tty)47495da310eSAlan Cox void usb_serial_generic_throttle(struct tty_struct *tty)
475253ca923SJoris van Rantwijk {
47695da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
477253ca923SJoris van Rantwijk 
478a8d78d9fSJohan Hovold 	set_bit(USB_SERIAL_THROTTLED, &port->flags);
479253ca923SJoris van Rantwijk }
480f1e949acSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_throttle);
481253ca923SJoris van Rantwijk 
usb_serial_generic_unthrottle(struct tty_struct * tty)48295da310eSAlan Cox void usb_serial_generic_unthrottle(struct tty_struct *tty)
483253ca923SJoris van Rantwijk {
48495da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
485253ca923SJoris van Rantwijk 
486a8d78d9fSJohan Hovold 	clear_bit(USB_SERIAL_THROTTLED, &port->flags);
487253ca923SJoris van Rantwijk 
4883f5edd58SJohan Hovold 	/*
4893f5edd58SJohan Hovold 	 * Matches the smp_mb__after_atomic() in
4903f5edd58SJohan Hovold 	 * usb_serial_generic_read_bulk_callback().
4913f5edd58SJohan Hovold 	 */
49232553441SJohan Hovold 	smp_mb__after_atomic();
4933f5edd58SJohan Hovold 
494d83b4053SJohan Hovold 	usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
495253ca923SJoris van Rantwijk }
496f1e949acSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle);
497253ca923SJoris van Rantwijk 
usb_serial_generic_msr_changed(struct tty_struct * tty,unsigned long arg,struct async_icount * cprev)498980373b7SJohan Hovold static bool usb_serial_generic_msr_changed(struct tty_struct *tty,
499980373b7SJohan Hovold 				unsigned long arg, struct async_icount *cprev)
500980373b7SJohan Hovold {
501980373b7SJohan Hovold 	struct usb_serial_port *port = tty->driver_data;
502980373b7SJohan Hovold 	struct async_icount cnow;
503980373b7SJohan Hovold 	unsigned long flags;
504980373b7SJohan Hovold 	bool ret;
505980373b7SJohan Hovold 
506980373b7SJohan Hovold 	/*
507980373b7SJohan Hovold 	 * Use tty-port initialised flag to detect all hangups including the
508980373b7SJohan Hovold 	 * one generated at USB-device disconnect.
509980373b7SJohan Hovold 	 */
510d41861caSPeter Hurley 	if (!tty_port_initialized(&port->port))
511980373b7SJohan Hovold 		return true;
512980373b7SJohan Hovold 
513980373b7SJohan Hovold 	spin_lock_irqsave(&port->lock, flags);
514980373b7SJohan Hovold 	cnow = port->icount;				/* atomic copy*/
515980373b7SJohan Hovold 	spin_unlock_irqrestore(&port->lock, flags);
516980373b7SJohan Hovold 
517980373b7SJohan Hovold 	ret =	((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) ||
518980373b7SJohan Hovold 		((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) ||
519980373b7SJohan Hovold 		((arg & TIOCM_CD)  && (cnow.dcd != cprev->dcd)) ||
520980373b7SJohan Hovold 		((arg & TIOCM_CTS) && (cnow.cts != cprev->cts));
521980373b7SJohan Hovold 
522980373b7SJohan Hovold 	*cprev = cnow;
523980373b7SJohan Hovold 
524980373b7SJohan Hovold 	return ret;
525980373b7SJohan Hovold }
526980373b7SJohan Hovold 
usb_serial_generic_tiocmiwait(struct tty_struct * tty,unsigned long arg)527980373b7SJohan Hovold int usb_serial_generic_tiocmiwait(struct tty_struct *tty, unsigned long arg)
528980373b7SJohan Hovold {
529980373b7SJohan Hovold 	struct usb_serial_port *port = tty->driver_data;
530980373b7SJohan Hovold 	struct async_icount cnow;
531980373b7SJohan Hovold 	unsigned long flags;
532980373b7SJohan Hovold 	int ret;
533980373b7SJohan Hovold 
534980373b7SJohan Hovold 	spin_lock_irqsave(&port->lock, flags);
535980373b7SJohan Hovold 	cnow = port->icount;				/* atomic copy */
536980373b7SJohan Hovold 	spin_unlock_irqrestore(&port->lock, flags);
537980373b7SJohan Hovold 
538980373b7SJohan Hovold 	ret = wait_event_interruptible(port->port.delta_msr_wait,
539980373b7SJohan Hovold 			usb_serial_generic_msr_changed(tty, arg, &cnow));
540d41861caSPeter Hurley 	if (!ret && !tty_port_initialized(&port->port))
541980373b7SJohan Hovold 		ret = -EIO;
542980373b7SJohan Hovold 
543980373b7SJohan Hovold 	return ret;
544980373b7SJohan Hovold }
545980373b7SJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_tiocmiwait);
546980373b7SJohan Hovold 
usb_serial_generic_get_icount(struct tty_struct * tty,struct serial_icounter_struct * icount)547befefcdaSJohan Hovold int usb_serial_generic_get_icount(struct tty_struct *tty,
548befefcdaSJohan Hovold 					struct serial_icounter_struct *icount)
549befefcdaSJohan Hovold {
550befefcdaSJohan Hovold 	struct usb_serial_port *port = tty->driver_data;
551befefcdaSJohan Hovold 	struct async_icount cnow;
552befefcdaSJohan Hovold 	unsigned long flags;
553befefcdaSJohan Hovold 
554befefcdaSJohan Hovold 	spin_lock_irqsave(&port->lock, flags);
555befefcdaSJohan Hovold 	cnow = port->icount;				/* atomic copy */
556befefcdaSJohan Hovold 	spin_unlock_irqrestore(&port->lock, flags);
557befefcdaSJohan Hovold 
558befefcdaSJohan Hovold 	icount->cts = cnow.cts;
559befefcdaSJohan Hovold 	icount->dsr = cnow.dsr;
560befefcdaSJohan Hovold 	icount->rng = cnow.rng;
561befefcdaSJohan Hovold 	icount->dcd = cnow.dcd;
562befefcdaSJohan Hovold 	icount->tx = cnow.tx;
563befefcdaSJohan Hovold 	icount->rx = cnow.rx;
564befefcdaSJohan Hovold 	icount->frame = cnow.frame;
565befefcdaSJohan Hovold 	icount->parity = cnow.parity;
566befefcdaSJohan Hovold 	icount->overrun = cnow.overrun;
567befefcdaSJohan Hovold 	icount->brk = cnow.brk;
568befefcdaSJohan Hovold 	icount->buf_overrun = cnow.buf_overrun;
569befefcdaSJohan Hovold 
570befefcdaSJohan Hovold 	return 0;
571befefcdaSJohan Hovold }
572befefcdaSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_get_icount);
573befefcdaSJohan Hovold 
5744fbfbdb5SJohan Hovold #if defined(CONFIG_USB_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
usb_serial_handle_sysrq_char(struct usb_serial_port * port,unsigned int ch)5756ee9f4b4SDmitry Torokhov int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch)
57698fcb5f7SJason Wessel {
57737ae2315SJohan Hovold 	if (port->sysrq) {
57898fcb5f7SJason Wessel 		if (ch && time_before(jiffies, port->sysrq)) {
579f335397dSDmitry Torokhov 			handle_sysrq(ch);
58098fcb5f7SJason Wessel 			port->sysrq = 0;
58198fcb5f7SJason Wessel 			return 1;
58298fcb5f7SJason Wessel 		}
58398fcb5f7SJason Wessel 		port->sysrq = 0;
58498fcb5f7SJason Wessel 	}
58598fcb5f7SJason Wessel 	return 0;
58698fcb5f7SJason Wessel }
58798fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char);
58898fcb5f7SJason Wessel 
usb_serial_handle_break(struct usb_serial_port * port)58998fcb5f7SJason Wessel int usb_serial_handle_break(struct usb_serial_port *port)
59098fcb5f7SJason Wessel {
5914b5cf2b8SJohan Hovold 	if (!port->port.console)
59237ae2315SJohan Hovold 		return 0;
59337ae2315SJohan Hovold 
59498fcb5f7SJason Wessel 	if (!port->sysrq) {
59598fcb5f7SJason Wessel 		port->sysrq = jiffies + HZ*5;
59698fcb5f7SJason Wessel 		return 1;
59798fcb5f7SJason Wessel 	}
59898fcb5f7SJason Wessel 	port->sysrq = 0;
59998fcb5f7SJason Wessel 	return 0;
60098fcb5f7SJason Wessel }
60198fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_handle_break);
6024b5cf2b8SJohan Hovold #endif
60398fcb5f7SJason Wessel 
604d14fc1a7SLibor Pechacek /**
605d14fc1a7SLibor Pechacek  * usb_serial_handle_dcd_change - handle a change of carrier detect state
60692ad2479SJohan Hovold  * @port: usb-serial port
60792ad2479SJohan Hovold  * @tty: tty for the port
608d14fc1a7SLibor Pechacek  * @status: new carrier detect status, nonzero if active
609d14fc1a7SLibor Pechacek  */
usb_serial_handle_dcd_change(struct usb_serial_port * port,struct tty_struct * tty,unsigned int status)6105e95dbb6SJohan Hovold void usb_serial_handle_dcd_change(struct usb_serial_port *port,
611d14fc1a7SLibor Pechacek 				struct tty_struct *tty, unsigned int status)
612d14fc1a7SLibor Pechacek {
6135e95dbb6SJohan Hovold 	dev_dbg(&port->dev, "%s - status %d\n", __func__, status);
614d14fc1a7SLibor Pechacek 
615833efc0eSPaul Chavent 	if (tty) {
616833efc0eSPaul Chavent 		struct tty_ldisc *ld = tty_ldisc_ref(tty);
617833efc0eSPaul Chavent 
618833efc0eSPaul Chavent 		if (ld) {
619833efc0eSPaul Chavent 			if (ld->ops->dcd_change)
620833efc0eSPaul Chavent 				ld->ops->dcd_change(tty, status);
621833efc0eSPaul Chavent 			tty_ldisc_deref(ld);
622833efc0eSPaul Chavent 		}
623833efc0eSPaul Chavent 	}
624833efc0eSPaul Chavent 
625d14fc1a7SLibor Pechacek 	if (status)
6265e95dbb6SJohan Hovold 		wake_up_interruptible(&port->port.open_wait);
627d14fc1a7SLibor Pechacek 	else if (tty && !C_CLOCAL(tty))
628d14fc1a7SLibor Pechacek 		tty_hangup(tty);
629d14fc1a7SLibor Pechacek }
630d14fc1a7SLibor Pechacek EXPORT_SYMBOL_GPL(usb_serial_handle_dcd_change);
631d14fc1a7SLibor Pechacek 
usb_serial_generic_resume(struct usb_serial * serial)6328e8dce06SDavid VomLehn int usb_serial_generic_resume(struct usb_serial *serial)
6338e8dce06SDavid VomLehn {
6348e8dce06SDavid VomLehn 	struct usb_serial_port *port;
6358e8dce06SDavid VomLehn 	int i, c = 0, r;
6368e8dce06SDavid VomLehn 
6378e8dce06SDavid VomLehn 	for (i = 0; i < serial->num_ports; i++) {
6388e8dce06SDavid VomLehn 		port = serial->port[i];
639d41861caSPeter Hurley 		if (!tty_port_initialized(&port->port))
6408e8dce06SDavid VomLehn 			continue;
6418e8dce06SDavid VomLehn 
642d83b4053SJohan Hovold 		if (port->bulk_in_size) {
643d83b4053SJohan Hovold 			r = usb_serial_generic_submit_read_urbs(port,
644d83b4053SJohan Hovold 								GFP_NOIO);
6458e8dce06SDavid VomLehn 			if (r < 0)
6468e8dce06SDavid VomLehn 				c++;
6478e8dce06SDavid VomLehn 		}
6488e8dce06SDavid VomLehn 
64927c7acf2SJohan Hovold 		if (port->bulk_out_size) {
650818f6036SJohan Hovold 			r = usb_serial_generic_write_start(port, GFP_NOIO);
6518e8dce06SDavid VomLehn 			if (r < 0)
6528e8dce06SDavid VomLehn 				c++;
6538e8dce06SDavid VomLehn 		}
6548e8dce06SDavid VomLehn 	}
6558e8dce06SDavid VomLehn 
6568e8dce06SDavid VomLehn 	return c ? -EIO : 0;
6578e8dce06SDavid VomLehn }
6588e8dce06SDavid VomLehn EXPORT_SYMBOL_GPL(usb_serial_generic_resume);
659