From 3416eaa1f8f8d516b77de514e14cf8da256d28fb Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 10 Feb 2008 20:23:19 -0600 Subject: [PATCH] USB: cypress_m8: Packet format is separate from characteristic size cypress_m8: Packet format is separate from characteristic size The Cypress app note states that when using an 8 byte packet buffer size that the packet format is modified (to be more compact). However I have since discovered that newer DeLorme Earthmate LT-20 devices (those that are low speed USB with 8 byte packet size) STILL use the format that is really supposed to correspond to 32 byte packets. Further confusing things is the subsequent discovery that there are actually two different types of LT-20 - older LT-20's use 32 byte packets which is probably why this issue wasn't originally encountered. The solution here is to flag the packet format separately from the buffer size. Then at initialization time, identify the correct combination and set it up. This is a critical fix for anyone with a newer LT-20. Older devices and non-Earthmate devices should remain unaffected by this change. (If other devices behave in this, uh, unexpected manner, it's now just a simple 1 line change to fix them as well (change the pkt_fmt member for that device). Default behavior with this patch is still to drive the format as per the app-note; of course for Earthmate devices this is overridden. Signed-off-by: Mike Isely Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cypress_m8.c | 111 +++++++++++++++++++------------- 1 file changed, 66 insertions(+), 45 deletions(-) diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 155b82a25d..c42d3bdbe9 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -122,6 +122,11 @@ static struct usb_driver cypress_driver = { .no_dynamic_id = 1, }; +enum packet_format { + packet_format_1, /* b0:status, b1:payload count */ + packet_format_2 /* b0[7:3]:status, b0[2:0]:payload count */ +}; + struct cypress_private { spinlock_t lock; /* private lock */ int chiptype; /* identifier of device, for quirks/etc */ @@ -139,6 +144,7 @@ struct cypress_private { __u8 current_status; /* received from last read - info on dsr,cts,cd,ri,etc */ __u8 current_config; /* stores the current configuration byte */ __u8 rx_flags; /* throttling - used from whiteheat/ftdi_sio */ + enum packet_format pkt_fmt; /* format to use for packet send / receive */ int baud_rate; /* stores current baud rate in integer form */ int cbr_mask; /* stores current baud rate in masked form */ int isthrottled; /* if throttled, discard reads */ @@ -532,6 +538,17 @@ static int generic_startup (struct usb_serial *serial) priv->termios_initialized = 0; priv->rx_flags = 0; priv->cbr_mask = B300; + /* Default packet format setting is determined by packet size. + Anything with a size larger then 9 must have a separate + count field since the 3 bit count field is otherwise too + small. Otherwise we can use the slightly more compact + format. This is in accordance with the cypress_m8 serial + converter app note. */ + if (port->interrupt_out_size > 9) { + priv->pkt_fmt = packet_format_1; + } else { + priv->pkt_fmt = packet_format_2; + } if (interval > 0) { priv->write_urb_interval = interval; priv->read_urb_interval = interval; @@ -564,6 +581,9 @@ static int cypress_earthmate_startup (struct usb_serial *serial) priv = usb_get_serial_port_data(serial->port[0]); priv->chiptype = CT_EARTHMATE; + /* All Earthmate devices use the separated-count packet + format! Idiotic. */ + priv->pkt_fmt = packet_format_1; return 0; } /* cypress_earthmate_startup */ @@ -811,21 +831,18 @@ static void cypress_send(struct usb_serial_port *port) memset(port->interrupt_out_urb->transfer_buffer, 0, port->interrupt_out_size); spin_lock_irqsave(&priv->lock, flags); - switch (port->interrupt_out_size) { - case 32: - /* this is for the CY7C64013... */ - offset = 2; - port->interrupt_out_buffer[0] = priv->line_control; - break; - case 8: - /* this is for the CY7C63743... */ - offset = 1; - port->interrupt_out_buffer[0] = priv->line_control; - break; - default: - dbg("%s - wrong packet size", __FUNCTION__); - spin_unlock_irqrestore(&priv->lock, flags); - return; + switch (priv->pkt_fmt) { + default: + case packet_format_1: + /* this is for the CY7C64013... */ + offset = 2; + port->interrupt_out_buffer[0] = priv->line_control; + break; + case packet_format_2: + /* this is for the CY7C63743... */ + offset = 1; + port->interrupt_out_buffer[0] = priv->line_control; + break; } if (priv->line_control & CONTROL_RESET) @@ -846,12 +863,13 @@ static void cypress_send(struct usb_serial_port *port) return; } - switch (port->interrupt_out_size) { - case 32: - port->interrupt_out_buffer[1] = count; - break; - case 8: - port->interrupt_out_buffer[0] |= count; + switch (priv->pkt_fmt) { + default: + case packet_format_1: + port->interrupt_out_buffer[1] = count; + break; + case packet_format_2: + port->interrupt_out_buffer[0] |= count; } dbg("%s - count is %d", __FUNCTION__, count); @@ -864,8 +882,9 @@ send: if (priv->cmd_ctrl) actual_size = 1; else - actual_size = count + (port->interrupt_out_size == 32 ? 2 : 1); - + actual_size = count + + (priv->pkt_fmt == packet_format_1 ? 2 : 1); + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, port->interrupt_out_size, port->interrupt_out_urb->transfer_buffer); @@ -1331,30 +1350,32 @@ static void cypress_read_int_callback(struct urb *urb) } spin_lock_irqsave(&priv->lock, flags); - switch(urb->actual_length) { - case 32: - /* This is for the CY7C64013... */ - priv->current_status = data[0] & 0xF8; - bytes = data[1] + 2; - i = 2; - if (bytes > 2) - havedata = 1; - break; - case 8: - /* This is for the CY7C63743... */ - priv->current_status = data[0] & 0xF8; - bytes = (data[0] & 0x07) + 1; - i = 1; - if (bytes > 1) - havedata = 1; - break; - default: - dbg("%s - wrong packet size - received %d bytes", - __FUNCTION__, urb->actual_length); - spin_unlock_irqrestore(&priv->lock, flags); - goto continue_read; + result = urb->actual_length; + switch (priv->pkt_fmt) { + default: + case packet_format_1: + /* This is for the CY7C64013... */ + priv->current_status = data[0] & 0xF8; + bytes = data[1] + 2; + i = 2; + if (bytes > 2) + havedata = 1; + break; + case packet_format_2: + /* This is for the CY7C63743... */ + priv->current_status = data[0] & 0xF8; + bytes = (data[0] & 0x07) + 1; + i = 1; + if (bytes > 1) + havedata = 1; + break; } spin_unlock_irqrestore(&priv->lock, flags); + if (result < bytes) { + dbg("%s - wrong packet size - received %d bytes but packet " + "said %d bytes", __func__, result, bytes); + goto continue_read; + } usb_serial_debug_data (debug, &port->dev, __FUNCTION__, urb->actual_length, data); -- 2.39.5