Mk hardware

This commit is contained in:
Peter D. Gray 2026-02-23 12:19:06 -05:00
parent 9b131b2eff
commit 38553d1ac5
No known key found for this signature in database
GPG Key ID: A2DCD558C2BE5D7C
43 changed files with 690 additions and 238 deletions

View File

@ -208,8 +208,9 @@ def readback(fname):
if v & MK_2_OK: d.append('Mk2') if v & MK_2_OK: d.append('Mk2')
if v & MK_3_OK: d.append('Mk3') if v & MK_3_OK: d.append('Mk3')
if v & MK_4_OK: d.append('Mk4') if v & MK_4_OK: d.append('Mk4')
if v & MK_5_OK: d.append('Mk5')
if v & MK_Q1_OK: d.append('Q1') if v & MK_Q1_OK: d.append('Q1')
if v & ~(MK_1_OK | MK_2_OK | MK_3_OK | MK_4_OK | MK_Q1_OK): if v & ~(MK_1_OK | MK_2_OK | MK_3_OK | MK_4_OK | MK_5_OK | MK_Q1_OK):
d.append('?other?') d.append('?other?')
v = nv + '+'.join(d) v = nv + '+'.join(d)
elif fld == 'timestamp': elif fld == 'timestamp':
@ -245,7 +246,7 @@ def readback(fname):
@click.option('--pubkey-num', '-k', type=int, help='Which key # to use for signing', default=0) @click.option('--pubkey-num', '-k', type=int, help='Which key # to use for signing', default=0)
@click.option('--high_water', '-h', is_flag=True, help='Mark version as new highwater mark (no downgrades below this version)') @click.option('--high_water', '-h', is_flag=True, help='Mark version as new highwater mark (no downgrades below this version)')
@click.option('--verbose', '-v', default=False, is_flag=True, help='Show numbers related to signature') @click.option('--verbose', '-v', default=False, is_flag=True, help='Show numbers related to signature')
@click.option('--hw-compat', '-m', type=str, metavar='Mk4', help="Set HW compat field (hw_label value)") @click.option('--hw-compat', '-m', type=str, metavar='mk', help="Set HW compat field (hw_label value)")
@click.option('--backdate', type=int, metavar='DAYS', @click.option('--backdate', type=int, metavar='DAYS',
help='Make downgrade attack test version', default=0) help='Make downgrade attack test version', default=0)
@click.option('--build_dir', '-b', default='l-port/build-COLDCARD') @click.option('--build_dir', '-b', default='l-port/build-COLDCARD')
@ -278,8 +279,9 @@ def doit(keydir, outfn=None, build_dir=None, high_water=False,
vectors = open(build_dir + '/firmware0.bin', 'rb').read() vectors = open(build_dir + '/firmware0.bin', 'rb').read()
body = open(build_dir + '/firmware1.bin', 'rb').read() body = open(build_dir + '/firmware1.bin', 'rb').read()
if hw_compat in { 'mk4', '4'}: if hw_compat in { 'mk4', '4', 'mk5', '5', 'mk' }:
hw_compat = MK_4_OK # Mk4 and 5 can run the same firmware, once Mk5 support was added
hw_compat = MK_4_OK | MK_5_OK
elif hw_compat == 'q1': elif hw_compat == 'q1':
hw_compat = MK_Q1_OK hw_compat = MK_Q1_OK
elif hw_compat in { 'mk3', '3'}: elif hw_compat in { 'mk3', '3'}:

View File

@ -17,6 +17,14 @@ class Graphics:
mk4_nfc_4 = (102, 49, 13, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xb0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xb0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\x9f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\x8f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00') mk4_nfc_4 = (102, 49, 13, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xb0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xb0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\x9f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\x8f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00')
mk5_nfc_1 = (126, 49, 16, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xf0\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x00\xe0\x0e\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x00\xe0\x0e\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x00\xe0\x0e\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xff\xff\xfe0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xff\xff\xfe0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\xff\xff\xfe\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\xff\xff\xfe\x0f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
mk5_nfc_2 = (118, 49, 15, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xf0\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x00\xe0\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x00\xe0\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x00\xe0\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e\x00\xe00\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xff\xff0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xff\xff0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e\x00\xe00\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x00\xe00\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x00\xe00\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\xff\xff\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\xff\xff\x0f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00')
mk5_nfc_3 = (110, 49, 14, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xf0\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x00\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x00\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e\x000\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xff0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xff0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e\x000\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x000\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x000\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\xff\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\xff\x0f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00')
mk5_nfc_4 = (102, 49, 13, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xf0\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xb0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xb0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\x9f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\x8f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00')
scroll = (3, 61, 1, 0, b'@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@@\xe0@') scroll = (3, 61, 1, 0, b'@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@@\xe0@')
selected = (9, 12, 2, 0, b'\x00\x00\x00\x00\x00\x80\x01\x80\x01\x00\x03\x00\x82\x00\xc6\x00d\x00<\x00\x18\x00\x00\x00') selected = (9, 12, 2, 0, b'\x00\x00\x00\x00\x00\x80\x01\x80\x01\x00\x03\x00\x82\x00\xc6\x00d\x00<\x00\x18\x00\x00\x00')

View File

@ -0,0 +1,49 @@
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxx xxxx xxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxx xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxx xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxx xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxx xxx xxx xxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxx xxx xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
xxx xxx xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
xxx xxx xxx xxx yy nn n f c c yyyyyyyyyyy
xxx xxx xxx xxx yy nn n f c c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n f c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n f c yyyy yyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
xxx xxx xxx xxx yy n n n f c yyyy yyy
xxx xxx xxx xxx yy n n n f c yyyy yyy
xxx xxx xxx xxx yy n nn f c yyyyyyyyyyy
xxx xxx xxx xxx yy n nn f c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
xxx xxx xxx xxx yy yyyyyyyyyyy
xxx xxx xxx xxx yy yyyyyyyyyyy
xxx xxx xxx xxx yy yyyyyyyyyyy
xxx xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

View File

@ -0,0 +1,49 @@
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxx xxxx xxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxx xxx xxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxx xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
xxx xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
xxx xxx xxx yy nn n f c c yyyyyyyyyyy
xxx xxx xxx yy nn n f c c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n f c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n f c yyyy yyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
xxx xxx xxx yy n n n f c yyyy yyy
xxx xxx xxx yy n n n f c yyyy yyy
xxx xxx xxx yy n nn f c yyyyyyyyyyy
xxx xxx xxx yy n nn f c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
xxx xxx xxx yy yyyyyyyyyyy
xxx xxx xxx yy yyyyyyyyyyy
xxx xxx xxx yy yyyyyyyyyyy
xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

View File

@ -0,0 +1,49 @@
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxx xxxx xxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxx xxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
xxx xxx yy nn n f c c yyyyyyyyyyy
xxx xxx yy nn n f c c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxx yy n n n f c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxx yy n n n f c yyyy yyy
xxxxxxxxxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
xxxxxxxxxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
xxx xxx yy n n n f c yyyy yyy
xxx xxx yy n n n f c yyyy yyy
xxx xxx yy n nn f c yyyyyyyyyyy
xxx xxx yy n nn f c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
xxx xxx yy yyyyyyyyyyy
xxx xxx yy yyyyyyyyyyy
xxx xxx yy yyyyyyyyyyy
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

View File

@ -0,0 +1,49 @@
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxx xxxx xxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxx xxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
xxx xxx yy nn n f c c yyyyyyyyyyy
xxx xxx yy nn n f c c yyyyyyyyyyy
xxxxxxxxxxxxxxxxx yy n n n f c yyyyyyyyyyy
xxxxxxxxxxxxxxxxx yy n n n f c yyyy yyy
xxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
xxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
xxx xxx yy n n n f c yyyy yyy
xxx xxx yy n n n f c yyyy yyy
xxx xxx yy n nn f c yyyyyyyyyyy
xxx xxx yy n nn f c yyyyyyyyyyy
xxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
xxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
xxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
xxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
xxx xxx yy yyyyyyyyyyy
xxx xxx yy yyyyyyyyyyy
xxx xxx yy yyyyyyyyyyy
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

View File

@ -2397,9 +2397,23 @@ async def microsd_2fa(*a):
async def keyboard_test(*a): async def keyboard_test(*a):
# to aid keyboard testing/dev # to aid keyboard testing/dev
from ux import ux_input_text if version.has_qwerty:
await ux_input_text('', max_len=128, scan_ok=True, confirm_exit=False, await ux_input_text('', max_len=128, scan_ok=True, confirm_exit=False,
prompt='Keyboard Test', placeholder='(type whatever)') prompt='Keyboard Test', placeholder='(type whatever)')
else:
from ux_mk4 import ux_input_digits
await ux_input_digits('')
async def quick_nfc_test(*a):
from selftest import test_nfc
await test_nfc()
async def clear_tested_flag(*a):
# so can re-create first time power up in
# factory case (direct to selftest)
settings.remove_key('tested')
settings.save()
await reset_self()
# #
# Q wrappers; these will be present, but are very short on mk4 # Q wrappers; these will be present, but are very short on mk4

View File

@ -2,9 +2,8 @@
# #
# display.py - OLED rendering # display.py - OLED rendering
# #
import machine, uzlib, ckcc, utime import machine, uzlib, ckcc, utime, version
from ssd1306 import SSD1306_SPI from ssd1306 import SSD1306_SPI
from version import is_devmode
import framebuf import framebuf
from graphics_mk4 import Graphics from graphics_mk4 import Graphics
from charcodes import OUT_CTRL_TITLE, OUT_CTRL_ADDRESS from charcodes import OUT_CTRL_TITLE, OUT_CTRL_ADDRESS
@ -35,11 +34,14 @@ class Display:
dc_pin = Pin('PA8', Pin.OUT) dc_pin = Pin('PA8', Pin.OUT)
cs_pin = Pin('PA4', Pin.OUT) cs_pin = Pin('PA4', Pin.OUT)
try: if version.mk_num == 5:
self.dis = SSD1306_SPI(128, 64, spi, dc_pin, reset_pin, cs_pin) # Early revs (A-D) needed this pin asserted to enable +12v to OLED
except OSError: # - removed in rev E and later boards, but keep here for dev boards
print("OLED unplugged?") # - remove this in 2027
raise vcc_en = Pin('V12EN', Pin.OUT) # aka PC1
vcc_en(1)
self.dis = SSD1306_SPI(128, 64, spi, dc_pin, reset_pin, cs_pin, is_mk5=(version.mk_num==5))
self.last_bar_update = 0 self.last_bar_update = 0
self.clear() self.clear()
@ -142,7 +144,7 @@ class Display:
self.icon(128-3, 1, 'scroll') self.icon(128-3, 1, 'scroll')
self.dis.fill_rect(128-2, pos, 1, bh, 1) self.dis.fill_rect(128-2, pos, 1, bh, 1)
if is_devmode and not ckcc.is_simulator(): if version.is_devmode and not ckcc.is_simulator():
self.dis.fill_rect(128-6, 20, 5, 21, 1) self.dis.fill_rect(128-6, 20, 5, 21, 1)
self.text(-2, 21, 'D', font=FontTiny, invert=1) self.text(-2, 21, 'D', font=FontTiny, invert=1)
self.text(-2, 28, 'E', font=FontTiny, invert=1) self.text(-2, 28, 'E', font=FontTiny, invert=1)
@ -204,61 +206,20 @@ class Display:
def busy_bar(self, enable): def busy_bar(self, enable):
# Render a continuous activity (not progress) bar in lower 8 lines of display # Render a continuous activity (not progress) bar in lower 8 lines of display
# - using OLED itself to do the animation, so smooth and CPU free #
# - cannot preserve bottom 8 lines, since we have to destructively write there
# - assumes normal horz addr mode: 0x20, 0x00
# - speed_code=>framedelay: 0=5fr, 1=64fr, 2=128, 3=256, 4=3, 5=4, 6=25, 7=2frames
# unused: assert 0 <= speed_code <= 7
setup = bytes([
0x21, 0x00, 0x7f, # setup column address range (start, end): 0-127
0x22, 7, 7, # setup page start/end address: page 7=last 8 lines
])
animate = bytes([
0x2e, # stop animations in progress
0x26, # scroll leftwards (stock ticker mode)
0, # placeholder
7, # start 'page' (vertical)
5, # "speed_code" # scroll speed: 7=fastest, but no order to it
7, # end 'page'
0, 0xff, # placeholders
0x2f # start
])
cleanup = bytes([
0x2e, # stop animation
0x20, 0x00, # horz addr-ing mode
0x21, 0x00, 0x7f, # setup column address range (start, end): 0-127
0x22, 7, 7, # setup page start/end address: page 7=last 8 lines
])
if not enable: if not enable:
# stop animation, and redraw old (new) screen self.dis.busy_bar(False, None)
self.write_cmds(cleanup)
self.show() self.show()
else: else:
# Need a pattern that repeats nicely mod 128
# a pattern that repeats nicely mod 128
# - each byte here is a vertical column, 8 pixels tall, MSB at bottom # - each byte here is a vertical column, 8 pixels tall, MSB at bottom
data = bytes(0x80 if (x%4)<2 else 0x0 for x in range(128)) pat = bytes(0x80 if (x%4)<2 else 0x0 for x in range(128))
if ckcc.is_simulator(): self.dis.busy_bar(True, pat)
# just show as static pattern
t = self.dis.buffer[:-128] + data
self.dis.write_data(t)
else:
self.write_cmds(setup)
self.dis.write_data(data)
self.write_cmds(animate)
def write_cmds(self, cmds):
for c in cmds:
self.dis.write_cmd(c)
def set_brightness(self, val): def set_brightness(self, val):
# normal = 0x7f, brightness=0xff, dim=0x00 (but they are all very similar) # normal = 0x7f, brightness=0xff, dim=0x00 (but they are all very similar)
self.dis.write_cmd(0x81) # Set Contrast Control return self.dis.contrast(val)
self.dis.write_cmd(val)
def menu_draw(self, ry, msg, is_sel, is_checked, space_indicators): def menu_draw(self, ry, msg, is_sel, is_checked, space_indicators):
# draw a menu item, perhaps selected, checked. # draw a menu item, perhaps selected, checked.

View File

@ -307,6 +307,8 @@ DebugFunctionsMenu = [
# xxxxxxxxxxxxxxxx # xxxxxxxxxxxxxxxx
MenuItem("Keyboard Test", f=keyboard_test), MenuItem("Keyboard Test", f=keyboard_test),
MenuItem('BBQr Demo', f=debug_bbqr_test, predicate=version.has_qwerty), MenuItem('BBQr Demo', f=debug_bbqr_test, predicate=version.has_qwerty),
MenuItem("NFC Test", f=quick_nfc_test),
MenuItem('Clear Tested', f=clear_tested_flag),
MenuItem('Debug: assert', f=debug_assert), MenuItem('Debug: assert', f=debug_assert),
MenuItem('Debug: except', f=debug_except), MenuItem('Debug: except', f=debug_except),
MenuItem('Check: BL FW', f=check_firewall_read), MenuItem('Check: BL FW', f=check_firewall_read),

View File

@ -1,6 +1,6 @@
# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC. # (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
# #
# mk4.py - Mk4 specific code, not needed on earlier devices. # mk4.py - Mk4 and Mk5 specific code, not needed on earlier devices.
# #
# #
import os, sys, pyb, ckcc, version, glob import os, sys, pyb, ckcc, version, glob

View File

@ -419,7 +419,8 @@ class NFCHandler:
dis.text(None, -3, line2) dis.text(None, -3, line2)
else: else:
from graphics_mk4 import Graphics from graphics_mk4 import Graphics
frames = [getattr(Graphics, 'mk4_nfc_%d'%i) for i in range(1, 5)] from version import mk_num
frames = [getattr(Graphics, 'mk%d_nfc_%d'%(mk_num, i)) for i in range(1, 5)]
aborted = True aborted = True
phase = -1 phase = -1

View File

@ -171,7 +171,7 @@ async def test_secure_element():
dis.clear() dis.clear()
if version.has_qwerty: if version.has_qwerty or version.mk_num == 5:
dis.text(0, 0, "^^-- Green? " if gg else " ^^-- Red?") dis.text(0, 0, "^^-- Green? " if gg else " ^^-- Red?")
else: else:
if gg: if gg:

View File

@ -1,6 +1,6 @@
# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. # (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
# #
# ssd1306.py - MicroPython SSD1306 OLED driver, I2C and SPI interfaces # ssd1306.py - MicroPython SSD1306 OLED driver, with SPI interface
# #
# Copied from ../external/micropython/drivers/display/ssd1306.py # Copied from ../external/micropython/drivers/display/ssd1306.py
# #
@ -28,49 +28,81 @@ SET_VCOM_DESEL = const(0xdb)
SET_CHARGE_PUMP = const(0x8d) SET_CHARGE_PUMP = const(0x8d)
# Subclassing FrameBuffer provides support for graphics primitives # Subclassing FrameBuffer provides support for graphics primitives
# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html # see <http://docs.micropython.org/en/latest/pyboard/library/framebuf.html>
#
class SSD1306(framebuf.FrameBuffer): class SSD1306(framebuf.FrameBuffer):
def __init__(self, width, height, external_vcc): def __init__(self, width, height, is_mk5):
self.width = width self.width = width
self.height = height self.height = height
self.external_vcc = external_vcc self.is_mk5 = is_mk5
self.pages = self.height // 8 self.pages = self.height // 8
#self.buffer = bytearray(self.pages * self.width)
self.buffer = bytearray(1024) self.buffer = bytearray(1024)
assert len(self.buffer) == self.pages * self.width #assert len(self.buffer) == self.pages * self.width
super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB) super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
self.init_display() self.init_display()
def init_display(self): def init_display(self):
for cmd in ( if not self.is_mk5:
SET_DISP | 0x00, # off # Mk4 and earlier
# address setting cmds = (
SET_MEM_ADDR, 0x00, # horizontal SET_DISP | 0x00, # display off
# resolution and layout # address setting
SET_DISP_START_LINE | 0x00, SET_MEM_ADDR, 0x00, # horizontal
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 # resolution and layout
SET_MUX_RATIO, self.height - 1, SET_DISP_START_LINE | 0x00,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_DISP_OFFSET, 0x00, SET_MUX_RATIO, self.height - 1,
SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
# timing and driving scheme SET_DISP_OFFSET, 0x00,
SET_DISP_CLK_DIV, 0xF0, SET_COM_PIN_CFG, 0x12,
SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, # timing and driving scheme
SET_VCOM_DESEL, 0x30, # 0.83*Vcc SET_DISP_CLK_DIV, 0xF0,
# display SET_PRECHARGE, 0xf1,
SET_CONTRAST, 0xff, # maximum SET_VCOM_DESEL, 0x30, # 0.83*Vcc
SET_ENTIRE_ON, # output follows RAM contents # display
SET_NORM_INV, # not inverted SET_CONTRAST, 0xff, # maximum
# charge pump SET_ENTIRE_ON, # output follows RAM contents
SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, SET_NORM_INV, # not inverted
SET_DISP | 0x01): # on # charge pump
self.write_cmd(cmd) SET_CHARGE_PUMP, 0x14)
else:
# Mk5 has external +12v power supply, and different setup protocol
cmds = (
SET_DISP | 0x00, # display off
# address setting
SET_MEM_ADDR, 0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x00, # column addr 0 mapped to SEG127
SET_MUX_RATIO, self.height - 1,
SET_COM_OUT_DIR | 0x00, # scan from COM[8] to COM[N]
SET_DISP_OFFSET, 0x00,
SET_COM_PIN_CFG, 0x12,
# timing and driving scheme
SET_DISP_CLK_DIV, 0xF0,
SET_PRECHARGE, 0x22,
SET_VCOM_DESEL, 0x40, # per spec sheet
# display
SET_CONTRAST, 0x85, # NOT maximum, because spec sheet
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
SET_CHARGE_PUMP, 0x10, # charge pump: DISABLE
)
self.write_cmds(cmds)
self.fill(0) self.fill(0)
self.show() self.show()
self.write_cmd(SET_DISP | 0x01)
def write_cmds(self, cmds):
for c in cmds:
self.write_cmd(c)
def poweroff(self): def poweroff(self):
self.write_cmd(SET_DISP | 0x00) self.write_cmd(SET_DISP | 0x00)
@ -78,6 +110,10 @@ class SSD1306(framebuf.FrameBuffer):
self.write_cmd(SET_DISP | 0x01) self.write_cmd(SET_DISP | 0x01)
def contrast(self, contrast): def contrast(self, contrast):
# brightness: normal = 0x7f, brightness=0xff, dim=0x00 (but they are all very similar)
if self.is_mk5:
# - limit to a specific max value from OLED specs used on Mk5
contrast = max(contrast, 0x85)
self.write_cmd(SET_CONTRAST) self.write_cmd(SET_CONTRAST)
self.write_cmd(contrast) self.write_cmd(contrast)
@ -85,56 +121,113 @@ class SSD1306(framebuf.FrameBuffer):
self.write_cmd(SET_NORM_INV | (invert & 1)) self.write_cmd(SET_NORM_INV | (invert & 1))
def show(self): def show(self):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
self.write_cmd(SET_COL_ADDR) self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0) self.write_cmd(0)
self.write_cmd(x1) self.write_cmd(self.width - 1)
self.write_cmd(SET_PAGE_ADDR) self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0) self.write_cmd(0)
self.write_cmd(self.pages - 1) self.write_cmd(self.pages - 1)
self.write_data(self.buffer) self.write_data(self.buffer)
SPI_RATE = const(40000000) # max chip can do, still slower than display limit tho def busy_bar(self, enable, pattern):
# Render a continuous activity (not progress) bar in lower 8 lines of display
# - using OLED itself to do the animation, so smooth and CPU free
# - cannot preserve bottom 8 lines, since we have to destructively write there
# - assumes normal horz addr mode: 0x20, 0x00
# - speed_code=>framedelay: 0=5fr, 1=64fr, 2=128, 3=256, 4=3, 5=4, 6=25, 7=2frames
# unused: assert 0 <= speed_code <= 7
setup = bytes([
0x21, 0x00, 0x7f, # setup column address range (start, end): 0-127
0x22, 7, 7, # setup page start/end address: page 7=last 8 lines
])
if not self.is_mk5:
animate = bytes([
0x2e, # stop animations in progress
0x26, # scroll leftwards (stock ticker mode)
0, # placeholder
7, # start 'page' (vertical)
5, # "speed_code" # scroll speed: 7=fastest, but no order to it
7, # end 'page'
0, 0x7f, # start/end columns
0x2f # start
])
else:
# SSD1309? doesn't implement 0x26 but has other commands
animate = bytes([
0x2e, # stop animations in progress
0x29, # Vert+Right horz animation setup
1, # A: enable horz scroll
7, # B: start 'page' (vertical)
5, # C: "speed_code" # scroll speed: 7=fastest, but no order to it
7, # D: end 'page'
1, # E: vert scrolling offset (unused)
0, 0x7f, # F,G: start/end columns
0xa3, # Set Vertical scroll Area
0, 0, # A, B: # of rows in fixed vs. scroll area
0x2f # start animating
])
cleanup = bytes([
0x2e, # stop animation
0x20, 0x00, # horz addr-ing mode
0x21, 0x00, 0x7f, # setup column address range (start, end): 0-127
0x22, 7, 7, # setup page start/end address: page 7=last 8 lines
])
if not enable:
# stop animation, and redraw old (new) screen
self.write_cmds(cleanup)
else:
# needs a pattern that repeats nicely mod 128
self.write_cmds(setup)
self.write_data(pattern)
self.write_cmds(animate)
class SSD1306_SPI(SSD1306): class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): def __init__(self, width, height, spi, dc, res, cs, is_mk5=False):
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi self.spi = spi
self.dc = dc self.dc = dc
self.res = res
self.cs = cs self.cs = cs
self.res(1) self.res = res
# initial states
dc(0)
cs(1)
# reset sequence
res(1)
time.sleep_ms(1) time.sleep_ms(1)
self.res(0) res(0)
time.sleep_ms(10) time.sleep_ms(10)
self.res(1) res(1)
super().__init__(width, height, external_vcc)
super().__init__(width, height, is_mk5)
def _setup_spi(self):
# need to re-do this constantly
# max chip can do, still slower than display limit tho
# - 40Mhz (target) is fine for short-cabled Mk4 (actual is lower?)
# - max spec is 10Mhz on Mk5
rate = 40_000_000 if not self.is_mk5 else 10_000_000
self.spi.init(baudrate=rate, polarity=0, phase=0)
def write_cmd(self, cmd): def write_cmd(self, cmd):
self.spi.init(baudrate=SPI_RATE, polarity=0, phase=0) self._setup_spi()
self.cs(1) self.cs(1)
self.dc(0) self.dc(0)
self.cs(0) self.cs(0)
try: self.spi.write(bytearray([cmd]))
self.spi.write(bytearray([cmd]))
except:
print("SPI[cmd]: %r" % self.spi)
self.cs(1) self.cs(1)
def write_data(self, buf): def write_data(self, buf):
self.spi.init(baudrate=SPI_RATE, polarity=0, phase=0) self._setup_spi()
self.cs(1) self.cs(1)
self.dc(1) self.dc(1)
self.cs(0) self.cs(0)
try: self.spi.write(buf)
self.spi.write(buf)
except:
print("SPI[data]: %r" % self.spi)
self.cs(1) self.cs(1)
# EOF

View File

@ -383,7 +383,7 @@ def check_firmware_hdr(hdr, binary_size):
# - hdr must be a bytearray(FW_HEADER_SIZE+more) # - hdr must be a bytearray(FW_HEADER_SIZE+more)
from sigheader import FW_HEADER_SIZE, FW_HEADER_MAGIC, FWH_PY_FORMAT from sigheader import FW_HEADER_SIZE, FW_HEADER_MAGIC, FWH_PY_FORMAT
from sigheader import MK_1_OK, MK_2_OK, MK_3_OK, MK_4_OK, MK_Q1_OK from sigheader import MK_1_OK, MK_2_OK, MK_3_OK, MK_4_OK, MK_5_OK, MK_Q1_OK
from ustruct import unpack_from from ustruct import unpack_from
from version import hw_label from version import hw_label
import callgate import callgate
@ -412,6 +412,8 @@ def check_firmware_hdr(hdr, binary_size):
ok = (hw_compat & MK_3_OK) ok = (hw_compat & MK_3_OK)
elif hw_label == 'mk4': elif hw_label == 'mk4':
ok = (hw_compat & MK_4_OK) ok = (hw_compat & MK_4_OK)
elif hw_label == 'mk5':
ok = (hw_compat & MK_5_OK)
elif hw_label == 'q1': elif hw_label == 'q1':
ok = (hw_compat & MK_Q1_OK) ok = (hw_compat & MK_Q1_OK)

View File

@ -80,6 +80,7 @@ def probe_system():
from sigheader import RAM_BOOT_FLAGS, RBF_FACTORY_MODE from sigheader import RAM_BOOT_FLAGS, RBF_FACTORY_MODE
import ckcc, callgate, machine import ckcc, callgate, machine
from machine import Pin
hw_label = 'mk4' hw_label = 'mk4'
has_608 = True has_608 = True
@ -97,7 +98,7 @@ def probe_system():
# detect Q1 based on pins.csv # detect Q1 based on pins.csv
try: try:
machine.Pin('LCD_TEAR') # only defined on Q1 build, will error otherwise Pin('LCD_TEAR') # only defined on Q1 build, will error otherwise
has_qr = True has_qr = True
num_sd_slots = 2 num_sd_slots = 2
hw_label = 'q1' hw_label = 'q1'
@ -108,6 +109,15 @@ def probe_system():
except ValueError: except ValueError:
pass pass
try:
# only defined on Mk4/5 build, will error otherwise; was open on Mk1-4, low on Mk5
s0 = Pin('STRAP_MK5', mode=Pin.IN, pull=Pin.PULL_UP)
if s0() == 0:
hw_label = 'mk5'
mk_num = 5
except ValueError:
pass
# Boot loader needs to tell us stuff about how we were booted, sometimes: # Boot loader needs to tell us stuff about how we were booted, sometimes:
# - did we just install a new version, for example (obsolete in mk4) # - did we just install a new version, for example (obsolete in mk4)
# - are we running in "factory mode" with flash un-secured? # - are we running in "factory mode" with flash un-secured?

View File

@ -89,3 +89,8 @@ SE2_SDA,PB14
NFC_SCL,PB6 NFC_SCL,PB6
NFC_SDA,PB7 NFC_SDA,PB7
NFC_ED,PC4 NFC_ED,PC4
STRAP_MK5,PE0
STRAP_S1,PE1
STRAP_S2,PE2
STRAP_S3,PE3
V12EN,PC1

1 D0 PA3
89 NFC_SCL PB6
90 NFC_SDA PB7
91 NFC_ED PC4
92 STRAP_MK5 PE0
93 STRAP_S1 PE1
94 STRAP_S2 PE2
95 STRAP_S3 PE3
96 V12EN PC1

View File

@ -2,20 +2,20 @@
# #
# Build micropython for stm32 (an ARM processor). Also handles signing of resulting firmware images. # Build micropython for stm32 (an ARM processor). Also handles signing of resulting firmware images.
# #
# MARK 4 with different chip and layout # COLDCARD Mk4 and Mk5 -- OLED display, calculator style.
# #
BOARD = COLDCARD_MK4 BOARD = COLDCARD_MK4
FIRMWARE_BASE = 0x08020000 FIRMWARE_BASE = 0x08020000
BOOTLOADER_BASE = 0x08000000 BOOTLOADER_BASE = 0x08000000
HW_MODEL = mk4 HW_MODEL = mk
PARENT_MKFILE = MK4-Makefile PARENT_MKFILE = MK-Makefile
# This is release of the bootloader that will be built into the factory.dfu # This is release of the bootloader that will be built into the factory.dfu
BOOTLOADER_VERSION = 3.2.1 BOOTLOADER_VERSION = 3.2.1
BOOTLOADER_DIR = mk4-bootloader BOOTLOADER_DIR = mk4-bootloader
LATEST_RELEASE = $(shell ls -t1 ../releases/*-mk4-*.dfu | head -1) LATEST_RELEASE = $(shell ls -t1 ../releases/*-mk-*.dfu ../releases/*-mk4-*.dfu | head -1)
# Our version for this release. # Our version for this release.
# - caution, the bootrom will not accept version < 3.0.0 # - caution, the bootrom will not accept version < 3.0.0

View File

@ -5,23 +5,23 @@
# #
# Normally you must use: # Normally you must use:
# #
# make -f MK4-Makefile # make -f MK-Makefile
# or # or
# make -f Q1-Makefile # make -f Q1-Makefile
# #
.DEFAULT_GOAL := all .DEFAULT_GOAL := all
.DEFAULT all: .DEFAULT all:
$(MAKE) DEBUG_BUILD=1 -f Q1-Makefile $(MAKECMDGOALS) $(MAKE) DEBUG_BUILD=1 -f MK-Makefile $(MAKECMDGOALS)
clean clobber repro: clean clobber repro:
@echo You should do either: @echo You should do either:
@echo @echo
@echo " make" -f Q1-Makefile $(MAKECMDGOALS) @echo " make" -f Q1-Makefile $(MAKECMDGOALS)
@echo "-OR-" @echo "-OR-"
@echo " make" -f MK4-Makefile $(MAKECMDGOALS) @echo " make" -f MK-Makefile $(MAKECMDGOALS)
rc1 rc2 release: rc1 rc2 release:
make -f Q1-Makefile $(MAKECMDGOALS) && make -f MK4-Makefile $(MAKECMDGOALS) make -f Q1-Makefile $(MAKECMDGOALS) && make -f MK-Makefile $(MAKECMDGOALS)
# EOF # EOF

View File

@ -8,7 +8,7 @@
#include "gpio.h" #include "gpio.h"
#include "stm32l4xx_hal.h" #include "stm32l4xx_hal.h"
// PA0 - onewire bus for 608a // PA0 - onewire bus for 608
#define ONEWIRE_PIN GPIO_PIN_0 #define ONEWIRE_PIN GPIO_PIN_0
#define ONEWIRE_PORT GPIOA #define ONEWIRE_PORT GPIOA
@ -62,15 +62,20 @@ gpio_setup(void)
// SD active LED: PC7 // SD active LED: PC7
// USB active LED: PC6 // USB active LED: PC6
// Mk5 (disconnected & unused on Mk4):
// PC0: early experiments, unused
// PC1: +12v en for OLED
// - but by rev E, neither being used, but keep support for older revs
{ GPIO_InitTypeDef setup = { { GPIO_InitTypeDef setup = {
.Pin = GPIO_PIN_7 | GPIO_PIN_6, .Pin = GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_0 | GPIO_PIN_1,
.Mode = GPIO_MODE_OUTPUT_PP, .Mode = GPIO_MODE_OUTPUT_PP,
.Pull = GPIO_NOPULL, .Pull = GPIO_NOPULL,
.Speed = GPIO_SPEED_FREQ_LOW, .Speed = GPIO_SPEED_FREQ_LOW,
}; };
HAL_GPIO_Init(GPIOC, &setup); HAL_GPIO_Init(GPIOC, &setup);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7|GPIO_PIN_6, 0); // turn LEDs off HAL_GPIO_WritePin(GPIOC, setup.Pin, 0); // turn all off
} }
// SD card detect switch: PC13 // SD card detect switch: PC13
@ -83,6 +88,18 @@ gpio_setup(void)
HAL_GPIO_Init(GPIOC, &setup); HAL_GPIO_Init(GPIOC, &setup);
} }
// Strapping pins (Mk5, but all disconnected on Mk4): PE0-3
// - important to have the internal pull-ups enabled on these
// - PE0: low if Mk5
{ GPIO_InitTypeDef setup = {
.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3,
.Mode = GPIO_MODE_INPUT,
.Pull = GPIO_PULLUP,
.Speed = GPIO_SPEED_FREQ_LOW,
};
HAL_GPIO_Init(GPIOE, &setup);
}
#if 0 #if 0
// TEST CODE -- keep // TEST CODE -- keep
@ -125,5 +142,14 @@ gpio_setup(void)
#endif #endif
} }
// is_mk5()
//
bool
is_mk5(void)
{
// sample the PE0 strapping pin to know if mk4 or 5
return !HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_0);
}
// EOF // EOF

View File

@ -6,10 +6,12 @@
// set directions, lock critical ones, etc. // set directions, lock critical ones, etc.
void gpio_setup(void); void gpio_setup(void);
// sample the DFU button
inline bool dfu_button_pressed(void) { return false; }
#ifdef FOR_Q1_ONLY #ifdef FOR_Q1_ONLY
// kill system power; instant // kill system power; instant
extern void turn_power_off(void); extern void turn_power_off(void);
#endif #endif
// sample the strapping pin to know if mk4 or 5
extern bool is_mk5(void);
// EOF

View File

@ -5,6 +5,7 @@
#include "delay.h" #include "delay.h"
#include "rng.h" #include "rng.h"
#include "console.h" #include "console.h"
#include "gpio.h" // for is_mk5()
#include "stm32l4xx_hal.h" #include "stm32l4xx_hal.h"
#include <string.h> #include <string.h>
@ -14,7 +15,7 @@
// Reset and config sequence. // Reset and config sequence.
// //
// As measured! No attempt to understand them here. // As measured! No attempt to understand them here.
static const uint8_t reset_commands[] = { static const uint8_t reset_commands_mk4[] = {
0xae, // display off 0xae, // display off
0x20, 0x00, // horz addr-ing mode 0x20, 0x00, // horz addr-ing mode
0x40, // ram display start line: 0 0x40, // ram display start line: 0
@ -33,6 +34,28 @@ static const uint8_t reset_commands[] = {
0xaf // display on 0xaf // display on
}; };
// .. similar but a little different on Mk5
static const uint8_t reset_commands_mk5[] = {
0xae, // display off
0xd5, 0x80, // set display clock divide ratio
0xa8, 0x3f, // set multiplex ratio: 64
0xd3, 0x00, // display offset (vertical shift): 0
0x40, // ram display start line: 0
0xad, 0x8a, // set charge pump (NEW)
0xa0, // column addr 127 mapped to seg0 (NEW was 0xa1)
0xc0, // remapped mode: scan from COMn to COM0 (NEW was 0xc8)
0xda, 0x12, // seq com pin conf: alt com pin
0x81, 0x65, // Contrast: was max, now 0x65 (NEW)
0xd9, 0x22, // set pre-change period (NEW, was 0xf1)
0xdb, 0x40, // Cvomh deselect level (NEW, was 0x30)
0xa6, // normal not inverted
0x20, 0x00, // horz addr-ing mode
0xa4, // display ram contents (not all on)
//0x8d, 0x14, // enable charge pump (not in use, omitted from their sequence)
0xaf // display on (done after VCC enabled)
};
// Bytes to send before sending the 1024 bytes of pixel data. // Bytes to send before sending the 1024 bytes of pixel data.
// //
static const uint8_t before_show[] = { static const uint8_t before_show[] = {
@ -50,6 +73,9 @@ static const uint8_t before_show[] = {
#define SPI_SCK GPIO_PIN_5 #define SPI_SCK GPIO_PIN_5
#define SPI_MOSI GPIO_PIN_7 #define SPI_MOSI GPIO_PIN_7
// port C, Mk5 only
#define VCC_EN_PIN GPIO_PIN_1
#ifndef DISABLE_OLED #ifndef DISABLE_OLED
static SPI_HandleTypeDef spi_port; static SPI_HandleTypeDef spi_port;
@ -177,7 +203,7 @@ oled_setup(void)
HAL_GPIO_Init(GPIOA, &setup); HAL_GPIO_Init(GPIOA, &setup);
// lock the RESET pin so that St's DFU code doesn't clear screen // lock the RESET pin so that St's DFU code doesn't clear screen
// it might be trying to use it a MISO signal for SPI loading // it might be trying to use it as a MISO signal for SPI loading
HAL_GPIO_LockPin(GPIOA, RESET_PIN | CS_PIN | DC_PIN); HAL_GPIO_LockPin(GPIOA, RESET_PIN | CS_PIN | DC_PIN);
// 10ms low-going pulse on reset pin // 10ms low-going pulse on reset pin
@ -196,7 +222,14 @@ oled_setup(void)
//SPI1->CR1 = 0x354; //SPI1->CR1 = 0x354;
// write a sequence to reset things // write a sequence to reset things
oled_write_cmd_sequence(sizeof(reset_commands), reset_commands); if(is_mk5()) {
// note: +12v is always on now, this line supports older revs
HAL_GPIO_WritePin(GPIOC, VCC_EN_PIN, 1);
oled_write_cmd_sequence(sizeof(reset_commands_mk5), reset_commands_mk5);
} else {
oled_write_cmd_sequence(sizeof(reset_commands_mk4), reset_commands_mk4);
}
rng_delay(); rng_delay();
} }
@ -429,13 +462,12 @@ oled_factory_busy(void)
// Render a continuous activity (not progress) bar in lower 8 lines of display // Render a continuous activity (not progress) bar in lower 8 lines of display
// - using OLED itself to do the animation, so smooth and CPU free // - using OLED itself to do the animation, so smooth and CPU free
// - cannot preserve bottom 8 lines, since we have to destructively write there // - cannot preserve bottom 8 lines, since we have to destructively write there
//oled_spi_setup();
static const uint8_t setup[] = { static const uint8_t setup[] = {
0x21, 0x00, 0x7f, // setup column address range (start, end): 0-127 0x21, 0x00, 0x7f, // setup column address range (start, end): 0-127
0x22, 7, 7, // setup page start/end address: page 7=last 8 lines 0x22, 7, 7, // setup page start/end address: page 7=last 8 lines
}; };
static const uint8_t animate[] = { static const uint8_t animate_mk4[] = {
0x2e, // stop animations in progress 0x2e, // stop animations in progress
0x26, // scroll leftwards (stock ticker mode) 0x26, // scroll leftwards (stock ticker mode)
0, // placeholder 0, // placeholder
@ -445,6 +477,21 @@ oled_factory_busy(void)
0, 0xff, // placeholders 0, 0xff, // placeholders
0x2f // start 0x2f // start
}; };
// slightly different on Mk5 display
static const uint8_t animate_mk5[] = {
0x2e, // stop animations in progress
0x29, // Vert+Right horz animation setup
1, // A: enable horz scroll
7, // B: start 'page' (vertical)
5, // C: "speed_code" # scroll speed: 7=fastest, but no order to it
7, // D: end 'page'
1, // E: vert scrolling offset (unused)
0, 0x7f, // F,G: start/end columns
0xa3, // Set Vertical scroll Area
0, 0, // A, B: # of rows in fixed vs. scroll area
0x2f // start animating
};
uint8_t data[128]; uint8_t data[128];
for(int x=0; x<128; x++) { for(int x=0; x<128; x++) {
@ -454,7 +501,11 @@ oled_factory_busy(void)
oled_write_cmd_sequence(sizeof(setup), setup); oled_write_cmd_sequence(sizeof(setup), setup);
oled_write_data(sizeof(data), data); oled_write_data(sizeof(data), data);
oled_write_cmd_sequence(sizeof(animate), animate); if(is_mk5()) {
oled_write_cmd_sequence(sizeof(animate_mk5), animate_mk5);
} else {
oled_write_cmd_sequence(sizeof(animate_mk4), animate_mk4);
}
} }
// EOF // EOF

View File

@ -1,6 +1,6 @@
# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC. # (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
# #
# Shared values and target rules for Mk4 and Q. # Shared values and target rules for Mk and Q platforms.
# #
# Define these vars to suit board # Define these vars to suit board

View File

@ -70,8 +70,7 @@ typedef struct {
#define MK_3_OK 0x04 #define MK_3_OK 0x04
#define MK_4_OK 0x08 #define MK_4_OK 0x08
#define MK_Q1_OK 0x10 #define MK_Q1_OK 0x10
// RFU: #define MK_5_OK 0x20
#define MK_6_OK 0x20
// (Mk1-3) There is a copy of the header at this location in RAM, copied by bootloader // (Mk1-3) There is a copy of the header at this location in RAM, copied by bootloader
// **after** it has been verified. If you write to this memory area, you will be reset! // **after** it has been verified. If you write to this memory area, you will be reset!

View File

@ -54,8 +54,7 @@ MK_2_OK = 0x02
MK_3_OK = 0x04 MK_3_OK = 0x04
MK_4_OK = 0x08 MK_4_OK = 0x08
MK_Q1_OK = 0x10 MK_Q1_OK = 0x10
# RFU: MK_5_OK = 0x20
MK_6_OK = 0x20
# (Mk1-3) There is a copy of the header at this location in RAM, copied by bootloader # (Mk1-3) There is a copy of the header at this location in RAM, copied by bootloader
# **after** it has been verified. If you write to this memory area, you will be reset! # **after** it has been verified. If you write to this memory area, you will be reset!

View File

@ -9,10 +9,23 @@ from ckcc_protocol.client import ColdcardDevice
def _clone(source, target): def _clone(source, target):
assert source in ["Q", "Mk4"] allowed_devices = ["Q", "Mk4", "Mk5"]
assert target in ["Q", "Mk4"] assert source in allowed_devices
source_sim_arg, source_is_Q = ("--q1", True) if source == "Q" else ("", False) assert target in allowed_devices
target_sim_arg, target_is_Q = ("--q1", True) if target == "Q" else ("", False)
source_is_Q = False
source_sim_arg = ""
if source == "Q":
source_sim_arg, source_is_Q = "--q1", True
elif source == "Mk4":
source_sim_arg, source_is_Q = "--mk4", False
target_is_Q = False
target_sim_arg = ""
if target == "Q":
target_sim_arg, target_is_Q = "--q1", True
elif target == "Mk4":
target_sim_arg, target_is_Q = "--mk4", False
# first the TARGET # first the TARGET
clean_sim_data() # remove all from previous clean_sim_data() # remove all from previous
@ -105,7 +118,7 @@ def _clone(source, target):
sim_target.stop() sim_target.stop()
@pytest.mark.parametrize("source,target", list(itertools.product(["Q", "Mk4"], repeat=2))) @pytest.mark.parametrize("source,target", list(itertools.product(["Q", "Mk4", "Mk5"], repeat=2)))
def test_clone(source, target): def test_clone(source, target):
_clone(source, target) _clone(source, target)
time.sleep(1) time.sleep(1)

View File

@ -1518,19 +1518,22 @@ def is_mark3(dev_hw_label):
def is_mark4(dev_hw_label): def is_mark4(dev_hw_label):
return (dev_hw_label == 'mk4') return (dev_hw_label == 'mk4')
@pytest.fixture(scope='session')
def is_mark5(dev_hw_label):
return (dev_hw_label == 'mk5')
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def is_q1(dev_hw_label): def is_q1(dev_hw_label):
return (dev_hw_label == 'q1') return (dev_hw_label == 'q1')
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def is_headless(request): def is_headless(request):
return request.config.getoption('--headless') return request.config.getoption('--headless')
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def is_mark4plus(is_mark4, is_q1): def is_mark4plus(is_mark4, is_q1, is_mark5):
# mark4 PLUS ... so Q1 and Mk4 # mark4 PLUS ... so Q1, Mk4 and Mk5
return is_mark4 or is_q1 return is_mark4 or is_q1 or is_mark5
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def mk_num(dev_hw_label): def mk_num(dev_hw_label):
@ -1544,35 +1547,29 @@ def mk_num(dev_hw_label):
else: else:
raise ValueError(v) raise ValueError(v)
@pytest.fixture(scope='session')
def only_mk4(is_mark4):
# NOTE: avoid this, and try to be more specific! ie. NFC vs. QR etc
if not is_mark4:
raise pytest.skip("Mk4 only")
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def only_q1(is_q1): def only_q1(is_q1):
if not is_q1: if not is_q1:
raise pytest.skip("Q only") raise pytest.skip("Q only")
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def needs_nfc(is_mark4, is_q1): def needs_nfc(is_mark4plus):
if is_mark4 or is_q1: if is_mark4plus:
return return
raise pytest.skip("Needs NFC support") raise pytest.skip("Needs NFC support")
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def needs_virtdisk(is_mark4, is_q1): def needs_virtdisk(is_mark4plus):
# TODO/MAYBE: test if feature enabled in settings? # TODO/MAYBE: test if feature enabled in settings?
if is_mark4 or is_q1: if is_mark4plus:
return return
raise pytest.skip("Needs VirtDisk support") raise pytest.skip("Needs VirtDisk support")
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def only_mk4plus(mk_num): def only_mk4plus(mk_num):
# Mk4 and Q1 # Mk4, Q1 and Mk5
if mk_num < 4: if mk_num < 4:
raise pytest.skip("Mk4/Q1 only") raise pytest.skip("Mk4/Mk5/Q1 only")
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def only_mk3(mk_num): def only_mk3(mk_num):

View File

@ -298,7 +298,8 @@ def main():
help="Choose how much to sleep after simulator is started") help="Choose how much to sleep after simulator is started")
parser.add_argument("-m", "--module", action="append", help="Choose only n modules to run") parser.add_argument("-m", "--module", action="append", help="Choose only n modules to run")
parser.add_argument("--pdb", action="store_true", help="Go to debugger on failure") parser.add_argument("--pdb", action="store_true", help="Go to debugger on failure")
parser.add_argument("--q1", action="store_true", help="Simulate a Q instead of Mk COLDCARD") parser.add_argument("--q1", action="store_true", help="Simulate a Q instead of Mk5 COLDCARD")
parser.add_argument("--mk4", action="store_true", help="Simulate a Mk4 instead of Mk5 COLDCARD")
parser.add_argument("--psbt2", action="store_true", help="`fake_txn` produces PSBTv2") parser.add_argument("--psbt2", action="store_true", help="`fake_txn` produces PSBTv2")
parser.add_argument("--ff", action="store_true", help="Run the last failures first") parser.add_argument("--ff", action="store_true", help="Run the last failures first")
parser.add_argument("--onetime", action="store_true", default=False, parser.add_argument("--onetime", action="store_true", default=False,
@ -381,8 +382,11 @@ def main():
# proper `settings.load` _ virtual disk # proper `settings.load` _ virtual disk
sim_args = ["--set", "nfc=1", "--set", "vidsk=1"] sim_args = ["--set", "nfc=1", "--set", "vidsk=1"]
# by default Mk5 is run
if args.q1 and '--q1' not in sim_args: if args.q1 and '--q1' not in sim_args:
sim_args.append('--q1') sim_args.append('--q1')
elif args.mk4 and '--mk4' not in sim_args:
sim_args.append("--mk4")
module_args.append((test_module, sim_args, args.pytest_k, args.pdb, module_args.append((test_module, sim_args, args.pytest_k, args.pdb,
args.ff, args.psbt2, args.q1, args.headless)) args.ff, args.psbt2, args.q1, args.headless))
@ -416,8 +420,10 @@ def main():
tmp_dir = "/tmp/cc-simulators" tmp_dir = "/tmp/cc-simulators"
clean_directory(tmp_dir) # clean it clean_directory(tmp_dir) # clean it
mk4_log_dir = f"{tmp_dir}/mk4_logs" mk4_log_dir = f"{tmp_dir}/mk4_logs"
mk5_log_dir = f"{tmp_dir}/mk5_logs"
q1_log_dir = f"{tmp_dir}/q1_logs" q1_log_dir = f"{tmp_dir}/q1_logs"
os.makedirs(mk4_log_dir, exist_ok=True) os.makedirs(mk4_log_dir, exist_ok=True)
os.makedirs(mk5_log_dir, exist_ok=True)
os.makedirs(q1_log_dir, exist_ok=True) os.makedirs(q1_log_dir, exist_ok=True)
q = [] # build priority queue q = [] # build priority queue
@ -455,7 +461,14 @@ def main():
break break
sim = ColdcardSimulator(sim_args, segregate=True) sim = ColdcardSimulator(sim_args, segregate=True)
sim.start(start_wait=0) sim.start(start_wait=0)
ld = q1_log_dir if "--q1" in sim_args else mk4_log_dir
if "--q1" in sim_args:
ld = q1_log_dir
elif "--mk4" in sim_args:
ld = mk4_log_dir
else:
ld = mk5_log_dir
q_chunks.append((sim, mn, mod_add, k, ld)) q_chunks.append((sim, mn, mod_add, k, ld))
time.sleep(5) time.sleep(5)
@ -468,7 +481,12 @@ def main():
if k: if k:
cmd_list.extend(["-k", k]) cmd_list.extend(["-k", k])
p = subprocess.Popen(cmd_list, preexec_fn=os.setsid, stdout=out_fd, stderr=out_fd) p = subprocess.Popen(cmd_list, preexec_fn=os.setsid, stdout=out_fd, stderr=out_fd)
mark = "Q" if "q1" in log_dir else "Mk4" if "q1" in log_dir:
mark = "Q"
elif "mk5" in log_dir:
mark = "Mk5"
else:
mark = "Mk4"
procs.append((mn+mod_add, p, out_fd, sim, mark, time.time())) procs.append((mn+mod_add, p, out_fd, sim, mark, time.time()))
print(f'started: {mark:<6}{mn+mod_add:<30}{sim.socket.split("-")[-1].split(".")[0]:<10}') print(f'started: {mark:<6}{mn+mod_add:<30}{sim.socket.split("-")[-1].split(".")[0]:<10}')

View File

@ -91,10 +91,13 @@ def verify_msg_bip322_por(cap_story, need_keypress, press_select, press_cancel,
[["p2pkh", None, None]] + ([["p2wpkh", None, 1000000]] * 5) + ([["p2sh-p2wpkh", None, 10000000]] * 5), [["p2pkh", None, None]] + ([["p2wpkh", None, 1000000]] * 5) + ([["p2sh-p2wpkh", None, 10000000]] * 5),
]) ])
def test_bip322_por(msg, ins, bip322_txn, start_sign, end_sign, cap_story, need_keypress, def test_bip322_por(msg, ins, bip322_txn, start_sign, end_sign, cap_story, need_keypress,
press_select, verify_msg_bip322_por): press_select, verify_msg_bip322_por, sim_root_dir):
num_ins = len(ins) num_ins = len(ins)
amt = sum([i[2] or 0 for i in ins]) amt = sum([i[2] or 0 for i in ins])
psbt, msg_challenge = bip322_txn(ins, msg=msg) psbt, msg_challenge = bip322_txn(ins, msg=msg)
with open(f'{sim_root_dir}/debug/last-b322-por.psbt', 'wb') as f:
f.write(psbt)
start_sign(psbt, finalize=True) start_sign(psbt, finalize=True)
verify_msg_bip322_por(msg.decode(), way="sd") verify_msg_bip322_por(msg.decode(), way="sd")

View File

@ -165,7 +165,10 @@ def test_b39p_refused(dev, press_cancel, pw='testing 123'):
@pytest.mark.parametrize('version', range(8)) @pytest.mark.parametrize('version', range(8))
def test_bip39_pick_words(target, version, cap_menu, pick_menu_item, cap_story, def test_bip39_pick_words(target, version, cap_menu, pick_menu_item, cap_story,
word_menu_entry, get_pp_sofar, reset_seed_words, word_menu_entry, get_pp_sofar, reset_seed_words,
press_select, only_mk4, go_to_passphrase): press_select, is_q1, go_to_passphrase):
if is_q1:
raise pytest.skip("not on Q")
# Check we can pick words # Check we can pick words
reset_seed_words() reset_seed_words()
@ -192,13 +195,16 @@ def test_bip39_pick_words(target, version, cap_menu, pick_menu_item, cap_story,
@pytest.mark.parametrize('target', ['123', '1', '4'*32, '12'*8]) @pytest.mark.parametrize('target', ['123', '1', '4'*32, '12'*8])
@pytest.mark.parametrize('backspaces', [1, 0, 12]) @pytest.mark.parametrize('backspaces', [1, 0, 12])
def test_bip39_add_nums(target, backspaces, pick_menu_item, cap_story, only_mk4, def test_bip39_add_nums(target, backspaces, pick_menu_item, cap_story, is_q1,
cap_menu, word_menu_entry, get_pp_sofar, need_keypress, cap_menu, word_menu_entry, get_pp_sofar, need_keypress,
press_select, press_cancel, go_to_passphrase): press_select, press_cancel, go_to_passphrase):
# Check we can pick numbers (appended) # Check we can pick numbers (appended)
# - also the "clear all" menu item # - also the "clear all" menu item
if is_q1:
raise pytest.skip("not on Q")
go_to_passphrase() go_to_passphrase()
pick_menu_item('Add Numbers') pick_menu_item('Add Numbers')

View File

@ -22,12 +22,12 @@ from psbt import BasicPSBT
SERVER_PUBKEY = '0231301ec4acec08c1c7d0181f4ffb8be70d693acccc86cccb8f00bf2e00fcabfd' SERVER_PUBKEY = '0231301ec4acec08c1c7d0181f4ffb8be70d693acccc86cccb8f00bf2e00fcabfd'
@pytest.fixture @pytest.fixture
def goto_ccc_menu(goto_home, pick_menu_item, is_mark4): def goto_ccc_menu(goto_home, pick_menu_item, is_q1):
def doit(): def doit():
goto_home() goto_home()
pick_menu_item("Advanced/Tools") pick_menu_item("Advanced/Tools")
pick_menu_item("Spending Policy") pick_menu_item("Spending Policy")
pick_menu_item("Co-Sign Multi." if is_mark4 else "Co-Sign Multisig (CCC)") pick_menu_item("Co-Sign Multisig (CCC)" if is_q1 else "Co-Sign Multi.")
return doit return doit

View File

@ -55,8 +55,7 @@ goto_top_menu()
@pytest.mark.parametrize('en_nfc', [ True, False] ) @pytest.mark.parametrize('en_nfc', [ True, False] )
@pytest.mark.parametrize('en_multisig', [ True, False] ) @pytest.mark.parametrize('en_multisig', [ True, False] )
def test_menu_contents(set_hobble, pick_menu_item, cap_menu, en_okeys, en_notes, settings_set, def test_menu_contents(set_hobble, pick_menu_item, cap_menu, en_okeys, en_notes, settings_set,
need_some_notes, is_q1, is_mark4, en_nfc, sim_exec, en_multisig, need_some_notes, is_q1, en_nfc, sim_exec, en_multisig, vdisk_disabled):
vdisk_disabled):
# just enough to pass/fail the menu predicates! # just enough to pass/fail the menu predicates!
settings_set('seedvault', True) settings_set('seedvault', True)

View File

@ -114,7 +114,10 @@ def compute_policy_hash(policy):
return b2a_hex(sha256(json_.encode()).digest()).decode() return b2a_hex(sha256(json_.encode()).digest()).decode()
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def enable_hsm_commands(dev, sim_exec, only_mk4): def enable_hsm_commands(dev, sim_exec, is_q1):
if is_q1:
raise pytest.skip("Q does not have HSM support")
cmd = 'from glob import settings; settings.set("hsmcmd", 1)' cmd = 'from glob import settings; settings.set("hsmcmd", 1)'
sim_exec(cmd) sim_exec(cmd)
yield yield

View File

@ -134,21 +134,13 @@ def test_psbt_proxy_parsing(fn, sim_execfile, sim_exec, src_root_dir, sim_root_d
assert oo == rb assert oo == rb
@pytest.mark.unfinalized @pytest.mark.unfinalized
def test_speed_test(dev, fake_txn, is_mark3, is_mark4, start_sign, end_sign, def test_speed_test(dev, fake_txn, start_sign, end_sign, press_select, press_cancel, sim_root_dir):
press_select, press_cancel, sim_root_dir, is_q1):
# measure time to sign a larger txn # measure time to sign a larger txn
if is_mark4 or is_q1: # Mk4: expect
# Mk4: expect # 20/250 => 15.5s (or 10.0 if seed is cached)
# 20/250 => 15.5s (or 10.0 if seed is cached) # 200/500 => 96.3s
# 200/500 => 96.3s num_in = 20
num_in = 20 num_out = 250
num_out = 250
elif is_mark3:
num_in = 20
num_out = 250
else:
num_in = 9
num_out = 100
psbt = fake_txn(num_in, num_out, dev.master_xpub, segwit_in=True) psbt = fake_txn(num_in, num_out, dev.master_xpub, segwit_in=True)
@ -179,9 +171,7 @@ if 0:
# see <https://bitcoin.stackexchange.com/questions/11542> # see <https://bitcoin.stackexchange.com/questions/11542>
# - how big woudl PSBT be? # - how big woudl PSBT be?
# - not a great test case because so slow. # - not a great test case because so slow.
def test_mega_txn(fake_txn, is_mark4, start_sign, end_sign, dev): def test_mega_txn(fake_txn, start_sign, end_sign, dev):
if not is_mark4:
raise pytest.xfail('no way')
psbt = fake_txn(5569, 1, dev.master_xpub) psbt = fake_txn(5569, 1, dev.master_xpub)
@ -3634,10 +3624,13 @@ def test_txn_nVersion_zero(segwit, fake_txn, start_sign, cap_story, goto_home):
goto_home() goto_home()
def hack(psbt): def hack(psbt):
t = CTransaction() if psbt.is_v2():
t.deserialize(BytesIO(psbt.txn)) psbt.txn_version = 0
t.nVersion = 0 else:
psbt.txn = t.serialize() t = CTransaction()
t.deserialize(BytesIO(psbt.txn))
t.nVersion = 0
psbt.txn = t.serialize()
psbt = fake_txn(1, 2, segwit_in=segwit, change_outputs=[0], psbt_hacker=hack) psbt = fake_txn(1, 2, segwit_in=segwit, change_outputs=[0], psbt_hacker=hack)
start_sign(psbt) start_sign(psbt)

View File

@ -11,7 +11,7 @@ from ckcc.protocol import CCProtocolPacker
@pytest.fixture @pytest.fixture
def goto_sssp_menu(goto_home, pick_menu_item, is_mark4): def goto_sssp_menu(goto_home, pick_menu_item):
def doit(): def doit():
goto_home() goto_home()
pick_menu_item("Advanced/Tools") pick_menu_item("Advanced/Tools")

View File

@ -92,18 +92,12 @@ def upgrade_by_sd(open_microsd, cap_story, pick_menu_item, goto_home, press_sele
@pytest.mark.parametrize('mode', ['compat', 'incompat']) @pytest.mark.parametrize('mode', ['compat', 'incompat'])
@pytest.mark.parametrize('transport', ['sd', 'usb']) @pytest.mark.parametrize('transport', ['sd', 'usb'])
def test_hacky_upgrade(mode, cap_story, transport, dev, sim_exec, make_firmware, upload_file, def test_hacky_upgrade(mode, cap_story, transport, dev, sim_exec, make_firmware, upload_file,
upgrade_by_sd, press_cancel, is_q1): upgrade_by_sd, press_cancel, is_q1, is_mark5):
if mode == 'compat': if mode == 'compat':
data = make_firmware("q1" if is_q1 else 4) data = make_firmware("q1" if is_q1 else (5 if is_mark5 else 4))
elif mode == 'incompat': elif mode == 'incompat':
if is_q1: data = make_firmware(4 if is_q1 else "q1")
data = make_firmware(4)
else:
with pytest.raises(RuntimeError) as err:
make_firmware(3)
assert "too big for our USB upgrades" in str(err)
return
hdr = data[FW_HEADER_OFFSET:FW_HEADER_OFFSET+FW_HEADER_SIZE] hdr = data[FW_HEADER_OFFSET:FW_HEADER_OFFSET+FW_HEADER_SIZE]

View File

@ -214,7 +214,7 @@ def test_virtdisk_signing(encoding, num_outs, partial, try_sign_virtdisk, fake_t
if 0: if 0:
@pytest.mark.parametrize('num_outs', [ 1, 20, 250]) @pytest.mark.parametrize('num_outs', [ 1, 20, 250])
def test_virtdisk_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress, cap_story, only_mk4): def test_virtdisk_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress, cap_story):
# Read signing result (transaction) over NFC, decode it. # Read signing result (transaction) over NFC, decode it.
psbt = fake_txn(1, num_outs) psbt = fake_txn(1, num_outs)
orig, result = try_sign(psbt, accept=True, finalize=True) orig, result = try_sign(psbt, accept=True, finalize=True)

View File

@ -33,6 +33,7 @@ wallet (on testnet, always with the same seed). But there are other options:
- `--mk2` => emulate mark2 hardware (older micro, etc), default is current-gen (mark4) - `--mk2` => emulate mark2 hardware (older micro, etc), default is current-gen (mark4)
- `--mk3` => emulate mark3 hardware - `--mk3` => emulate mark3 hardware
- `--mk4` => emulate mark4 hardware - `--mk4` => emulate mark4 hardware
- `--mk5` => emulate mark5 hardware (default)
- `--q1` => emulate Q1 hardware - `--q1` => emulate Q1 hardware
- `--addr` => go to the address explorer at startup - `--addr` => go to the address explorer at startup
- `--xw` => go to the wallet export submenu - `--xw` => go to the wallet export submenu

BIN
unix/mk5-images/background.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
unix/mk5-images/led-red.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -439,15 +439,6 @@ class LCDSimulator(SimulatedScreen):
self.draw_single_led(spriterenderer, 465, 315) self.draw_single_led(spriterenderer, 465, 315)
class OLEDSimulator(SimulatedScreen): class OLEDSimulator(SimulatedScreen):
# top-left coord of OLED area; size is 1:1 with real pixels... 128x64 pixels
OLED_ACTIVE = (46, 85)
# keypad touch buttons
KEYPAD_LEFT = 52
KEYPAD_TOP = 216
KEYPAD_PITCH = 73
background_img = 'mk4-images/background.png'
def __init__(self, factory): def __init__(self, factory):
self.movie = None self.movie = None
@ -462,12 +453,8 @@ class OLEDSimulator(SimulatedScreen):
sdl2.ext.fill(s, self.bg) sdl2.ext.fill(s, self.bg)
self.mv = sdl2.ext.pixels2d(self.sprite, transpose=False) self.mv = sdl2.ext.pixels2d(self.sprite, transpose=False)
# for genuine/caution lights and other LED's self.load_leds(factory)
self.led_red = factory.from_image("mk4-images/led-red.png")
self.led_green = factory.from_image("mk4-images/led-green.png")
self.led_sdcard = factory.from_image("mk4-images/led-sd.png")
self.led_usb = factory.from_image("mk4-images/led-usb.png")
def new_contents(self, readable): def new_contents(self, readable):
# got bytes for new update. # got bytes for new update.
@ -509,13 +496,59 @@ class OLEDSimulator(SimulatedScreen):
SD_LED = 0x2 SD_LED = 0x2
USB_LED = 0x4 USB_LED = 0x4
spriterenderer.render(self.led_green if (active_set & GEN_LED) else self.led_red) spriterenderer.render(self.led_genuine if (active_set & GEN_LED) else self.led_unsafe)
if active_set & SD_LED: if active_set & SD_LED:
spriterenderer.render(self.led_sdcard) spriterenderer.render(self.led_sdcard)
if active_set & USB_LED: if active_set & USB_LED:
spriterenderer.render(self.led_usb) spriterenderer.render(self.led_usb)
class Mk4OLEDSimulator(OLEDSimulator):
# top-left coord of OLED area; size is 1:1 with real pixels... 128x64 pixels
OLED_ACTIVE = (46, 85)
# keypad touch buttons
KEYPAD_LEFT = 52
KEYPAD_TOP = 216
KEYPAD_PITCH = 73
background_img = 'mk4-images/background.png'
def load_leds(self, factory):
# for genuine/caution lights and other LED's
# - these are pre-positioned where they need to end up
self.led_unsafe = factory.from_image("mk4-images/led-red.png")
self.led_genuine = factory.from_image("mk4-images/led-green.png")
self.led_sdcard = factory.from_image("mk4-images/led-sd.png")
self.led_usb = factory.from_image("mk4-images/led-usb.png")
class Mk5OLEDSimulator(OLEDSimulator):
OLED_ACTIVE = (28, 41)
# keypad touch buttons
KEYPAD_LEFT = 28
KEYPAD_TOP = 125
KEYPAD_PITCH = 42
background_img = 'mk5-images/background.png'
def load_leds(self, factory):
# position each carefully
r = factory.from_image("mk5-images/led-red.png")
g = factory.from_image("mk5-images/led-green.png")
self.led_unsafe = r.subsprite(r.area)
self.led_genuine = g.subsprite(g.area)
self.led_sdcard = g.subsprite(g.area)
self.led_usb = g.subsprite(g.area)
self.led_unsafe.position = (14, -9)
self.led_genuine.position = (-1, -9)
self.led_sdcard.position = (-14, 23)
self.led_usb.position = (65, 283)
def load_shared_mod(name, path): def load_shared_mod(name, path):
# load indicated file.py as a module # load indicated file.py as a module
# from <https://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path> # from <https://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path>
@ -783,7 +816,15 @@ Q1 specials:
factory = sdl2.ext.SpriteFactory(sdl2.ext.SOFTWARE) factory = sdl2.ext.SpriteFactory(sdl2.ext.SOFTWARE)
simdis = (OLEDSimulator if not is_q1 else LCDSimulator)(factory) if is_q1:
simdis = LCDSimulator(factory)
elif ('--mk4' in sys.argv):
# retro look
simdis = Mk4OLEDSimulator(factory)
else:
# default: Mk5
simdis = Mk5OLEDSimulator(factory)
bg = factory.from_image(simdis.background_img) bg = factory.from_image(simdis.background_img)
window = sdl2.ext.Window("Coldcard Simulator", size=bg.size, position=(100, 100)) window = sdl2.ext.Window("Coldcard Simulator", size=bg.size, position=(100, 100))
@ -952,6 +993,7 @@ Q1 specials:
if not pressed: if not pressed:
numpad_tx.write(b'\0') # all up signal numpad_tx.write(b'\0') # all up signal
while running: while running:
events = sdl2.ext.get_events() events = sdl2.ext.get_events()
for event in events: for event in events:

View File

@ -26,10 +26,10 @@ SET_CHARGE_PUMP = const(0x8d)
# Subclassing FrameBuffer provides support for graphics primitives # Subclassing FrameBuffer provides support for graphics primitives
# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
class SSD1306(framebuf.FrameBuffer): class SSD1306(framebuf.FrameBuffer):
def __init__(self, width, height, external_vcc): def __init__(self, width, height, is_mk5):
self.width = width self.width = width
self.height = height self.height = height
self.external_vcc = external_vcc self.is_mk5 = is_mk5
self.pages = self.height // 8 self.pages = self.height // 8
self.buffer = bytearray(self.pages * self.width) self.buffer = bytearray(self.pages * self.width)
super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB) super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
@ -72,12 +72,20 @@ class SSD1306(framebuf.FrameBuffer):
self.write_cmd(self.pages - 1) self.write_cmd(self.pages - 1)
self.write_data(self.buffer) self.write_data(self.buffer)
def busy_bar(self, enable, pattern):
# Render a continuous activity (not progress) bar in lower 8 lines of display
if enable:
# just show as static pattern
t = self.buffer[:-128] + pattern
self.write_data(t)
class SSD1306_SPI(SSD1306): class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): def __init__(self, width, height, spi, dc, res, cs, is_mk5=False):
import sys import sys
self.pipe = open(int(sys.argv[1]), 'wb') self.pipe = open(int(sys.argv[1]), 'wb')
super().__init__(width, height, external_vcc) super().__init__(width, height, is_mk5)
def write_cmd(self, cmd): def write_cmd(self, cmd):
pass pass

View File

@ -32,8 +32,8 @@ def get_header_value(fld_name):
return b'\x18\x07\x11\x19S\x08\x00\x00' return b'\x18\x07\x11\x19S\x08\x00\x00'
return 0 return 0
# default is Mk4 hardware # default is Mk5 hardware
hw_label = 'mk4' hw_label = 'mk5'
has_608 = True has_608 = True
has_membrane = True has_membrane = True
supports_hsm = True supports_hsm = True
@ -47,7 +47,7 @@ has_qwerty = False
is_edge = False is_edge = False
if '--mk1' in sys.argv: if '--mk1' in sys.argv:
# doubt this works still # doubt this works anymore
hw_label = 'mk1' hw_label = 'mk1'
has_608 = False has_608 = False
has_membrane = False has_membrane = False
@ -72,6 +72,9 @@ if '--mk3' in sys.argv:
has_nfc = False has_nfc = False
supports_hsm = False supports_hsm = False
if '--mk4' in sys.argv:
hw_label = 'mk4'
mk_num = int(hw_label[2:]) mk_num = int(hw_label[2:])
if '--q1' in sys.argv: if '--q1' in sys.argv:
@ -81,6 +84,7 @@ if '--q1' in sys.argv:
has_battery = True has_battery = True
has_qwerty = True has_qwerty = True
supports_hsm = False supports_hsm = False
mk_num = 4
from public_constants import MAX_TXN_LEN, MAX_UPLOAD_LEN from public_constants import MAX_TXN_LEN, MAX_UPLOAD_LEN
from public_constants import MAX_TXN_LEN_MK4, MAX_UPLOAD_LEN_MK4 from public_constants import MAX_TXN_LEN_MK4, MAX_UPLOAD_LEN_MK4