[PATCH] Deferred URB submission for timesharing the DMA channels of DWC_OTG host controller in Raspberry PI

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Srinivasan R <srinivasan.r@xxxxxxxxxxx>
---
 drivers/usb/serial/ftdi_sio.c | 153 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 152 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 1d8077e..552d7f8 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -40,12 +40,16 @@
 #include <linux/usb.h>
 #include <linux/serial.h>
 #include <linux/usb/serial.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
 #include "ftdi_sio.h"
 #include "ftdi_sio_ids.h"

 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@xxxxxxxxx>, Bill Ryder <bryder@xxxxxxx>, Kuba Ober <kuba@xxxxxxxxxxxxxxx>, Andreas Mohr, Johan Hovold <jhovold@xxxxxxxxx>"
 #define DRIVER_DESC "USB FTDI Serial Converters Driver"

+#define HUNDREDMS    (100 * 1000000)
+#define TIMEOUT  (30 * 1000000)  // mill seconds, default value

 struct ftdi_private {
 enum ftdi_chip_type chip_type;
@@ -72,6 +76,11 @@ struct ftdi_private {
 unsigned int latency;/* latency setting in use */
 unsigned short max_packet_size;
 struct mutex cfg_lock; /* Avoid mess by parallel calls of config ioctl() and change_speed() */
+spinlock_tlock;
+ktime_t ktime;
+struct hrtimer etx_hr_timer;
+struct tty_struct *tty;
+struct device   *dev;
 };

 /* struct ftdi_sio_quirk is used by devices requiring special attention. */
@@ -1046,10 +1055,15 @@ static int  ftdi_sio_probe(struct usb_serial *serial,
 static int  ftdi_sio_port_probe(struct usb_serial_port *port);
 static int  ftdi_sio_port_remove(struct usb_serial_port *port);
 static int  ftdi_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void ftdi_close(struct usb_serial_port *port);
+static void ftdi_cancel_timer(struct usb_serial_port *port);
 static void ftdi_dtr_rts(struct usb_serial_port *port, int on);
 static void ftdi_process_read_urb(struct urb *urb);
 static int ftdi_prepare_write_buffer(struct usb_serial_port *port,
 void *dest, size_t size);
+static void ftdi_serial_read_bulk_callback(struct urb *urb);
+static void ftdi_serial_throttle(struct tty_struct *tty);
+static void ftdi_serial_unthrottle(struct tty_struct *tty);
 static void ftdi_set_termios(struct tty_struct *tty,
 struct usb_serial_port *port, struct ktermios *old);
 static int  ftdi_tiocmget(struct tty_struct *tty);
@@ -1083,10 +1097,12 @@ static struct usb_serial_driver ftdi_sio_device = {
 .port_probe =ftdi_sio_port_probe,
 .port_remove =ftdi_sio_port_remove,
 .open =ftdi_open,
+.close =ftdi_close,
 .dtr_rts =ftdi_dtr_rts,
 .throttle =usb_serial_generic_throttle,
 .unthrottle =usb_serial_generic_unthrottle,
 .process_read_urb =ftdi_process_read_urb,
+.read_bulk_callback = ftdi_serial_read_bulk_callback,
 .prepare_write_buffer =ftdi_prepare_write_buffer,
 .tiocmget =ftdi_tiocmget,
 .tiocmset =ftdi_tiocmset,
@@ -1102,6 +1118,13 @@ static struct usb_serial_driver * const serial_drivers[] = {
 &ftdi_sio_device, NULL
 };

+/*
+ * Module parameter to control URB defer timer for FTDI-based .
+ * USB serial converter, If this value is not set in /etc/modprobe.d/
+ * its value will be set to 30ms, maximum value of the delay can be 100ms
+ */
+static unsigned long urb_defer_timer = TIMEOUT;
+

 #define WDR_TIMEOUT 5000 /* default urb timeout */
 #define WDR_SHORT_TIMEOUT 1000/* shorter urb timeout */
@@ -1112,6 +1135,33 @@ static struct usb_serial_driver * const serial_drivers[] = {
  * ***************************************************************************
  */

+static enum hrtimer_restart timer_callbackserial(struct hrtimer *timer)
+{
+struct ftdi_private *priv = container_of(timer, struct ftdi_private,
+etx_hr_timer);
+int stopped = 0;
+unsigned long flags;
+
+spin_lock_irqsave(&priv->lock, flags);
+if (!priv->tty)
+stopped = 1;
+spin_unlock_irqrestore(&priv->lock, flags);
+if (!stopped)
+ftdi_serial_unthrottle(priv->tty);
+return HRTIMER_NORESTART;
+}
+
+static void ftdi_cancel_timer(struct usb_serial_port *port)
+{
+unsigned long flags;
+struct ftdi_private *priv = usb_get_serial_port_data(port);
+
+hrtimer_cancel(&priv->etx_hr_timer);
+spin_lock_irqsave(&port->lock, flags);
+priv->tty = NULL;
+spin_unlock_irqrestore(&port->lock, flags);
+}
+
 static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base)
 {
 unsigned short int divisor;
@@ -1815,9 +1865,87 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
 priv->latency = 16;
 write_latency_timer(port);
 create_sysfs_attrs(port);
+priv->ktime = ktime_set(0, ((urb_defer_timer > 0) &&
+(urb_defer_timer <= HUNDREDMS)) ? urb_defer_timer : TIMEOUT);
+hrtimer_init(&priv->etx_hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+priv->etx_hr_timer.function = &timer_callbackserial;
+priv->dev = &port->dev;
+spin_lock_init(&port->lock);
 return 0;
 }

+static void ftdi_serial_read_bulk_callback(struct urb *urb)
+{
+struct usb_serial_port *port = urb->context;
+unsigned long flags;
+int status = urb->status;
+int i;
+
+for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
+if (urb == port->read_urbs[i])
+break;
+}
+set_bit(i, &port->read_urbs_free);
+
+dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i,
+urb->actual_length);
+switch (status) {
+case 0:
+break;
+case -ENOENT:
+case -ECONNRESET:
+case -ESHUTDOWN:
+dev_dbg(&port->dev, "%s - urb stopped: %d\n",
+__func__, status);
+return;
+case -EPIPE:
+dev_err(&port->dev, "%s - urb stopped: %d\n",
+__func__, status);
+return;
+default:
+dev_dbg(&port->dev, "%s - nonzero urb status: %d\n",
+__func__, status);
+return;
+}
+
+spin_lock_irqsave(&port->lock, flags);
+port->throttled = port->throttle_req;
+if (!port->throttled) {
+spin_unlock_irqrestore(&port->lock, flags);
+/*Stop submitting back the URB, later in the Timer callback
+ * submit
+ */
+ftdi_serial_throttle(tty_port_tty_get(&port->port));
+} else {
+spin_unlock_irqrestore(&port->lock, flags);
+}
+ftdi_process_read_urb(urb);
+}
+static void ftdi_serial_throttle(struct tty_struct *tty)
+{
+struct usb_serial_port *port = tty->driver_data;
+unsigned long flags;
+
+spin_lock_irqsave(&port->lock, flags);
+port->throttle_req = 1;
+spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void ftdi_serial_unthrottle(struct tty_struct *tty)
+{
+struct usb_serial_port *port = tty->driver_data;
+unsigned long flags;
+int was_throttled;
+
+spin_lock_irqsave(&port->lock, flags);
+was_throttled = port->throttled;
+port->throttled = port->throttle_req = 0;
+spin_unlock_irqrestore(&port->lock, flags);
+
+if (was_throttled)
+usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
+}
+
 /* Setup for the USB-UIRT device, which requires hardwired
  * baudrate (38400 gets mapped to 312500) */
 /* Called from usbserial:serial_probe */
@@ -1956,11 +2084,18 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port)
    This is same behaviour as serial.c/rs_open() - Kuba */

 /* ftdi_set_termios  will send usb control messages */
-if (tty)
+if (tty) {
 ftdi_set_termios(tty, port, NULL);
+priv->tty = tty;
+}

 return usb_serial_generic_open(tty, port);
 }
+static void ftdi_close(struct usb_serial_port *port)
+{
+usb_serial_generic_close(port);
+ftdi_cancel_timer(port);
+}

 static void ftdi_dtr_rts(struct usb_serial_port *port, int on)
 {
@@ -2127,6 +2262,8 @@ static void ftdi_process_read_urb(struct urb *urb)
 int i;
 int len;
 int count = 0;
+unsigned long flags;
+bool isstoped = false;

 for (i = 0; i < urb->actual_length; i += priv->max_packet_size) {
 len = min_t(int, urb->actual_length - i, priv->max_packet_size);
@@ -2135,6 +2272,18 @@ static void ftdi_process_read_urb(struct urb *urb)

 if (count)
 tty_flip_buffer_push(&port->port);
+
+spin_lock_irqsave(&port->lock, flags);
+if (!priv->tty)
+isstoped = true;
+/*Start only one Timer for all the URB Buffers per port*/
+if ((port->throttled) && (isstoped == false)) {
+spin_unlock_irqrestore(&port->lock, flags);
+hrtimer_start(&priv->etx_hr_timer, priv->ktime,
+HRTIMER_MODE_REL);
+} else {
+spin_unlock_irqrestore(&port->lock, flags);
+}
 }

 static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
@@ -2475,3 +2624,5 @@ MODULE_LICENSE("GPL");

 module_param(ndi_latency_timer, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(ndi_latency_timer, "NDI device latency timer override");
+module_param(urb_defer_timer, ulong, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(urb_defer_timer, "urb_defer delay in ms, default value 30ms");
--
2.7.4


Please note BRT have updated their Privacy Policy.

Please click on the following link to access and review.

BRT Privacy Policy<http://brtchip.com/privacy-policy/>




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux