diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index e19a39f..cf01231 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -826,6 +826,20 @@ static data_frame_tx_t *cmd_processor_viking_write_to_t55xx(uint16_t cmd, uint16 return data_frame_make(cmd, status, 0, NULL); } +static data_frame_tx_t *cmd_processor_pac_write_to_t55xx(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + typedef struct { + uint8_t id[LF_PAC_TAG_ID_SIZE]; + uint8_t new_key[4]; + uint8_t old_keys[4]; + } PACKED payload_t; + payload_t *payload = (payload_t *)data; + if (length < sizeof(payload_t) || (length - offsetof(payload_t, old_keys)) % sizeof(payload->old_keys) != 0) { + return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL); + } + status = write_pac_to_t55xx(payload->id, payload->new_key, payload->old_keys, (length - offsetof(payload_t, old_keys)) / sizeof(payload->old_keys)); + return data_frame_make(cmd, status, 0, NULL); +} + #define GENERIC_READ_LEN 800 #define GENERIC_READ_TIMEOUT_MS 500 static data_frame_tx_t *cmd_processor_generic_read(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { @@ -1828,6 +1842,7 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_IOPROX_SCAN, before_reader_run, cmd_processor_ioprox_scan, NULL }, { DATA_CMD_IOPROX_WRITE_TO_T55XX, before_reader_run, cmd_processor_ioprox_write_to_t55xx, NULL }, { DATA_CMD_PAC_SCAN, before_reader_run, cmd_processor_pac_scan, NULL }, + { DATA_CMD_PAC_WRITE_TO_T55XX, before_reader_run, cmd_processor_pac_write_to_t55xx, NULL }, { DATA_CMD_ADC_GENERIC_READ, before_reader_run, cmd_processor_generic_read, NULL }, { DATA_CMD_HF14A_SET_FIELD_ON, before_reader_run, cmd_processor_hf14a_set_field_on, NULL }, diff --git a/firmware/application/src/data_cmd.h b/firmware/application/src/data_cmd.h index f102307..4022a0e 100644 --- a/firmware/application/src/data_cmd.h +++ b/firmware/application/src/data_cmd.h @@ -94,6 +94,7 @@ #define DATA_CMD_HIDPROX_SCAN (3002) #define DATA_CMD_HIDPROX_WRITE_TO_T55XX (3003) #define DATA_CMD_PAC_SCAN (3010) +#define DATA_CMD_PAC_WRITE_TO_T55XX (3011) #define DATA_CMD_VIKING_SCAN (3004) #define DATA_CMD_VIKING_WRITE_TO_T55XX (3005) #define DATA_CMD_ADC_GENERIC_READ (3009) diff --git a/firmware/application/src/rfid/nfctag/lf/protocols/pac.c b/firmware/application/src/rfid/nfctag/lf/protocols/pac.c index 7306bfc..71a8730 100644 --- a/firmware/application/src/rfid/nfctag/lf/protocols/pac.c +++ b/firmware/application/src/rfid/nfctag/lf/protocols/pac.c @@ -6,6 +6,7 @@ #include "nordic_common.h" #include "nrf_pwm.h" #include "protocols.h" +#include "t55xx.h" #include "tag_base_type.h" #define PAC_DATA_SIZE 8 // 8-byte ASCII card ID @@ -333,12 +334,29 @@ static const nrf_pwm_sequence_t *pac_modulator(pac_codec *d, uint8_t *buf) { // Per nRF52840 PS: compare = 0 → pin held LOW; compare >= counter_top → pin held HIGH. // No polarity bits needed — avoids edge-case ambiguity with compare = 0. for (int i = 0; i < PAC_FRAME_BITS; i++) { - m_pac_pwm_seq_vals[i].channel_0 = bits[i] ? 32 : 0; - m_pac_pwm_seq_vals[i].counter_top = 32; + m_pac_pwm_seq_vals[i].channel_0 = bits[i] ? PAC_RF_PER_BIT : 0; + m_pac_pwm_seq_vals[i].counter_top = PAC_RF_PER_BIT; } return &m_pac_pwm_seq; } +#define PAC_T55XX_BLOCK_COUNT 5 // 1 config + 4 data blocks + +uint8_t pac_t55xx_writer(uint8_t *data, uint32_t *blks) { + uint8_t bits[PAC_FRAME_BITS]; + pac_build_bitstream(data, bits); + + blks[0] = T5577_PAC_CONFIG; + for (int b = 0; b < 4; b++) { + uint32_t word = 0; + for (int i = 0; i < 32; i++) { + word = (word << 1) | bits[b * 32 + i]; + } + blks[b + 1] = word; + } + return PAC_T55XX_BLOCK_COUNT; +} + const protocol pac = { .tag_type = TAG_TYPE_PAC, .data_size = PAC_DATA_SIZE, diff --git a/firmware/application/src/rfid/nfctag/lf/protocols/pac.h b/firmware/application/src/rfid/nfctag/lf/protocols/pac.h index e9a6d46..2dc7b69 100644 --- a/firmware/application/src/rfid/nfctag/lf/protocols/pac.h +++ b/firmware/application/src/rfid/nfctag/lf/protocols/pac.h @@ -3,3 +3,4 @@ #include "protocols.h" extern const protocol pac; +uint8_t pac_t55xx_writer(uint8_t *data, uint32_t *blks); diff --git a/firmware/application/src/rfid/nfctag/lf/protocols/t55xx.h b/firmware/application/src/rfid/nfctag/lf/protocols/t55xx.h index c46b561..c0383ba 100644 --- a/firmware/application/src/rfid/nfctag/lf/protocols/t55xx.h +++ b/firmware/application/src/rfid/nfctag/lf/protocols/t55xx.h @@ -75,6 +75,12 @@ extern "C" { T5577_PWD | \ (2 << T5577_MAXBLOCK_SHIFT)) +#define T5577_PAC_CONFIG ( \ + T5577_MODULATION_DIRECT | \ + T5577_BITRATE_RF_32 | \ + T5577_PWD | \ + (4 << T5577_MAXBLOCK_SHIFT)) + void t55xx_write_data(uint32_t passwd, uint32_t *blks, uint8_t blk_count); void t55xx_reset_passwd(uint32_t old_passwd, uint32_t new_passwd); diff --git a/firmware/application/src/rfid/reader/lf/lf_reader_main.c b/firmware/application/src/rfid/reader/lf/lf_reader_main.c index 458637a..b96fa0d 100644 --- a/firmware/application/src/rfid/reader/lf/lf_reader_main.c +++ b/firmware/application/src/rfid/reader/lf/lf_reader_main.c @@ -198,6 +198,13 @@ uint8_t write_viking_to_t55xx(uint8_t *uid, uint8_t *new_passwd, uint8_t *old_pa return write_t55xx(blks, blk_count, new_passwd, old_passwds, old_passwd_count); } +uint8_t write_pac_to_t55xx(uint8_t *data, uint8_t *new_passwd, uint8_t *old_passwds, uint8_t old_passwd_count) { + uint32_t blks[7] = {0x00}; + uint8_t blk_count = pac_t55xx_writer(data, blks); + if (blk_count == 0) return STATUS_PAR_ERR; + return write_t55xx(blks, blk_count, new_passwd, old_passwds, old_passwd_count); +} + /** * Set the LF card scanning timeout value (in milliseconds). */ diff --git a/firmware/application/src/rfid/reader/lf/lf_reader_main.h b/firmware/application/src/rfid/reader/lf/lf_reader_main.h index 96f65b3..1438756 100644 --- a/firmware/application/src/rfid/reader/lf/lf_reader_main.h +++ b/firmware/application/src/rfid/reader/lf/lf_reader_main.h @@ -20,3 +20,4 @@ uint8_t write_em410x_electra_to_t55xx(uint8_t *uid, uint8_t *newkey, uint8_t *ol uint8_t write_hidprox_to_t55xx(uint8_t format, uint32_t fc, uint64_t cn, uint32_t il, uint32_t oem, uint8_t *new_passwd, uint8_t *old_passwds, uint8_t old_passwd_count); uint8_t write_ioprox_to_t55xx(uint8_t *raw_data, uint8_t *new_passwd, uint8_t *old_passwds, uint8_t old_passwd_count); uint8_t write_viking_to_t55xx(uint8_t *uid, uint8_t *newkey, uint8_t *old_keys, uint8_t old_key_count); +uint8_t write_pac_to_t55xx(uint8_t *data, uint8_t *new_passwd, uint8_t *old_passwds, uint8_t old_passwd_count); diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index 07b514e..c2ee2ec 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -592,6 +592,23 @@ class ChameleonCMD: resp.parsed = resp.data[:8] return resp + @expect_response(Status.LF_TAG_OK) + def pac_write_to_t55xx(self, id_bytes: bytes, new_key: bytes = b'\x00\x00\x00\x00', old_keys: list = None): + """ + Write PAC/Stanley card data to a T55XX tag. + + :param id_bytes: 8-byte ASCII card ID + :param new_key: new password (4 bytes) + :param old_keys: list of old passwords to try (each 4 bytes) + :return: + """ + if old_keys is None: + old_keys = [b'\x00\x00\x00\x00'] + if len(id_bytes) != 8: + raise ValueError("The id bytes length must equal 8") + data = struct.pack(f'!8s4s{4*len(old_keys)}s', id_bytes, new_key, b''.join(old_keys)) + return self.device.send_cmd_sync(Command.PAC_WRITE_TO_T55XX, data) + @expect_response(Status.LF_TAG_OK) def adc_generic_read(self): """ diff --git a/software/script/chameleon_enum.py b/software/script/chameleon_enum.py index 9ba31e9..e07f580 100644 --- a/software/script/chameleon_enum.py +++ b/software/script/chameleon_enum.py @@ -84,6 +84,7 @@ class Command(enum.IntEnum): VIKING_SCAN = 3004 VIKING_WRITE_TO_T55XX = 3005 PAC_SCAN = 3010 + PAC_WRITE_TO_T55XX = 3011 ADC_GENERIC_READ = 3009 IOPROX_SCAN = 3010 IOPROX_WRITE_TO_T55XX = 3011