in pn544/i2c.c [735:858]
static void pn544_hci_i2c_fw_work(struct work_struct *work)
{
struct pn544_i2c_phy *phy = container_of(work, struct pn544_i2c_phy,
fw_work);
int r;
struct pn544_i2c_fw_blob *blob;
struct pn544_i2c_fw_secure_blob *secure_blob;
switch (phy->fw_work_state) {
case FW_WORK_STATE_START:
pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE);
r = request_firmware(&phy->fw, phy->firmware_name,
&phy->i2c_dev->dev);
if (r < 0)
goto exit_state_start;
phy->fw_written = 0;
switch (phy->hw_variant) {
case PN544_HW_VARIANT_C2:
blob = (struct pn544_i2c_fw_blob *) phy->fw->data;
phy->fw_blob_size = get_unaligned_be32(&blob->be_size);
phy->fw_blob_dest_addr = get_unaligned_be32(
&blob->be_destaddr);
phy->fw_blob_data = blob->data;
r = pn544_hci_i2c_fw_write_chunk(phy);
break;
case PN544_HW_VARIANT_C3:
secure_blob = (struct pn544_i2c_fw_secure_blob *)
phy->fw->data;
phy->fw_blob_data = secure_blob->data;
phy->fw_size = phy->fw->size;
r = pn544_hci_i2c_fw_secure_write_frame(phy);
break;
default:
r = -ENOTSUPP;
break;
}
exit_state_start:
if (r < 0)
pn544_hci_i2c_fw_work_complete(phy, r);
break;
case FW_WORK_STATE_WAIT_WRITE_ANSWER:
r = phy->fw_cmd_result;
if (r < 0)
goto exit_state_wait_write_answer;
if (phy->fw_written == phy->fw_blob_size) {
r = pn544_hci_i2c_fw_check_cmd(phy->i2c_dev,
phy->fw_blob_dest_addr,
phy->fw_blob_data,
phy->fw_blob_size);
if (r < 0)
goto exit_state_wait_write_answer;
phy->fw_work_state = FW_WORK_STATE_WAIT_CHECK_ANSWER;
break;
}
r = pn544_hci_i2c_fw_write_chunk(phy);
exit_state_wait_write_answer:
if (r < 0)
pn544_hci_i2c_fw_work_complete(phy, r);
break;
case FW_WORK_STATE_WAIT_CHECK_ANSWER:
r = phy->fw_cmd_result;
if (r < 0)
goto exit_state_wait_check_answer;
blob = (struct pn544_i2c_fw_blob *) (phy->fw_blob_data +
phy->fw_blob_size);
phy->fw_blob_size = get_unaligned_be32(&blob->be_size);
if (phy->fw_blob_size != 0) {
phy->fw_blob_dest_addr =
get_unaligned_be32(&blob->be_destaddr);
phy->fw_blob_data = blob->data;
phy->fw_written = 0;
r = pn544_hci_i2c_fw_write_chunk(phy);
}
exit_state_wait_check_answer:
if (r < 0 || phy->fw_blob_size == 0)
pn544_hci_i2c_fw_work_complete(phy, r);
break;
case FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER:
r = phy->fw_cmd_result;
if (r < 0)
goto exit_state_wait_secure_write_answer;
if (r == PN544_FW_CMD_RESULT_CHUNK_OK) {
r = pn544_hci_i2c_fw_secure_write_frame(phy);
goto exit_state_wait_secure_write_answer;
}
if (phy->fw_written == phy->fw_blob_size) {
secure_blob = (struct pn544_i2c_fw_secure_blob *)
(phy->fw_blob_data + phy->fw_blob_size);
phy->fw_size -= phy->fw_blob_size +
PN544_FW_SECURE_BLOB_HEADER_LEN;
if (phy->fw_size >= PN544_FW_SECURE_BLOB_HEADER_LEN
+ PN544_FW_SECURE_FRAME_HEADER_LEN) {
phy->fw_blob_data = secure_blob->data;
phy->fw_written = 0;
r = pn544_hci_i2c_fw_secure_write_frame(phy);
}
}
exit_state_wait_secure_write_answer:
if (r < 0 || phy->fw_size == 0)
pn544_hci_i2c_fw_work_complete(phy, r);
break;
default:
break;
}
}