diff --git a/cli/signit.py b/cli/signit.py index 62a2bb43..31ddff22 100755 --- a/cli/signit.py +++ b/cli/signit.py @@ -208,8 +208,9 @@ def readback(fname): if v & MK_2_OK: d.append('Mk2') if v & MK_3_OK: d.append('Mk3') 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_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?') v = nv + '+'.join(d) 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('--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('--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', help='Make downgrade attack test version', default=0) @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() body = open(build_dir + '/firmware1.bin', 'rb').read() - if hw_compat in { 'mk4', '4'}: - hw_compat = MK_4_OK + if hw_compat in { 'mk4', '4', 'mk5', '5', 'mk' }: + # 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': hw_compat = MK_Q1_OK elif hw_compat in { 'mk3', '3'}: diff --git a/graphics/graphics_mk4.py b/graphics/graphics_mk4.py index 4db6a7dd..7b2e7522 100644 --- a/graphics/graphics_mk4.py +++ b/graphics/graphics_mk4.py @@ -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') + 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@') 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') diff --git a/graphics/mono/mk5_nfc_1.txt b/graphics/mono/mk5_nfc_1.txt new file mode 100644 index 00000000..ba3527e5 --- /dev/null +++ b/graphics/mono/mk5_nfc_1.txt @@ -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 diff --git a/graphics/mono/mk5_nfc_2.txt b/graphics/mono/mk5_nfc_2.txt new file mode 100644 index 00000000..71317e94 --- /dev/null +++ b/graphics/mono/mk5_nfc_2.txt @@ -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 diff --git a/graphics/mono/mk5_nfc_3.txt b/graphics/mono/mk5_nfc_3.txt new file mode 100644 index 00000000..86386d96 --- /dev/null +++ b/graphics/mono/mk5_nfc_3.txt @@ -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 diff --git a/graphics/mono/mk5_nfc_4.txt b/graphics/mono/mk5_nfc_4.txt new file mode 100644 index 00000000..7bdcab91 --- /dev/null +++ b/graphics/mono/mk5_nfc_4.txt @@ -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 diff --git a/shared/actions.py b/shared/actions.py index bd59671c..996ae369 100644 --- a/shared/actions.py +++ b/shared/actions.py @@ -2397,9 +2397,23 @@ async def microsd_2fa(*a): async def keyboard_test(*a): # to aid keyboard testing/dev - from ux import ux_input_text - await ux_input_text('', max_len=128, scan_ok=True, confirm_exit=False, - prompt='Keyboard Test', placeholder='(type whatever)') + if version.has_qwerty: + await ux_input_text('', max_len=128, scan_ok=True, confirm_exit=False, + 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 diff --git a/shared/display.py b/shared/display.py index 6bfd83f9..805f1e36 100644 --- a/shared/display.py +++ b/shared/display.py @@ -2,9 +2,8 @@ # # display.py - OLED rendering # -import machine, uzlib, ckcc, utime +import machine, uzlib, ckcc, utime, version from ssd1306 import SSD1306_SPI -from version import is_devmode import framebuf from graphics_mk4 import Graphics from charcodes import OUT_CTRL_TITLE, OUT_CTRL_ADDRESS @@ -35,11 +34,14 @@ class Display: dc_pin = Pin('PA8', Pin.OUT) cs_pin = Pin('PA4', Pin.OUT) - try: - self.dis = SSD1306_SPI(128, 64, spi, dc_pin, reset_pin, cs_pin) - except OSError: - print("OLED unplugged?") - raise + if version.mk_num == 5: + # Early revs (A-D) needed this pin asserted to enable +12v to OLED + # - removed in rev E and later boards, but keep here for dev boards + # - remove this in 2027 + 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.clear() @@ -142,7 +144,7 @@ class Display: self.icon(128-3, 1, 'scroll') 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.text(-2, 21, 'D', font=FontTiny, invert=1) self.text(-2, 28, 'E', font=FontTiny, invert=1) @@ -204,61 +206,20 @@ class Display: def busy_bar(self, enable): # 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: - # stop animation, and redraw old (new) screen - self.write_cmds(cleanup) + self.dis.busy_bar(False, None) self.show() else: - - # a pattern that repeats nicely mod 128 + # Need a pattern that repeats nicely mod 128 # - 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(): - # 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) + self.dis.busy_bar(True, pat) def set_brightness(self, val): # normal = 0x7f, brightness=0xff, dim=0x00 (but they are all very similar) - self.dis.write_cmd(0x81) # Set Contrast Control - self.dis.write_cmd(val) + return self.dis.contrast(val) def menu_draw(self, ry, msg, is_sel, is_checked, space_indicators): # draw a menu item, perhaps selected, checked. diff --git a/shared/flow.py b/shared/flow.py index 1aef9597..09b42655 100644 --- a/shared/flow.py +++ b/shared/flow.py @@ -307,6 +307,8 @@ DebugFunctionsMenu = [ # xxxxxxxxxxxxxxxx MenuItem("Keyboard Test", f=keyboard_test), 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: except', f=debug_except), MenuItem('Check: BL FW', f=check_firewall_read), diff --git a/shared/mk4.py b/shared/mk4.py index 69b024ce..3a3e229d 100644 --- a/shared/mk4.py +++ b/shared/mk4.py @@ -1,6 +1,6 @@ # (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 diff --git a/shared/nfc.py b/shared/nfc.py index c66c6548..115a3865 100644 --- a/shared/nfc.py +++ b/shared/nfc.py @@ -419,7 +419,8 @@ class NFCHandler: dis.text(None, -3, line2) else: 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 phase = -1 diff --git a/shared/selftest.py b/shared/selftest.py index 4550b171..5f105ce0 100644 --- a/shared/selftest.py +++ b/shared/selftest.py @@ -171,7 +171,7 @@ async def test_secure_element(): dis.clear() - if version.has_qwerty: + if version.has_qwerty or version.mk_num == 5: dis.text(0, 0, "^^-- Green? " if gg else " ^^-- Red?") else: if gg: diff --git a/shared/ssd1306.py b/shared/ssd1306.py index 11a14058..c08a0642 100644 --- a/shared/ssd1306.py +++ b/shared/ssd1306.py @@ -1,6 +1,6 @@ # (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 # @@ -28,49 +28,81 @@ SET_VCOM_DESEL = const(0xdb) SET_CHARGE_PUMP = const(0x8d) # Subclassing FrameBuffer provides support for graphics primitives -# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html +# see +# class SSD1306(framebuf.FrameBuffer): - def __init__(self, width, height, external_vcc): + def __init__(self, width, height, is_mk5): self.width = width self.height = height - self.external_vcc = external_vcc + self.is_mk5 = is_mk5 self.pages = self.height // 8 - #self.buffer = bytearray(self.pages * self.width) - 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) self.init_display() def init_display(self): - for cmd in ( - SET_DISP | 0x00, # off - # address setting - SET_MEM_ADDR, 0x00, # horizontal - # resolution and layout - SET_DISP_START_LINE | 0x00, - SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 - SET_MUX_RATIO, self.height - 1, - SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 - SET_DISP_OFFSET, 0x00, - SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, - # timing and driving scheme - SET_DISP_CLK_DIV, 0xF0, - SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, - SET_VCOM_DESEL, 0x30, # 0.83*Vcc - # display - SET_CONTRAST, 0xff, # maximum - SET_ENTIRE_ON, # output follows RAM contents - SET_NORM_INV, # not inverted - # charge pump - SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, - SET_DISP | 0x01): # on - self.write_cmd(cmd) + if not self.is_mk5: + # Mk4 and earlier + cmds = ( + SET_DISP | 0x00, # display off + # address setting + SET_MEM_ADDR, 0x00, # horizontal + # resolution and layout + SET_DISP_START_LINE | 0x00, + SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 + SET_MUX_RATIO, self.height - 1, + SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 + SET_DISP_OFFSET, 0x00, + SET_COM_PIN_CFG, 0x12, + # timing and driving scheme + SET_DISP_CLK_DIV, 0xF0, + SET_PRECHARGE, 0xf1, + SET_VCOM_DESEL, 0x30, # 0.83*Vcc + # display + SET_CONTRAST, 0xff, # maximum + SET_ENTIRE_ON, # output follows RAM contents + SET_NORM_INV, # not inverted + # charge pump + 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.show() + self.write_cmd(SET_DISP | 0x01) + + def write_cmds(self, cmds): + for c in cmds: + self.write_cmd(c) + def poweroff(self): self.write_cmd(SET_DISP | 0x00) @@ -78,6 +110,10 @@ class SSD1306(framebuf.FrameBuffer): self.write_cmd(SET_DISP | 0x01) 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(contrast) @@ -85,56 +121,113 @@ class SSD1306(framebuf.FrameBuffer): self.write_cmd(SET_NORM_INV | (invert & 1)) 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(x0) - self.write_cmd(x1) + self.write_cmd(0) + self.write_cmd(self.width - 1) + self.write_cmd(SET_PAGE_ADDR) self.write_cmd(0) self.write_cmd(self.pages - 1) + 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): - def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): - dc.init(dc.OUT, value=0) - res.init(res.OUT, value=0) - cs.init(cs.OUT, value=1) + def __init__(self, width, height, spi, dc, res, cs, is_mk5=False): self.spi = spi self.dc = dc - self.res = res self.cs = cs - self.res(1) + self.res = res + + # initial states + dc(0) + cs(1) + + # reset sequence + res(1) time.sleep_ms(1) - self.res(0) + res(0) time.sleep_ms(10) - self.res(1) - super().__init__(width, height, external_vcc) + res(1) + + 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): - self.spi.init(baudrate=SPI_RATE, polarity=0, phase=0) + self._setup_spi() self.cs(1) self.dc(0) self.cs(0) - try: - self.spi.write(bytearray([cmd])) - except: - print("SPI[cmd]: %r" % self.spi) + self.spi.write(bytearray([cmd])) self.cs(1) def write_data(self, buf): - self.spi.init(baudrate=SPI_RATE, polarity=0, phase=0) + self._setup_spi() self.cs(1) self.dc(1) self.cs(0) - try: - self.spi.write(buf) - except: - print("SPI[data]: %r" % self.spi) + self.spi.write(buf) self.cs(1) + +# EOF diff --git a/shared/utils.py b/shared/utils.py index 93d60294..3bfa4f20 100644 --- a/shared/utils.py +++ b/shared/utils.py @@ -383,7 +383,7 @@ def check_firmware_hdr(hdr, binary_size): # - hdr must be a bytearray(FW_HEADER_SIZE+more) 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 version import hw_label import callgate @@ -412,6 +412,8 @@ def check_firmware_hdr(hdr, binary_size): ok = (hw_compat & MK_3_OK) elif hw_label == 'mk4': ok = (hw_compat & MK_4_OK) + elif hw_label == 'mk5': + ok = (hw_compat & MK_5_OK) elif hw_label == 'q1': ok = (hw_compat & MK_Q1_OK) diff --git a/shared/version.py b/shared/version.py index 3fab78a9..ee90a290 100644 --- a/shared/version.py +++ b/shared/version.py @@ -80,6 +80,7 @@ def probe_system(): from sigheader import RAM_BOOT_FLAGS, RBF_FACTORY_MODE import ckcc, callgate, machine + from machine import Pin hw_label = 'mk4' has_608 = True @@ -97,7 +98,7 @@ def probe_system(): # detect Q1 based on pins.csv 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 num_sd_slots = 2 hw_label = 'q1' @@ -108,6 +109,15 @@ def probe_system(): except ValueError: 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: # - did we just install a new version, for example (obsolete in mk4) # - are we running in "factory mode" with flash un-secured? diff --git a/stm32/COLDCARD_MK4/pins.csv b/stm32/COLDCARD_MK4/pins.csv index 5a285429..70a861f6 100644 --- a/stm32/COLDCARD_MK4/pins.csv +++ b/stm32/COLDCARD_MK4/pins.csv @@ -89,3 +89,8 @@ SE2_SDA,PB14 NFC_SCL,PB6 NFC_SDA,PB7 NFC_ED,PC4 +STRAP_MK5,PE0 +STRAP_S1,PE1 +STRAP_S2,PE2 +STRAP_S3,PE3 +V12EN,PC1 diff --git a/stm32/MK4-Makefile b/stm32/MK-Makefile similarity index 83% rename from stm32/MK4-Makefile rename to stm32/MK-Makefile index 43ccdcc5..1ae8915b 100644 --- a/stm32/MK4-Makefile +++ b/stm32/MK-Makefile @@ -2,20 +2,20 @@ # # 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 FIRMWARE_BASE = 0x08020000 BOOTLOADER_BASE = 0x08000000 -HW_MODEL = mk4 -PARENT_MKFILE = MK4-Makefile +HW_MODEL = mk +PARENT_MKFILE = MK-Makefile # This is release of the bootloader that will be built into the factory.dfu BOOTLOADER_VERSION = 3.2.1 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. # - caution, the bootrom will not accept version < 3.0.0 diff --git a/stm32/Makefile b/stm32/Makefile index 6e5cb457..818524f9 100644 --- a/stm32/Makefile +++ b/stm32/Makefile @@ -5,23 +5,23 @@ # # Normally you must use: # -# make -f MK4-Makefile +# make -f MK-Makefile # or # make -f Q1-Makefile # .DEFAULT_GOAL := all .DEFAULT all: - $(MAKE) DEBUG_BUILD=1 -f Q1-Makefile $(MAKECMDGOALS) + $(MAKE) DEBUG_BUILD=1 -f MK-Makefile $(MAKECMDGOALS) clean clobber repro: @echo You should do either: @echo @echo " make" -f Q1-Makefile $(MAKECMDGOALS) @echo "-OR-" - @echo " make" -f MK4-Makefile $(MAKECMDGOALS) + @echo " make" -f MK-Makefile $(MAKECMDGOALS) rc1 rc2 release: - make -f Q1-Makefile $(MAKECMDGOALS) && make -f MK4-Makefile $(MAKECMDGOALS) + make -f Q1-Makefile $(MAKECMDGOALS) && make -f MK-Makefile $(MAKECMDGOALS) # EOF diff --git a/stm32/mk4-bootloader/gpio.c b/stm32/mk4-bootloader/gpio.c index 71146421..c858f22a 100644 --- a/stm32/mk4-bootloader/gpio.c +++ b/stm32/mk4-bootloader/gpio.c @@ -8,7 +8,7 @@ #include "gpio.h" #include "stm32l4xx_hal.h" -// PA0 - onewire bus for 608a +// PA0 - onewire bus for 608 #define ONEWIRE_PIN GPIO_PIN_0 #define ONEWIRE_PORT GPIOA @@ -62,15 +62,20 @@ gpio_setup(void) // SD active LED: PC7 // 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 = { - .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, .Pull = GPIO_NOPULL, .Speed = GPIO_SPEED_FREQ_LOW, }; + 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 @@ -83,6 +88,18 @@ gpio_setup(void) 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 // TEST CODE -- keep @@ -125,5 +142,14 @@ gpio_setup(void) #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 diff --git a/stm32/mk4-bootloader/gpio.h b/stm32/mk4-bootloader/gpio.h index 6d78cf23..dd93072f 100644 --- a/stm32/mk4-bootloader/gpio.h +++ b/stm32/mk4-bootloader/gpio.h @@ -6,10 +6,12 @@ // set directions, lock critical ones, etc. void gpio_setup(void); -// sample the DFU button -inline bool dfu_button_pressed(void) { return false; } - #ifdef FOR_Q1_ONLY // kill system power; instant extern void turn_power_off(void); #endif + +// sample the strapping pin to know if mk4 or 5 +extern bool is_mk5(void); + +// EOF diff --git a/stm32/mk4-bootloader/oled.c b/stm32/mk4-bootloader/oled.c index 18823c07..6eded6b2 100644 --- a/stm32/mk4-bootloader/oled.c +++ b/stm32/mk4-bootloader/oled.c @@ -5,6 +5,7 @@ #include "delay.h" #include "rng.h" #include "console.h" +#include "gpio.h" // for is_mk5() #include "stm32l4xx_hal.h" #include @@ -14,7 +15,7 @@ // Reset and config sequence. // // As measured! No attempt to understand them here. -static const uint8_t reset_commands[] = { +static const uint8_t reset_commands_mk4[] = { 0xae, // display off 0x20, 0x00, // horz addr-ing mode 0x40, // ram display start line: 0 @@ -33,6 +34,28 @@ static const uint8_t reset_commands[] = { 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. // static const uint8_t before_show[] = { @@ -50,6 +73,9 @@ static const uint8_t before_show[] = { #define SPI_SCK GPIO_PIN_5 #define SPI_MOSI GPIO_PIN_7 +// port C, Mk5 only +#define VCC_EN_PIN GPIO_PIN_1 + #ifndef DISABLE_OLED static SPI_HandleTypeDef spi_port; @@ -177,7 +203,7 @@ oled_setup(void) HAL_GPIO_Init(GPIOA, &setup); // 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); // 10ms low-going pulse on reset pin @@ -196,7 +222,14 @@ oled_setup(void) //SPI1->CR1 = 0x354; // 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(); } @@ -429,13 +462,12 @@ oled_factory_busy(void) // 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 - //oled_spi_setup(); static const uint8_t setup[] = { 0x21, 0x00, 0x7f, // setup column address range (start, end): 0-127 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 0x26, // scroll leftwards (stock ticker mode) 0, // placeholder @@ -445,6 +477,21 @@ oled_factory_busy(void) 0, 0xff, // placeholders 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]; for(int x=0; x<128; x++) { @@ -454,7 +501,11 @@ oled_factory_busy(void) oled_write_cmd_sequence(sizeof(setup), setup); 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 diff --git a/stm32/shared.mk b/stm32/shared.mk index a568e056..6cf47be6 100644 --- a/stm32/shared.mk +++ b/stm32/shared.mk @@ -1,6 +1,6 @@ # (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 diff --git a/stm32/sigheader.h b/stm32/sigheader.h index e0868ea3..dec3c99d 100644 --- a/stm32/sigheader.h +++ b/stm32/sigheader.h @@ -70,8 +70,7 @@ typedef struct { #define MK_3_OK 0x04 #define MK_4_OK 0x08 #define MK_Q1_OK 0x10 -// RFU: -#define MK_6_OK 0x20 +#define MK_5_OK 0x20 // (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! diff --git a/stm32/sigheader.py b/stm32/sigheader.py index 609bf6fe..ef11649b 100644 --- a/stm32/sigheader.py +++ b/stm32/sigheader.py @@ -54,8 +54,7 @@ MK_2_OK = 0x02 MK_3_OK = 0x04 MK_4_OK = 0x08 MK_Q1_OK = 0x10 -# RFU: -MK_6_OK = 0x20 +MK_5_OK = 0x20 # (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! diff --git a/testing/clone_tests.py b/testing/clone_tests.py index 31ab03a1..d4b1e4e5 100644 --- a/testing/clone_tests.py +++ b/testing/clone_tests.py @@ -9,10 +9,23 @@ from ckcc_protocol.client import ColdcardDevice def _clone(source, target): - assert source in ["Q", "Mk4"] - assert target in ["Q", "Mk4"] - source_sim_arg, source_is_Q = ("--q1", True) if source == "Q" else ("", False) - target_sim_arg, target_is_Q = ("--q1", True) if target == "Q" else ("", False) + allowed_devices = ["Q", "Mk4", "Mk5"] + assert source in allowed_devices + assert target in allowed_devices + + 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 clean_sim_data() # remove all from previous @@ -105,7 +118,7 @@ def _clone(source, target): 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): _clone(source, target) time.sleep(1) diff --git a/testing/conftest.py b/testing/conftest.py index b7f6ba50..bf246de2 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -1518,19 +1518,22 @@ def is_mark3(dev_hw_label): def is_mark4(dev_hw_label): return (dev_hw_label == 'mk4') +@pytest.fixture(scope='session') +def is_mark5(dev_hw_label): + return (dev_hw_label == 'mk5') + @pytest.fixture(scope='session') def is_q1(dev_hw_label): return (dev_hw_label == 'q1') - @pytest.fixture(scope="session") def is_headless(request): return request.config.getoption('--headless') @pytest.fixture(scope='session') -def is_mark4plus(is_mark4, is_q1): - # mark4 PLUS ... so Q1 and Mk4 - return is_mark4 or is_q1 +def is_mark4plus(is_mark4, is_q1, is_mark5): + # mark4 PLUS ... so Q1, Mk4 and Mk5 + return is_mark4 or is_q1 or is_mark5 @pytest.fixture(scope='session') def mk_num(dev_hw_label): @@ -1544,35 +1547,29 @@ def mk_num(dev_hw_label): else: 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') def only_q1(is_q1): if not is_q1: raise pytest.skip("Q only") @pytest.fixture(scope='session') -def needs_nfc(is_mark4, is_q1): - if is_mark4 or is_q1: +def needs_nfc(is_mark4plus): + if is_mark4plus: return raise pytest.skip("Needs NFC support") @pytest.fixture(scope='session') -def needs_virtdisk(is_mark4, is_q1): +def needs_virtdisk(is_mark4plus): # TODO/MAYBE: test if feature enabled in settings? - if is_mark4 or is_q1: + if is_mark4plus: return raise pytest.skip("Needs VirtDisk support") @pytest.fixture(scope='session') def only_mk4plus(mk_num): - # Mk4 and Q1 + # Mk4, Q1 and Mk5 if mk_num < 4: - raise pytest.skip("Mk4/Q1 only") + raise pytest.skip("Mk4/Mk5/Q1 only") @pytest.fixture(scope='session') def only_mk3(mk_num): diff --git a/testing/run_sim_tests.py b/testing/run_sim_tests.py index 29eb89e9..4782cfe0 100644 --- a/testing/run_sim_tests.py +++ b/testing/run_sim_tests.py @@ -298,7 +298,8 @@ def main(): 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("--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("--ff", action="store_true", help="Run the last failures first") parser.add_argument("--onetime", action="store_true", default=False, @@ -381,8 +382,11 @@ def main(): # proper `settings.load` _ virtual disk sim_args = ["--set", "nfc=1", "--set", "vidsk=1"] + # by default Mk5 is run if args.q1 and '--q1' not in sim_args: 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, args.ff, args.psbt2, args.q1, args.headless)) @@ -416,8 +420,10 @@ def main(): tmp_dir = "/tmp/cc-simulators" clean_directory(tmp_dir) # clean it mk4_log_dir = f"{tmp_dir}/mk4_logs" + mk5_log_dir = f"{tmp_dir}/mk5_logs" q1_log_dir = f"{tmp_dir}/q1_logs" os.makedirs(mk4_log_dir, exist_ok=True) + os.makedirs(mk5_log_dir, exist_ok=True) os.makedirs(q1_log_dir, exist_ok=True) q = [] # build priority queue @@ -455,7 +461,14 @@ def main(): break sim = ColdcardSimulator(sim_args, segregate=True) 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)) time.sleep(5) @@ -468,7 +481,12 @@ def main(): if k: cmd_list.extend(["-k", k]) 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())) print(f'started: {mark:<6}{mn+mod_add:<30}{sim.socket.split("-")[-1].split(".")[0]:<10}') diff --git a/testing/test_bip322.py b/testing/test_bip322.py index b3120ff4..f561267b 100644 --- a/testing/test_bip322.py +++ b/testing/test_bip322.py @@ -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), ]) 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) amt = sum([i[2] or 0 for i in ins]) 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) verify_msg_bip322_por(msg.decode(), way="sd") diff --git a/testing/test_bip39pw.py b/testing/test_bip39pw.py index 95b33293..c03b8ad6 100644 --- a/testing/test_bip39pw.py +++ b/testing/test_bip39pw.py @@ -165,7 +165,10 @@ def test_b39p_refused(dev, press_cancel, pw='testing 123'): @pytest.mark.parametrize('version', range(8)) def test_bip39_pick_words(target, version, cap_menu, pick_menu_item, cap_story, 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 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('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, press_select, press_cancel, go_to_passphrase): # Check we can pick numbers (appended) # - also the "clear all" menu item + if is_q1: + raise pytest.skip("not on Q") + go_to_passphrase() pick_menu_item('Add Numbers') diff --git a/testing/test_ccc.py b/testing/test_ccc.py index 4af2aead..cd5b3ac0 100644 --- a/testing/test_ccc.py +++ b/testing/test_ccc.py @@ -22,12 +22,12 @@ from psbt import BasicPSBT SERVER_PUBKEY = '0231301ec4acec08c1c7d0181f4ffb8be70d693acccc86cccb8f00bf2e00fcabfd' @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(): goto_home() pick_menu_item("Advanced/Tools") 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 diff --git a/testing/test_hobble.py b/testing/test_hobble.py index cdf2a223..96b129c5 100644 --- a/testing/test_hobble.py +++ b/testing/test_hobble.py @@ -55,8 +55,7 @@ goto_top_menu() @pytest.mark.parametrize('en_nfc', [ 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, - need_some_notes, is_q1, is_mark4, en_nfc, sim_exec, en_multisig, - vdisk_disabled): + need_some_notes, is_q1, en_nfc, sim_exec, en_multisig, vdisk_disabled): # just enough to pass/fail the menu predicates! settings_set('seedvault', True) diff --git a/testing/test_hsm.py b/testing/test_hsm.py index fb9af23c..f5e99cae 100644 --- a/testing/test_hsm.py +++ b/testing/test_hsm.py @@ -114,7 +114,10 @@ def compute_policy_hash(policy): return b2a_hex(sha256(json_.encode()).digest()).decode() @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)' sim_exec(cmd) yield diff --git a/testing/test_sign.py b/testing/test_sign.py index 9a4620ea..a8797d6b 100644 --- a/testing/test_sign.py +++ b/testing/test_sign.py @@ -134,21 +134,13 @@ def test_psbt_proxy_parsing(fn, sim_execfile, sim_exec, src_root_dir, sim_root_d assert oo == rb @pytest.mark.unfinalized -def test_speed_test(dev, fake_txn, is_mark3, is_mark4, start_sign, end_sign, - press_select, press_cancel, sim_root_dir, is_q1): +def test_speed_test(dev, fake_txn, start_sign, end_sign, press_select, press_cancel, sim_root_dir): # measure time to sign a larger txn - if is_mark4 or is_q1: - # Mk4: expect - # 20/250 => 15.5s (or 10.0 if seed is cached) - # 200/500 => 96.3s - num_in = 20 - num_out = 250 - elif is_mark3: - num_in = 20 - num_out = 250 - else: - num_in = 9 - num_out = 100 + # Mk4: expect + # 20/250 => 15.5s (or 10.0 if seed is cached) + # 200/500 => 96.3s + num_in = 20 + num_out = 250 psbt = fake_txn(num_in, num_out, dev.master_xpub, segwit_in=True) @@ -179,9 +171,7 @@ if 0: # see # - how big woudl PSBT be? # - not a great test case because so slow. - def test_mega_txn(fake_txn, is_mark4, start_sign, end_sign, dev): - if not is_mark4: - raise pytest.xfail('no way') + def test_mega_txn(fake_txn, start_sign, end_sign, dev): 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() def hack(psbt): - t = CTransaction() - t.deserialize(BytesIO(psbt.txn)) - t.nVersion = 0 - psbt.txn = t.serialize() + if psbt.is_v2(): + psbt.txn_version = 0 + else: + 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) start_sign(psbt) diff --git a/testing/test_sssp.py b/testing/test_sssp.py index 85f4e06b..e043f30a 100644 --- a/testing/test_sssp.py +++ b/testing/test_sssp.py @@ -11,7 +11,7 @@ from ckcc.protocol import CCProtocolPacker @pytest.fixture -def goto_sssp_menu(goto_home, pick_menu_item, is_mark4): +def goto_sssp_menu(goto_home, pick_menu_item): def doit(): goto_home() pick_menu_item("Advanced/Tools") diff --git a/testing/test_upgrades.py b/testing/test_upgrades.py index b89eac03..984a4799 100644 --- a/testing/test_upgrades.py +++ b/testing/test_upgrades.py @@ -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('transport', ['sd', 'usb']) 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': - 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': - if is_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 + data = make_firmware(4 if is_q1 else "q1") hdr = data[FW_HEADER_OFFSET:FW_HEADER_OFFSET+FW_HEADER_SIZE] diff --git a/testing/test_vdisk.py b/testing/test_vdisk.py index b6db2d3d..c7a5e013 100644 --- a/testing/test_vdisk.py +++ b/testing/test_vdisk.py @@ -214,7 +214,7 @@ def test_virtdisk_signing(encoding, num_outs, partial, try_sign_virtdisk, fake_t if 0: @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. psbt = fake_txn(1, num_outs) orig, result = try_sign(psbt, accept=True, finalize=True) diff --git a/unix/README.md b/unix/README.md index 03e79d71..c3a122d1 100644 --- a/unix/README.md +++ b/unix/README.md @@ -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) - `--mk3` => emulate mark3 hardware - `--mk4` => emulate mark4 hardware +- `--mk5` => emulate mark5 hardware (default) - `--q1` => emulate Q1 hardware - `--addr` => go to the address explorer at startup - `--xw` => go to the wallet export submenu diff --git a/unix/mk5-images/background.png b/unix/mk5-images/background.png new file mode 100755 index 00000000..8702e20e Binary files /dev/null and b/unix/mk5-images/background.png differ diff --git a/unix/mk5-images/led-green.png b/unix/mk5-images/led-green.png new file mode 100644 index 00000000..8058d526 Binary files /dev/null and b/unix/mk5-images/led-green.png differ diff --git a/unix/mk5-images/led-red.png b/unix/mk5-images/led-red.png new file mode 100644 index 00000000..7bc306b9 Binary files /dev/null and b/unix/mk5-images/led-red.png differ diff --git a/unix/simulator.py b/unix/simulator.py index c6363773..2852b837 100755 --- a/unix/simulator.py +++ b/unix/simulator.py @@ -439,15 +439,6 @@ class LCDSimulator(SimulatedScreen): self.draw_single_led(spriterenderer, 465, 315) 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): self.movie = None @@ -462,12 +453,8 @@ class OLEDSimulator(SimulatedScreen): sdl2.ext.fill(s, self.bg) self.mv = sdl2.ext.pixels2d(self.sprite, transpose=False) - - # for genuine/caution lights and other LED's - 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") + + self.load_leds(factory) def new_contents(self, readable): # got bytes for new update. @@ -509,13 +496,59 @@ class OLEDSimulator(SimulatedScreen): SD_LED = 0x2 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: spriterenderer.render(self.led_sdcard) if active_set & USB_LED: 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): # load indicated file.py as a module # from @@ -783,7 +816,15 @@ Q1 specials: 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) window = sdl2.ext.Window("Coldcard Simulator", size=bg.size, position=(100, 100)) @@ -952,6 +993,7 @@ Q1 specials: if not pressed: numpad_tx.write(b'\0') # all up signal + while running: events = sdl2.ext.get_events() for event in events: diff --git a/unix/variant/ssd1306.py b/unix/variant/ssd1306.py index bd3d5f7a..385ac554 100644 --- a/unix/variant/ssd1306.py +++ b/unix/variant/ssd1306.py @@ -26,10 +26,10 @@ SET_CHARGE_PUMP = const(0x8d) # Subclassing FrameBuffer provides support for graphics primitives # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html class SSD1306(framebuf.FrameBuffer): - def __init__(self, width, height, external_vcc): + def __init__(self, width, height, is_mk5): self.width = width self.height = height - self.external_vcc = external_vcc + self.is_mk5 = is_mk5 self.pages = self.height // 8 self.buffer = bytearray(self.pages * self.width) 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_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): - 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 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): pass diff --git a/unix/variant/version.py b/unix/variant/version.py index 62875beb..0eee86d4 100644 --- a/unix/variant/version.py +++ b/unix/variant/version.py @@ -32,8 +32,8 @@ def get_header_value(fld_name): return b'\x18\x07\x11\x19S\x08\x00\x00' return 0 -# default is Mk4 hardware -hw_label = 'mk4' +# default is Mk5 hardware +hw_label = 'mk5' has_608 = True has_membrane = True supports_hsm = True @@ -47,7 +47,7 @@ has_qwerty = False is_edge = False if '--mk1' in sys.argv: - # doubt this works still + # doubt this works anymore hw_label = 'mk1' has_608 = False has_membrane = False @@ -72,6 +72,9 @@ if '--mk3' in sys.argv: has_nfc = False supports_hsm = False +if '--mk4' in sys.argv: + hw_label = 'mk4' + mk_num = int(hw_label[2:]) if '--q1' in sys.argv: @@ -81,6 +84,7 @@ if '--q1' in sys.argv: has_battery = True has_qwerty = True supports_hsm = False + mk_num = 4 from public_constants import MAX_TXN_LEN, MAX_UPLOAD_LEN from public_constants import MAX_TXN_LEN_MK4, MAX_UPLOAD_LEN_MK4