@@ -43,6 +43,8 @@ MODULE_DEVICE_TABLE(usb, if_usb_table);
static void if_usb_receive(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_host_to_card(struct if_usb_card *cardp, uint8_t type,
+ uint8_t *payload, uint16_t nb);
static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type,
uint8_t *payload, uint16_t nb);
static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
@@ -50,6 +52,8 @@ static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
static void if_usb_free(struct if_usb_card *cardp);
static int if_usb_submit_rx_urb(struct if_usb_card *cardp);
static int if_usb_reset_device(struct if_usb_card *cardp);
+static int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
+ void (*callbackfn)(struct urb *urb));
/**
* if_usb_wrike_bulk_callback - call back to handle URB status
@@ -97,22 +101,143 @@ static void if_usb_free(struct if_usb_card *cardp)
lbtf_deb_leave(LBTF_DEB_USB);
}
-static void if_usb_setup_firmware(struct lbtf_private *priv)
+/**
+ * if_usb_receive_hw_spec - read data received from the device.
+ *
+ * @urb pointer to struct urb
+ */
+static void if_usb_receive_cmd_response(struct urb *urb)
+{
+ struct if_usb_card *cardp = urb->context;
+ struct sk_buff *skb = cardp->rx_skb;
+ int recvlength = urb->actual_length;
+ uint8_t *recvbuff = NULL;
+ uint32_t recvtype = 0;
+ __le32 *pkt = (__le32 *)(skb->data);
+ struct cmd_ds_get_hw_spec *cmd;
+
+ lbtf_deb_enter(LBTF_DEB_USB);
+
+ if (recvlength>0) {
+ if (urb->status) {
+ lbtf_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n",
+ urb->status);
+ kfree_skb(skb);
+ goto setup_for_next;
+ }
+
+ recvbuff = skb->data;
+ recvtype = le32_to_cpu(pkt[0]);
+ lbtf_deb_usb("Recv length = 0x%x, Recv type = 0x%X",
+ recvlength, recvtype);
+
+ lbtf_deb_hex(LBTF_DEB_CMD, "CMD Data ", recvbuff, min_t(unsigned int, recvlength, 100));
+
+ } else if (urb->status) {
+ kfree_skb(skb);
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return;
+ }
+
+ if (CMD_TYPE_REQUEST == recvtype) {
+ if (recvlength > LBS_CMD_BUFFER_SIZE) {
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "The receive buffer is too large\n");
+ kfree_skb(skb);
+ goto setup_for_next;
+ }
+
+ BUG_ON(!in_interrupt());
+
+ cmd = (struct cmd_ds_get_hw_spec *)(recvbuff + MESSAGE_HEADER_LEN);
+
+ switch (le16_to_cpu(cmd->hdr.command)) {
+ case (CMD_GET_HW_SPEC | 0x8000):
+ lbtf_deb_usb("received hw spec reponse");
+
+ /* Process cmd return */
+ cardp->fwcapinfo = le32_to_cpu(cmd->fwcapinfo);
+
+ /* The firmware release is in an interesting format: the patch
+ * level is in the most significant nibble ... so fix that: */
+ cardp->fwrelease = le32_to_cpu(cmd->fwrelease);
+ cardp->fwrelease = (cardp->fwrelease << 8) |
+ (cardp->fwrelease >> 24 & 0xff);
+
+ printk(KERN_INFO "libertas_tf_usb: %pM, fw %u.%u.%up%u, cap 0x%08x\n",
+ cmd->permanentaddr,
+ cardp->fwrelease >> 24 & 0xff,
+ cardp->fwrelease >> 16 & 0xff,
+ cardp->fwrelease >> 8 & 0xff,
+ cardp->fwrelease & 0xff,
+ cardp->fwcapinfo);
+ lbtf_deb_usb("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n",
+ cmd->hwifversion, cmd->version);
+
+ memmove(cardp->hw_addr, cmd->permanentaddr, ETH_ALEN);
+
+ cardp->cmdresp = 1;
+ wake_up(&cardp->fw_wq);
+ break;
+
+ case (CMD_SET_BOOT2_VER | 0x8000):
+ lbtf_deb_usb("received boot2 ver reponse");
+ cardp->cmdresp = 1;
+ wake_up(&cardp->fw_wq);
+ break;
+
+ default:
+ lbtf_deb_usb("received unhandled cmd reponse 0x%x",
+ le16_to_cpu(cmd->hdr.command));
+ break;
+ }
+
+ kfree_skb(skb);
+ } else {
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "libertastf: unknown command type 0x%X\n", recvtype);
+ kfree_skb(skb);
+ }
+
+
+setup_for_next:
+ if (!cardp->cmdresp)
+ __if_usb_submit_rx_urb(cardp, &if_usb_receive_cmd_response);
+ lbtf_deb_leave(LBTF_DEB_USB);
+}
+
+/**
+ * if_usb_setup_firmware - Setup firmware by sending boot2 ver command
+ *
+ * Returns: 0
+ */
+static void if_usb_setup_firmware(struct if_usb_card *cardp)
{
- struct if_usb_card *cardp = priv->card;
struct cmd_ds_set_boot2_ver b2_cmd;
lbtf_deb_enter(LBTF_DEB_USB);
- if_usb_submit_rx_urb(cardp);
- b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd));
+ if (__if_usb_submit_rx_urb(cardp, &if_usb_receive_cmd_response) < 0) {
+ lbtf_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
+ }
+
+ memset(&b2_cmd, 0, sizeof(struct cmd_ds_set_boot2_ver));
+
+ b2_cmd.hdr.command = cpu_to_le16(CMD_SET_BOOT2_VER);
+ b2_cmd.hdr.size = cpu_to_le16(sizeof(struct cmd_ds_set_boot2_ver));
b2_cmd.action = 0;
b2_cmd.version = cardp->boot2_version;
- if (lbtf_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd))
- lbtf_deb_usb("Setting boot2 version failed\n");
+ cardp->cmdresp = 0;
+
+ _if_usb_host_to_card(cardp, MVMS_CMD, (uint8_t *)&b2_cmd, sizeof(b2_cmd));
+
+ wait_event_interruptible_timeout(cardp->fw_wq, cardp->cmdresp, 5 * (HZ));
+
+ usb_kill_urb(cardp->rx_urb);
lbtf_deb_leave(LBTF_DEB_USB);
+ return;
}
static void if_usb_fw_timeo(unsigned long priv)
@@ -132,6 +257,69 @@ static void if_usb_fw_timeo(unsigned long priv)
}
/**
+ * if_usb_issue_hw_spec_command - Issue hw spec command.
+ *
+ * Returns: 0
+ */
+static int if_usb_issue_hw_spec_command(struct if_usb_card *cardp)
+{
+ struct cmd_ds_get_hw_spec cmd;
+ lbtf_deb_enter(LBTF_DEB_USB);
+
+ memset(&cmd, 0, sizeof(struct cmd_ds_get_hw_spec));
+ cmd.hdr.command = cpu_to_le16(CMD_GET_HW_SPEC);
+ cmd.hdr.size = cpu_to_le16(sizeof(struct cmd_ds_get_hw_spec));
+ memcpy(cmd.permanentaddr, cardp->hw_addr, ETH_ALEN);
+
+ _if_usb_host_to_card(cardp, MVMS_CMD, (uint8_t *)&cmd, sizeof(cmd));
+
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return 0;
+}
+
+/**
+ * if_usb_update_hw_spec: Updates the hardware details.
+ *
+ * @card A pointer to card structure
+ *
+ * Returns: 0 on success, error on failure
+ */
+int if_usb_update_hw_spec(struct if_usb_card *cardp)
+{
+ int ret = -1;
+
+ lbtf_deb_enter(LBTF_DEB_USB);
+
+ if (__if_usb_submit_rx_urb(cardp, &if_usb_receive_cmd_response) < 0) {
+ lbtf_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
+ }
+
+ /* Send and wait for the response */
+ cardp->cmdresp = 0;
+
+ /* Issue hw spec command */
+ if_usb_issue_hw_spec_command(cardp);
+
+ /* wait for command response */
+ wait_event_interruptible_timeout(cardp->fw_wq, cardp->cmdresp, 5 * (HZ));
+
+ /* Process response */
+ if (cardp->cmdresp) {
+ lbtf_deb_usb("Getting hw spec succeded\n");
+ ret = 0;
+ } else {
+ lbtf_deb_usb("Getting hw spec failed\n");
+ ret = 1;
+ }
+
+ usb_kill_urb(cardp->rx_urb);
+
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return ret;
+}
+
+
+/**
* if_usb_probe - sets the configuration values
*
* @ifnum interface number
@@ -148,6 +336,7 @@ static int if_usb_probe(struct usb_interface *intf,
struct lbtf_private *priv;
struct if_usb_card *cardp;
int i;
+ int ret = 0;
lbtf_deb_enter(LBTF_DEB_USB);
udev = interface_to_usbdev(intf);
@@ -224,7 +413,33 @@ static int if_usb_probe(struct usb_interface *intf,
goto dealloc;
}
- priv = lbtf_add_card(cardp, &udev->dev);
+ cardp->boot2_version = udev->descriptor.bcdDevice;
+
+ usb_get_dev(udev);
+ usb_set_intfdata(intf, cardp);
+
+ /* Upload firmware */
+ lbtf_deb_usbd(&udev->dev, "Going to upload fw...");
+ if (if_usb_prog_firmware(cardp))
+ goto dealloc;
+
+ if_usb_setup_firmware(cardp);
+
+ /*
+ * We need to get the hw spec here because we must have the
+ * MAC address before we call lbtf_add_card
+ *
+ * Read priv address from HW
+ */
+ memset(cardp->hw_addr, 0xff, ETH_ALEN);
+
+ ret = if_usb_update_hw_spec(cardp);
+ if (ret) {
+ ret = -1;
+ pr_err("Error fetching MAC address from hardware.");
+ }
+
+ priv = lbtf_add_card(cardp, &udev->dev, cardp->hw_addr);
if (!priv)
goto dealloc;
@@ -233,10 +448,11 @@ static int if_usb_probe(struct usb_interface *intf,
priv->hw_host_to_card = if_usb_host_to_card;
priv->hw_prog_firmware = if_usb_prog_firmware;
priv->hw_reset_device = if_usb_reset_device;
- cardp->boot2_version = udev->descriptor.bcdDevice;
- usb_get_dev(udev);
- usb_set_intfdata(intf, cardp);
+ cardp->priv->fw_ready = 1;
+
+ /* "turn on" rx */
+ if_usb_submit_rx_urb(cardp);
return 0;
@@ -387,9 +603,11 @@ static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
lbtf_deb_enter(LBTF_DEB_USB);
/* check if device is removed */
- if (cardp->priv->surpriseremoved) {
- lbtf_deb_usbd(&cardp->udev->dev, "Device removed\n");
- goto tx_ret;
+ if (cardp->priv) {
+ if (cardp->priv->surpriseremoved) {
+ lbtf_deb_usbd(&cardp->udev->dev, "Device removed\n");
+ goto tx_ret;
+ }
}
if (data)
@@ -713,19 +931,18 @@ setup_for_next:
}
/**
- * if_usb_host_to_card - Download data to the device
+ * _if_usb_host_to_card - Download data to the device
*
- * @priv pointer to struct lbtf_private structure
+ * @cardp pointer to struct if_usb_card structure
* @type type of data
* @buf pointer to data buffer
* @len number of bytes
*
* Returns: 0 on success, nonzero otherwise
*/
-static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type,
+static int _if_usb_host_to_card(struct if_usb_card *cardp, uint8_t type,
uint8_t *payload, uint16_t nb)
{
- struct if_usb_card *cardp = priv->card;
u8 data = 0;
lbtf_deb_usbd(&cardp->udev->dev, "*** type = %u\n", type);
@@ -745,6 +962,22 @@ static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type,
}
/**
+ * if_usb_host_to_card - Download data to the device
+ *
+ * @priv pointer to struct lbtf_private structure
+ * @type type of data
+ * @buf pointer to data buffer
+ * @len number of bytes
+ *
+ * Returns: 0 on success, nonzero otherwise
+ */
+static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type,
+ uint8_t *payload, uint16_t nb)
+{
+ return _if_usb_host_to_card(priv->card, type, payload, nb);
+}
+
+/**
* if_usb_issue_boot_command - Issue boot command to Boot2.
*
* @ivalue 1 boots from FW by USB-Download, 2 boots from FW in EEPROM.
@@ -879,8 +1112,11 @@ restart:
if_usb_send_fw_pkt(cardp);
/* ... and wait for the process to complete */
- wait_event_interruptible(cardp->fw_wq, cardp->priv->surpriseremoved ||
- cardp->fwdnldover);
+ if (cardp->priv)
+ wait_event_interruptible(cardp->fw_wq, cardp->priv->surpriseremoved ||
+ cardp->fwdnldover);
+ else
+ wait_event_interruptible(cardp->fw_wq, cardp->fwdnldover);
del_timer_sync(&cardp->fw_timeout);
usb_kill_urb(cardp->rx_urb);
@@ -897,14 +1133,13 @@ restart:
goto release_fw;
}
- cardp->priv->fw_ready = 1;
+ if (cardp->priv)
+ cardp->priv->fw_ready = 1;
release_fw:
release_firmware(cardp->fw);
cardp->fw = NULL;
- if_usb_setup_firmware(cardp->priv);
-
done:
lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret);
return ret;
@@ -70,6 +70,11 @@ struct if_usb_card {
uint8_t fwfinalblk;
__le16 boot2_version;
+
+ int cmdresp;
+ u8 hw_addr[ETH_ALEN];
+ u32 fwrelease;
+ u32 fwcapinfo;
};
/** fwheader */