diff options
author | Brian Cavagnolo <brian@cozybit.com> | 2008-07-21 14:02:46 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-08-22 16:29:49 -0400 |
commit | 1556c0f22df77800d2e99342ce354a4ce94c5a0f (patch) | |
tree | 76c8af4251f78f1afc4504135c9d355ca15d4127 /drivers/net/wireless/libertas/if_usb.c | |
parent | 38e3b0d86eaa0bf90a74677b6d6c442ec66daa0c (diff) |
libertas: support boot commands to write persistent firmware and bootloader
Add locking and non-locking versions of if_usb_prog_firmware to support
programming firmware after and before driver bring-up respectively. Add more
suitable error codes for firmware programming process. Add capability checks
for persistent features before attempting to use them.
Based on patches from Brajesh Dave and Priyank Singh.
Signed-off-by: Brian Cavagnolo <brian@cozybit.com>
Acked-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas/if_usb.c')
-rw-r--r-- | drivers/net/wireless/libertas/if_usb.c | 112 |
1 files changed, 92 insertions, 20 deletions
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index 632c291404ab..b5013ce31b9a 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c | |||
@@ -39,7 +39,10 @@ MODULE_DEVICE_TABLE(usb, if_usb_table); | |||
39 | 39 | ||
40 | static void if_usb_receive(struct urb *urb); | 40 | static void if_usb_receive(struct urb *urb); |
41 | static void if_usb_receive_fwload(struct urb *urb); | 41 | static void if_usb_receive_fwload(struct urb *urb); |
42 | static int if_usb_prog_firmware(struct if_usb_card *cardp); | 42 | static int __if_usb_prog_firmware(struct if_usb_card *cardp, |
43 | const char *fwname, int cmd); | ||
44 | static int if_usb_prog_firmware(struct if_usb_card *cardp, | ||
45 | const char *fwname, int cmd); | ||
43 | static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, | 46 | static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, |
44 | uint8_t *payload, uint16_t nb); | 47 | uint8_t *payload, uint16_t nb); |
45 | static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, | 48 | static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, |
@@ -66,10 +69,10 @@ static void if_usb_write_bulk_callback(struct urb *urb) | |||
66 | lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", | 69 | lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", |
67 | urb->actual_length); | 70 | urb->actual_length); |
68 | 71 | ||
69 | /* Used for both firmware TX and regular TX. priv isn't | 72 | /* Boot commands such as UPDATE_FW and UPDATE_BOOT2 are not |
70 | * valid at firmware load time. | 73 | * passed up to the lbs level. |
71 | */ | 74 | */ |
72 | if (priv) | 75 | if (priv && priv->dnld_sent != DNLD_BOOTCMD_SENT) |
73 | lbs_host_to_card_done(priv); | 76 | lbs_host_to_card_done(priv); |
74 | } else { | 77 | } else { |
75 | /* print the failure status number for debug */ | 78 | /* print the failure status number for debug */ |
@@ -231,7 +234,7 @@ static int if_usb_probe(struct usb_interface *intf, | |||
231 | } | 234 | } |
232 | 235 | ||
233 | /* Upload firmware */ | 236 | /* Upload firmware */ |
234 | if (if_usb_prog_firmware(cardp)) { | 237 | if (__if_usb_prog_firmware(cardp, lbs_fw_name, BOOT_CMD_FW_BY_USB)) { |
235 | lbs_deb_usbd(&udev->dev, "FW upload failed\n"); | 238 | lbs_deb_usbd(&udev->dev, "FW upload failed\n"); |
236 | goto err_prog_firmware; | 239 | goto err_prog_firmware; |
237 | } | 240 | } |
@@ -510,7 +513,7 @@ static void if_usb_receive_fwload(struct urb *urb) | |||
510 | if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { | 513 | if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { |
511 | kfree_skb(skb); | 514 | kfree_skb(skb); |
512 | if_usb_submit_rx_urb_fwload(cardp); | 515 | if_usb_submit_rx_urb_fwload(cardp); |
513 | cardp->bootcmdresp = 1; | 516 | cardp->bootcmdresp = BOOT_CMD_RESP_OK; |
514 | lbs_deb_usbd(&cardp->udev->dev, | 517 | lbs_deb_usbd(&cardp->udev->dev, |
515 | "Received valid boot command response\n"); | 518 | "Received valid boot command response\n"); |
516 | return; | 519 | return; |
@@ -526,7 +529,9 @@ static void if_usb_receive_fwload(struct urb *urb) | |||
526 | lbs_pr_info("boot cmd response wrong magic number (0x%x)\n", | 529 | lbs_pr_info("boot cmd response wrong magic number (0x%x)\n", |
527 | le32_to_cpu(bootcmdresp.magic)); | 530 | le32_to_cpu(bootcmdresp.magic)); |
528 | } | 531 | } |
529 | } else if (bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) { | 532 | } else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) && |
533 | (bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) && | ||
534 | (bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) { | ||
530 | lbs_pr_info("boot cmd response cmd_tag error (%d)\n", | 535 | lbs_pr_info("boot cmd response cmd_tag error (%d)\n", |
531 | bootcmdresp.cmd); | 536 | bootcmdresp.cmd); |
532 | } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) { | 537 | } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) { |
@@ -564,8 +569,8 @@ static void if_usb_receive_fwload(struct urb *urb) | |||
564 | 569 | ||
565 | kfree_skb(skb); | 570 | kfree_skb(skb); |
566 | 571 | ||
567 | /* reschedule timer for 200ms hence */ | 572 | /* Give device 5s to either write firmware to its RAM or eeprom */ |
568 | mod_timer(&cardp->fw_timeout, jiffies + (HZ/5)); | 573 | mod_timer(&cardp->fw_timeout, jiffies + (HZ*5)); |
569 | 574 | ||
570 | if (cardp->fwfinalblk) { | 575 | if (cardp->fwfinalblk) { |
571 | cardp->fwdnldover = 1; | 576 | cardp->fwdnldover = 1; |
@@ -809,7 +814,54 @@ static int check_fwfile_format(const uint8_t *data, uint32_t totlen) | |||
809 | } | 814 | } |
810 | 815 | ||
811 | 816 | ||
812 | static int if_usb_prog_firmware(struct if_usb_card *cardp) | 817 | /** |
818 | * @brief This function programs the firmware subject to cmd | ||
819 | * | ||
820 | * @param cardp the if_usb_card descriptor | ||
821 | * fwname firmware or boot2 image file name | ||
822 | * cmd either BOOT_CMD_FW_BY_USB, BOOT_CMD_UPDATE_FW, | ||
823 | * or BOOT_CMD_UPDATE_BOOT2. | ||
824 | * @return 0 or error code | ||
825 | */ | ||
826 | static int if_usb_prog_firmware(struct if_usb_card *cardp, | ||
827 | const char *fwname, int cmd) | ||
828 | { | ||
829 | struct lbs_private *priv = cardp->priv; | ||
830 | unsigned long flags, caps; | ||
831 | int ret; | ||
832 | |||
833 | caps = priv->fwcapinfo; | ||
834 | if (((cmd == BOOT_CMD_UPDATE_FW) && !(caps & FW_CAPINFO_FIRMWARE_UPGRADE)) || | ||
835 | ((cmd == BOOT_CMD_UPDATE_BOOT2) && !(caps & FW_CAPINFO_BOOT2_UPGRADE))) | ||
836 | return -EOPNOTSUPP; | ||
837 | |||
838 | /* Ensure main thread is idle. */ | ||
839 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
840 | while (priv->cur_cmd != NULL || priv->dnld_sent != DNLD_RES_RECEIVED) { | ||
841 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
842 | if (wait_event_interruptible(priv->waitq, | ||
843 | (priv->cur_cmd == NULL && | ||
844 | priv->dnld_sent == DNLD_RES_RECEIVED))) { | ||
845 | return -ERESTARTSYS; | ||
846 | } | ||
847 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
848 | } | ||
849 | priv->dnld_sent = DNLD_BOOTCMD_SENT; | ||
850 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
851 | |||
852 | ret = __if_usb_prog_firmware(cardp, fwname, cmd); | ||
853 | |||
854 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
855 | priv->dnld_sent = DNLD_RES_RECEIVED; | ||
856 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
857 | |||
858 | wake_up_interruptible(&priv->waitq); | ||
859 | |||
860 | return ret; | ||
861 | } | ||
862 | |||
863 | static int __if_usb_prog_firmware(struct if_usb_card *cardp, | ||
864 | const char *fwname, int cmd) | ||
813 | { | 865 | { |
814 | int i = 0; | 866 | int i = 0; |
815 | static int reset_count = 10; | 867 | static int reset_count = 10; |
@@ -817,20 +869,32 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp) | |||
817 | 869 | ||
818 | lbs_deb_enter(LBS_DEB_USB); | 870 | lbs_deb_enter(LBS_DEB_USB); |
819 | 871 | ||
820 | if ((ret = request_firmware(&cardp->fw, lbs_fw_name, | 872 | ret = request_firmware(&cardp->fw, fwname, &cardp->udev->dev); |
821 | &cardp->udev->dev)) < 0) { | 873 | if (ret < 0) { |
822 | lbs_pr_err("request_firmware() failed with %#x\n", ret); | 874 | lbs_pr_err("request_firmware() failed with %#x\n", ret); |
823 | lbs_pr_err("firmware %s not found\n", lbs_fw_name); | 875 | lbs_pr_err("firmware %s not found\n", fwname); |
824 | goto done; | 876 | goto done; |
825 | } | 877 | } |
826 | 878 | ||
827 | if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) | 879 | if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) { |
880 | ret = -EINVAL; | ||
828 | goto release_fw; | 881 | goto release_fw; |
882 | } | ||
883 | |||
884 | /* Cancel any pending usb business */ | ||
885 | usb_kill_urb(cardp->rx_urb); | ||
886 | usb_kill_urb(cardp->tx_urb); | ||
887 | |||
888 | cardp->fwlastblksent = 0; | ||
889 | cardp->fwdnldover = 0; | ||
890 | cardp->totalbytes = 0; | ||
891 | cardp->fwfinalblk = 0; | ||
892 | cardp->bootcmdresp = 0; | ||
829 | 893 | ||
830 | restart: | 894 | restart: |
831 | if (if_usb_submit_rx_urb_fwload(cardp) < 0) { | 895 | if (if_usb_submit_rx_urb_fwload(cardp) < 0) { |
832 | lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); | 896 | lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); |
833 | ret = -1; | 897 | ret = -EIO; |
834 | goto release_fw; | 898 | goto release_fw; |
835 | } | 899 | } |
836 | 900 | ||
@@ -838,8 +902,7 @@ restart: | |||
838 | do { | 902 | do { |
839 | int j = 0; | 903 | int j = 0; |
840 | i++; | 904 | i++; |
841 | /* Issue Boot command = 1, Boot from Download-FW */ | 905 | if_usb_issue_boot_command(cardp, cmd); |
842 | if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); | ||
843 | /* wait for command response */ | 906 | /* wait for command response */ |
844 | do { | 907 | do { |
845 | j++; | 908 | j++; |
@@ -847,12 +910,21 @@ restart: | |||
847 | } while (cardp->bootcmdresp == 0 && j < 10); | 910 | } while (cardp->bootcmdresp == 0 && j < 10); |
848 | } while (cardp->bootcmdresp == 0 && i < 5); | 911 | } while (cardp->bootcmdresp == 0 && i < 5); |
849 | 912 | ||
850 | if (cardp->bootcmdresp <= 0) { | 913 | if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) { |
914 | /* Return to normal operation */ | ||
915 | ret = -EOPNOTSUPP; | ||
916 | usb_kill_urb(cardp->rx_urb); | ||
917 | usb_kill_urb(cardp->tx_urb); | ||
918 | if (if_usb_submit_rx_urb(cardp) < 0) | ||
919 | ret = -EIO; | ||
920 | goto release_fw; | ||
921 | } else if (cardp->bootcmdresp <= 0) { | ||
851 | if (--reset_count >= 0) { | 922 | if (--reset_count >= 0) { |
852 | if_usb_reset_device(cardp); | 923 | if_usb_reset_device(cardp); |
853 | goto restart; | 924 | goto restart; |
854 | } | 925 | } |
855 | return -1; | 926 | ret = -EIO; |
927 | goto release_fw; | ||
856 | } | 928 | } |
857 | 929 | ||
858 | i = 0; | 930 | i = 0; |
@@ -882,7 +954,7 @@ restart: | |||
882 | } | 954 | } |
883 | 955 | ||
884 | lbs_pr_info("FW download failure, time = %d ms\n", i * 100); | 956 | lbs_pr_info("FW download failure, time = %d ms\n", i * 100); |
885 | ret = -1; | 957 | ret = -EIO; |
886 | goto release_fw; | 958 | goto release_fw; |
887 | } | 959 | } |
888 | 960 | ||