feat(registers): NTAG I2C plus config/session/PT_I2C registers
Add self-describing registers for the NTAG I2C plus (NT3H2111/2211),
decoded bit-exact from the NT3H2111 datasheet (Tables 10-14):
- NtagI2CConfig config registers E8h/E9h (NC_REG: pass-through, FD_ON/
FD_OFF, SRAM mirror, transfer dir; LAST_NDEF_BLOCK;
SRAM_MIRROR_BLOCK; watchdog; I2C_CLOCK_STR; REG_LOCK)
- NtagI2CSession session registers ECh/EDh — same NC_REG copy plus the
NS_REG status byte and NEG_AUTH_REACHED
- NtagI2CPtI2C PT_I2C (E7h): 2K_PROT / SRAM_PROT / I2C_PROT
Config and session share one _NtagI2CRegBlock base (byte 6 differs:
REG_LOCK vs NS_REG). Bound via tag.config / tag.session / tag.pt_i2c;
the fresh-tag decode matches the shipped defaults. 7 new tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ from pm3py.sim import (
|
||||
Ntag21xConfig, NTAG213, MF0UL21, MifareUltralightC,
|
||||
MifareAccessConditions, MifareClassicTag,
|
||||
EM4100Code, EM4100Tag,
|
||||
NtagI2CConfig, NtagI2CSession, NtagI2CPtI2C, NT3H2111, NT3H2211,
|
||||
)
|
||||
|
||||
|
||||
@@ -299,3 +300,78 @@ class TestEM4100Code:
|
||||
assert "EM4100 64-bit code" in text
|
||||
assert "0102030405" in text
|
||||
assert "valid" in text and "yes" in text
|
||||
|
||||
|
||||
class TestNtagI2CRegisters:
|
||||
def test_fresh_config_defaults(self):
|
||||
# NT3H2111 ships E8=01 00 F8 48, E9=08 01 00 00 (datasheet defaults)
|
||||
cfg = NT3H2111().config
|
||||
assert cfg.transfer_dir == "NFC->I2C" # NC_REG bit0 = 1
|
||||
assert cfg.pthru_on is False
|
||||
assert cfg.sram_mirror_on is False
|
||||
assert cfg.sram_mirror_block == 0xF8
|
||||
assert cfg.watchdog == 0x0848
|
||||
assert cfg.i2c_clock_str is True
|
||||
assert cfg.reg_lock_i2c is False and cfg.reg_lock_nfc is False
|
||||
|
||||
def test_nc_reg_fields_roundtrip(self):
|
||||
tag = NT3H2111()
|
||||
cfg = tag.config
|
||||
cfg.pthru_on = True
|
||||
cfg.sram_mirror_on = True
|
||||
cfg.fd_on = "tag selected" # 0b10
|
||||
cfg.fd_off = "field off / last NDEF read" # 0b10
|
||||
cfg.last_ndef_block = 0x2A
|
||||
tag.config = cfg # program E8/E9
|
||||
back = tag.config
|
||||
assert back.pthru_on is True and back.sram_mirror_on is True
|
||||
assert back.fd_on == "tag selected"
|
||||
assert back.fd_off == "field off / last NDEF read"
|
||||
assert back.last_ndef_block == 0x2A
|
||||
|
||||
def test_reg_lock(self):
|
||||
cfg = NtagI2CConfig(0)
|
||||
cfg.reg_lock_nfc = True
|
||||
# REG_LOCK is byte 6 (bits 15:8); bit0 = NFC lock -> byte6 == 0x01
|
||||
assert bytes(cfg)[6] == 0x01
|
||||
assert cfg.reg_lock_nfc is True and cfg.reg_lock_i2c is False
|
||||
|
||||
def test_session_ns_reg_decode(self):
|
||||
# NS_REG is byte 6 of the EC/ED block; set NDEF_DATA_READ + RF_FIELD_PRESENT + busy
|
||||
sess = NtagI2CSession(0)
|
||||
assert sess.rf_field_present is False
|
||||
raw = int(NtagI2CSession(0))
|
||||
# byte6 bit7 (ndef_data_read)=bit15, bit0 (rf_field_present)=bit8, bit1 (busy)=bit9
|
||||
sess = NtagI2CSession((1 << 15) | (1 << 8) | (1 << 9))
|
||||
assert sess.ndef_data_read is True
|
||||
assert sess.rf_field_present is True
|
||||
assert sess.eeprom_wr_busy is True
|
||||
assert sess.rf_locked is False
|
||||
|
||||
def test_session_binding(self):
|
||||
tag = NT3H2111()
|
||||
sess = tag.session
|
||||
# session copy shares NC_REG defaults (transfer_dir=1)
|
||||
assert sess.transfer_dir == "NFC->I2C"
|
||||
assert sess.rf_field_present is False # NS_REG starts clear
|
||||
|
||||
def test_pt_i2c(self):
|
||||
pt = NtagI2CPtI2C(0)
|
||||
pt.sram_prot = True
|
||||
pt.i2c_prot = "protected area read-only" # 0b01, unambiguous
|
||||
assert pt.sram_prot is True
|
||||
assert pt.i2c_prot == "protected area read-only"
|
||||
assert int(pt) == (1 << 2) | (1 << 0)
|
||||
# datasheet 1Xb == "no access to protected area" for both codes 2 and 3
|
||||
assert NtagI2CPtI2C(2).i2c_prot == "protected area no access"
|
||||
assert NtagI2CPtI2C(3).i2c_prot == "protected area no access"
|
||||
# tag binding
|
||||
tag = NT3H2211()
|
||||
tag.pt_i2c = pt
|
||||
assert tag.pt_i2c.i2c_prot == "protected area read-only"
|
||||
|
||||
def test_repr_is_decode_table(self):
|
||||
text = repr(NT3H2111().config)
|
||||
assert "NTAG I2C config registers" in text
|
||||
assert "transfer_dir" in text and "NFC->I2C" in text
|
||||
assert "sram_mirror_block" in text
|
||||
|
||||
Reference in New Issue
Block a user