diff options
Diffstat (limited to 'drivers/net/wireless/zd1211rw/zd_usb.c')
| -rw-r--r-- | drivers/net/wireless/zd1211rw/zd_usb.c | 220 |
1 files changed, 176 insertions, 44 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 81e80489a05..a912dc05111 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -15,8 +15,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> @@ -28,6 +27,7 @@ #include <linux/skbuff.h> #include <linux/usb.h> #include <linux/workqueue.h> +#include <linux/module.h> #include <net/mac80211.h> #include <asm/unaligned.h> @@ -60,6 +60,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x157e, 0x300a), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x157e, 0x3204), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x157e, 0x3207), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x1740, 0x2000), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, /* ZD1211B */ @@ -110,6 +111,9 @@ MODULE_DEVICE_TABLE(usb, usb_ids); #define FW_ZD1211_PREFIX "zd1211/zd1211_" #define FW_ZD1211B_PREFIX "zd1211/zd1211b_" +static bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req, + unsigned int count); + /* USB device initialization */ static void int_urb_complete(struct urb *urb); @@ -150,7 +154,6 @@ static int upload_code(struct usb_device *udev, */ p = kmalloc(MAX_TRANSFER_SIZE, GFP_KERNEL); if (!p) { - dev_err(&udev->dev, "out of memory\n"); r = -ENOMEM; goto error; } @@ -364,6 +367,20 @@ exit: #define urb_dev(urb) (&(urb)->dev->dev) +static inline void handle_regs_int_override(struct urb *urb) +{ + struct zd_usb *usb = urb->context; + struct zd_usb_interrupt *intr = &usb->intr; + + spin_lock(&intr->lock); + if (atomic_read(&intr->read_regs_enabled)) { + atomic_set(&intr->read_regs_enabled, 0); + intr->read_regs_int_overridden = 1; + complete(&intr->read_regs.completion); + } + spin_unlock(&intr->lock); +} + static inline void handle_regs_int(struct urb *urb) { struct zd_usb *usb = urb->context; @@ -382,25 +399,45 @@ static inline void handle_regs_int(struct urb *urb) USB_MAX_EP_INT_BUFFER); spin_unlock(&mac->lock); schedule_work(&mac->process_intr); - } else if (intr->read_regs_enabled) { - intr->read_regs.length = len = urb->actual_length; - + } else if (atomic_read(&intr->read_regs_enabled)) { + len = urb->actual_length; + intr->read_regs.length = urb->actual_length; if (len > sizeof(intr->read_regs.buffer)) len = sizeof(intr->read_regs.buffer); + memcpy(intr->read_regs.buffer, urb->transfer_buffer, len); - intr->read_regs_enabled = 0; + + /* Sometimes USB_INT_ID_REGS is not overridden, but comes after + * USB_INT_ID_RETRY_FAILED. Read-reg retry then gets this + * delayed USB_INT_ID_REGS, but leaves USB_INT_ID_REGS of + * retry unhandled. Next read-reg command then might catch + * this wrong USB_INT_ID_REGS. Fix by ignoring wrong reads. + */ + if (!check_read_regs(usb, intr->read_regs.req, + intr->read_regs.req_count)) + goto out; + + atomic_set(&intr->read_regs_enabled, 0); + intr->read_regs_int_overridden = 0; complete(&intr->read_regs.completion); + goto out; } out: spin_unlock(&intr->lock); + + /* CR_INTERRUPT might override read_reg too. */ + if (int_num == CR_INTERRUPT && atomic_read(&intr->read_regs_enabled)) + handle_regs_int_override(urb); } static void int_urb_complete(struct urb *urb) { int r; struct usb_int_header *hdr; + struct zd_usb *usb; + struct zd_usb_interrupt *intr; switch (urb->status) { case 0: @@ -429,6 +466,14 @@ static void int_urb_complete(struct urb *urb) goto resubmit; } + /* USB_INT_ID_RETRY_FAILED triggered by tx-urb submit can override + * pending USB_INT_ID_REGS causing read command timeout. + */ + usb = urb->context; + intr = &usb->intr; + if (hdr->id != USB_INT_ID_REGS && atomic_read(&intr->read_regs_enabled)) + handle_regs_int_override(urb); + switch (hdr->id) { case USB_INT_ID_REGS: handle_regs_int(urb); @@ -578,8 +623,8 @@ static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer, if (length < sizeof(struct rx_length_info)) { /* It's not a complete packet anyhow. */ - printk("%s: invalid, small RX packet : %d\n", - __func__, length); + dev_dbg_f(zd_usb_dev(usb), "invalid, small RX packet : %d\n", + length); return; } length_info = (struct rx_length_info *) @@ -642,7 +687,7 @@ static void rx_urb_complete(struct urb *urb) usb = urb->context; rx = &usb->rx; - zd_usb_reset_rx_idle_timer(usb); + tasklet_schedule(&rx->reset_timer_tasklet); if (length%rx->usb_packet_size > rx->usb_packet_size-4) { /* If there is an old first fragment, we don't care. */ @@ -811,6 +856,7 @@ void zd_usb_disable_rx(struct zd_usb *usb) __zd_usb_disable_rx(usb); mutex_unlock(&rx->setup_mutex); + tasklet_kill(&rx->reset_timer_tasklet); cancel_delayed_work_sync(&rx->idle_work); } @@ -1056,7 +1102,7 @@ static void zd_tx_watchdog_handler(struct work_struct *work) goto out; /* TX halted, try reset */ - dev_warn(zd_usb_dev(usb), "TX-stall detected, reseting device..."); + dev_warn(zd_usb_dev(usb), "TX-stall detected, resetting device..."); usb_queue_reset_device(usb->intf); @@ -1105,12 +1151,18 @@ static void zd_rx_idle_timer_handler(struct work_struct *work) zd_usb_reset_rx(usb); } +static void zd_usb_reset_rx_idle_timer_tasklet(unsigned long param) +{ + struct zd_usb *usb = (struct zd_usb *)param; + + zd_usb_reset_rx_idle_timer(usb); +} + void zd_usb_reset_rx_idle_timer(struct zd_usb *usb) { struct zd_usb_rx *rx = &usb->rx; - cancel_delayed_work(&rx->idle_work); - queue_delayed_work(zd_workqueue, &rx->idle_work, ZD_RX_IDLE_INTERVAL); + mod_delayed_work(zd_workqueue, &rx->idle_work, ZD_RX_IDLE_INTERVAL); } static inline void init_usb_interrupt(struct zd_usb *usb) @@ -1120,12 +1172,14 @@ static inline void init_usb_interrupt(struct zd_usb *usb) spin_lock_init(&intr->lock); intr->interval = int_urb_interval(zd_usb_to_usbdev(usb)); init_completion(&intr->read_regs.completion); + atomic_set(&intr->read_regs_enabled, 0); intr->read_regs.cr_int_addr = cpu_to_le16((u16)CR_INTERRUPT); } static inline void init_usb_rx(struct zd_usb *usb) { struct zd_usb_rx *rx = &usb->rx; + spin_lock_init(&rx->lock); mutex_init(&rx->setup_mutex); if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH) { @@ -1135,11 +1189,14 @@ static inline void init_usb_rx(struct zd_usb *usb) } ZD_ASSERT(rx->fragment_length == 0); INIT_DELAYED_WORK(&rx->idle_work, zd_rx_idle_timer_handler); + rx->reset_timer_tasklet.func = zd_usb_reset_rx_idle_timer_tasklet; + rx->reset_timer_tasklet.data = (unsigned long)usb; } static inline void init_usb_tx(struct zd_usb *usb) { struct zd_usb_tx *tx = &usb->tx; + spin_lock_init(&tx->lock); atomic_set(&tx->enabled, 0); tx->stopped = 0; @@ -1482,6 +1539,7 @@ static struct usb_driver driver = { .disconnect = disconnect, .pre_reset = pre_reset, .post_reset = post_reset, + .disable_hub_initiated_lpm = 1, }; struct workqueue_struct *zd_workqueue; @@ -1520,18 +1578,47 @@ static void __exit usb_exit(void) module_init(usb_init); 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) { return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data); } -static void prepare_read_regs_int(struct zd_usb *usb) +static void prepare_read_regs_int(struct zd_usb *usb, + struct usb_req_read_regs *req, + unsigned int count) { struct zd_usb_interrupt *intr = &usb->intr; spin_lock_irq(&intr->lock); - intr->read_regs_enabled = 1; - INIT_COMPLETION(intr->read_regs.completion); + atomic_set(&intr->read_regs_enabled, 1); + intr->read_regs.req = req; + intr->read_regs.req_count = count; + reinit_completion(&intr->read_regs.completion); spin_unlock_irq(&intr->lock); } @@ -1540,22 +1627,18 @@ static void disable_read_regs_int(struct zd_usb *usb) struct zd_usb_interrupt *intr = &usb->intr; spin_lock_irq(&intr->lock); - intr->read_regs_enabled = 0; + atomic_set(&intr->read_regs_enabled, 0); spin_unlock_irq(&intr->lock); } -static int get_results(struct zd_usb *usb, u16 *values, - struct usb_req_read_regs *req, unsigned int count) +static bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req, + unsigned int count) { - int r; int i; struct zd_usb_interrupt *intr = &usb->intr; struct read_regs_int *rr = &intr->read_regs; struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; - spin_lock_irq(&intr->lock); - - r = -EIO; /* The created block size seems to be larger than expected. * However results appear to be correct. */ @@ -1563,13 +1646,14 @@ static int get_results(struct zd_usb *usb, u16 *values, dev_dbg_f(zd_usb_dev(usb), "error: actual length %d less than expected %d\n", rr->length, usb_int_regs_length(count)); - goto error_unlock; + return false; } + if (rr->length > sizeof(rr->buffer)) { dev_dbg_f(zd_usb_dev(usb), "error: actual length %d exceeds buffer size %zu\n", rr->length, sizeof(rr->buffer)); - goto error_unlock; + return false; } for (i = 0; i < count; i++) { @@ -1579,8 +1663,39 @@ static int get_results(struct zd_usb *usb, u16 *values, "rd[%d] addr %#06hx expected %#06hx\n", i, le16_to_cpu(rd->addr), le16_to_cpu(req->addr[i])); - goto error_unlock; + return false; } + } + + return true; +} + +static int get_results(struct zd_usb *usb, u16 *values, + struct usb_req_read_regs *req, unsigned int count, + bool *retry) +{ + int r; + int i; + struct zd_usb_interrupt *intr = &usb->intr; + struct read_regs_int *rr = &intr->read_regs; + struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; + + spin_lock_irq(&intr->lock); + + r = -EIO; + + /* Read failed because firmware bug? */ + *retry = !!intr->read_regs_int_overridden; + if (*retry) + goto error_unlock; + + if (!check_read_regs(usb, req, count)) { + dev_dbg_f(zd_usb_dev(usb), "error: invalid read regs\n"); + goto error_unlock; + } + + for (i = 0; i < count; i++) { + struct reg_data *rd = ®s->regs[i]; values[i] = le16_to_cpu(rd->value); } @@ -1593,11 +1708,11 @@ error_unlock: int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, const zd_addr_t *addresses, unsigned int count) { - int r; - int i, req_len, actual_req_len; + int r, i, req_len, actual_req_len, try_count = 0; struct usb_device *udev; struct usb_req_read_regs *req = NULL; unsigned long timeout; + bool retry = false; if (count < 1) { dev_dbg_f(zd_usb_dev(usb), "error: count is zero\n"); @@ -1633,17 +1748,18 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, for (i = 0; i < count; i++) req->addr[i] = cpu_to_le16((u16)addresses[i]); +retry_read: + try_count++; udev = zd_usb_to_usbdev(usb); - prepare_read_regs_int(usb); - r = usb_interrupt_msg(udev, usb_sndintpipe(udev, EP_REGS_OUT), - req, req_len, &actual_req_len, 50 /* ms */); + prepare_read_regs_int(usb, req, count); + r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/); if (r) { 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; } 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, actual_req_len); r = -EIO; @@ -1659,7 +1775,12 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, goto error; } - r = get_results(usb, values, req, count); + r = get_results(usb, values, req, count, &retry); + if (retry && try_count < 20) { + dev_dbg_f(zd_usb_dev(usb), "read retry, tries so far: %d\n", + try_count); + goto retry_read; + } error: return r; } @@ -1670,6 +1791,10 @@ static void iowrite16v_urb_complete(struct urb *urb) if (urb->status && !usb->cmd_error) usb->cmd_error = urb->status; + + if (!usb->cmd_error && + urb->actual_length != urb->transfer_buffer_length) + usb->cmd_error = -EIO; } static int zd_submit_waiting_urb(struct zd_usb *usb, bool last) @@ -1801,10 +1926,18 @@ int zd_usb_iowrite16v_async(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, rw->value = cpu_to_le16(ioreqs[i].value); } - usb_fill_int_urb(urb, udev, usb_sndintpipe(udev, EP_REGS_OUT), - req, req_len, iowrite16v_urb_complete, usb, - ep->desc.bInterval); - urb->transfer_flags |= URB_FREE_BUFFER | URB_SHORT_NOT_OK; + /* 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), + req, req_len, iowrite16v_urb_complete, usb, + 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; /* Submit previous URB */ r = zd_submit_waiting_urb(usb, false); @@ -1876,10 +2009,10 @@ int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits) dev_dbg_f(zd_usb_dev(usb), "value %#09x bits %d\n", value, bits); - r = zd_usb_ioread16(usb, &bit_value_template, CR203); + r = zd_usb_ioread16(usb, &bit_value_template, ZD_CR203); if (r) { dev_dbg_f(zd_usb_dev(usb), - "error %d: Couldn't read CR203\n", r); + "error %d: Couldn't read ZD_CR203\n", r); return r; } bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA); @@ -1907,15 +2040,14 @@ int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits) } udev = zd_usb_to_usbdev(usb); - r = usb_interrupt_msg(udev, usb_sndintpipe(udev, EP_REGS_OUT), - req, req_len, &actual_req_len, 50 /* ms */); + r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/); if (r) { 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; } 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, actual_req_len); r = -EIO; |
