xref: /openbmc/linux/drivers/usb/serial/generic.c (revision 56a1df46)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * USB Serial Converter Generic functions
31da177e4SLinus Torvalds  *
427c7acf2SJohan Hovold  * Copyright (C) 2010 Johan Hovold (jhovold@gmail.com)
51da177e4SLinus Torvalds  * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *	This program is free software; you can redistribute it and/or
81da177e4SLinus Torvalds  *	modify it under the terms of the GNU General Public License version
91da177e4SLinus Torvalds  *	2 as published by the Free Software Foundation.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds #include <linux/kernel.h>
141da177e4SLinus Torvalds #include <linux/errno.h>
151da177e4SLinus Torvalds #include <linux/slab.h>
167f0ae3a8SRandy Dunlap #include <linux/sysrq.h>
171da177e4SLinus Torvalds #include <linux/tty.h>
181da177e4SLinus Torvalds #include <linux/tty_flip.h>
191da177e4SLinus Torvalds #include <linux/module.h>
201da177e4SLinus Torvalds #include <linux/moduleparam.h>
211da177e4SLinus Torvalds #include <linux/usb.h>
22a969888cSGreg Kroah-Hartman #include <linux/usb/serial.h>
23ae64387aSAlan Cox #include <linux/uaccess.h>
248e8dce06SDavid VomLehn #include <linux/kfifo.h>
251f87158eSAlan Stern #include <linux/serial.h>
26d9b1b787SJohannes Hölzl 
271da177e4SLinus Torvalds static int debug;
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #ifdef CONFIG_USB_SERIAL_GENERIC
30b46d60fcSDavid Brownell 
31b46d60fcSDavid Brownell static int generic_probe(struct usb_interface *interface,
32b46d60fcSDavid Brownell 			 const struct usb_device_id *id);
33b46d60fcSDavid Brownell 
341da177e4SLinus Torvalds static __u16 vendor  = 0x05f9;
351da177e4SLinus Torvalds static __u16 product = 0xffff;
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds module_param(vendor, ushort, 0);
381da177e4SLinus Torvalds MODULE_PARM_DESC(vendor, "User specified USB idVendor");
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds module_param(product, ushort, 0);
411da177e4SLinus Torvalds MODULE_PARM_DESC(product, "User specified USB idProduct");
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
441da177e4SLinus Torvalds 
45d9b1b787SJohannes Hölzl /* we want to look at all devices, as the vendor/product id can change
46d9b1b787SJohannes Hölzl  * depending on the command line argument */
477d40d7e8SNémeth Márton static const struct usb_device_id generic_serial_ids[] = {
48d9b1b787SJohannes Hölzl 	{.driver_info = 42},
49d9b1b787SJohannes Hölzl 	{}
50d9b1b787SJohannes Hölzl };
51d9b1b787SJohannes Hölzl 
52d9b1b787SJohannes Hölzl static struct usb_driver generic_driver = {
53d9b1b787SJohannes Hölzl 	.name =		"usbserial_generic",
54d9b1b787SJohannes Hölzl 	.probe =	generic_probe,
55d9b1b787SJohannes Hölzl 	.disconnect =	usb_serial_disconnect,
56d9b1b787SJohannes Hölzl 	.id_table =	generic_serial_ids,
57d9b1b787SJohannes Hölzl 	.no_dynamic_id =	1,
58d9b1b787SJohannes Hölzl };
59d9b1b787SJohannes Hölzl 
601da177e4SLinus Torvalds /* All of the device info needed for the Generic Serial Converter */
61ea65370dSGreg Kroah-Hartman struct usb_serial_driver usb_serial_generic_device = {
6218fcac35SGreg Kroah-Hartman 	.driver = {
631da177e4SLinus Torvalds 		.owner =	THIS_MODULE,
64269bda1cSGreg Kroah-Hartman 		.name =		"generic",
6518fcac35SGreg Kroah-Hartman 	},
661da177e4SLinus Torvalds 	.id_table =		generic_device_ids,
67d9b1b787SJohannes Hölzl 	.usb_driver = 		&generic_driver,
681da177e4SLinus Torvalds 	.num_ports =		1,
69f9c99bb8SAlan Stern 	.disconnect =		usb_serial_generic_disconnect,
70f9c99bb8SAlan Stern 	.release =		usb_serial_generic_release,
71253ca923SJoris van Rantwijk 	.throttle =		usb_serial_generic_throttle,
72253ca923SJoris van Rantwijk 	.unthrottle =		usb_serial_generic_unthrottle,
73ec22559eSOliver Neukum 	.resume =		usb_serial_generic_resume,
741da177e4SLinus Torvalds };
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds static int generic_probe(struct usb_interface *interface,
771da177e4SLinus Torvalds 			       const struct usb_device_id *id)
781da177e4SLinus Torvalds {
791da177e4SLinus Torvalds 	const struct usb_device_id *id_pattern;
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds 	id_pattern = usb_match_id(interface, generic_device_ids);
821da177e4SLinus Torvalds 	if (id_pattern != NULL)
831da177e4SLinus Torvalds 		return usb_serial_probe(interface, id);
841da177e4SLinus Torvalds 	return -ENODEV;
851da177e4SLinus Torvalds }
861da177e4SLinus Torvalds #endif
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds int usb_serial_generic_register(int _debug)
891da177e4SLinus Torvalds {
901da177e4SLinus Torvalds 	int retval = 0;
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds 	debug = _debug;
931da177e4SLinus Torvalds #ifdef CONFIG_USB_SERIAL_GENERIC
941da177e4SLinus Torvalds 	generic_device_ids[0].idVendor = vendor;
951da177e4SLinus Torvalds 	generic_device_ids[0].idProduct = product;
96ae64387aSAlan Cox 	generic_device_ids[0].match_flags =
97ae64387aSAlan Cox 		USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 	/* register our generic driver with ourselves */
1001da177e4SLinus Torvalds 	retval = usb_serial_register(&usb_serial_generic_device);
1011da177e4SLinus Torvalds 	if (retval)
1021da177e4SLinus Torvalds 		goto exit;
1031da177e4SLinus Torvalds 	retval = usb_register(&generic_driver);
1041da177e4SLinus Torvalds 	if (retval)
1051da177e4SLinus Torvalds 		usb_serial_deregister(&usb_serial_generic_device);
1061da177e4SLinus Torvalds exit:
1071da177e4SLinus Torvalds #endif
1081da177e4SLinus Torvalds 	return retval;
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds void usb_serial_generic_deregister(void)
1121da177e4SLinus Torvalds {
1131da177e4SLinus Torvalds #ifdef CONFIG_USB_SERIAL_GENERIC
1141da177e4SLinus Torvalds 	/* remove our generic driver */
1151da177e4SLinus Torvalds 	usb_deregister(&generic_driver);
1161da177e4SLinus Torvalds 	usb_serial_deregister(&usb_serial_generic_device);
1171da177e4SLinus Torvalds #endif
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds 
120a509a7e4SAlan Cox int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port)
1211da177e4SLinus Torvalds {
1221da177e4SLinus Torvalds 	int result = 0;
123253ca923SJoris van Rantwijk 	unsigned long flags;
1241da177e4SLinus Torvalds 
125441b62c1SHarvey Harrison 	dbg("%s - port %d", __func__, port->number);
1261da177e4SLinus Torvalds 
127253ca923SJoris van Rantwijk 	/* clear the throttle flags */
128253ca923SJoris van Rantwijk 	spin_lock_irqsave(&port->lock, flags);
129253ca923SJoris van Rantwijk 	port->throttled = 0;
130253ca923SJoris van Rantwijk 	port->throttle_req = 0;
131253ca923SJoris van Rantwijk 	spin_unlock_irqrestore(&port->lock, flags);
132253ca923SJoris van Rantwijk 
133253ca923SJoris van Rantwijk 	/* if we have a bulk endpoint, start reading from it */
13441bd72f9SJohan Hovold 	if (port->bulk_in_size)
13541bd72f9SJohan Hovold 		result = usb_serial_generic_submit_read_urb(port, GFP_KERNEL);
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 	return result;
1381da177e4SLinus Torvalds }
139815ddc99SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(usb_serial_generic_open);
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds static void generic_cleanup(struct usb_serial_port *port)
1421da177e4SLinus Torvalds {
1431da177e4SLinus Torvalds 	struct usb_serial *serial = port->serial;
144ec3ee508SJohan Hovold 	unsigned long flags;
14527c7acf2SJohan Hovold 	int i;
1461da177e4SLinus Torvalds 
147441b62c1SHarvey Harrison 	dbg("%s - port %d", __func__, port->number);
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds 	if (serial->dev) {
150eb8878a8SJohan Hovold 		/* shutdown any bulk transfers that might be going on */
151ec3ee508SJohan Hovold 		if (port->bulk_out_size) {
1521da177e4SLinus Torvalds 			usb_kill_urb(port->write_urb);
15327c7acf2SJohan Hovold 			for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
15427c7acf2SJohan Hovold 				usb_kill_urb(port->write_urbs[i]);
155ec3ee508SJohan Hovold 
156ec3ee508SJohan Hovold 			spin_lock_irqsave(&port->lock, flags);
157ec3ee508SJohan Hovold 			kfifo_reset_out(&port->write_fifo);
158ec3ee508SJohan Hovold 			spin_unlock_irqrestore(&port->lock, flags);
159ec3ee508SJohan Hovold 		}
160eb8878a8SJohan Hovold 		if (port->bulk_in_size)
1611da177e4SLinus Torvalds 			usb_kill_urb(port->read_urb);
1621da177e4SLinus Torvalds 	}
1631da177e4SLinus Torvalds }
1641da177e4SLinus Torvalds 
165335f8514SAlan Cox void usb_serial_generic_close(struct usb_serial_port *port)
1661da177e4SLinus Torvalds {
167441b62c1SHarvey Harrison 	dbg("%s - port %d", __func__, port->number);
1681da177e4SLinus Torvalds 	generic_cleanup(port);
1691da177e4SLinus Torvalds }
170f26788daSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_close);
1711da177e4SLinus Torvalds 
172eaa3bcb0SJohan Hovold int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
173c23e5fc1SJohan Hovold 						void *dest, size_t size)
174eaa3bcb0SJohan Hovold {
175c23e5fc1SJohan Hovold 	return kfifo_out_locked(&port->write_fifo, dest, size, &port->lock);
176715b1dc0SJason Wessel }
177715b1dc0SJason Wessel 
1788e8dce06SDavid VomLehn /**
1798e8dce06SDavid VomLehn  * usb_serial_generic_write_start - kick off an URB write
1808e8dce06SDavid VomLehn  * @port:	Pointer to the &struct usb_serial_port data
1818e8dce06SDavid VomLehn  *
18227c7acf2SJohan Hovold  * Returns zero on success, or a negative errno value
1838e8dce06SDavid VomLehn  */
1848e8dce06SDavid VomLehn static int usb_serial_generic_write_start(struct usb_serial_port *port)
1851da177e4SLinus Torvalds {
18627c7acf2SJohan Hovold 	struct urb *urb;
18727c7acf2SJohan Hovold 	int count, result;
188acd2a847SJiri Kosina 	unsigned long flags;
18927c7acf2SJohan Hovold 	int i;
190715b1dc0SJason Wessel 
19127c7acf2SJohan Hovold 	if (test_and_set_bit_lock(USB_SERIAL_WRITE_BUSY, &port->flags))
19227c7acf2SJohan Hovold 		return 0;
19327c7acf2SJohan Hovold retry:
194acd2a847SJiri Kosina 	spin_lock_irqsave(&port->lock, flags);
19527c7acf2SJohan Hovold 	if (!port->write_urbs_free || !kfifo_len(&port->write_fifo)) {
19627c7acf2SJohan Hovold 		clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
197acd2a847SJiri Kosina 		spin_unlock_irqrestore(&port->lock, flags);
1988e8dce06SDavid VomLehn 		return 0;
19950a5f70cSJohan Hovold 	}
20027c7acf2SJohan Hovold 	i = (int)find_first_bit(&port->write_urbs_free,
20127c7acf2SJohan Hovold 						ARRAY_SIZE(port->write_urbs));
20250a5f70cSJohan Hovold 	spin_unlock_irqrestore(&port->lock, flags);
2031da177e4SLinus Torvalds 
20427c7acf2SJohan Hovold 	urb = port->write_urbs[i];
205eaa3bcb0SJohan Hovold 	count = port->serial->type->prepare_write_buffer(port,
206c23e5fc1SJohan Hovold 						urb->transfer_buffer,
207c23e5fc1SJohan Hovold 						port->bulk_out_size);
20827c7acf2SJohan Hovold 	urb->transfer_buffer_length = count;
20927c7acf2SJohan Hovold 	usb_serial_debug_data(debug, &port->dev, __func__, count,
21027c7acf2SJohan Hovold 						urb->transfer_buffer);
21127c7acf2SJohan Hovold 	result = usb_submit_urb(urb, GFP_ATOMIC);
212507ca9bcSGreg Kroah-Hartman 	if (result) {
2131a1405e2SJohan Hovold 		dev_err(&port->dev, "%s - error submitting urb: %d\n",
214ae64387aSAlan Cox 						__func__, result);
21527c7acf2SJohan Hovold 		clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
2161da177e4SLinus Torvalds 		return result;
2171da177e4SLinus Torvalds 	}
21827c7acf2SJohan Hovold 	clear_bit(i, &port->write_urbs_free);
2191da177e4SLinus Torvalds 
22030af7fb5SJohan Hovold 	spin_lock_irqsave(&port->lock, flags);
22125d514caSJohan Hovold 	port->tx_bytes += count;
22230af7fb5SJohan Hovold 	spin_unlock_irqrestore(&port->lock, flags);
22330af7fb5SJohan Hovold 
22427c7acf2SJohan Hovold 	/* Try sending off another urb, unless in irq context (in which case
22527c7acf2SJohan Hovold 	 * there will be no free urb). */
22627c7acf2SJohan Hovold 	if (!in_irq())
22727c7acf2SJohan Hovold 		goto retry;
22827c7acf2SJohan Hovold 
22927c7acf2SJohan Hovold 	clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
23027c7acf2SJohan Hovold 
23127c7acf2SJohan Hovold 	return 0;
23230af7fb5SJohan Hovold }
23330af7fb5SJohan Hovold 
2348e8dce06SDavid VomLehn /**
2358e8dce06SDavid VomLehn  * usb_serial_generic_write - generic write function for serial USB devices
2368e8dce06SDavid VomLehn  * @tty:	Pointer to &struct tty_struct for the device
2378e8dce06SDavid VomLehn  * @port:	Pointer to the &usb_serial_port structure for the device
2388e8dce06SDavid VomLehn  * @buf:	Pointer to the data to write
2398e8dce06SDavid VomLehn  * @count:	Number of bytes to write
2408e8dce06SDavid VomLehn  *
2418e8dce06SDavid VomLehn  * Returns the number of characters actually written, which may be anything
2428e8dce06SDavid VomLehn  * from zero to @count. If an error occurs, it returns the negative errno
2438e8dce06SDavid VomLehn  * value.
2448e8dce06SDavid VomLehn  */
2458e8dce06SDavid VomLehn int usb_serial_generic_write(struct tty_struct *tty,
2468e8dce06SDavid VomLehn 	struct usb_serial_port *port, const unsigned char *buf, int count)
2478e8dce06SDavid VomLehn {
2488e8dce06SDavid VomLehn 	int result;
2498e8dce06SDavid VomLehn 
2508e8dce06SDavid VomLehn 	dbg("%s - port %d", __func__, port->number);
2518e8dce06SDavid VomLehn 
252eb8878a8SJohan Hovold 	/* only do something if we have a bulk out endpoint */
253eb8878a8SJohan Hovold 	if (!port->bulk_out_size)
254eb8878a8SJohan Hovold 		return -ENODEV;
255eb8878a8SJohan Hovold 
2561a1405e2SJohan Hovold 	if (!count)
257507ca9bcSGreg Kroah-Hartman 		return 0;
2588e8dce06SDavid VomLehn 
259119eecc8SStefani Seibold 	count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock);
2608e8dce06SDavid VomLehn 	result = usb_serial_generic_write_start(port);
261c23e5fc1SJohan Hovold 	if (result)
2628e8dce06SDavid VomLehn 		return result;
263c23e5fc1SJohan Hovold 
264c23e5fc1SJohan Hovold 	return count;
2658e8dce06SDavid VomLehn }
26698fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_generic_write);
2671da177e4SLinus Torvalds 
26895da310eSAlan Cox int usb_serial_generic_write_room(struct tty_struct *tty)
2691da177e4SLinus Torvalds {
27095da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
271715b1dc0SJason Wessel 	unsigned long flags;
27225d514caSJohan Hovold 	int room;
2731da177e4SLinus Torvalds 
274441b62c1SHarvey Harrison 	dbg("%s - port %d", __func__, port->number);
275eb8878a8SJohan Hovold 
276eb8878a8SJohan Hovold 	if (!port->bulk_out_size)
277eb8878a8SJohan Hovold 		return 0;
278eb8878a8SJohan Hovold 
279715b1dc0SJason Wessel 	spin_lock_irqsave(&port->lock, flags);
280119eecc8SStefani Seibold 	room = kfifo_avail(&port->write_fifo);
281715b1dc0SJason Wessel 	spin_unlock_irqrestore(&port->lock, flags);
2821da177e4SLinus Torvalds 
283441b62c1SHarvey Harrison 	dbg("%s - returns %d", __func__, room);
284a5b6f60cSAlan Cox 	return room;
2851da177e4SLinus Torvalds }
2861da177e4SLinus Torvalds 
28795da310eSAlan Cox int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
2881da177e4SLinus Torvalds {
28995da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
290715b1dc0SJason Wessel 	unsigned long flags;
291eb8878a8SJohan Hovold 	int chars;
2921da177e4SLinus Torvalds 
293441b62c1SHarvey Harrison 	dbg("%s - port %d", __func__, port->number);
2941da177e4SLinus Torvalds 
295eb8878a8SJohan Hovold 	if (!port->bulk_out_size)
296eb8878a8SJohan Hovold 		return 0;
297eb8878a8SJohan Hovold 
298715b1dc0SJason Wessel 	spin_lock_irqsave(&port->lock, flags);
29925d514caSJohan Hovold 	chars = kfifo_len(&port->write_fifo) + port->tx_bytes;
30025719e6bSStefani Seibold 	spin_unlock_irqrestore(&port->lock, flags);
3011da177e4SLinus Torvalds 
302441b62c1SHarvey Harrison 	dbg("%s - returns %d", __func__, chars);
30395da310eSAlan Cox 	return chars;
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
30641bd72f9SJohan Hovold int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
30798fcb5f7SJason Wessel 					gfp_t mem_flags)
3081da177e4SLinus Torvalds {
3091da177e4SLinus Torvalds 	int result;
3101da177e4SLinus Torvalds 
311056afc0fSJohan Hovold 	result = usb_submit_urb(port->read_urb, mem_flags);
3120ae14743SJohan Hovold 	if (result && result != -EPERM) {
3131a1405e2SJohan Hovold 		dev_err(&port->dev, "%s - error submitting urb: %d\n",
314ae64387aSAlan Cox 							__func__, result);
3151da177e4SLinus Torvalds 	}
31641bd72f9SJohan Hovold 	return result;
3170ae14743SJohan Hovold }
31841bd72f9SJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urb);
319253ca923SJoris van Rantwijk 
32023154320SJohan Hovold void usb_serial_generic_process_read_urb(struct urb *urb)
3211abdeeb1SOliver Neukum {
3220f3d5baeSJohan Hovold 	struct usb_serial_port *port = urb->context;
3230f3d5baeSJohan Hovold 	struct tty_struct *tty;
32498fcb5f7SJason Wessel 	char *ch = (char *)urb->transfer_buffer;
32598fcb5f7SJason Wessel 	int i;
32698fcb5f7SJason Wessel 
32756a1df46SJohan Hovold 	if (!urb->actual_length)
32856a1df46SJohan Hovold 		return;
32956a1df46SJohan Hovold 
3300f3d5baeSJohan Hovold 	tty = tty_port_tty_get(&port->port);
33198fcb5f7SJason Wessel 	if (!tty)
3320f3d5baeSJohan Hovold 		return;
3331abdeeb1SOliver Neukum 
3344cd1de0aSAlan Cox 	/* The per character mucking around with sysrq path it too slow for
3354cd1de0aSAlan Cox 	   stuff like 3G modems, so shortcircuit it in the 99.9999999% of cases
3364cd1de0aSAlan Cox 	   where the USB serial is not a console anyway */
337bd5afa9eSJason Wessel 	if (!port->port.console || !port->sysrq)
3384cd1de0aSAlan Cox 		tty_insert_flip_string(tty, ch, urb->actual_length);
3394cd1de0aSAlan Cox 	else {
34098fcb5f7SJason Wessel 		for (i = 0; i < urb->actual_length; i++, ch++) {
34124a15a62SAlan Cox 			if (!usb_serial_handle_sysrq_char(tty, port, *ch))
34298fcb5f7SJason Wessel 				tty_insert_flip_char(tty, *ch, TTY_NORMAL);
34398fcb5f7SJason Wessel 		}
3444cd1de0aSAlan Cox 	}
345b507cc97SPete Zaitcev 	tty_flip_buffer_push(tty);
3464a90f09bSAlan Cox 	tty_kref_put(tty);
3471abdeeb1SOliver Neukum }
34823154320SJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_process_read_urb);
3491abdeeb1SOliver Neukum 
350253ca923SJoris van Rantwijk void usb_serial_generic_read_bulk_callback(struct urb *urb)
351253ca923SJoris van Rantwijk {
352cdc97792SMing Lei 	struct usb_serial_port *port = urb->context;
353253ca923SJoris van Rantwijk 	unsigned char *data = urb->transfer_buffer;
354fbd27225SGreg Kroah-Hartman 	int status = urb->status;
355bfaeafcfSBorislav Petkov 	unsigned long flags;
356253ca923SJoris van Rantwijk 
357441b62c1SHarvey Harrison 	dbg("%s - port %d", __func__, port->number);
358253ca923SJoris van Rantwijk 
359fbd27225SGreg Kroah-Hartman 	if (unlikely(status != 0)) {
360fbd27225SGreg Kroah-Hartman 		dbg("%s - nonzero read bulk status received: %d",
361441b62c1SHarvey Harrison 		    __func__, status);
362253ca923SJoris van Rantwijk 		return;
363253ca923SJoris van Rantwijk 	}
364253ca923SJoris van Rantwijk 
365ae64387aSAlan Cox 	usb_serial_debug_data(debug, &port->dev, __func__,
366ae64387aSAlan Cox 						urb->actual_length, data);
36723154320SJohan Hovold 	port->serial->type->process_read_urb(urb);
368253ca923SJoris van Rantwijk 
369253ca923SJoris van Rantwijk 	/* Throttle the device if requested by tty */
370bfaeafcfSBorislav Petkov 	spin_lock_irqsave(&port->lock, flags);
371ae64387aSAlan Cox 	port->throttled = port->throttle_req;
372ae64387aSAlan Cox 	if (!port->throttled) {
373bfaeafcfSBorislav Petkov 		spin_unlock_irqrestore(&port->lock, flags);
3740f3d5baeSJohan Hovold 		usb_serial_generic_submit_read_urb(port, GFP_ATOMIC);
375ae64387aSAlan Cox 	} else
376b507cc97SPete Zaitcev 		spin_unlock_irqrestore(&port->lock, flags);
377b507cc97SPete Zaitcev }
378166ffccfSLuiz Fernando N. Capitulino EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);
3791da177e4SLinus Torvalds 
3807d12e780SDavid Howells void usb_serial_generic_write_bulk_callback(struct urb *urb)
3811da177e4SLinus Torvalds {
382715b1dc0SJason Wessel 	unsigned long flags;
383cdc97792SMing Lei 	struct usb_serial_port *port = urb->context;
384fbd27225SGreg Kroah-Hartman 	int status = urb->status;
38527c7acf2SJohan Hovold 	int i;
3861da177e4SLinus Torvalds 
387441b62c1SHarvey Harrison 	dbg("%s - port %d", __func__, port->number);
3881da177e4SLinus Torvalds 
38927c7acf2SJohan Hovold 	for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
39027c7acf2SJohan Hovold 		if (port->write_urbs[i] == urb)
39127c7acf2SJohan Hovold 			break;
39227c7acf2SJohan Hovold 
39330af7fb5SJohan Hovold 	spin_lock_irqsave(&port->lock, flags);
39425d514caSJohan Hovold 	port->tx_bytes -= urb->transfer_buffer_length;
39527c7acf2SJohan Hovold 	set_bit(i, &port->write_urbs_free);
39630af7fb5SJohan Hovold 	spin_unlock_irqrestore(&port->lock, flags);
3978e8dce06SDavid VomLehn 
39850dbb852SJohan Hovold 	if (status) {
399c23e5fc1SJohan Hovold 		dbg("%s - non-zero urb status: %d", __func__, status);
400c23e5fc1SJohan Hovold 
40150dbb852SJohan Hovold 		spin_lock_irqsave(&port->lock, flags);
402119eecc8SStefani Seibold 		kfifo_reset_out(&port->write_fifo);
40350dbb852SJohan Hovold 		spin_unlock_irqrestore(&port->lock, flags);
40450dbb852SJohan Hovold 	} else {
4058e8dce06SDavid VomLehn 		usb_serial_generic_write_start(port);
4068e8dce06SDavid VomLehn 	}
40763136202SJohan Hovold 
408cf2c7481SPete Zaitcev 	usb_serial_port_softint(port);
4091da177e4SLinus Torvalds }
410bb833986SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback);
4111da177e4SLinus Torvalds 
41295da310eSAlan Cox void usb_serial_generic_throttle(struct tty_struct *tty)
413253ca923SJoris van Rantwijk {
41495da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
415253ca923SJoris van Rantwijk 	unsigned long flags;
416253ca923SJoris van Rantwijk 
417441b62c1SHarvey Harrison 	dbg("%s - port %d", __func__, port->number);
418253ca923SJoris van Rantwijk 
419253ca923SJoris van Rantwijk 	/* Set the throttle request flag. It will be picked up
420253ca923SJoris van Rantwijk 	 * by usb_serial_generic_read_bulk_callback(). */
421253ca923SJoris van Rantwijk 	spin_lock_irqsave(&port->lock, flags);
422253ca923SJoris van Rantwijk 	port->throttle_req = 1;
423253ca923SJoris van Rantwijk 	spin_unlock_irqrestore(&port->lock, flags);
424253ca923SJoris van Rantwijk }
425f1e949acSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_throttle);
426253ca923SJoris van Rantwijk 
42795da310eSAlan Cox void usb_serial_generic_unthrottle(struct tty_struct *tty)
428253ca923SJoris van Rantwijk {
42995da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
430253ca923SJoris van Rantwijk 	int was_throttled;
431253ca923SJoris van Rantwijk 
432441b62c1SHarvey Harrison 	dbg("%s - port %d", __func__, port->number);
433253ca923SJoris van Rantwijk 
434253ca923SJoris van Rantwijk 	/* Clear the throttle flags */
4350f3d5baeSJohan Hovold 	spin_lock_irq(&port->lock);
436253ca923SJoris van Rantwijk 	was_throttled = port->throttled;
437253ca923SJoris van Rantwijk 	port->throttled = port->throttle_req = 0;
4380f3d5baeSJohan Hovold 	spin_unlock_irq(&port->lock);
439253ca923SJoris van Rantwijk 
4400f3d5baeSJohan Hovold 	if (was_throttled)
4410f3d5baeSJohan Hovold 		usb_serial_generic_submit_read_urb(port, GFP_KERNEL);
442253ca923SJoris van Rantwijk }
443f1e949acSJohan Hovold EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle);
444253ca923SJoris van Rantwijk 
4457f0ae3a8SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ
44624a15a62SAlan Cox int usb_serial_handle_sysrq_char(struct tty_struct *tty,
44724a15a62SAlan Cox 			struct usb_serial_port *port, unsigned int ch)
44898fcb5f7SJason Wessel {
449bd5afa9eSJason Wessel 	if (port->sysrq && port->port.console) {
45098fcb5f7SJason Wessel 		if (ch && time_before(jiffies, port->sysrq)) {
45124a15a62SAlan Cox 			handle_sysrq(ch, tty);
45298fcb5f7SJason Wessel 			port->sysrq = 0;
45398fcb5f7SJason Wessel 			return 1;
45498fcb5f7SJason Wessel 		}
45598fcb5f7SJason Wessel 		port->sysrq = 0;
45698fcb5f7SJason Wessel 	}
45798fcb5f7SJason Wessel 	return 0;
45898fcb5f7SJason Wessel }
4597f0ae3a8SRandy Dunlap #else
4607f0ae3a8SRandy Dunlap int usb_serial_handle_sysrq_char(struct tty_struct *tty,
4617f0ae3a8SRandy Dunlap 			struct usb_serial_port *port, unsigned int ch)
4627f0ae3a8SRandy Dunlap {
4637f0ae3a8SRandy Dunlap 	return 0;
4647f0ae3a8SRandy Dunlap }
4657f0ae3a8SRandy Dunlap #endif
46698fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char);
46798fcb5f7SJason Wessel 
46898fcb5f7SJason Wessel int usb_serial_handle_break(struct usb_serial_port *port)
46998fcb5f7SJason Wessel {
47098fcb5f7SJason Wessel 	if (!port->sysrq) {
47198fcb5f7SJason Wessel 		port->sysrq = jiffies + HZ*5;
47298fcb5f7SJason Wessel 		return 1;
47398fcb5f7SJason Wessel 	}
47498fcb5f7SJason Wessel 	port->sysrq = 0;
47598fcb5f7SJason Wessel 	return 0;
47698fcb5f7SJason Wessel }
47798fcb5f7SJason Wessel EXPORT_SYMBOL_GPL(usb_serial_handle_break);
47898fcb5f7SJason Wessel 
4798e8dce06SDavid VomLehn int usb_serial_generic_resume(struct usb_serial *serial)
4808e8dce06SDavid VomLehn {
4818e8dce06SDavid VomLehn 	struct usb_serial_port *port;
4828e8dce06SDavid VomLehn 	int i, c = 0, r;
4838e8dce06SDavid VomLehn 
4848e8dce06SDavid VomLehn 	for (i = 0; i < serial->num_ports; i++) {
4858e8dce06SDavid VomLehn 		port = serial->port[i];
4861f87158eSAlan Stern 		if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags))
4878e8dce06SDavid VomLehn 			continue;
4888e8dce06SDavid VomLehn 
4898e8dce06SDavid VomLehn 		if (port->read_urb) {
4908e8dce06SDavid VomLehn 			r = usb_submit_urb(port->read_urb, GFP_NOIO);
4918e8dce06SDavid VomLehn 			if (r < 0)
4928e8dce06SDavid VomLehn 				c++;
4938e8dce06SDavid VomLehn 		}
4948e8dce06SDavid VomLehn 
49527c7acf2SJohan Hovold 		if (port->bulk_out_size) {
4968e8dce06SDavid VomLehn 			r = usb_serial_generic_write_start(port);
4978e8dce06SDavid VomLehn 			if (r < 0)
4988e8dce06SDavid VomLehn 				c++;
4998e8dce06SDavid VomLehn 		}
5008e8dce06SDavid VomLehn 	}
5018e8dce06SDavid VomLehn 
5028e8dce06SDavid VomLehn 	return c ? -EIO : 0;
5038e8dce06SDavid VomLehn }
5048e8dce06SDavid VomLehn EXPORT_SYMBOL_GPL(usb_serial_generic_resume);
5058e8dce06SDavid VomLehn 
506f9c99bb8SAlan Stern void usb_serial_generic_disconnect(struct usb_serial *serial)
5071da177e4SLinus Torvalds {
5081da177e4SLinus Torvalds 	int i;
5091da177e4SLinus Torvalds 
510441b62c1SHarvey Harrison 	dbg("%s", __func__);
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds 	/* stop reads and writes on all ports */
513ae64387aSAlan Cox 	for (i = 0; i < serial->num_ports; ++i)
5141da177e4SLinus Torvalds 		generic_cleanup(serial->port[i]);
5151da177e4SLinus Torvalds }
5161da177e4SLinus Torvalds 
517f9c99bb8SAlan Stern void usb_serial_generic_release(struct usb_serial *serial)
518f9c99bb8SAlan Stern {
519f9c99bb8SAlan Stern 	dbg("%s", __func__);
520f9c99bb8SAlan Stern }
521