USB: serial: add generic wait_until_sent implementation
authorJohan Hovold <jhovold@gmail.com>
Wed, 8 May 2013 15:51:43 +0000 (17:51 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 17 May 2013 00:32:21 +0000 (17:32 -0700)
Add generic wait_until_sent implementation which polls for empty
hardware buffers using the new port-operation tx_empty.

The generic implementation will be used for all sub-drivers that
implement tx_empty but does not define wait_until_sent.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/serial/generic.c
drivers/usb/serial/usb-serial.c
include/linux/usb/serial.h

index 297665fdd16d31993a5e95cddd313626453be6c7..ba45170c78e5f601ec6b2c57f5f05600247803f4 100644 (file)
@@ -253,6 +253,37 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
 }
 EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer);
 
+void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout)
+{
+       struct usb_serial_port *port = tty->driver_data;
+       unsigned int bps;
+       unsigned long period;
+       unsigned long expire;
+
+       bps = tty_get_baud_rate(tty);
+       if (!bps)
+               bps = 9600;     /* B0 */
+       /*
+        * Use a poll-period of roughly the time it takes to send one
+        * character or at least one jiffy.
+        */
+       period = max_t(unsigned long, (10 * HZ / bps), 1);
+       period = min_t(unsigned long, period, timeout);
+
+       dev_dbg(&port->dev, "%s - timeout = %u ms, period = %u ms\n",
+                                       __func__, jiffies_to_msecs(timeout),
+                                       jiffies_to_msecs(period));
+       expire = jiffies + timeout;
+       while (!port->serial->type->tx_empty(port)) {
+               schedule_timeout_interruptible(period);
+               if (signal_pending(current))
+                       break;
+               if (time_after(jiffies, expire))
+                       break;
+       }
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_wait_until_sent);
+
 static int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
                                                int index, gfp_t mem_flags)
 {
index 31d27686151f620bebaf46643a2381da56c27010..60caf9cb99f402da7178cd36f831696349782b13 100644 (file)
@@ -1333,6 +1333,8 @@ static void usb_serial_operations_init(struct usb_serial_driver *device)
        set_to_generic_if_null(device, close);
        set_to_generic_if_null(device, write_room);
        set_to_generic_if_null(device, chars_in_buffer);
+       if (device->tx_empty)
+               set_to_generic_if_null(device, wait_until_sent);
        set_to_generic_if_null(device, read_bulk_callback);
        set_to_generic_if_null(device, write_bulk_callback);
        set_to_generic_if_null(device, process_read_urb);
index afbb7eeaac5fc62094ac7f181f27485ee0a6db70..302ddf55d2daca5d74291b1eb9aaa2b4127fe7d2 100644 (file)
@@ -269,6 +269,7 @@ struct usb_serial_driver {
        void (*break_ctl)(struct tty_struct *tty, int break_state);
        int  (*chars_in_buffer)(struct tty_struct *tty);
        void (*wait_until_sent)(struct tty_struct *tty, long timeout);
+       bool (*tx_empty)(struct usb_serial_port *port);
        void (*throttle)(struct tty_struct *tty);
        void (*unthrottle)(struct tty_struct *tty);
        int  (*tiocmget)(struct tty_struct *tty);
@@ -328,6 +329,8 @@ extern void usb_serial_generic_close(struct usb_serial_port *port);
 extern int usb_serial_generic_resume(struct usb_serial *serial);
 extern int usb_serial_generic_write_room(struct tty_struct *tty);
 extern int usb_serial_generic_chars_in_buffer(struct tty_struct *tty);
+extern void usb_serial_generic_wait_until_sent(struct tty_struct *tty,
+                                                               long timeout);
 extern void usb_serial_generic_read_bulk_callback(struct urb *urb);
 extern void usb_serial_generic_write_bulk_callback(struct urb *urb);
 extern void usb_serial_generic_throttle(struct tty_struct *tty);