Commit 1556c0f2 authored by Brian Cavagnolo's avatar Brian Cavagnolo Committed by John W. Linville

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: default avatarBrian Cavagnolo <brian@cozybit.com>
Acked-by: default avatarDan Williams <dcbw@redhat.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 38e3b0d8
...@@ -1033,9 +1033,9 @@ int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, ...@@ -1033,9 +1033,9 @@ int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
return ret; return ret;
} }
int lbs_mesh_config_send(struct lbs_private *priv, static int __lbs_mesh_config_send(struct lbs_private *priv,
struct cmd_ds_mesh_config *cmd, struct cmd_ds_mesh_config *cmd,
uint16_t action, uint16_t type) uint16_t action, uint16_t type)
{ {
int ret; int ret;
...@@ -1054,6 +1054,19 @@ int lbs_mesh_config_send(struct lbs_private *priv, ...@@ -1054,6 +1054,19 @@ int lbs_mesh_config_send(struct lbs_private *priv,
return ret; return ret;
} }
int lbs_mesh_config_send(struct lbs_private *priv,
struct cmd_ds_mesh_config *cmd,
uint16_t action, uint16_t type)
{
int ret;
if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG))
return -EOPNOTSUPP;
ret = __lbs_mesh_config_send(priv, cmd, action, type);
return ret;
}
/* This function is the CMD_MESH_CONFIG legacy function. It only handles the /* This function is the CMD_MESH_CONFIG legacy function. It only handles the
* START and STOP actions. The extended actions supported by CMD_MESH_CONFIG * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG
* are all handled by preparing a struct cmd_ds_mesh_config and passing it to * 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,7 +1108,7 @@ int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan)
action, priv->mesh_tlv, chan, action, priv->mesh_tlv, chan,
escape_essid(priv->mesh_ssid, priv->mesh_ssid_len)); escape_essid(priv->mesh_ssid, priv->mesh_ssid_len));
return lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
} }
static int lbs_cmd_bcn_ctrl(struct lbs_private * priv, static int lbs_cmd_bcn_ctrl(struct lbs_private * priv,
......
...@@ -243,6 +243,9 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in ...@@ -243,6 +243,9 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
#define CMD_F_HOSTCMD (1 << 0) #define CMD_F_HOSTCMD (1 << 0)
#define FW_CAPINFO_WPA (1 << 0) #define FW_CAPINFO_WPA (1 << 0)
#define FW_CAPINFO_FIRMWARE_UPGRADE (1 << 13)
#define FW_CAPINFO_BOOT2_UPGRADE (1<<14)
#define FW_CAPINFO_PERSISTENT_CONFIG (1<<15)
#define KEY_LEN_WPA_AES 16 #define KEY_LEN_WPA_AES 16
#define KEY_LEN_WPA_TKIP 32 #define KEY_LEN_WPA_TKIP 32
...@@ -316,7 +319,8 @@ enum PS_STATE { ...@@ -316,7 +319,8 @@ enum PS_STATE {
enum DNLD_STATE { enum DNLD_STATE {
DNLD_RES_RECEIVED, DNLD_RES_RECEIVED,
DNLD_DATA_SENT, DNLD_DATA_SENT,
DNLD_CMD_SENT DNLD_CMD_SENT,
DNLD_BOOTCMD_SENT,
}; };
/** LBS_MEDIA_STATE */ /** LBS_MEDIA_STATE */
......
...@@ -39,7 +39,10 @@ MODULE_DEVICE_TABLE(usb, if_usb_table); ...@@ -39,7 +39,10 @@ MODULE_DEVICE_TABLE(usb, if_usb_table);
static void if_usb_receive(struct urb *urb); static void if_usb_receive(struct urb *urb);
static void if_usb_receive_fwload(struct urb *urb); static void if_usb_receive_fwload(struct urb *urb);
static int if_usb_prog_firmware(struct if_usb_card *cardp); static int __if_usb_prog_firmware(struct if_usb_card *cardp,
const char *fwname, int cmd);
static int if_usb_prog_firmware(struct if_usb_card *cardp,
const char *fwname, int cmd);
static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type,
uint8_t *payload, uint16_t nb); uint8_t *payload, uint16_t nb);
static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, 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,10 +69,10 @@ static void if_usb_write_bulk_callback(struct urb *urb)
lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n",
urb->actual_length); urb->actual_length);
/* Used for both firmware TX and regular TX. priv isn't /* Boot commands such as UPDATE_FW and UPDATE_BOOT2 are not
* valid at firmware load time. * passed up to the lbs level.
*/ */
if (priv) if (priv && priv->dnld_sent != DNLD_BOOTCMD_SENT)
lbs_host_to_card_done(priv); lbs_host_to_card_done(priv);
} else { } else {
/* print the failure status number for debug */ /* print the failure status number for debug */
...@@ -231,7 +234,7 @@ static int if_usb_probe(struct usb_interface *intf, ...@@ -231,7 +234,7 @@ static int if_usb_probe(struct usb_interface *intf,
} }
/* Upload firmware */ /* Upload firmware */
if (if_usb_prog_firmware(cardp)) { if (__if_usb_prog_firmware(cardp, lbs_fw_name, BOOT_CMD_FW_BY_USB)) {
lbs_deb_usbd(&udev->dev, "FW upload failed\n"); lbs_deb_usbd(&udev->dev, "FW upload failed\n");
goto err_prog_firmware; goto err_prog_firmware;
} }
...@@ -510,7 +513,7 @@ static void if_usb_receive_fwload(struct urb *urb) ...@@ -510,7 +513,7 @@ static void if_usb_receive_fwload(struct urb *urb)
if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) {
kfree_skb(skb); kfree_skb(skb);
if_usb_submit_rx_urb_fwload(cardp); if_usb_submit_rx_urb_fwload(cardp);
cardp->bootcmdresp = 1; cardp->bootcmdresp = BOOT_CMD_RESP_OK;
lbs_deb_usbd(&cardp->udev->dev, lbs_deb_usbd(&cardp->udev->dev,
"Received valid boot command response\n"); "Received valid boot command response\n");
return; return;
...@@ -526,7 +529,9 @@ static void if_usb_receive_fwload(struct urb *urb) ...@@ -526,7 +529,9 @@ static void if_usb_receive_fwload(struct urb *urb)
lbs_pr_info("boot cmd response wrong magic number (0x%x)\n", lbs_pr_info("boot cmd response wrong magic number (0x%x)\n",
le32_to_cpu(bootcmdresp.magic)); le32_to_cpu(bootcmdresp.magic));
} }
} else if (bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) { } else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) &&
(bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) &&
(bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) {
lbs_pr_info("boot cmd response cmd_tag error (%d)\n", lbs_pr_info("boot cmd response cmd_tag error (%d)\n",
bootcmdresp.cmd); bootcmdresp.cmd);
} else if (bootcmdresp.result != BOOT_CMD_RESP_OK) { } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) {
...@@ -564,8 +569,8 @@ static void if_usb_receive_fwload(struct urb *urb) ...@@ -564,8 +569,8 @@ static void if_usb_receive_fwload(struct urb *urb)
kfree_skb(skb); kfree_skb(skb);
/* reschedule timer for 200ms hence */ /* Give device 5s to either write firmware to its RAM or eeprom */
mod_timer(&cardp->fw_timeout, jiffies + (HZ/5)); mod_timer(&cardp->fw_timeout, jiffies + (HZ*5));
if (cardp->fwfinalblk) { if (cardp->fwfinalblk) {
cardp->fwdnldover = 1; cardp->fwdnldover = 1;
...@@ -809,7 +814,54 @@ static int check_fwfile_format(const uint8_t *data, uint32_t totlen) ...@@ -809,7 +814,54 @@ static int check_fwfile_format(const uint8_t *data, uint32_t totlen)
} }
static int if_usb_prog_firmware(struct if_usb_card *cardp) /**
* @brief This function programs the firmware subject to cmd
*
* @param cardp the if_usb_card descriptor
* fwname firmware or boot2 image file name
* cmd either BOOT_CMD_FW_BY_USB, BOOT_CMD_UPDATE_FW,
* or BOOT_CMD_UPDATE_BOOT2.
* @return 0 or error code
*/
static int if_usb_prog_firmware(struct if_usb_card *cardp,
const char *fwname, int cmd)
{
struct lbs_private *priv = cardp->priv;
unsigned long flags, caps;
int ret;
caps = priv->fwcapinfo;
if (((cmd == BOOT_CMD_UPDATE_FW) && !(caps & FW_CAPINFO_FIRMWARE_UPGRADE)) ||
((cmd == BOOT_CMD_UPDATE_BOOT2) && !(caps & FW_CAPINFO_BOOT2_UPGRADE)))
return -EOPNOTSUPP;
/* Ensure main thread is idle. */
spin_lock_irqsave(&priv->driver_lock, flags);
while (priv->cur_cmd != NULL || priv->dnld_sent != DNLD_RES_RECEIVED) {
spin_unlock_irqrestore(&priv->driver_lock, flags);
if (wait_event_interruptible(priv->waitq,
(priv->cur_cmd == NULL &&
priv->dnld_sent == DNLD_RES_RECEIVED))) {
return -ERESTARTSYS;
}
spin_lock_irqsave(&priv->driver_lock, flags);
}
priv->dnld_sent = DNLD_BOOTCMD_SENT;
spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = __if_usb_prog_firmware(cardp, fwname, cmd);
spin_lock_irqsave(&priv->driver_lock, flags);
priv->dnld_sent = DNLD_RES_RECEIVED;
spin_unlock_irqrestore(&priv->driver_lock, flags);
wake_up_interruptible(&priv->waitq);
return ret;
}
static int __if_usb_prog_firmware(struct if_usb_card *cardp,
const char *fwname, int cmd)
{ {
int i = 0; int i = 0;
static int reset_count = 10; static int reset_count = 10;
...@@ -817,20 +869,32 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp) ...@@ -817,20 +869,32 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp)
lbs_deb_enter(LBS_DEB_USB); lbs_deb_enter(LBS_DEB_USB);
if ((ret = request_firmware(&cardp->fw, lbs_fw_name, ret = request_firmware(&cardp->fw, fwname, &cardp->udev->dev);
&cardp->udev->dev)) < 0) { if (ret < 0) {
lbs_pr_err("request_firmware() failed with %#x\n", ret); lbs_pr_err("request_firmware() failed with %#x\n", ret);
lbs_pr_err("firmware %s not found\n", lbs_fw_name); lbs_pr_err("firmware %s not found\n", fwname);
goto done; goto done;
} }
if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) {
ret = -EINVAL;
goto release_fw; goto release_fw;
}
/* Cancel any pending usb business */
usb_kill_urb(cardp->rx_urb);
usb_kill_urb(cardp->tx_urb);
cardp->fwlastblksent = 0;
cardp->fwdnldover = 0;
cardp->totalbytes = 0;
cardp->fwfinalblk = 0;
cardp->bootcmdresp = 0;
restart: restart:
if (if_usb_submit_rx_urb_fwload(cardp) < 0) { if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
ret = -1; ret = -EIO;
goto release_fw; goto release_fw;
} }
...@@ -838,8 +902,7 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp) ...@@ -838,8 +902,7 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp)
do { do {
int j = 0; int j = 0;
i++; i++;
/* Issue Boot command = 1, Boot from Download-FW */ if_usb_issue_boot_command(cardp, cmd);
if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB);
/* wait for command response */ /* wait for command response */
do { do {
j++; j++;
...@@ -847,12 +910,21 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp) ...@@ -847,12 +910,21 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp)
} while (cardp->bootcmdresp == 0 && j < 10); } while (cardp->bootcmdresp == 0 && j < 10);
} while (cardp->bootcmdresp == 0 && i < 5); } while (cardp->bootcmdresp == 0 && i < 5);
if (cardp->bootcmdresp <= 0) { if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) {
/* Return to normal operation */
ret = -EOPNOTSUPP;
usb_kill_urb(cardp->rx_urb);
usb_kill_urb(cardp->tx_urb);
if (if_usb_submit_rx_urb(cardp) < 0)
ret = -EIO;
goto release_fw;
} else if (cardp->bootcmdresp <= 0) {
if (--reset_count >= 0) { if (--reset_count >= 0) {
if_usb_reset_device(cardp); if_usb_reset_device(cardp);
goto restart; goto restart;
} }
return -1; ret = -EIO;
goto release_fw;
} }
i = 0; i = 0;
...@@ -882,7 +954,7 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp) ...@@ -882,7 +954,7 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp)
} }
lbs_pr_info("FW download failure, time = %d ms\n", i * 100); lbs_pr_info("FW download failure, time = %d ms\n", i * 100);
ret = -1; ret = -EIO;
goto release_fw; goto release_fw;
} }
......
...@@ -30,6 +30,7 @@ struct bootcmd ...@@ -30,6 +30,7 @@ struct bootcmd
#define BOOT_CMD_RESP_OK 0x0001 #define BOOT_CMD_RESP_OK 0x0001
#define BOOT_CMD_RESP_FAIL 0x0000 #define BOOT_CMD_RESP_FAIL 0x0000
#define BOOT_CMD_RESP_NOT_SUPPORTED 0x0002
struct bootcmdresp struct bootcmdresp
{ {
...@@ -50,6 +51,10 @@ struct if_usb_card { ...@@ -50,6 +51,10 @@ struct if_usb_card {
uint8_t ep_in; uint8_t ep_in;
uint8_t ep_out; uint8_t ep_out;
/* bootcmdresp == 0 means command is pending
* bootcmdresp < 0 means error
* bootcmdresp > 0 is a BOOT_CMD_RESP_* from firmware
*/
int8_t bootcmdresp; int8_t bootcmdresp;
int ep_in_size; int ep_in_size;
......
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