mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
FEAT! Add T55 write commands
This commit is contained in:
committed by
Niel Nielsen
parent
a3d3c1fc34
commit
e16505e6a7
@@ -833,17 +833,33 @@ 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) {
|
||||
static data_frame_tx_t *cmd_processor_lf_t55xx_write(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];
|
||||
uint8_t block; /* block number */
|
||||
uint8_t word[4]; /* 32-bit data word, big-endian */
|
||||
uint8_t use_pwd; /* 1 = password write, 0 = open write */
|
||||
uint8_t pwd[4]; /* 32-bit password, big-endian (ignored when use_pwd == 0) */
|
||||
uint8_t page1; /* 1 = target page 1, 0 = page 0 */
|
||||
} 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) {
|
||||
|
||||
if (length < sizeof(payload_t)) {
|
||||
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));
|
||||
|
||||
payload_t *p = (payload_t *)data;
|
||||
|
||||
bool page1 = (bool)p->page1;
|
||||
uint8_t max_block = page1 ? 3u : 7u;
|
||||
|
||||
if (p->block > max_block) {
|
||||
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
|
||||
}
|
||||
|
||||
uint32_t word = bytes_to_num(p->word, 4);
|
||||
uint32_t passwd = bytes_to_num(p->pwd, 4);
|
||||
bool use_pwd = (bool)p->use_pwd;
|
||||
|
||||
status = lf_t55xx_write_block(p->block, word, passwd, use_pwd, page1);
|
||||
return data_frame_make(cmd, status, 0, NULL);
|
||||
}
|
||||
|
||||
@@ -2361,6 +2377,7 @@ static cmd_data_map_t m_data_cmd_map[] = {
|
||||
{ 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_LF_T55XX_WRITE, before_reader_run, cmd_processor_lf_t55xx_write, 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 },
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
#define DATA_CMD_IOPROX_WRITE_TO_T55XX (3011)
|
||||
#define DATA_CMD_IOPROX_DECODE_RAW (3012)
|
||||
#define DATA_CMD_IOPROX_COMPOSE_ID (3013)
|
||||
#define DATA_CMD_LF_T55XX_WRITE (3014)
|
||||
|
||||
//
|
||||
// ******************************************************************
|
||||
|
||||
@@ -81,9 +81,11 @@ extern "C" {
|
||||
T5577_PWD | \
|
||||
(4 << T5577_MAXBLOCK_SHIFT))
|
||||
|
||||
#if defined(PROJECT_CHAMELEON_ULTRA)
|
||||
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);
|
||||
|
||||
void t55xx_send_cmd(uint8_t opcode, uint32_t *passwd, uint8_t data_len, uint32_t *data, uint8_t block);
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "lf_reader_main.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "bsp_delay.h"
|
||||
#include "bsp_time.h"
|
||||
#include "hex_utils.h"
|
||||
@@ -209,3 +210,35 @@ uint8_t write_pac_to_t55xx(uint8_t *data, uint8_t *new_passwd, uint8_t *old_pass
|
||||
* Set the LF card scanning timeout value (in milliseconds).
|
||||
*/
|
||||
void set_scan_tag_timeout(uint32_t ms) { g_timeout_readem_ms = ms; }
|
||||
|
||||
#if defined(PROJECT_CHAMELEON_ULTRA)
|
||||
/**
|
||||
* Write a single raw 32-bit word to a T55xx block.
|
||||
*
|
||||
* Unlike write_em410x_to_t55xx() and friends, this writes the exact word
|
||||
* supplied with no protocol encoding — useful for custom configuration
|
||||
* words, recovery of locked tags, or scripted programming.
|
||||
*
|
||||
* Only available on Chameleon Ultra (Lite has no LF writer hardware).
|
||||
*
|
||||
* @param block Block number (0-7 for page 0, 0-3 for page 1)
|
||||
* @param word 32-bit data word to write
|
||||
* @param passwd Password for password-protected write (ignored when use_passwd is false)
|
||||
* @param use_passwd true = password-protected write, false = open write
|
||||
* @param page1 true = target page 1, false = page 0
|
||||
* @return STATUS_LF_TAG_OK always (T55xx gives no ACK; verify by reading back)
|
||||
*/
|
||||
uint8_t lf_t55xx_write_block(uint8_t block, uint32_t word, uint32_t passwd, bool use_passwd, bool page1) {
|
||||
uint8_t opcode = page1 ? T5577_OPCODE_PAGE1 : T5577_OPCODE_PAGE0;
|
||||
uint32_t *pwd_ptr = use_passwd ? &passwd : NULL;
|
||||
|
||||
start_lf_125khz_radio();
|
||||
bsp_delay_ms(1); // Delay for a while after starting the field
|
||||
|
||||
t55xx_send_cmd(opcode, pwd_ptr, 0, &word, block);
|
||||
t55xx_send_cmd(T5577_OPCODE_RESET, NULL, 0, NULL, 0);
|
||||
|
||||
stop_lf_125khz_radio();
|
||||
return STATUS_LF_TAG_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -24,3 +24,6 @@ uint8_t write_hidprox_to_t55xx(uint8_t format, uint32_t fc, uint64_t cn, uint32_
|
||||
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);
|
||||
#if defined(PROJECT_CHAMELEON_ULTRA)
|
||||
uint8_t lf_t55xx_write_block(uint8_t block, uint32_t word, uint32_t passwd, bool use_passwd, bool page1);
|
||||
#endif
|
||||
|
||||
@@ -764,6 +764,7 @@ lf_hid_prox = lf_hid.subgroup("prox", "HID Prox commands")
|
||||
lf_ioprox = lf.subgroup("ioprox", "ioProx commands")
|
||||
lf_pac = lf.subgroup("pac", "PAC/Stanley commands")
|
||||
lf_viking = lf.subgroup("viking", "Viking commands")
|
||||
lf_t55xx = lf.subgroup("t55xx", "T55xx raw commands")
|
||||
lf_generic = lf.subgroup("generic", "Generic commands")
|
||||
|
||||
|
||||
@@ -6008,6 +6009,173 @@ class LFVikingWriteT55xx(LFVikingIdArgsUnit, ReaderRequiredUnit):
|
||||
print(f" - Viking ID(8H): {id_hex} write done.")
|
||||
|
||||
|
||||
@lf_t55xx.command("clone")
|
||||
class LFT55xxClone(ReaderRequiredUnit):
|
||||
"""
|
||||
Clone a scanned or manually-specified LF card ID onto a blank T55xx tag.
|
||||
|
||||
Supported types and their required arguments:
|
||||
|
||||
em410x --id <10 hex> e.g. --id DEADBEEF88
|
||||
electra --id <26 hex> e.g. --id DEADBEEF880102030405060708
|
||||
hid -f <format> --cn <n> e.g. -f H10301 --fc 10 --cn 1234
|
||||
ioprox --ver <n> --fc <n> --cn <n> OR --raw8 <16 hex>
|
||||
viking --id <8 hex> e.g. --id DEADBEEF
|
||||
|
||||
Only supported on Chameleon Ultra (Lite has no LF writer).
|
||||
"""
|
||||
|
||||
TYPES = ["em410x", "electra", "hid", "ioprox", "viking"]
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit:
|
||||
parser = ArgumentParserNoExit()
|
||||
parser.description = (
|
||||
"Clone a LF card ID onto a blank T55xx tag.\n"
|
||||
"Supported types: em410x, electra, hid, ioprox, viking.\n"
|
||||
"Only supported on Chameleon Ultra (Lite has no LF writer)."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t", "--type",
|
||||
type=str,
|
||||
required=True,
|
||||
choices=self.TYPES,
|
||||
metavar="TYPE",
|
||||
help="Card type: " + ", ".join(self.TYPES),
|
||||
)
|
||||
# EM410x / Electra / Viking
|
||||
parser.add_argument(
|
||||
"--id",
|
||||
type=str,
|
||||
required=False,
|
||||
metavar="HEX",
|
||||
help="Card ID in hex: 10 hex for em410x, 26 for electra, 8 for viking",
|
||||
)
|
||||
# HID Prox
|
||||
parser.add_argument(
|
||||
"-f", "--format",
|
||||
type=str,
|
||||
required=False,
|
||||
choices=[x.name for x in HIDFormat],
|
||||
metavar="FORMAT",
|
||||
help="HID Prox format, e.g. H10301 (required for hid type)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--fc",
|
||||
type=int,
|
||||
required=False,
|
||||
metavar="INT",
|
||||
help="Facility code (HID / ioProx)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--cn",
|
||||
type=int,
|
||||
required=False,
|
||||
metavar="INT",
|
||||
help="Card number (HID / ioProx)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--il",
|
||||
type=int,
|
||||
required=False,
|
||||
metavar="INT",
|
||||
help="Issue level (HID, optional)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--oem",
|
||||
type=int,
|
||||
required=False,
|
||||
metavar="INT",
|
||||
help="OEM code (HID, optional)",
|
||||
)
|
||||
# ioProx
|
||||
parser.add_argument(
|
||||
"--ver",
|
||||
type=int,
|
||||
required=False,
|
||||
metavar="INT",
|
||||
help="Version byte (ioProx)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--raw8",
|
||||
type=str,
|
||||
required=False,
|
||||
metavar="HEX",
|
||||
help="ioProx raw 8 bytes in hex, e.g. 007854E03A5D65AB",
|
||||
)
|
||||
return parser
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
t = args.type
|
||||
|
||||
if t in ("em410x", "electra"):
|
||||
if args.id is None:
|
||||
raise ArgsParserError("--id is required for em410x / electra")
|
||||
expected = 10 if t == "em410x" else 26
|
||||
if not re.match(r"^[a-fA-F0-9]{" + str(expected) + r"}$", args.id):
|
||||
raise ArgsParserError(
|
||||
f"--id must be exactly {expected} hex characters for {t}"
|
||||
)
|
||||
id_bytes = bytes.fromhex(args.id)
|
||||
self.cmd.em410x_write_to_t55xx(id_bytes)
|
||||
label = "EM410x Electra" if t == "electra" else "EM410x"
|
||||
print(f" - {label} ID cloned to T55xx: {args.id.upper()}")
|
||||
|
||||
elif t == "hid":
|
||||
if args.format is None:
|
||||
raise ArgsParserError("-f/--format is required for hid")
|
||||
if args.cn is None:
|
||||
raise ArgsParserError("--cn is required for hid")
|
||||
fmt = HIDFormat[args.format]
|
||||
fc = args.fc if args.fc is not None else 0
|
||||
il = args.il if args.il is not None else 0
|
||||
oem = args.oem if args.oem is not None else 0
|
||||
LFHIDIdArgsUnit.check_limits(fmt.value, fc, args.cn, il, oem)
|
||||
cn = args.cn
|
||||
id_bytes = struct.pack(
|
||||
">BIBIBH",
|
||||
fmt.value,
|
||||
fc,
|
||||
(cn >> 32),
|
||||
cn & 0xFFFFFFFF,
|
||||
il,
|
||||
oem,
|
||||
)
|
||||
self.cmd.hidprox_write_to_t55xx(id_bytes)
|
||||
print(f" - HID Prox cloned to T55xx")
|
||||
print(f" Format : {fmt.name}")
|
||||
if fc: print(f" FC : {fc}")
|
||||
if il: print(f" IL : {il}")
|
||||
if oem: print(f" OEM : {oem}")
|
||||
print(f" CN : {cn}")
|
||||
|
||||
elif t == "ioprox":
|
||||
ver = args.ver if args.ver is not None else 1
|
||||
fc = int(args.fc, 0) if args.fc is not None else 0
|
||||
cn = args.cn if args.cn is not None else 0
|
||||
if args.raw8 is not None:
|
||||
raw8 = LFIOProxIdArgsUnit.parse_raw8(args.raw8)
|
||||
ver, fc, cn, raw8, *_ = self.cmd.ioprox_decode_raw(raw8)
|
||||
else:
|
||||
res = self.cmd.ioprox_compose_id(ver, fc, cn)
|
||||
raw8 = res[3]
|
||||
payload16 = struct.pack(">BBH8s4x", ver & 0xFF, fc & 0xFF, cn & 0xFFFF, raw8)
|
||||
self.cmd.ioprox_write_to_t55xx(payload16)
|
||||
print(f" - ioProx cloned to T55xx")
|
||||
print(f" Ver : {ver}")
|
||||
print(f" FC : {fc} [0x{fc:02X}]")
|
||||
print(f" CN : {cn}")
|
||||
print(f" Raw8 : {raw8.hex().upper()}")
|
||||
|
||||
elif t == "viking":
|
||||
if args.id is None:
|
||||
raise ArgsParserError("--id is required for viking")
|
||||
if not re.match(r"^[a-fA-F0-9]{8}$", args.id):
|
||||
raise ArgsParserError("--id must be exactly 8 hex characters for viking")
|
||||
id_bytes = bytes.fromhex(args.id)
|
||||
self.cmd.viking_write_to_t55xx(id_bytes)
|
||||
print(f" - Viking ID cloned to T55xx: {args.id.upper()}")
|
||||
|
||||
|
||||
@lf_generic.command("adcread")
|
||||
class LFADCGenericRead(ReaderRequiredUnit):
|
||||
def args_parser(self) -> ArgumentParserNoExit:
|
||||
|
||||
@@ -668,6 +668,7 @@ class ChameleonCMD:
|
||||
|
||||
|
||||
|
||||
|
||||
def lf_sniff(self, timeout_ms: int = 2000):
|
||||
"""
|
||||
Capture raw LF field ADC samples.
|
||||
|
||||
@@ -92,6 +92,7 @@ class Command(enum.IntEnum):
|
||||
IOPROX_WRITE_TO_T55XX = 3011
|
||||
IOPROX_DECODE_RAW = 3012
|
||||
IOPROX_COMPOSE_ID = 3013
|
||||
LF_T55XX_WRITE = 3014
|
||||
|
||||
MF1_WRITE_EMU_BLOCK_DATA = 4000
|
||||
HF14A_SET_ANTI_COLL_DATA = 4001
|
||||
|
||||
Reference in New Issue
Block a user