diff options
-rw-r--r-- | drivers/nfc/pn544/i2c.c | 180 |
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 | ||
72 | struct pn544_i2c_fw_frame_write { | 75 | struct 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 | ||
102 | struct pn544_i2c_fw_secure_frame { | ||
103 | u8 cmd; | ||
104 | u16 be_datalen; | ||
105 | u8 data[]; | ||
106 | } __packed; | ||
107 | |||
108 | struct 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 | ||
121 | struct pn544_i2c_phy { | 148 | struct 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 | ||
655 | static 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 | |||
687 | static 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 | |||
721 | exit: | ||
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 | |||
613 | static void pn544_hci_i2c_fw_work(struct work_struct *work) | 735 | static 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 | ||
637 | exit_state_start: | 776 | exit_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 | |||
850 | exit_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 | } |