static void pn544_hci_i2c_fw_work()

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;
	}
}