diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_usb.c | 125 | ||||
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_usb.h | 5 |
2 files changed, 109 insertions, 21 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 621c2cc9fc85..cf0d69dd7be5 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c | |||
@@ -111,6 +111,9 @@ MODULE_DEVICE_TABLE(usb, usb_ids); | |||
111 | #define FW_ZD1211_PREFIX "zd1211/zd1211_" | 111 | #define FW_ZD1211_PREFIX "zd1211/zd1211_" |
112 | #define FW_ZD1211B_PREFIX "zd1211/zd1211b_" | 112 | #define FW_ZD1211B_PREFIX "zd1211/zd1211b_" |
113 | 113 | ||
114 | static bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req, | ||
115 | unsigned int count); | ||
116 | |||
114 | /* USB device initialization */ | 117 | /* USB device initialization */ |
115 | static void int_urb_complete(struct urb *urb); | 118 | static void int_urb_complete(struct urb *urb); |
116 | 119 | ||
@@ -365,6 +368,20 @@ exit: | |||
365 | 368 | ||
366 | #define urb_dev(urb) (&(urb)->dev->dev) | 369 | #define urb_dev(urb) (&(urb)->dev->dev) |
367 | 370 | ||
371 | static inline void handle_regs_int_override(struct urb *urb) | ||
372 | { | ||
373 | struct zd_usb *usb = urb->context; | ||
374 | struct zd_usb_interrupt *intr = &usb->intr; | ||
375 | |||
376 | spin_lock(&intr->lock); | ||
377 | if (atomic_read(&intr->read_regs_enabled)) { | ||
378 | atomic_set(&intr->read_regs_enabled, 0); | ||
379 | intr->read_regs_int_overridden = 1; | ||
380 | complete(&intr->read_regs.completion); | ||
381 | } | ||
382 | spin_unlock(&intr->lock); | ||
383 | } | ||
384 | |||
368 | static inline void handle_regs_int(struct urb *urb) | 385 | static inline void handle_regs_int(struct urb *urb) |
369 | { | 386 | { |
370 | struct zd_usb *usb = urb->context; | 387 | struct zd_usb *usb = urb->context; |
@@ -383,25 +400,45 @@ static inline void handle_regs_int(struct urb *urb) | |||
383 | USB_MAX_EP_INT_BUFFER); | 400 | USB_MAX_EP_INT_BUFFER); |
384 | spin_unlock(&mac->lock); | 401 | spin_unlock(&mac->lock); |
385 | schedule_work(&mac->process_intr); | 402 | schedule_work(&mac->process_intr); |
386 | } else if (intr->read_regs_enabled) { | 403 | } else if (atomic_read(&intr->read_regs_enabled)) { |
387 | intr->read_regs.length = len = urb->actual_length; | 404 | len = urb->actual_length; |
388 | 405 | intr->read_regs.length = urb->actual_length; | |
389 | if (len > sizeof(intr->read_regs.buffer)) | 406 | if (len > sizeof(intr->read_regs.buffer)) |
390 | len = sizeof(intr->read_regs.buffer); | 407 | len = sizeof(intr->read_regs.buffer); |
408 | |||
391 | memcpy(intr->read_regs.buffer, urb->transfer_buffer, len); | 409 | memcpy(intr->read_regs.buffer, urb->transfer_buffer, len); |
392 | intr->read_regs_enabled = 0; | 410 | |
411 | /* Sometimes USB_INT_ID_REGS is not overridden, but comes after | ||
412 | * USB_INT_ID_RETRY_FAILED. Read-reg retry then gets this | ||
413 | * delayed USB_INT_ID_REGS, but leaves USB_INT_ID_REGS of | ||
414 | * retry unhandled. Next read-reg command then might catch | ||
415 | * this wrong USB_INT_ID_REGS. Fix by ignoring wrong reads. | ||
416 | */ | ||
417 | if (!check_read_regs(usb, intr->read_regs.req, | ||
418 | intr->read_regs.req_count)) | ||
419 | goto out; | ||
420 | |||
421 | atomic_set(&intr->read_regs_enabled, 0); | ||
422 | intr->read_regs_int_overridden = 0; | ||
393 | complete(&intr->read_regs.completion); | 423 | complete(&intr->read_regs.completion); |
424 | |||
394 | goto out; | 425 | goto out; |
395 | } | 426 | } |
396 | 427 | ||
397 | out: | 428 | out: |
398 | spin_unlock(&intr->lock); | 429 | spin_unlock(&intr->lock); |
430 | |||
431 | /* CR_INTERRUPT might override read_reg too. */ | ||
432 | if (int_num == CR_INTERRUPT && atomic_read(&intr->read_regs_enabled)) | ||
433 | handle_regs_int_override(urb); | ||
399 | } | 434 | } |
400 | 435 | ||
401 | static void int_urb_complete(struct urb *urb) | 436 | static void int_urb_complete(struct urb *urb) |
402 | { | 437 | { |
403 | int r; | 438 | int r; |
404 | struct usb_int_header *hdr; | 439 | struct usb_int_header *hdr; |
440 | struct zd_usb *usb; | ||
441 | struct zd_usb_interrupt *intr; | ||
405 | 442 | ||
406 | switch (urb->status) { | 443 | switch (urb->status) { |
407 | case 0: | 444 | case 0: |
@@ -430,6 +467,14 @@ static void int_urb_complete(struct urb *urb) | |||
430 | goto resubmit; | 467 | goto resubmit; |
431 | } | 468 | } |
432 | 469 | ||
470 | /* USB_INT_ID_RETRY_FAILED triggered by tx-urb submit can override | ||
471 | * pending USB_INT_ID_REGS causing read command timeout. | ||
472 | */ | ||
473 | usb = urb->context; | ||
474 | intr = &usb->intr; | ||
475 | if (hdr->id != USB_INT_ID_REGS && atomic_read(&intr->read_regs_enabled)) | ||
476 | handle_regs_int_override(urb); | ||
477 | |||
433 | switch (hdr->id) { | 478 | switch (hdr->id) { |
434 | case USB_INT_ID_REGS: | 479 | case USB_INT_ID_REGS: |
435 | handle_regs_int(urb); | 480 | handle_regs_int(urb); |
@@ -1129,6 +1174,7 @@ static inline void init_usb_interrupt(struct zd_usb *usb) | |||
1129 | spin_lock_init(&intr->lock); | 1174 | spin_lock_init(&intr->lock); |
1130 | intr->interval = int_urb_interval(zd_usb_to_usbdev(usb)); | 1175 | intr->interval = int_urb_interval(zd_usb_to_usbdev(usb)); |
1131 | init_completion(&intr->read_regs.completion); | 1176 | init_completion(&intr->read_regs.completion); |
1177 | atomic_set(&intr->read_regs_enabled, 0); | ||
1132 | intr->read_regs.cr_int_addr = cpu_to_le16((u16)CR_INTERRUPT); | 1178 | intr->read_regs.cr_int_addr = cpu_to_le16((u16)CR_INTERRUPT); |
1133 | } | 1179 | } |
1134 | 1180 | ||
@@ -1563,12 +1609,16 @@ static int usb_int_regs_length(unsigned int count) | |||
1563 | return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data); | 1609 | return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data); |
1564 | } | 1610 | } |
1565 | 1611 | ||
1566 | static void prepare_read_regs_int(struct zd_usb *usb) | 1612 | static void prepare_read_regs_int(struct zd_usb *usb, |
1613 | struct usb_req_read_regs *req, | ||
1614 | unsigned int count) | ||
1567 | { | 1615 | { |
1568 | struct zd_usb_interrupt *intr = &usb->intr; | 1616 | struct zd_usb_interrupt *intr = &usb->intr; |
1569 | 1617 | ||
1570 | spin_lock_irq(&intr->lock); | 1618 | spin_lock_irq(&intr->lock); |
1571 | intr->read_regs_enabled = 1; | 1619 | atomic_set(&intr->read_regs_enabled, 1); |
1620 | intr->read_regs.req = req; | ||
1621 | intr->read_regs.req_count = count; | ||
1572 | INIT_COMPLETION(intr->read_regs.completion); | 1622 | INIT_COMPLETION(intr->read_regs.completion); |
1573 | spin_unlock_irq(&intr->lock); | 1623 | spin_unlock_irq(&intr->lock); |
1574 | } | 1624 | } |
@@ -1578,22 +1628,18 @@ static void disable_read_regs_int(struct zd_usb *usb) | |||
1578 | struct zd_usb_interrupt *intr = &usb->intr; | 1628 | struct zd_usb_interrupt *intr = &usb->intr; |
1579 | 1629 | ||
1580 | spin_lock_irq(&intr->lock); | 1630 | spin_lock_irq(&intr->lock); |
1581 | intr->read_regs_enabled = 0; | 1631 | atomic_set(&intr->read_regs_enabled, 0); |
1582 | spin_unlock_irq(&intr->lock); | 1632 | spin_unlock_irq(&intr->lock); |
1583 | } | 1633 | } |
1584 | 1634 | ||
1585 | static int get_results(struct zd_usb *usb, u16 *values, | 1635 | static bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req, |
1586 | struct usb_req_read_regs *req, unsigned int count) | 1636 | unsigned int count) |
1587 | { | 1637 | { |
1588 | int r; | ||
1589 | int i; | 1638 | int i; |
1590 | struct zd_usb_interrupt *intr = &usb->intr; | 1639 | struct zd_usb_interrupt *intr = &usb->intr; |
1591 | struct read_regs_int *rr = &intr->read_regs; | 1640 | struct read_regs_int *rr = &intr->read_regs; |
1592 | struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; | 1641 | struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; |
1593 | 1642 | ||
1594 | spin_lock_irq(&intr->lock); | ||
1595 | |||
1596 | r = -EIO; | ||
1597 | /* The created block size seems to be larger than expected. | 1643 | /* The created block size seems to be larger than expected. |
1598 | * However results appear to be correct. | 1644 | * However results appear to be correct. |
1599 | */ | 1645 | */ |
@@ -1601,13 +1647,14 @@ static int get_results(struct zd_usb *usb, u16 *values, | |||
1601 | dev_dbg_f(zd_usb_dev(usb), | 1647 | dev_dbg_f(zd_usb_dev(usb), |
1602 | "error: actual length %d less than expected %d\n", | 1648 | "error: actual length %d less than expected %d\n", |
1603 | rr->length, usb_int_regs_length(count)); | 1649 | rr->length, usb_int_regs_length(count)); |
1604 | goto error_unlock; | 1650 | return false; |
1605 | } | 1651 | } |
1652 | |||
1606 | if (rr->length > sizeof(rr->buffer)) { | 1653 | if (rr->length > sizeof(rr->buffer)) { |
1607 | dev_dbg_f(zd_usb_dev(usb), | 1654 | dev_dbg_f(zd_usb_dev(usb), |
1608 | "error: actual length %d exceeds buffer size %zu\n", | 1655 | "error: actual length %d exceeds buffer size %zu\n", |
1609 | rr->length, sizeof(rr->buffer)); | 1656 | rr->length, sizeof(rr->buffer)); |
1610 | goto error_unlock; | 1657 | return false; |
1611 | } | 1658 | } |
1612 | 1659 | ||
1613 | for (i = 0; i < count; i++) { | 1660 | for (i = 0; i < count; i++) { |
@@ -1617,8 +1664,39 @@ static int get_results(struct zd_usb *usb, u16 *values, | |||
1617 | "rd[%d] addr %#06hx expected %#06hx\n", i, | 1664 | "rd[%d] addr %#06hx expected %#06hx\n", i, |
1618 | le16_to_cpu(rd->addr), | 1665 | le16_to_cpu(rd->addr), |
1619 | le16_to_cpu(req->addr[i])); | 1666 | le16_to_cpu(req->addr[i])); |
1620 | goto error_unlock; | 1667 | return false; |
1621 | } | 1668 | } |
1669 | } | ||
1670 | |||
1671 | return true; | ||
1672 | } | ||
1673 | |||
1674 | static int get_results(struct zd_usb *usb, u16 *values, | ||
1675 | struct usb_req_read_regs *req, unsigned int count, | ||
1676 | bool *retry) | ||
1677 | { | ||
1678 | int r; | ||
1679 | int i; | ||
1680 | struct zd_usb_interrupt *intr = &usb->intr; | ||
1681 | struct read_regs_int *rr = &intr->read_regs; | ||
1682 | struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; | ||
1683 | |||
1684 | spin_lock_irq(&intr->lock); | ||
1685 | |||
1686 | r = -EIO; | ||
1687 | |||
1688 | /* Read failed because firmware bug? */ | ||
1689 | *retry = !!intr->read_regs_int_overridden; | ||
1690 | if (*retry) | ||
1691 | goto error_unlock; | ||
1692 | |||
1693 | if (!check_read_regs(usb, req, count)) { | ||
1694 | dev_dbg_f(zd_usb_dev(usb), "error: invalid read regs\n"); | ||
1695 | goto error_unlock; | ||
1696 | } | ||
1697 | |||
1698 | for (i = 0; i < count; i++) { | ||
1699 | struct reg_data *rd = ®s->regs[i]; | ||
1622 | values[i] = le16_to_cpu(rd->value); | 1700 | values[i] = le16_to_cpu(rd->value); |
1623 | } | 1701 | } |
1624 | 1702 | ||
@@ -1631,11 +1709,11 @@ error_unlock: | |||
1631 | int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, | 1709 | int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, |
1632 | const zd_addr_t *addresses, unsigned int count) | 1710 | const zd_addr_t *addresses, unsigned int count) |
1633 | { | 1711 | { |
1634 | int r; | 1712 | int r, i, req_len, actual_req_len, try_count = 0; |
1635 | int i, req_len, actual_req_len; | ||
1636 | struct usb_device *udev; | 1713 | struct usb_device *udev; |
1637 | struct usb_req_read_regs *req = NULL; | 1714 | struct usb_req_read_regs *req = NULL; |
1638 | unsigned long timeout; | 1715 | unsigned long timeout; |
1716 | bool retry = false; | ||
1639 | 1717 | ||
1640 | if (count < 1) { | 1718 | if (count < 1) { |
1641 | dev_dbg_f(zd_usb_dev(usb), "error: count is zero\n"); | 1719 | dev_dbg_f(zd_usb_dev(usb), "error: count is zero\n"); |
@@ -1671,8 +1749,10 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, | |||
1671 | for (i = 0; i < count; i++) | 1749 | for (i = 0; i < count; i++) |
1672 | req->addr[i] = cpu_to_le16((u16)addresses[i]); | 1750 | req->addr[i] = cpu_to_le16((u16)addresses[i]); |
1673 | 1751 | ||
1752 | retry_read: | ||
1753 | try_count++; | ||
1674 | udev = zd_usb_to_usbdev(usb); | 1754 | udev = zd_usb_to_usbdev(usb); |
1675 | prepare_read_regs_int(usb); | 1755 | prepare_read_regs_int(usb, req, count); |
1676 | r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/); | 1756 | r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/); |
1677 | if (r) { | 1757 | if (r) { |
1678 | dev_dbg_f(zd_usb_dev(usb), | 1758 | dev_dbg_f(zd_usb_dev(usb), |
@@ -1696,7 +1776,12 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, | |||
1696 | goto error; | 1776 | goto error; |
1697 | } | 1777 | } |
1698 | 1778 | ||
1699 | r = get_results(usb, values, req, count); | 1779 | r = get_results(usb, values, req, count, &retry); |
1780 | if (retry && try_count < 20) { | ||
1781 | dev_dbg_f(zd_usb_dev(usb), "read retry, tries so far: %d\n", | ||
1782 | try_count); | ||
1783 | goto retry_read; | ||
1784 | } | ||
1700 | error: | 1785 | error: |
1701 | return r; | 1786 | return r; |
1702 | } | 1787 | } |
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h index bf942843b733..99193b456a79 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.h +++ b/drivers/net/wireless/zd1211rw/zd_usb.h | |||
@@ -144,6 +144,8 @@ struct usb_int_retry_fail { | |||
144 | 144 | ||
145 | struct read_regs_int { | 145 | struct read_regs_int { |
146 | struct completion completion; | 146 | struct completion completion; |
147 | struct usb_req_read_regs *req; | ||
148 | unsigned int req_count; | ||
147 | /* Stores the USB int structure and contains the USB address of the | 149 | /* Stores the USB int structure and contains the USB address of the |
148 | * first requested register before request. | 150 | * first requested register before request. |
149 | */ | 151 | */ |
@@ -169,7 +171,8 @@ struct zd_usb_interrupt { | |||
169 | void *buffer; | 171 | void *buffer; |
170 | dma_addr_t buffer_dma; | 172 | dma_addr_t buffer_dma; |
171 | int interval; | 173 | int interval; |
172 | u8 read_regs_enabled:1; | 174 | atomic_t read_regs_enabled; |
175 | u8 read_regs_int_overridden:1; | ||
173 | }; | 176 | }; |
174 | 177 | ||
175 | static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr) | 178 | static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr) |