Skip to content

Commit

Permalink
USB: ftdi_sio: fix hi-speed device packet size calculation
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Mark Adamson authored and Greg Kroah-Hartman committed Jun 16, 2009
1 parent 094c2e6 commit 895f28b
Showing 1 changed file with 48 additions and 8 deletions.
56 changes: 48 additions & 8 deletions drivers/usb/serial/ftdi_sio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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/
Expand All @@ -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);
Expand Down

0 comments on commit 895f28b

Please sign in to comment.