X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fserial%2Fftdi_sio.c;h=c15f3e57cffa8c8e8f8b42b546db78d0141d433d;hb=746550cff061581f89c687ada8523670768364f2;hp=d9949dedae3800b7b045aa06555f2e39f36628f6;hpb=86090fcac5e27b630656fe3d963a6b80e26dac44;p=linux-2.6.git diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index d9949deda..c15f3e57c 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -17,6 +17,9 @@ * 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. * @@ -584,6 +587,10 @@ static struct usb_driver ftdi_driver = { #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 */ @@ -598,6 +605,8 @@ struct ftdi_private { 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 */ @@ -625,6 +634,7 @@ static int ftdi_write_room (struct usb_serial_port *port); 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); @@ -1110,6 +1120,7 @@ static int ftdi_common_startup (struct usb_serial *serial) } 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 */ @@ -1273,8 +1284,8 @@ static void ftdi_shutdown (struct usb_serial *serial) */ if (priv) { - kfree(priv); usb_set_serial_port_data(port, NULL); + kfree(priv); } } /* ftdi_shutdown */ @@ -1282,8 +1293,9 @@ static void ftdi_shutdown (struct usb_serial *serial) 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 */ @@ -1295,7 +1307,7 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) /* 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); @@ -1317,9 +1329,14 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) 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); @@ -1342,47 +1359,42 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) 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 */ @@ -1397,7 +1409,6 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp) 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; @@ -1484,8 +1495,8 @@ static int ftdi_write (struct usb_serial_port *port, int from_user, 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); @@ -1513,9 +1524,6 @@ static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs) /* 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) { @@ -1558,16 +1566,8 @@ static int ftdi_chars_in_buffer (struct usb_serial_port *port) 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__, @@ -1575,20 +1575,11 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs) 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__); @@ -1596,6 +1587,14 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs) } 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 */ @@ -1603,6 +1602,59 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs) 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); @@ -1696,8 +1748,8 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs) /* 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); @@ -1707,12 +1759,11 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs) } 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]; @@ -1728,7 +1779,7 @@ static void ftdi_break_ctl( struct usb_serial_port *port, int break_state ) } - 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, @@ -1748,7 +1799,7 @@ static void ftdi_break_ctl( struct usb_serial_port *port, int break_state ) 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 */ @@ -1807,7 +1858,7 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_ * 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, @@ -1818,7 +1869,7 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_ /* 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, @@ -1844,8 +1895,8 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_ /* 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, @@ -1870,8 +1921,8 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_ 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, @@ -1882,8 +1933,8 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_ /* 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, @@ -1899,7 +1950,6 @@ 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) { - struct usb_serial *serial = port->serial; struct ftdi_private *priv = usb_get_serial_port_data(port); unsigned char buf[2]; int ret; @@ -1908,8 +1958,8 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file) 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, @@ -1923,8 +1973,8 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file) 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, @@ -2099,28 +2149,32 @@ static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigne 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)