| From fdcba53e2d58272bcdb5f1fad694602ccf02ad46 Mon Sep 17 00:00:00 2001 |
| From: Rainer Weikusat <rainer.weikusat@sncag.com> |
| Date: Wed, 3 Jan 2007 15:36:25 +0100 |
| Subject: fix for bugzilla #7544 (keyspan USB-to-serial converter) |
| |
| At least the Keyspan USA-19HS USB-to-serial converter supports |
| two different configurations, one where the input endpoints |
| have interrupt transfer type and one where they are bulk endpoints. |
| The default UHCI configuration uses the interrupt input endpoints. |
| The keyspan driver, OTOH, assumes that the device has only bulk |
| endpoints (all URBs are initialized by calling usb_fill_bulk_urb |
| in keyspan.c/ keyspan_setup_urb). This causes the interval field |
| of the input URBs to have a value of zero instead of one, which |
| 'accidentally' worked with Linux at least up to 2.6.17.11 but |
| stopped to with 2.6.18, which changed the UHCI support code handling |
| URBs for interrupt endpoints. The patch below modifies to driver to |
| initialize its input URBs either as interrupt or as bulk URBs, |
| depending on the transfertype contained in the associated endpoint |
| descriptor (only tested with the default configuration) enabling |
| the driver to again receive data from the serial converter. |
| |
| Greg K-H reworked the patch. |
| |
| Signed-off-by: Rainer Weikusat <rweikusat@sncag.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| drivers/usb/serial/keyspan.c | 49 +++++++++++++++++++++++++++++++++++++++---- |
| 1 file changed, 45 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/usb/serial/keyspan.c |
| +++ b/drivers/usb/serial/keyspan.c |
| @@ -1275,11 +1275,31 @@ static int keyspan_fake_startup (struct |
| } |
| |
| /* Helper functions used by keyspan_setup_urbs */ |
| +static struct usb_endpoint_descriptor const *find_ep(struct usb_serial const *serial, |
| + int endpoint) |
| +{ |
| + struct usb_host_interface *iface_desc; |
| + struct usb_endpoint_descriptor *ep; |
| + int i; |
| + |
| + iface_desc = serial->interface->cur_altsetting; |
| + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { |
| + ep = &iface_desc->endpoint[i].desc; |
| + if (ep->bEndpointAddress == endpoint) |
| + return ep; |
| + } |
| + dev_warn(&serial->interface->dev, "found no endpoint descriptor for " |
| + "endpoint %x\n", endpoint); |
| + return NULL; |
| +} |
| + |
| static struct urb *keyspan_setup_urb (struct usb_serial *serial, int endpoint, |
| int dir, void *ctx, char *buf, int len, |
| void (*callback)(struct urb *)) |
| { |
| struct urb *urb; |
| + struct usb_endpoint_descriptor const *ep_desc; |
| + char const *ep_type_name; |
| |
| if (endpoint == -1) |
| return NULL; /* endpoint not needed */ |
| @@ -1291,11 +1311,32 @@ static struct urb *keyspan_setup_urb (st |
| return NULL; |
| } |
| |
| - /* Fill URB using supplied data. */ |
| - usb_fill_bulk_urb(urb, serial->dev, |
| - usb_sndbulkpipe(serial->dev, endpoint) | dir, |
| - buf, len, callback, ctx); |
| + ep_desc = find_ep(serial, endpoint); |
| + if (!ep_desc) { |
| + /* leak the urb, something's wrong and the callers don't care */ |
| + return urb; |
| + } |
| + if (usb_endpoint_xfer_int(ep_desc)) { |
| + ep_type_name = "INT"; |
| + usb_fill_int_urb(urb, serial->dev, |
| + usb_sndintpipe(serial->dev, endpoint) | dir, |
| + buf, len, callback, ctx, |
| + ep_desc->bInterval); |
| + } else if (usb_endpoint_xfer_bulk(ep_desc)) { |
| + ep_type_name = "BULK"; |
| + usb_fill_bulk_urb(urb, serial->dev, |
| + usb_sndbulkpipe(serial->dev, endpoint) | dir, |
| + buf, len, callback, ctx); |
| + } else { |
| + dev_warn(&serial->interface->dev, |
| + "unsupported endpoint type %x\n", |
| + ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); |
| + usb_free_urb(urb); |
| + return NULL; |
| + } |
| |
| + dbg("%s - using urb %p for %s endpoint %x", |
| + __func__, urb, ep_type_name, endpoint); |
| return urb; |
| } |
| |