in nfcmrvl/fw_dnld.c [247:346]
static int process_state_fw_dnld(struct nfcmrvl_private *priv,
struct sk_buff *skb)
{
uint16_t len;
uint16_t comp_len;
struct sk_buff *out_skb;
switch (priv->fw_dnld.substate) {
case SUBSTATE_WAIT_COMMAND:
/*
* Command format:
* B0..2: NCI header
* B3 : Helper command (0xA5)
* B4..5: le16 data size
* B6..7: le16 data size complement (~)
* B8..N: payload
*/
/* Remove NCI HDR */
skb_pull(skb, 3);
if (skb->data[0] != HELPER_CMD_PACKET_FORMAT || skb->len != 5) {
nfc_err(priv->dev, "bad command");
return -EINVAL;
}
skb_pull(skb, 1);
len = get_unaligned_le16(skb->data);
skb_pull(skb, 2);
comp_len = get_unaligned_le16(skb->data);
memcpy(&comp_len, skb->data, 2);
skb_pull(skb, 2);
if (((~len) & 0xFFFF) != comp_len) {
nfc_err(priv->dev, "bad len complement: %x %x %x",
len, comp_len, (~len & 0xFFFF));
out_skb = alloc_lc_skb(priv, 1);
if (!out_skb)
return -ENOMEM;
skb_put_u8(out_skb, 0xBF);
nci_send_frame(priv->ndev, out_skb);
priv->fw_dnld.substate = SUBSTATE_WAIT_NACK_CREDIT;
return 0;
}
priv->fw_dnld.chunk_len = len;
out_skb = alloc_lc_skb(priv, 1);
if (!out_skb)
return -ENOMEM;
skb_put_u8(out_skb, HELPER_ACK_PACKET_FORMAT);
nci_send_frame(priv->ndev, out_skb);
priv->fw_dnld.substate = SUBSTATE_WAIT_ACK_CREDIT;
break;
case SUBSTATE_WAIT_ACK_CREDIT:
if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
skb->len)) {
nfc_err(priv->dev, "bad packet: waiting for credit");
return -EINVAL;
}
if (priv->fw_dnld.chunk_len == 0) {
/* FW Loading is done */
uint8_t conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
priv->fw_dnld.state = STATE_CLOSE_LC;
nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CLOSE_CMD,
1, &conn_id);
} else {
out_skb = alloc_lc_skb(priv, priv->fw_dnld.chunk_len);
if (!out_skb)
return -ENOMEM;
skb_put_data(out_skb,
((uint8_t *)priv->fw_dnld.fw->data) + priv->fw_dnld.offset,
priv->fw_dnld.chunk_len);
nci_send_frame(priv->ndev, out_skb);
priv->fw_dnld.substate = SUBSTATE_WAIT_DATA_CREDIT;
}
break;
case SUBSTATE_WAIT_DATA_CREDIT:
if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
skb->len)) {
nfc_err(priv->dev, "bad packet: waiting for credit");
return -EINVAL;
}
priv->fw_dnld.offset += priv->fw_dnld.chunk_len;
priv->fw_dnld.chunk_len = 0;
priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
break;
case SUBSTATE_WAIT_NACK_CREDIT:
if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
skb->len)) {
nfc_err(priv->dev, "bad packet: waiting for credit");
return -EINVAL;
}
priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
break;
}
return 0;
}