* See http://ftdi-usb-sio.sourceforge.net for upto date testing info
* and extra documentation
*
+ * (27/May/2004) Ian Abbott
+ * Improved throttling code, mostly stolen from the WhiteHEAT driver.
+ *
* (26/Mar/2004) Jan Capek
* Added PID's for ICD-U20/ICD-U40 - incircuit PIC debuggers from CCS Inc.
*
#define BUFSZ 512
#define PKTSZ 64
+/* rx_flags */
+#define THROTTLED 0x01
+#define ACTUALLY_THROTTLED 0x02
+
struct ftdi_private {
ftdi_chip_type_t chip_type;
/* type of the device, either SIO or FT8U232AM */
unsigned long last_dtr_rts; /* saved modem control outputs */
wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
char prev_status, diff_status; /* Used for TIOCMIWAIT */
+ __u8 rx_flags; /* receive state flags (throttling) */
+ spinlock_t rx_lock; /* spinlock for receive state */
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 */
static int ftdi_chars_in_buffer (struct usb_serial_port *port);
static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
+static void ftdi_process_read (struct usb_serial_port *port);
static void ftdi_set_termios (struct usb_serial_port *port, struct termios * old);
static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file);
static int ftdi_tiocmset (struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear);
}
memset(priv, 0, sizeof(*priv));
+ spin_lock_init(&priv->rx_lock);
init_waitqueue_head(&priv->delta_msr_wait);
/* This will push the characters through immediately rather
than queue a task to deliver them */
*/
if (priv) {
- kfree(priv);
usb_set_serial_port_data(port, NULL);
+ kfree(priv);
}
} /* ftdi_shutdown */
static int ftdi_open (struct usb_serial_port *port, struct file *filp)
{ /* ftdi_open */
struct termios tmp_termios;
- struct usb_serial *serial = port->serial;
+ struct usb_device *dev = port->serial->dev;
struct ftdi_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
int result = 0;
char buf[1]; /* Needed for the usb_control_msg I think */
/* No error checking for this (will get errors later anyway) */
/* See ftdi_sio.h for description of what is reset */
- usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
FTDI_SIO_RESET_SIO,
0, buf, 0, WDR_TIMEOUT);
err("%s Error from RTS HIGH urb", __FUNCTION__);
}
+ /* Not throttled */
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
+
/* Start reading from the device */
- usb_fill_bulk_urb(port->read_urb, serial->dev,
- usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+ usb_fill_bulk_urb(port->read_urb, dev,
+ usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
ftdi_read_bulk_callback, port);
result = usb_submit_urb(port->read_urb, GFP_KERNEL);
static void ftdi_close (struct usb_serial_port *port, struct file *filp)
{ /* ftdi_close */
- struct usb_serial *serial;
unsigned int c_cflag = port->tty->termios->c_cflag;
char buf[1];
dbg("%s", __FUNCTION__);
- serial = get_usb_serial ( port, __FUNCTION__);
- if (!serial)
- return;
-
- if (serial->dev) {
- if (c_cflag & HUPCL){
- /* Disable flow control */
- if (usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_FLOW_CTRL_REQUEST,
- FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
- 0, 0, buf, 0, WDR_TIMEOUT) < 0) {
- err("error from flowcontrol urb");
- }
+ if (c_cflag & HUPCL){
+ /* Disable flow control */
+ if (usb_control_msg(port->serial->dev,
+ usb_sndctrlpipe(port->serial->dev, 0),
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+ 0, 0, buf, 0, WDR_TIMEOUT) < 0) {
+ err("error from flowcontrol urb");
+ }
- /* drop DTR */
- if (set_dtr(port, LOW) < 0){
- err("Error from DTR LOW urb");
- }
- /* drop RTS */
- if (set_rts(port, LOW) < 0) {
- err("Error from RTS LOW urb");
- }
- } /* Note change no line if hupcl is off */
-
- /* shutdown our bulk read */
- if (port->read_urb) {
- if (usb_unlink_urb (port->read_urb) < 0) {
- err("Error unlinking read urb");
- }
+ /* drop DTR */
+ if (set_dtr(port, LOW) < 0){
+ err("Error from DTR LOW urb");
}
-
- } /* if (serial->dev) */
-
-
+ /* drop RTS */
+ if (set_rts(port, LOW) < 0) {
+ err("Error from RTS LOW urb");
+ }
+ } /* Note change no line if hupcl is off */
+
+ /* shutdown our bulk read */
+ if (port->read_urb) {
+ if (usb_unlink_urb (port->read_urb) < 0) {
+ /* Generally, this isn't an error. If the previous
+ read bulk callback occurred (or is about to occur)
+ while the port was being closed or was throtted
+ (and is still throttled), the read urb will not
+ have been submitted. */
+ dbg("%s - failed to unlink read urb (generally not an error)", __FUNCTION__);
+ }
+ }
} /* ftdi_close */
static int ftdi_write (struct usb_serial_port *port, int from_user,
const unsigned char *buf, int count)
{ /* ftdi_write */
- struct usb_serial *serial = get_usb_serial ( port, __FUNCTION__);
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct urb *urb;
unsigned char *buffer;
usb_serial_debug_data (__FILE__, __FUNCTION__, transfer_size, buffer);
/* fill the buffer and send it */
- usb_fill_bulk_urb(urb, serial->dev,
- usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
+ usb_fill_bulk_urb(urb, port->serial->dev,
+ usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress),
buffer, transfer_size,
ftdi_write_bulk_callback, port);
/* free up the transfer buffer, as usb_free_urb() does not do this */
kfree (urb->transfer_buffer);
- if (port_paranoia_check (port, __FUNCTION__))
- return;
-
dbg("%s - port %d", __FUNCTION__, port->number);
if (urb->status) {
static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
{ /* ftdi_read_bulk_callback */
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct usb_serial *serial;
struct tty_struct *tty;
struct ftdi_private *priv;
- char error_flag;
- unsigned char *data = urb->transfer_buffer;
-
- int i;
- int result;
- int need_flip;
- int packet_offset;
if (urb->number_of_packets > 0) {
err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__,
err("%s transfer_flags %x ", __FUNCTION__,urb->transfer_flags );
}
- dbg("%s", __FUNCTION__);
+ dbg("%s - port %d", __FUNCTION__, port->number);
- if (port_paranoia_check (port, __FUNCTION__)) {
- return;
- }
if (port->open_count <= 0)
return;
- serial = get_usb_serial(port,__FUNCTION__);
- if (!serial){
- dbg("%s - bad serial pointer - exiting",__FUNCTION__);
- return;
- }
-
tty = port->tty;
if (!tty) {
dbg("%s - bad tty pointer - exiting",__FUNCTION__);
}
priv = usb_get_serial_port_data(port);
+ if (!priv) {
+ dbg("%s - bad port private data pointer - exiting", __FUNCTION__);
+ return;
+ }
+
+ if (urb != port->read_urb) {
+ err("%s - Not my urb!", __FUNCTION__);
+ }
if (urb->status) {
/* This will happen at close every time so it is a dbg not an err */
return;
}
+ /* If throttled, delay receive processing until unthrottled. */
+ spin_lock(&priv->rx_lock);
+ if (priv->rx_flags & THROTTLED) {
+ dbg("Deferring read urb processing until unthrottled");
+ priv->rx_flags |= ACTUALLY_THROTTLED;
+ spin_unlock(&priv->rx_lock);
+ return;
+ }
+ spin_unlock(&priv->rx_lock);
+
+ ftdi_process_read(port);
+
+} /* ftdi_read_bulk_callback */
+
+
+static void ftdi_process_read (struct usb_serial_port *port)
+{ /* ftdi_process_read */
+ struct urb *urb;
+ struct tty_struct *tty;
+ struct ftdi_private *priv;
+ char error_flag;
+ unsigned char *data;
+
+ int i;
+ int result;
+ int need_flip;
+ int packet_offset;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (port->open_count <= 0)
+ return;
+
+ tty = port->tty;
+ if (!tty) {
+ dbg("%s - bad tty pointer - exiting",__FUNCTION__);
+ return;
+ }
+
+ priv = usb_get_serial_port_data(port);
+ if (!priv) {
+ dbg("%s - bad port private data pointer - exiting", __FUNCTION__);
+ return;
+ }
+
+ urb = port->read_urb;
+ if (!urb) {
+ dbg("%s - bad read_urb pointer - exiting", __FUNCTION__);
+ return;
+ }
+
+ data = urb->transfer_buffer;
+
/* The first two bytes of every read packet are status */
if (urb->actual_length > 2) {
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
/* if the port is closed stop trying to read */
if (port->open_count > 0){
/* Continue trying to always read */
- usb_fill_bulk_urb(port->read_urb, serial->dev,
- usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+ usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+ usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
ftdi_read_bulk_callback, port);
}
return;
-} /* ftdi_read_bulk_callback */
+} /* ftdi_process_read */
static void ftdi_break_ctl( struct usb_serial_port *port, int break_state )
{
- struct usb_serial *serial = port->serial;
struct ftdi_private *priv = usb_get_serial_port_data(port);
__u16 urb_value = 0;
char buf[1];
}
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ if (usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
FTDI_SIO_SET_DATA_REQUEST,
FTDI_SIO_SET_DATA_REQUEST_TYPE,
urb_value , 0,
static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_termios)
{ /* ftdi_termios */
- struct usb_serial *serial = port->serial;
+ struct usb_device *dev = port->serial->dev;
unsigned int cflag = port->tty->termios->c_cflag;
struct ftdi_private *priv = usb_get_serial_port_data(port);
__u16 urb_value; /* will hold the new flags */
* or'ed with this value */
priv->last_set_data_urb_value = urb_value;
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_DATA_REQUEST,
FTDI_SIO_SET_DATA_REQUEST_TYPE,
urb_value , 0,
/* Now do the baudrate */
if ((cflag & CBAUD) == B0 ) {
/* Disable flow control */
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
0, 0,
/* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
if (cflag & CRTSCTS) {
dbg("%s Setting to CRTSCTS flow control", __FUNCTION__);
- if (usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
+ if (usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
0 , FTDI_SIO_RTS_CTS_HS,
vstop=port->tty->termios->c_cc[VSTOP];
urb_value=(vstop << 8) | (vstart);
- if (usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
+ if (usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
urb_value , FTDI_SIO_XON_XOFF_HS,
/* else clause to only run if cfag ! CRTSCTS and iflag ! XOFF */
/* CHECKME Assuming XON/XOFF handled by tty stack - not by device */
dbg("%s Turning off hardware flow control", __FUNCTION__);
- if (usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
+ if (usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
0, 0,
static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
{
- struct usb_serial *serial = port->serial;
struct ftdi_private *priv = usb_get_serial_port_data(port);
unsigned char buf[2];
int ret;
switch (priv->chip_type) {
case SIO:
/* Request the status from the device */
- if ((ret = usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev, 0),
+ if ((ret = usb_control_msg(port->serial->dev,
+ usb_rcvctrlpipe(port->serial->dev, 0),
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
0, 0,
case FT232BM:
/* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same
format as the data returned from the in point */
- if ((ret = usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev, 0),
+ if ((ret = usb_control_msg(port->serial->dev,
+ usb_rcvctrlpipe(port->serial->dev, 0),
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
0, 0,
static void ftdi_throttle (struct usb_serial_port *port)
{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
dbg("%s - port %d", __FUNCTION__, port->number);
- usb_unlink_urb (port->read_urb);
+
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ priv->rx_flags |= THROTTLED;
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
}
static void ftdi_unthrottle (struct usb_serial_port *port)
{
- int result;
- struct usb_serial *serial = port->serial;
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ int actually_throttled;
+ unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number);
- port->read_urb->dev = serial->dev;
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED;
+ priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
- usb_fill_bulk_urb(port->read_urb, serial->dev,
- usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
- port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
- ftdi_read_bulk_callback, port);
-
- result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
- if (result)
- err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
+ if (actually_throttled)
+ ftdi_process_read(port);
}
static int __init ftdi_init (void)