|
@@ -550,11 +550,17 @@ struct ftdi_private {
|
|
spinlock_t rx_lock; /* spinlock for receive state */
|
|
spinlock_t rx_lock; /* spinlock for receive state */
|
|
struct work_struct rx_work;
|
|
struct work_struct rx_work;
|
|
int rx_processed;
|
|
int rx_processed;
|
|
|
|
+ unsigned long rx_bytes;
|
|
|
|
|
|
__u16 interface; /* FT2232C port interface (0 for FT232/245) */
|
|
__u16 interface; /* FT2232C port interface (0 for FT232/245) */
|
|
|
|
|
|
int force_baud; /* if non-zero, force the baud rate to this value */
|
|
int force_baud; /* if non-zero, force the baud rate to this value */
|
|
int force_rtscts; /* if non-zero, force RTS-CTS to always be enabled */
|
|
int force_rtscts; /* if non-zero, force RTS-CTS to always be enabled */
|
|
|
|
+
|
|
|
|
+ spinlock_t tx_lock; /* spinlock for transmit state */
|
|
|
|
+ unsigned long tx_bytes;
|
|
|
|
+ unsigned long tx_outstanding_bytes;
|
|
|
|
+ unsigned long tx_outstanding_urbs;
|
|
};
|
|
};
|
|
|
|
|
|
/* Used for TIOCMIWAIT */
|
|
/* Used for TIOCMIWAIT */
|
|
@@ -628,6 +634,9 @@ static struct usb_serial_driver ftdi_sio_device = {
|
|
#define HIGH 1
|
|
#define HIGH 1
|
|
#define LOW 0
|
|
#define LOW 0
|
|
|
|
|
|
|
|
+/* number of outstanding urbs to prevent userspace DoS from happening */
|
|
|
|
+#define URB_UPPER_LIMIT 42
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* ***************************************************************************
|
|
* ***************************************************************************
|
|
* Utlity functions
|
|
* Utlity functions
|
|
@@ -1158,6 +1167,7 @@ static int ftdi_sio_attach (struct usb_serial *serial)
|
|
}
|
|
}
|
|
|
|
|
|
spin_lock_init(&priv->rx_lock);
|
|
spin_lock_init(&priv->rx_lock);
|
|
|
|
+ spin_lock_init(&priv->tx_lock);
|
|
init_waitqueue_head(&priv->delta_msr_wait);
|
|
init_waitqueue_head(&priv->delta_msr_wait);
|
|
/* This will push the characters through immediately rather
|
|
/* This will push the characters through immediately rather
|
|
than queue a task to deliver them */
|
|
than queue a task to deliver them */
|
|
@@ -1272,6 +1282,13 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp)
|
|
|
|
|
|
dbg("%s", __FUNCTION__);
|
|
dbg("%s", __FUNCTION__);
|
|
|
|
|
|
|
|
+ spin_lock_irqsave(&priv->tx_lock, flags);
|
|
|
|
+ priv->tx_bytes = 0;
|
|
|
|
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
|
|
|
|
+ spin_lock_irqsave(&priv->rx_lock, flags);
|
|
|
|
+ priv->rx_bytes = 0;
|
|
|
|
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
|
|
|
|
+
|
|
if (port->tty)
|
|
if (port->tty)
|
|
port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
|
|
port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
|
|
|
|
|
|
@@ -1374,6 +1391,7 @@ static int ftdi_write (struct usb_serial_port *port,
|
|
int data_offset ; /* will be 1 for the SIO and 0 otherwise */
|
|
int data_offset ; /* will be 1 for the SIO and 0 otherwise */
|
|
int status;
|
|
int status;
|
|
int transfer_size;
|
|
int transfer_size;
|
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
dbg("%s port %d, %d bytes", __FUNCTION__, port->number, count);
|
|
dbg("%s port %d, %d bytes", __FUNCTION__, port->number, count);
|
|
|
|
|
|
@@ -1381,6 +1399,13 @@ static int ftdi_write (struct usb_serial_port *port,
|
|
dbg("write request of 0 bytes");
|
|
dbg("write request of 0 bytes");
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+ spin_lock_irqsave(&priv->tx_lock, flags);
|
|
|
|
+ if (priv->tx_outstanding_urbs > URB_UPPER_LIMIT) {
|
|
|
|
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
|
|
|
|
+ dbg("%s - write limit hit\n", __FUNCTION__);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
|
|
|
|
|
|
data_offset = priv->write_offset;
|
|
data_offset = priv->write_offset;
|
|
dbg("data_offset set to %d",data_offset);
|
|
dbg("data_offset set to %d",data_offset);
|
|
@@ -1447,6 +1472,12 @@ static int ftdi_write (struct usb_serial_port *port,
|
|
err("%s - failed submitting write urb, error %d", __FUNCTION__, status);
|
|
err("%s - failed submitting write urb, error %d", __FUNCTION__, status);
|
|
count = status;
|
|
count = status;
|
|
kfree (buffer);
|
|
kfree (buffer);
|
|
|
|
+ } else {
|
|
|
|
+ spin_lock_irqsave(&priv->tx_lock, flags);
|
|
|
|
+ ++priv->tx_outstanding_urbs;
|
|
|
|
+ priv->tx_outstanding_bytes += count;
|
|
|
|
+ priv->tx_bytes += count;
|
|
|
|
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
|
|
}
|
|
}
|
|
|
|
|
|
/* we are done with this urb, so let the host driver
|
|
/* we are done with this urb, so let the host driver
|
|
@@ -1462,7 +1493,11 @@ static int ftdi_write (struct usb_serial_port *port,
|
|
|
|
|
|
static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
|
|
static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
|
|
{
|
|
{
|
|
|
|
+ unsigned long flags;
|
|
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
|
|
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
|
|
|
|
+ struct ftdi_private *priv;
|
|
|
|
+ int data_offset; /* will be 1 for the SIO and 0 otherwise */
|
|
|
|
+ unsigned long countback;
|
|
|
|
|
|
/* free up the transfer buffer, as usb_free_urb() does not do this */
|
|
/* free up the transfer buffer, as usb_free_urb() does not do this */
|
|
kfree (urb->transfer_buffer);
|
|
kfree (urb->transfer_buffer);
|
|
@@ -1474,34 +1509,67 @@ static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ priv = usb_get_serial_port_data(port);
|
|
|
|
+ if (!priv) {
|
|
|
|
+ dbg("%s - bad port private data pointer - exiting", __FUNCTION__);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ /* account for transferred data */
|
|
|
|
+ countback = urb->actual_length;
|
|
|
|
+ data_offset = priv->write_offset;
|
|
|
|
+ if (data_offset > 0) {
|
|
|
|
+ /* Subtract the control bytes */
|
|
|
|
+ countback -= (data_offset * ((countback + (PKTSZ - 1)) / PKTSZ));
|
|
|
|
+ }
|
|
|
|
+ spin_lock_irqsave(&priv->tx_lock, flags);
|
|
|
|
+ --priv->tx_outstanding_urbs;
|
|
|
|
+ priv->tx_outstanding_bytes -= countback;
|
|
|
|
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
|
|
|
|
+
|
|
usb_serial_port_softint(port);
|
|
usb_serial_port_softint(port);
|
|
} /* ftdi_write_bulk_callback */
|
|
} /* ftdi_write_bulk_callback */
|
|
|
|
|
|
|
|
|
|
static int ftdi_write_room( struct usb_serial_port *port )
|
|
static int ftdi_write_room( struct usb_serial_port *port )
|
|
{
|
|
{
|
|
|
|
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
|
|
|
|
+ int room;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+
|
|
dbg("%s - port %d", __FUNCTION__, port->number);
|
|
dbg("%s - port %d", __FUNCTION__, port->number);
|
|
|
|
|
|
- /*
|
|
|
|
- * We really can take anything the user throws at us
|
|
|
|
- * but let's pick a nice big number to tell the tty
|
|
|
|
- * layer that we have lots of free space
|
|
|
|
- */
|
|
|
|
- return 2048;
|
|
|
|
|
|
+ spin_lock_irqsave(&priv->tx_lock, flags);
|
|
|
|
+ if (priv->tx_outstanding_urbs < URB_UPPER_LIMIT) {
|
|
|
|
+ /*
|
|
|
|
+ * We really can take anything the user throws at us
|
|
|
|
+ * but let's pick a nice big number to tell the tty
|
|
|
|
+ * layer that we have lots of free space
|
|
|
|
+ */
|
|
|
|
+ room = 2048;
|
|
|
|
+ } else {
|
|
|
|
+ room = 0;
|
|
|
|
+ }
|
|
|
|
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
|
|
|
|
+ return room;
|
|
} /* ftdi_write_room */
|
|
} /* ftdi_write_room */
|
|
|
|
|
|
|
|
|
|
static int ftdi_chars_in_buffer (struct usb_serial_port *port)
|
|
static int ftdi_chars_in_buffer (struct usb_serial_port *port)
|
|
{ /* ftdi_chars_in_buffer */
|
|
{ /* ftdi_chars_in_buffer */
|
|
|
|
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
|
|
|
|
+ int buffered;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+
|
|
dbg("%s - port %d", __FUNCTION__, port->number);
|
|
dbg("%s - port %d", __FUNCTION__, port->number);
|
|
|
|
|
|
- /*
|
|
|
|
- * We can't really account for how much data we
|
|
|
|
- * have sent out, but hasn't made it through to the
|
|
|
|
- * device, so just tell the tty layer that everything
|
|
|
|
- * is flushed.
|
|
|
|
- */
|
|
|
|
- return 0;
|
|
|
|
|
|
+ spin_lock_irqsave(&priv->tx_lock, flags);
|
|
|
|
+ buffered = (int)priv->tx_outstanding_bytes;
|
|
|
|
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
|
|
|
|
+ if (buffered < 0) {
|
|
|
|
+ err("%s outstanding tx bytes is negative!", __FUNCTION__);
|
|
|
|
+ buffered = 0;
|
|
|
|
+ }
|
|
|
|
+ return buffered;
|
|
} /* ftdi_chars_in_buffer */
|
|
} /* ftdi_chars_in_buffer */
|
|
|
|
|
|
|
|
|
|
@@ -1511,6 +1579,8 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
|
|
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
|
|
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
|
|
struct tty_struct *tty;
|
|
struct tty_struct *tty;
|
|
struct ftdi_private *priv;
|
|
struct ftdi_private *priv;
|
|
|
|
+ unsigned long countread;
|
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
if (urb->number_of_packets > 0) {
|
|
if (urb->number_of_packets > 0) {
|
|
err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__,
|
|
err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__,
|
|
@@ -1545,6 +1615,13 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* count data bytes, but not status bytes */
|
|
|
|
+ countread = urb->actual_length;
|
|
|
|
+ countread -= 2 * ((countread + (PKTSZ - 1)) / PKTSZ);
|
|
|
|
+ spin_lock_irqsave(&priv->rx_lock, flags);
|
|
|
|
+ priv->rx_bytes += countread;
|
|
|
|
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
|
|
|
|
+
|
|
ftdi_process_read(port);
|
|
ftdi_process_read(port);
|
|
|
|
|
|
} /* ftdi_read_bulk_callback */
|
|
} /* ftdi_read_bulk_callback */
|