Commit 895f28ba authored by Mark Adamson's avatar Mark Adamson Committed by Greg Kroah-Hartman

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: default avatarMark J. Adamson <mark.adamson@ftdichip.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 094c2e6d
...@@ -95,6 +95,7 @@ struct ftdi_private { ...@@ -95,6 +95,7 @@ struct ftdi_private {
unsigned long tx_bytes; unsigned long tx_bytes;
unsigned long tx_outstanding_bytes; unsigned long tx_outstanding_bytes;
unsigned long tx_outstanding_urbs; unsigned long tx_outstanding_urbs;
unsigned short max_packet_size;
}; };
/* struct ftdi_sio_quirk is used by devices requiring special attention. */ /* struct ftdi_sio_quirk is used by devices requiring special attention. */
...@@ -703,7 +704,6 @@ static const char *ftdi_chip_name[] = { ...@@ -703,7 +704,6 @@ static const char *ftdi_chip_name[] = {
/* Constants for read urb and write urb */ /* Constants for read urb and write urb */
#define BUFSZ 512 #define BUFSZ 512
#define PKTSZ 64
/* rx_flags */ /* rx_flags */
#define THROTTLED 0x01 #define THROTTLED 0x01
...@@ -1296,6 +1296,45 @@ static void ftdi_determine_type(struct usb_serial_port *port) ...@@ -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 * Sysfs Attribute
...@@ -1485,6 +1524,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) ...@@ -1485,6 +1524,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
usb_set_serial_port_data(port, priv); usb_set_serial_port_data(port, priv);
ftdi_determine_type(port); ftdi_determine_type(port);
ftdi_set_max_packet_size(port);
read_latency_timer(port); read_latency_timer(port);
create_sysfs_attrs(port); create_sysfs_attrs(port);
return 0; return 0;
...@@ -1740,8 +1780,8 @@ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, ...@@ -1740,8 +1780,8 @@ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port,
if (data_offset > 0) { if (data_offset > 0) {
/* Original sio needs control bytes too... */ /* Original sio needs control bytes too... */
transfer_size += (data_offset * transfer_size += (data_offset *
((count + (PKTSZ - 1 - data_offset)) / ((count + (priv->max_packet_size - 1 - data_offset)) /
(PKTSZ - data_offset))); (priv->max_packet_size - data_offset)));
} }
buffer = kmalloc(transfer_size, GFP_ATOMIC); buffer = kmalloc(transfer_size, GFP_ATOMIC);
...@@ -1763,7 +1803,7 @@ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, ...@@ -1763,7 +1803,7 @@ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port,
if (data_offset > 0) { if (data_offset > 0) {
/* Original sio requires control byte at start of /* Original sio requires control byte at start of
each packet. */ each packet. */
int user_pktsz = PKTSZ - data_offset; int user_pktsz = priv->max_packet_size - data_offset;
int todo = count; int todo = count;
unsigned char *first_byte = buffer; unsigned char *first_byte = buffer;
const unsigned char *current_position = buf; const unsigned char *current_position = buf;
...@@ -1859,7 +1899,7 @@ static void ftdi_write_bulk_callback(struct urb *urb) ...@@ -1859,7 +1899,7 @@ static void ftdi_write_bulk_callback(struct urb *urb)
data_offset = priv->write_offset; data_offset = priv->write_offset;
if (data_offset > 0) { if (data_offset > 0) {
/* Subtract the control bytes */ /* 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); spin_lock_irqsave(&priv->tx_lock, flags);
--priv->tx_outstanding_urbs; --priv->tx_outstanding_urbs;
...@@ -1961,7 +2001,7 @@ static void ftdi_read_bulk_callback(struct urb *urb) ...@@ -1961,7 +2001,7 @@ static void ftdi_read_bulk_callback(struct urb *urb)
/* count data bytes, but not status bytes */ /* count data bytes, but not status bytes */
countread = urb->actual_length; 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); spin_lock_irqsave(&priv->rx_lock, flags);
priv->rx_bytes += countread; priv->rx_bytes += countread;
spin_unlock_irqrestore(&priv->rx_lock, flags); spin_unlock_irqrestore(&priv->rx_lock, flags);
...@@ -2034,7 +2074,7 @@ static void ftdi_process_read(struct work_struct *work) ...@@ -2034,7 +2074,7 @@ static void ftdi_process_read(struct work_struct *work)
need_flip = 0; need_flip = 0;
for (packet_offset = priv->rx_processed; 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; int length;
/* Compare new line status to the old one, signal if different/ /* Compare new line status to the old one, signal if different/
...@@ -2049,7 +2089,7 @@ static void ftdi_process_read(struct work_struct *work) ...@@ -2049,7 +2089,7 @@ static void ftdi_process_read(struct work_struct *work)
priv->prev_status = new_status; 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) { if (length < 0) {
dev_err(&port->dev, "%s - bad packet length: %d\n", dev_err(&port->dev, "%s - bad packet length: %d\n",
__func__, length+2); __func__, length+2);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment