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