diff options
author | Mark Adamson <mark.adamson@ftdichip.com> | 2009-05-01 11:48:45 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-15 21:44:41 -0700 |
commit | 895f28badce96cd903026b0076966e3571b6968e (patch) | |
tree | 30881e196227ba32ff78fae9f6ebc37f89dff455 | |
parent | 094c2e6db4be381f708ad8a2e0532d7782f05ea4 (diff) |
USB: ftdi_sio: fix hi-speed device packet size calculation
Added a function to set the packet size to be used based on the value from the
device endpoint descriptor. The FT2232H and FT4232H hi-speed devices will have
wMaxPacketSize of 512 bytes when connected to a USB 2.0 hi-speed host, but will
use alternative descriptors with wMaxPacketSize of 64 bytes if connected to a
USB 1.1 host or hub. All other FTDI devices have wMaxPacketSize of 64 bytes,
except some FT232R and FT245R devices which customers have mistakenly
programmed to have wMaxPacketSize of 0 - this is an error and will be
overridden to use wMaxPacketSize of 64 bytes. The packet size used is
important as it determines where the driver removes the status bytes from the
incoming data. If it is incorrect, it will lead to data corruption.
Signed-off-by: Mark J. Adamson <mark.adamson@ftdichip.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/serial/ftdi_sio.c | 56 |
1 files changed, 48 insertions, 8 deletions
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 80ccfa13e0b..74cc3c83160 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -95,6 +95,7 @@ struct ftdi_private { unsigned long tx_bytes; unsigned long tx_outstanding_bytes; unsigned long tx_outstanding_urbs; + unsigned short max_packet_size; }; /* struct ftdi_sio_quirk is used by devices requiring special attention. */ @@ -703,7 +704,6 @@ static const char *ftdi_chip_name[] = { /* Constants for read urb and write urb */ #define BUFSZ 512 -#define PKTSZ 64 /* rx_flags */ #define THROTTLED 0x01 @@ -1296,6 +1296,45 @@ static void ftdi_determine_type(struct usb_serial_port *port) } +/* Determine the maximum packet size for the device. This depends on the chip + * type and the USB host capabilities. The value should be obtained from the + * device descriptor as the chip will use the appropriate values for the host.*/ +static void ftdi_set_max_packet_size(struct usb_serial_port *port) +{ + struct ftdi_private *priv = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + struct usb_device *udev = serial->dev; + + struct usb_interface *interface = serial->interface; + struct usb_endpoint_descriptor *ep_desc = &interface->cur_altsetting->endpoint[1].desc; + + unsigned num_endpoints; + int i = 0; + + num_endpoints = interface->cur_altsetting->desc.bNumEndpoints; + dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints); + + /* NOTE: some customers have programmed FT232R/FT245R devices + * with an endpoint size of 0 - not good. In this case, we + * want to override the endpoint descriptor setting and use a + * value of 64 for wMaxPacketSize */ + for (i = 0; i < num_endpoints; i++) { + dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1, + interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize); + ep_desc = &interface->cur_altsetting->endpoint[i].desc; + if (ep_desc->wMaxPacketSize == 0) { + ep_desc->wMaxPacketSize = cpu_to_le16(0x40); + dev_info(&udev->dev, "Overriding wMaxPacketSize on endpoint %d\n", i); + } + } + + /* set max packet size based on descriptor */ + priv->max_packet_size = ep_desc->wMaxPacketSize; + + dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size); +} + + /* * *************************************************************************** * Sysfs Attribute @@ -1485,6 +1524,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, priv); ftdi_determine_type(port); + ftdi_set_max_packet_size(port); read_latency_timer(port); create_sysfs_attrs(port); return 0; @@ -1740,8 +1780,8 @@ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, if (data_offset > 0) { /* Original sio needs control bytes too... */ transfer_size += (data_offset * - ((count + (PKTSZ - 1 - data_offset)) / - (PKTSZ - data_offset))); + ((count + (priv->max_packet_size - 1 - data_offset)) / + (priv->max_packet_size - data_offset))); } buffer = kmalloc(transfer_size, GFP_ATOMIC); @@ -1763,7 +1803,7 @@ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, if (data_offset > 0) { /* Original sio requires control byte at start of each packet. */ - int user_pktsz = PKTSZ - data_offset; + int user_pktsz = priv->max_packet_size - data_offset; int todo = count; unsigned char *first_byte = buffer; const unsigned char *current_position = buf; @@ -1859,7 +1899,7 @@ static void ftdi_write_bulk_callback(struct urb *urb) data_offset = priv->write_offset; if (data_offset > 0) { /* Subtract the control bytes */ - countback -= (data_offset * DIV_ROUND_UP(countback, PKTSZ)); + countback -= (data_offset * DIV_ROUND_UP(countback, priv->max_packet_size)); } spin_lock_irqsave(&priv->tx_lock, flags); --priv->tx_outstanding_urbs; @@ -1961,7 +2001,7 @@ static void ftdi_read_bulk_callback(struct urb *urb) /* count data bytes, but not status bytes */ countread = urb->actual_length; - countread -= 2 * DIV_ROUND_UP(countread, PKTSZ); + countread -= 2 * DIV_ROUND_UP(countread, priv->max_packet_size); spin_lock_irqsave(&priv->rx_lock, flags); priv->rx_bytes += countread; spin_unlock_irqrestore(&priv->rx_lock, flags); @@ -2034,7 +2074,7 @@ static void ftdi_process_read(struct work_struct *work) need_flip = 0; for (packet_offset = priv->rx_processed; - packet_offset < urb->actual_length; packet_offset += PKTSZ) { + packet_offset < urb->actual_length; packet_offset += priv->max_packet_size) { int length; /* Compare new line status to the old one, signal if different/ @@ -2049,7 +2089,7 @@ static void ftdi_process_read(struct work_struct *work) priv->prev_status = new_status; } - length = min_t(u32, PKTSZ, urb->actual_length-packet_offset)-2; + length = min_t(u32, priv->max_packet_size, urb->actual_length-packet_offset)-2; if (length < 0) { dev_err(&port->dev, "%s - bad packet length: %d\n", __func__, length+2); |