Commit 59342f6a authored by Jussi Kivilinna's avatar Jussi Kivilinna Committed by John W. Linville

zd1211rw: fix to work on OHCI

zd1211 devices register 'EP 4 OUT' endpoint as Interrupt type on USB 2.0:

      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x04  EP 4 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1

However on USB 1.1 endpoint becomes Bulk:

      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x04  EP 4 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0

Commit 37939810 assumed that endpoint is
always interrupt type and changed usb_bulk_msg() calls to usb_interrupt_msg().

Problem here is that usb_bulk_msg() on interrupt endpoint selfcorrects the
call and changes requested pipe to interrupt type (see usb_bulk_msg).
However with usb_interrupt_msg() on bulk endpoint does not correct the
pipe type to bulk, but instead URB is submitted with interrupt type pipe.

So pre-2.6.39 used usb_bulk_msg() and therefore worked with both endpoint
types, however in 2.6.39 usb_interrupt_msg() with bulk endpoint causes
ohci_hcd to fail submitted URB instantly with -ENOSPC and preventing zd1211rw
from working with OHCI.

Fix this by detecting endpoint type and using correct endpoint/pipe types
for URB. Also fix asynchronous zd_usb_iowrite16v_async() to use right
URB type on 'EP 4 OUT'.

Cc: stable@kernel.org
Signed-off-by: default avatarJussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 66870b1c
...@@ -1533,6 +1533,31 @@ static void __exit usb_exit(void) ...@@ -1533,6 +1533,31 @@ static void __exit usb_exit(void)
module_init(usb_init); module_init(usb_init);
module_exit(usb_exit); module_exit(usb_exit);
static int zd_ep_regs_out_msg(struct usb_device *udev, void *data, int len,
int *actual_length, int timeout)
{
/* In USB 2.0 mode EP_REGS_OUT endpoint is interrupt type. However in
* USB 1.1 mode endpoint is bulk. Select correct type URB by endpoint
* descriptor.
*/
struct usb_host_endpoint *ep;
unsigned int pipe;
pipe = usb_sndintpipe(udev, EP_REGS_OUT);
ep = usb_pipe_endpoint(udev, pipe);
if (!ep)
return -EINVAL;
if (usb_endpoint_xfer_int(&ep->desc)) {
return usb_interrupt_msg(udev, pipe, data, len,
actual_length, timeout);
} else {
pipe = usb_sndbulkpipe(udev, EP_REGS_OUT);
return usb_bulk_msg(udev, pipe, data, len, actual_length,
timeout);
}
}
static int usb_int_regs_length(unsigned int count) static int usb_int_regs_length(unsigned int count)
{ {
return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data); return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data);
...@@ -1648,15 +1673,14 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, ...@@ -1648,15 +1673,14 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
udev = zd_usb_to_usbdev(usb); udev = zd_usb_to_usbdev(usb);
prepare_read_regs_int(usb); prepare_read_regs_int(usb);
r = usb_interrupt_msg(udev, usb_sndintpipe(udev, EP_REGS_OUT), r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/);
req, req_len, &actual_req_len, 50 /* ms */);
if (r) { if (r) {
dev_dbg_f(zd_usb_dev(usb), dev_dbg_f(zd_usb_dev(usb),
"error in usb_interrupt_msg(). Error number %d\n", r); "error in zd_ep_regs_out_msg(). Error number %d\n", r);
goto error; goto error;
} }
if (req_len != actual_req_len) { if (req_len != actual_req_len) {
dev_dbg_f(zd_usb_dev(usb), "error in usb_interrupt_msg()\n" dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg()\n"
" req_len %d != actual_req_len %d\n", " req_len %d != actual_req_len %d\n",
req_len, actual_req_len); req_len, actual_req_len);
r = -EIO; r = -EIO;
...@@ -1818,9 +1842,17 @@ int zd_usb_iowrite16v_async(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, ...@@ -1818,9 +1842,17 @@ int zd_usb_iowrite16v_async(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
rw->value = cpu_to_le16(ioreqs[i].value); rw->value = cpu_to_le16(ioreqs[i].value);
} }
/* In USB 2.0 mode endpoint is interrupt type. However in USB 1.1 mode
* endpoint is bulk. Select correct type URB by endpoint descriptor.
*/
if (usb_endpoint_xfer_int(&ep->desc))
usb_fill_int_urb(urb, udev, usb_sndintpipe(udev, EP_REGS_OUT), usb_fill_int_urb(urb, udev, usb_sndintpipe(udev, EP_REGS_OUT),
req, req_len, iowrite16v_urb_complete, usb, req, req_len, iowrite16v_urb_complete, usb,
ep->desc.bInterval); ep->desc.bInterval);
else
usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_REGS_OUT),
req, req_len, iowrite16v_urb_complete, usb);
urb->transfer_flags |= URB_FREE_BUFFER; urb->transfer_flags |= URB_FREE_BUFFER;
/* Submit previous URB */ /* Submit previous URB */
...@@ -1924,15 +1956,14 @@ int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits) ...@@ -1924,15 +1956,14 @@ int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits)
} }
udev = zd_usb_to_usbdev(usb); udev = zd_usb_to_usbdev(usb);
r = usb_interrupt_msg(udev, usb_sndintpipe(udev, EP_REGS_OUT), r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/);
req, req_len, &actual_req_len, 50 /* ms */);
if (r) { if (r) {
dev_dbg_f(zd_usb_dev(usb), dev_dbg_f(zd_usb_dev(usb),
"error in usb_interrupt_msg(). Error number %d\n", r); "error in zd_ep_regs_out_msg(). Error number %d\n", r);
goto out; goto out;
} }
if (req_len != actual_req_len) { if (req_len != actual_req_len) {
dev_dbg_f(zd_usb_dev(usb), "error in usb_interrupt_msg()" dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg()"
" req_len %d != actual_req_len %d\n", " req_len %d != actual_req_len %d\n",
req_len, actual_req_len); req_len, actual_req_len);
r = -EIO; r = -EIO;
......
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