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 | |
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>
-rw-r--r-- | drivers/net/wireless/libertas/cmd.c | 21 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/defs.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/if_usb.c | 112 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/if_usb.h | 5 |
4 files changed, 119 insertions, 25 deletions
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 75427e61898d..af5fd709887f 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c | |||
@@ -1033,9 +1033,9 @@ int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, | |||
1033 | return ret; | 1033 | return ret; |
1034 | } | 1034 | } |
1035 | 1035 | ||
1036 | int lbs_mesh_config_send(struct lbs_private *priv, | 1036 | static int __lbs_mesh_config_send(struct lbs_private *priv, |
1037 | struct cmd_ds_mesh_config *cmd, | 1037 | struct cmd_ds_mesh_config *cmd, |
1038 | uint16_t action, uint16_t type) | 1038 | uint16_t action, uint16_t type) |
1039 | { | 1039 | { |
1040 | int ret; | 1040 | int ret; |
1041 | 1041 | ||
@@ -1054,6 +1054,19 @@ int lbs_mesh_config_send(struct lbs_private *priv, | |||
1054 | return ret; | 1054 | return ret; |
1055 | } | 1055 | } |
1056 | 1056 | ||
1057 | int lbs_mesh_config_send(struct lbs_private *priv, | ||
1058 | struct cmd_ds_mesh_config *cmd, | ||
1059 | uint16_t action, uint16_t type) | ||
1060 | { | ||
1061 | int ret; | ||
1062 | |||
1063 | if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG)) | ||
1064 | return -EOPNOTSUPP; | ||
1065 | |||
1066 | ret = __lbs_mesh_config_send(priv, cmd, action, type); | ||
1067 | return ret; | ||
1068 | } | ||
1069 | |||
1057 | /* This function is the CMD_MESH_CONFIG legacy function. It only handles the | 1070 | /* This function is the CMD_MESH_CONFIG legacy function. It only handles the |
1058 | * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG | 1071 | * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG |
1059 | * are all handled by preparing a struct cmd_ds_mesh_config and passing it to | 1072 | * are all handled by preparing a struct cmd_ds_mesh_config and passing it to |
@@ -1095,7 +1108,7 @@ int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan) | |||
1095 | action, priv->mesh_tlv, chan, | 1108 | action, priv->mesh_tlv, chan, |
1096 | escape_essid(priv->mesh_ssid, priv->mesh_ssid_len)); | 1109 | escape_essid(priv->mesh_ssid, priv->mesh_ssid_len)); |
1097 | 1110 | ||
1098 | return lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); | 1111 | return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); |
1099 | } | 1112 | } |
1100 | 1113 | ||
1101 | static int lbs_cmd_bcn_ctrl(struct lbs_private * priv, | 1114 | static int lbs_cmd_bcn_ctrl(struct lbs_private * priv, |
diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index 12e687550bce..4b2428ac2223 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h | |||
@@ -243,6 +243,9 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in | |||
243 | 243 | ||
244 | #define CMD_F_HOSTCMD (1 << 0) | 244 | #define CMD_F_HOSTCMD (1 << 0) |
245 | #define FW_CAPINFO_WPA (1 << 0) | 245 | #define FW_CAPINFO_WPA (1 << 0) |
246 | #define FW_CAPINFO_FIRMWARE_UPGRADE (1 << 13) | ||
247 | #define FW_CAPINFO_BOOT2_UPGRADE (1<<14) | ||
248 | #define FW_CAPINFO_PERSISTENT_CONFIG (1<<15) | ||
246 | 249 | ||
247 | #define KEY_LEN_WPA_AES 16 | 250 | #define KEY_LEN_WPA_AES 16 |
248 | #define KEY_LEN_WPA_TKIP 32 | 251 | #define KEY_LEN_WPA_TKIP 32 |
@@ -316,7 +319,8 @@ enum PS_STATE { | |||
316 | enum DNLD_STATE { | 319 | enum DNLD_STATE { |
317 | DNLD_RES_RECEIVED, | 320 | DNLD_RES_RECEIVED, |
318 | DNLD_DATA_SENT, | 321 | DNLD_DATA_SENT, |
319 | DNLD_CMD_SENT | 322 | DNLD_CMD_SENT, |
323 | DNLD_BOOTCMD_SENT, | ||
320 | }; | 324 | }; |
321 | 325 | ||
322 | /** LBS_MEDIA_STATE */ | 326 | /** LBS_MEDIA_STATE */ |
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 | ||
diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/libertas/if_usb.h index 5771a83a43f0..5ba0aee0eb2f 100644 --- a/drivers/net/wireless/libertas/if_usb.h +++ b/drivers/net/wireless/libertas/if_usb.h | |||
@@ -30,6 +30,7 @@ struct bootcmd | |||
30 | 30 | ||
31 | #define BOOT_CMD_RESP_OK 0x0001 | 31 | #define BOOT_CMD_RESP_OK 0x0001 |
32 | #define BOOT_CMD_RESP_FAIL 0x0000 | 32 | #define BOOT_CMD_RESP_FAIL 0x0000 |
33 | #define BOOT_CMD_RESP_NOT_SUPPORTED 0x0002 | ||
33 | 34 | ||
34 | struct bootcmdresp | 35 | struct bootcmdresp |
35 | { | 36 | { |
@@ -50,6 +51,10 @@ struct if_usb_card { | |||
50 | uint8_t ep_in; | 51 | uint8_t ep_in; |
51 | uint8_t ep_out; | 52 | uint8_t ep_out; |
52 | 53 | ||
54 | /* bootcmdresp == 0 means command is pending | ||
55 | * bootcmdresp < 0 means error | ||
56 | * bootcmdresp > 0 is a BOOT_CMD_RESP_* from firmware | ||
57 | */ | ||
53 | int8_t bootcmdresp; | 58 | int8_t bootcmdresp; |
54 | 59 | ||
55 | int ep_in_size; | 60 | int ep_in_size; |