aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/nfc
diff options
context:
space:
mode:
authorArron Wang <arron.wang@intel.com>2013-12-11 04:25:24 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2014-02-16 17:49:54 -0500
commitf1dd56fdc4b7474bfbda1753b42526dc144b55dd (patch)
tree927ac4d59da852533f11075f35ba087f197bcdb6 /drivers/nfc
parent971d63cff6d70ab09237b7718f39615e40ad0828 (diff)
NFC: pn544: i2c: Support PN544 C3 secure firmware download
PN544 C3 firmwares already contain the command frames to be sent, but as they may exceed the i2c maximum payload, we need to fragment them into secure chunks and send them through the secure write command. Signed-off-by: Arron Wang <arron.wang@intel.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/nfc')
-rw-r--r--drivers/nfc/pn544/i2c.c180
1 files changed, 174 insertions, 6 deletions
diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c
index c7e277cddb48..f2acd85be86e 100644
--- a/drivers/nfc/pn544/i2c.c
+++ b/drivers/nfc/pn544/i2c.c
@@ -66,8 +66,11 @@ MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table);
66#define PN544_HW_VARIANT_C2 0xa 66#define PN544_HW_VARIANT_C2 0xa
67#define PN544_HW_VARIANT_C3 0xb 67#define PN544_HW_VARIANT_C3 0xb
68 68
69#define PN544_FW_CMD_RESET 0x01
69#define PN544_FW_CMD_WRITE 0x08 70#define PN544_FW_CMD_WRITE 0x08
70#define PN544_FW_CMD_CHECK 0x06 71#define PN544_FW_CMD_CHECK 0x06
72#define PN544_FW_CMD_SECURE_WRITE 0x0C
73#define PN544_FW_CMD_SECURE_CHUNK_WRITE 0x0D
71 74
72struct pn544_i2c_fw_frame_write { 75struct pn544_i2c_fw_frame_write {
73 u8 cmd; 76 u8 cmd;
@@ -96,13 +99,31 @@ struct pn544_i2c_fw_blob {
96 u8 data[]; 99 u8 data[];
97}; 100};
98 101
102struct pn544_i2c_fw_secure_frame {
103 u8 cmd;
104 u16 be_datalen;
105 u8 data[];
106} __packed;
107
108struct pn544_i2c_fw_secure_blob {
109 u64 header;
110 u8 data[];
111};
112
99#define PN544_FW_CMD_RESULT_TIMEOUT 0x01 113#define PN544_FW_CMD_RESULT_TIMEOUT 0x01
100#define PN544_FW_CMD_RESULT_BAD_CRC 0x02 114#define PN544_FW_CMD_RESULT_BAD_CRC 0x02
101#define PN544_FW_CMD_RESULT_ACCESS_DENIED 0x08 115#define PN544_FW_CMD_RESULT_ACCESS_DENIED 0x08
102#define PN544_FW_CMD_RESULT_PROTOCOL_ERROR 0x0B 116#define PN544_FW_CMD_RESULT_PROTOCOL_ERROR 0x0B
103#define PN544_FW_CMD_RESULT_INVALID_PARAMETER 0x11 117#define PN544_FW_CMD_RESULT_INVALID_PARAMETER 0x11
118#define PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND 0x13
104#define PN544_FW_CMD_RESULT_INVALID_LENGTH 0x18 119#define PN544_FW_CMD_RESULT_INVALID_LENGTH 0x18
120#define PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR 0x19
121#define PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR 0x1D
122#define PN544_FW_CMD_RESULT_MEMORY_ERROR 0x20
123#define PN544_FW_CMD_RESULT_CHUNK_OK 0x21
105#define PN544_FW_CMD_RESULT_WRITE_FAILED 0x74 124#define PN544_FW_CMD_RESULT_WRITE_FAILED 0x74
125#define PN544_FW_CMD_RESULT_COMMAND_REJECTED 0xE0
126#define PN544_FW_CMD_RESULT_CHUNK_ERROR 0xE6
106 127
107#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) 128#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
108 129
@@ -112,11 +133,17 @@ struct pn544_i2c_fw_blob {
112#define PN544_FW_I2C_WRITE_DATA_MAX_LEN MIN((PN544_FW_I2C_MAX_PAYLOAD -\ 133#define PN544_FW_I2C_WRITE_DATA_MAX_LEN MIN((PN544_FW_I2C_MAX_PAYLOAD -\
113 PN544_FW_I2C_WRITE_FRAME_HEADER_LEN),\ 134 PN544_FW_I2C_WRITE_FRAME_HEADER_LEN),\
114 PN544_FW_WRITE_BUFFER_MAX_LEN) 135 PN544_FW_WRITE_BUFFER_MAX_LEN)
136#define PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN 3
137#define PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN (PN544_FW_I2C_MAX_PAYLOAD -\
138 PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN)
139#define PN544_FW_SECURE_FRAME_HEADER_LEN 3
140#define PN544_FW_SECURE_BLOB_HEADER_LEN 8
115 141
116#define FW_WORK_STATE_IDLE 1 142#define FW_WORK_STATE_IDLE 1
117#define FW_WORK_STATE_START 2 143#define FW_WORK_STATE_START 2
118#define FW_WORK_STATE_WAIT_WRITE_ANSWER 3 144#define FW_WORK_STATE_WAIT_WRITE_ANSWER 3
119#define FW_WORK_STATE_WAIT_CHECK_ANSWER 4 145#define FW_WORK_STATE_WAIT_CHECK_ANSWER 4
146#define FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER 5
120 147
121struct pn544_i2c_phy { 148struct pn544_i2c_phy {
122 struct i2c_client *i2c_dev; 149 struct i2c_client *i2c_dev;
@@ -137,6 +164,8 @@ struct pn544_i2c_phy {
137 size_t fw_blob_size; 164 size_t fw_blob_size;
138 const u8 *fw_blob_data; 165 const u8 *fw_blob_data;
139 size_t fw_written; 166 size_t fw_written;
167 size_t fw_size;
168
140 int fw_cmd_result; 169 int fw_cmd_result;
141 170
142 int powered; 171 int powered;
@@ -400,6 +429,8 @@ static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy)
400 switch (response.status) { 429 switch (response.status) {
401 case 0: 430 case 0:
402 return 0; 431 return 0;
432 case PN544_FW_CMD_RESULT_CHUNK_OK:
433 return response.status;
403 case PN544_FW_CMD_RESULT_TIMEOUT: 434 case PN544_FW_CMD_RESULT_TIMEOUT:
404 return -ETIMEDOUT; 435 return -ETIMEDOUT;
405 case PN544_FW_CMD_RESULT_BAD_CRC: 436 case PN544_FW_CMD_RESULT_BAD_CRC:
@@ -410,9 +441,20 @@ static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy)
410 return -EPROTO; 441 return -EPROTO;
411 case PN544_FW_CMD_RESULT_INVALID_PARAMETER: 442 case PN544_FW_CMD_RESULT_INVALID_PARAMETER:
412 return -EINVAL; 443 return -EINVAL;
444 case PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND:
445 return -ENOTSUPP;
413 case PN544_FW_CMD_RESULT_INVALID_LENGTH: 446 case PN544_FW_CMD_RESULT_INVALID_LENGTH:
414 return -EBADMSG; 447 return -EBADMSG;
448 case PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR:
449 return -ENOKEY;
450 case PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR:
451 return -EINVAL;
452 case PN544_FW_CMD_RESULT_MEMORY_ERROR:
453 return -ENOMEM;
454 case PN544_FW_CMD_RESULT_COMMAND_REJECTED:
455 return -EACCES;
415 case PN544_FW_CMD_RESULT_WRITE_FAILED: 456 case PN544_FW_CMD_RESULT_WRITE_FAILED:
457 case PN544_FW_CMD_RESULT_CHUNK_ERROR:
416 return -EIO; 458 return -EIO;
417 default: 459 default:
418 return -EIO; 460 return -EIO;
@@ -610,12 +652,93 @@ static int pn544_hci_i2c_fw_write_chunk(struct pn544_i2c_phy *phy)
610 return 0; 652 return 0;
611} 653}
612 654
655static int pn544_hci_i2c_fw_secure_write_frame_cmd(struct pn544_i2c_phy *phy,
656 const u8 *data, u16 datalen)
657{
658 u8 buf[PN544_FW_I2C_MAX_PAYLOAD];
659 struct pn544_i2c_fw_secure_frame *chunk;
660 int chunklen;
661 int r;
662
663 if (datalen > PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN)
664 datalen = PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN;
665
666 chunk = (struct pn544_i2c_fw_secure_frame *) buf;
667
668 chunk->cmd = PN544_FW_CMD_SECURE_CHUNK_WRITE;
669
670 put_unaligned_be16(datalen, &chunk->be_datalen);
671
672 memcpy(chunk->data, data, datalen);
673
674 chunklen = sizeof(chunk->cmd) + sizeof(chunk->be_datalen) + datalen;
675
676 r = i2c_master_send(phy->i2c_dev, buf, chunklen);
677
678 if (r == chunklen)
679 return datalen;
680 else if (r < 0)
681 return r;
682 else
683 return -EIO;
684
685}
686
687static int pn544_hci_i2c_fw_secure_write_frame(struct pn544_i2c_phy *phy)
688{
689 struct pn544_i2c_fw_secure_frame *framep;
690 int r;
691
692 framep = (struct pn544_i2c_fw_secure_frame *) phy->fw_blob_data;
693 if (phy->fw_written == 0)
694 phy->fw_blob_size = get_unaligned_be16(&framep->be_datalen)
695 + PN544_FW_SECURE_FRAME_HEADER_LEN;
696
697 /* Only secure write command can be chunked*/
698 if (phy->fw_blob_size > PN544_FW_I2C_MAX_PAYLOAD &&
699 framep->cmd != PN544_FW_CMD_SECURE_WRITE)
700 return -EINVAL;
701
702 /* The firmware also have other commands, we just send them directly */
703 if (phy->fw_blob_size < PN544_FW_I2C_MAX_PAYLOAD) {
704 r = i2c_master_send(phy->i2c_dev,
705 (const char *) phy->fw_blob_data, phy->fw_blob_size);
706
707 if (r == phy->fw_blob_size)
708 goto exit;
709 else if (r < 0)
710 return r;
711 else
712 return -EIO;
713 }
714
715 r = pn544_hci_i2c_fw_secure_write_frame_cmd(phy,
716 phy->fw_blob_data + phy->fw_written,
717 phy->fw_blob_size - phy->fw_written);
718 if (r < 0)
719 return r;
720
721exit:
722 phy->fw_written += r;
723 phy->fw_work_state = FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER;
724
725 /* SW reset command will not trig any response from PN544 */
726 if (framep->cmd == PN544_FW_CMD_RESET) {
727 pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE);
728 phy->fw_cmd_result = 0;
729 schedule_work(&phy->fw_work);
730 }
731
732 return 0;
733}
734
613static void pn544_hci_i2c_fw_work(struct work_struct *work) 735static void pn544_hci_i2c_fw_work(struct work_struct *work)
614{ 736{
615 struct pn544_i2c_phy *phy = container_of(work, struct pn544_i2c_phy, 737 struct pn544_i2c_phy *phy = container_of(work, struct pn544_i2c_phy,
616 fw_work); 738 fw_work);
617 int r; 739 int r;
618 struct pn544_i2c_fw_blob *blob; 740 struct pn544_i2c_fw_blob *blob;
741 struct pn544_i2c_fw_secure_blob *secure_blob;
619 742
620 switch (phy->fw_work_state) { 743 switch (phy->fw_work_state) {
621 case FW_WORK_STATE_START: 744 case FW_WORK_STATE_START:
@@ -626,13 +749,29 @@ static void pn544_hci_i2c_fw_work(struct work_struct *work)
626 if (r < 0) 749 if (r < 0)
627 goto exit_state_start; 750 goto exit_state_start;
628 751
629 blob = (struct pn544_i2c_fw_blob *) phy->fw->data;
630 phy->fw_blob_size = get_unaligned_be32(&blob->be_size);
631 phy->fw_blob_dest_addr = get_unaligned_be32(&blob->be_destaddr);
632 phy->fw_blob_data = blob->data;
633
634 phy->fw_written = 0; 752 phy->fw_written = 0;
635 r = pn544_hci_i2c_fw_write_chunk(phy); 753
754 switch (phy->hw_variant) {
755 case PN544_HW_VARIANT_C2:
756 blob = (struct pn544_i2c_fw_blob *) phy->fw->data;
757 phy->fw_blob_size = get_unaligned_be32(&blob->be_size);
758 phy->fw_blob_dest_addr = get_unaligned_be32(
759 &blob->be_destaddr);
760 phy->fw_blob_data = blob->data;
761
762 r = pn544_hci_i2c_fw_write_chunk(phy);
763 break;
764 case PN544_HW_VARIANT_C3:
765 secure_blob = (struct pn544_i2c_fw_secure_blob *)
766 phy->fw->data;
767 phy->fw_blob_data = secure_blob->data;
768 phy->fw_size = phy->fw->size;
769 r = pn544_hci_i2c_fw_secure_write_frame(phy);
770 break;
771 default:
772 r = -ENOTSUPP;
773 break;
774 }
636 775
637exit_state_start: 776exit_state_start:
638 if (r < 0) 777 if (r < 0)
@@ -684,6 +823,35 @@ exit_state_wait_check_answer:
684 pn544_hci_i2c_fw_work_complete(phy, r); 823 pn544_hci_i2c_fw_work_complete(phy, r);
685 break; 824 break;
686 825
826 case FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER:
827 r = phy->fw_cmd_result;
828 if (r < 0)
829 goto exit_state_wait_secure_write_answer;
830
831 if (r == PN544_FW_CMD_RESULT_CHUNK_OK) {
832 r = pn544_hci_i2c_fw_secure_write_frame(phy);
833 goto exit_state_wait_secure_write_answer;
834 }
835
836 if (phy->fw_written == phy->fw_blob_size) {
837 secure_blob = (struct pn544_i2c_fw_secure_blob *)
838 (phy->fw_blob_data + phy->fw_blob_size);
839 phy->fw_size -= phy->fw_blob_size +
840 PN544_FW_SECURE_BLOB_HEADER_LEN;
841 if (phy->fw_size >= PN544_FW_SECURE_BLOB_HEADER_LEN
842 + PN544_FW_SECURE_FRAME_HEADER_LEN) {
843 phy->fw_blob_data = secure_blob->data;
844
845 phy->fw_written = 0;
846 r = pn544_hci_i2c_fw_secure_write_frame(phy);
847 }
848 }
849
850exit_state_wait_secure_write_answer:
851 if (r < 0 || phy->fw_size == 0)
852 pn544_hci_i2c_fw_work_complete(phy, r);
853 break;
854
687 default: 855 default:
688 break; 856 break;
689 } 857 }