diff --git a/unix/README.md b/unix/README.md index dca58d39..3d5ec269 100644 --- a/unix/README.md +++ b/unix/README.md @@ -9,6 +9,10 @@ make && ./simulator.py +OR + + make && ./simulator.py --q1 + ## Other Startup Flags @@ -28,6 +32,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 +- `--q1` => emulate Q1 hardware - `-g` => don't skip login sequence - `--addr` => go to the address explorer at startup - `--xw` => go to the wallet export submenu diff --git a/unix/bare.py b/unix/bare.py new file mode 100644 index 00000000..c6bf9600 --- /dev/null +++ b/unix/bare.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python +# +# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# Connect to a real device, and allow simulator to use it's hardware for SE access. +# +# This is a normal python3 program, not micropython. +# +import os, sys, tty, pty, termios, time, pdb, tempfile +from PIL import Image +from select import select +import fcntl +from binascii import b2a_hex, a2b_hex + + +class BareMetal: + # + # Use a real Coldcard device's bootrom and Secure Elements + # + def __init__(self, req_r, resp_w): + self.open() + self.request = open(req_r, 'rt', closefd=0) + self.response = open(resp_w, 'wb', closefd=0, buffering=0) + + def open(self, name='usbserial-AQ00T1RR'): + # return a file-descriptor ready to be used for access to a real Coldcard's console I/O. + # - assume only one coldcard + import sys, serial + from serial.tools.list_ports import comports + + for d in comports(): + if not name: + if d.pid != 0xcc10: continue + else: + if name not in d.name: continue + + sio = serial.Serial(d.device, write_timeout=1, baudrate=115200) + + print("Connecting to: %s" % d.device) + break + else: + raise RuntimeError("Can't find usb serial port for real Coldcard") + + self.sio = sio + sio.timeout = 0.250 + + if d.pid == 0xcc10: + # USB mode a litte easier + greet = sio.readlines() + if greet and b'Welcome to Coldcard!' in greet[1]: + sio.write(b'\x03') # ctrl-C + while 1: + sio.timeout = 1 + lns = sio.readlines() + if not lns: break + else: + # real serial port + sio.write(b'\x03') # ctrl-C + while 1: + sio.timeout = 3 + lns = sio.readlines() + print("ECHO: " + repr(lns)) + if not lns: break + + # hit enter, expect prompt + sio.timeout = 0.100 + sio.write(b'\r') + ln = sio.readlines() + #assert ln[-1] == b'>>> ', ln + #assert ln[-1] == b'=== ', ln + assert ln[-1] in {b'>>> ', b'=== '}, ln #ok if in paste mode + + print(" Connected to: %s" % d.device) + + sio.write(b'''\x05\ +from glob import dis +from ubinascii import hexlify as b2a_hex +from ubinascii import unhexlify as a2b_hex +import ckcc +dis.fullscreen("BareMetal") +try: + busy = dis.busy_bar +except: + busy = lambda x: None +\x04'''.replace(b'\n', b'\r')) + + # above is quick but will be echoed, so clear it out + lns = self.wait_done() + #print(f"setup: {lns}") + + + def read_sflash(self): + # capture contents of SPI flash (settings area only: last 128k) + # XXX not working, and not for Mk4 + self.sio.write(b'''\x05\ +busy(1) +from main import sf +dis.fullscreen("SPI Flash") +buf = bytearray(256) +addr = 0xe0000 +for i in range(0, 0x20000, 256): + sf.read(addr+i, buf) + print(b2a_hex(buf).decode()) +busy(0) +dis.fullscreen("BareMetal") +\x04\r'''.replace(b'\n', b'\r')) + + count = 0 + self.sio.timeout = 0.5 + for ln in self.sio.readlines(): + ln = ln.decode('ascii') + if len(ln) == 512 + 2: + self.response.write(ln[:-2].encode('ascii') + b'\n') + count += 1 + elif ln.startswith('>>> '): + break + elif not ln or not ln.strip() or ln.startswith('=== ') or 'paste mode' in ln: + pass + else: + print(f'junk: {ln}') + + assert count == (128*1024)//256, count + + print("Sent real SPI Flash contents to simulated Coldcard.") + + def wait_done(self, timeout=1): + sio = self.sio + sio.timeout = timeout + rv = sio.read_until('>>> ') + return [str(i, 'ascii') for i in rv.split(b'\r\n')] + + def readable(self): + # expects (method, hex, arg2) as string on one line + ln = self.request.readline() + + arg1, bb, arg2 = ln.split(', ') + + method = int(arg1) + arg2 = int(arg2) + buf_io = a2b_hex(bb) if bb != 'None' else None + + if method == -99: + # internal to us: read SPI flash contents + return self.read_sflash() + elif method in {2, 3}: + # these methods always die; not helpful for testing + print(f"FATAL Callgate(method={method}, arg2={arg2}) => execution would stop") + self.response.write(b'0,\n') + return + + sio = self.sio + + sio.timeout = 0.1 + sio.read_all() + + sio.write(b'\r\x05') # CTRL-E => paste mode + + if buf_io is None: + sio.write(b'bb = None\r') + else: + sio.write(b'bb = bytearray(a2b_hex("%s"))\r' % b2a_hex(buf_io)) + + sio.write(b'busy(1)\r') + sio.write(b'rv = ckcc.gate(%d, bb, %d)\r' % (method, arg2)) + sio.write(b'busy(0)\r') + if buf_io is None: + sio.write(b'print("%d," % rv)\r') + else: + sio.write(b'print("%d, %s" % (rv, b2a_hex(bb).decode()))\r') + sio.write(b'\x04\r') # CTRL-D, end paste; start exec + + lines = [] + for retries in range(10): + lines.extend(self.wait_done()) + #print('back: \n' + '\n'.join( f'[{n}] {l}' for n,l in enumerate(lines))) + if len(lines) >= 2 and lines[-1] == lines[-2] == '>>> ': + break + else: + raise RuntimeError("timed out") + + # result is in lines between final === and first >>> ... typically a single + # line, but might overflow into next 'line' + assert '=== ' in lines and '>>> ' in lines + a = -list(reversed(lines)).index('=== ') + b = lines[a:].index('>>> ') + rv = ''.join(lines[a:a+b]).strip() + + assert rv + assert ',' in rv + assert not rv.startswith('===') + + if 1: + # trace output + print(f"Callgate(method={method}, {len(buf_io) if buf_io else 0} bytes, "\ + f"arg2={arg2}) => rv={rv}") + + self.response.write(rv.encode('ascii') + b'\n') + +# EOF diff --git a/unix/background.png b/unix/mk4-images/background.png similarity index 100% rename from unix/background.png rename to unix/mk4-images/background.png diff --git a/unix/led-green.png b/unix/mk4-images/led-green.png similarity index 100% rename from unix/led-green.png rename to unix/mk4-images/led-green.png diff --git a/unix/led-red.png b/unix/mk4-images/led-red.png similarity index 100% rename from unix/led-red.png rename to unix/mk4-images/led-red.png diff --git a/unix/led-sd.png b/unix/mk4-images/led-sd.png similarity index 100% rename from unix/led-sd.png rename to unix/mk4-images/led-sd.png diff --git a/unix/led-usb.png b/unix/mk4-images/led-usb.png similarity index 100% rename from unix/led-usb.png rename to unix/mk4-images/led-usb.png diff --git a/unix/q1-images/background.png b/unix/q1-images/background.png new file mode 100644 index 00000000..bebf66c2 Binary files /dev/null and b/unix/q1-images/background.png differ diff --git a/unix/q1-images/led-green.png b/unix/q1-images/led-green.png new file mode 100644 index 00000000..b10bdf91 Binary files /dev/null and b/unix/q1-images/led-green.png differ diff --git a/unix/q1-images/led-red.png b/unix/q1-images/led-red.png new file mode 100644 index 00000000..53733fe1 Binary files /dev/null and b/unix/q1-images/led-red.png differ diff --git a/unix/simulator.py b/unix/simulator.py index 25a6dbde..3e130c5c 100755 --- a/unix/simulator.py +++ b/unix/simulator.py @@ -19,51 +19,15 @@ from PIL import Image from select import select import fcntl from binascii import b2a_hex, a2b_hex +from bare import BareMetal MPY_UNIX = 'l-port/micropython' UNIX_SOCKET_PATH = '/tmp/ckcc-simulator.sock' -# 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 - - -class OLEDSimulator: - - def __init__(self, factory): - self.movie = None - - s = factory.create_software_sprite( (128,64), bpp=32) - self.sprite = s - s.x, s.y = OLED_ACTIVE - s.depth = 100 - - self.fg = sdl2.ext.prepare_color('#ccf', s) - self.bg = sdl2.ext.prepare_color('#111', s) - sdl2.ext.fill(s, self.bg) - - self.mv = sdl2.ext.PixelView(self.sprite) - - def render(self, window, buf): - # do a full-screen update of the OLED contents and display - assert len(buf) == 1024, len(buf) - - for y in range(0, 64, 8): - line = buf[y*128//8:] - for x in range(128): - val = buf[(y*128//8) + x] - mask = 0x01 - for i in range(8): - self.mv[y+i][x] = self.fg if (val & mask) else self.bg - mask <<= 1 - - if self.movie is not None: - self.new_frame() +class SimulatedScreen: + # a base class def snapshot(self): fn = time.strftime('../snapshot-%j-%H%M%S.png') @@ -109,209 +73,97 @@ class OLEDSimulator: img = img.convert('P') self.movie.append((dt, img)) -class BareMetal: - # - # Use a real Coldcard device's bootrom and Secure Elements - # - def __init__(self, req_r, resp_w): - self.open() - self.request = open(req_r, 'rt', closefd=0) - self.response = open(resp_w, 'wb', closefd=0, buffering=0) +class LCDSimulator(SimulatedScreen): + pass - def open(self, name='usbserial-AQ00T1RR'): - # return a file-descriptor ready to be used for access to a real Coldcard's console I/O. - # - assume only one coldcard - import sys, serial - from serial.tools.list_ports import comports +class OLEDSimulator(SimulatedScreen): + # top-left coord of OLED area; size is 1:1 with real pixels... 128x64 pixels + OLED_ACTIVE = (46, 85) - for d in comports(): - if not name: - if d.pid != 0xcc10: continue - else: - if name not in d.name: continue + # keypad touch buttons + KEYPAD_LEFT = 52 + KEYPAD_TOP = 216 + KEYPAD_PITCH = 73 - sio = serial.Serial(d.device, write_timeout=1, baudrate=115200) + background_img = 'mk4-images/background.png' - print("Connecting to: %s" % d.device) - break - else: - raise RuntimeError("Can't find usb serial port for real Coldcard") + def __init__(self, factory): + self.movie = None - self.sio = sio - sio.timeout = 0.250 + s = factory.create_software_sprite( (128,64), bpp=32) + self.sprite = s + s.x, s.y = self.OLED_ACTIVE + s.depth = 100 - if d.pid == 0xcc10: - # USB mode a litte easier - greet = sio.readlines() - if greet and b'Welcome to Coldcard!' in greet[1]: - sio.write(b'\x03') # ctrl-C - while 1: - sio.timeout = 1 - lns = sio.readlines() - if not lns: break - else: - # real serial port - sio.write(b'\x03') # ctrl-C - while 1: - sio.timeout = 3 - lns = sio.readlines() - print("ECHO: " + repr(lns)) - if not lns: break + self.fg = sdl2.ext.prepare_color('#ccf', s) + self.bg = sdl2.ext.prepare_color('#111', s) + sdl2.ext.fill(s, self.bg) - # hit enter, expect prompt - sio.timeout = 0.100 - sio.write(b'\r') - ln = sio.readlines() - #assert ln[-1] == b'>>> ', ln - #assert ln[-1] == b'=== ', ln - assert ln[-1] in {b'>>> ', b'=== '}, ln #ok if in paste mode - - print(" Connected to: %s" % d.device) - - sio.write(b'''\x05\ -from glob import dis -from ubinascii import hexlify as b2a_hex -from ubinascii import unhexlify as a2b_hex -import ckcc -dis.fullscreen("BareMetal") -try: - busy = dis.busy_bar -except: - busy = lambda x: None -\x04'''.replace(b'\n', b'\r')) - - # above is quick but will be echoed, so clear it out - lns = self.wait_done() - #print(f"setup: {lns}") - - - def read_sflash(self): - # capture contents of SPI flash (settings area only: last 128k) - # XXX not working, and not for Mk4 - self.sio.write(b'''\x05\ -busy(1) -from main import sf -dis.fullscreen("SPI Flash") -buf = bytearray(256) -addr = 0xe0000 -for i in range(0, 0x20000, 256): - sf.read(addr+i, buf) - print(b2a_hex(buf).decode()) -busy(0) -dis.fullscreen("BareMetal") -\x04\r'''.replace(b'\n', b'\r')) - - count = 0 - self.sio.timeout = 0.5 - for ln in self.sio.readlines(): - ln = ln.decode('ascii') - if len(ln) == 512 + 2: - self.response.write(ln[:-2].encode('ascii') + b'\n') - count += 1 - elif ln.startswith('>>> '): - break - elif not ln or not ln.strip() or ln.startswith('=== ') or 'paste mode' in ln: - pass - else: - print(f'junk: {ln}') - - assert count == (128*1024)//256, count - - print("Sent real SPI Flash contents to simulated Coldcard.") - - def wait_done(self, timeout=1): - sio = self.sio - sio.timeout = timeout - rv = sio.read_until('>>> ') - return [str(i, 'ascii') for i in rv.split(b'\r\n')] - - def readable(self): - # expects (method, hex, arg2) as string on one line - ln = self.request.readline() - - arg1, bb, arg2 = ln.split(', ') - - method = int(arg1) - arg2 = int(arg2) - buf_io = a2b_hex(bb) if bb != 'None' else None - - if method == -99: - # internal to us: read SPI flash contents - return self.read_sflash() - elif method in {2, 3}: - # these methods always die; not helpful for testing - print(f"FATAL Callgate(method={method}, arg2={arg2}) => execution would stop") - self.response.write(b'0,\n') - return - - sio = self.sio - - sio.timeout = 0.1 - sio.read_all() - - sio.write(b'\r\x05') # CTRL-E => paste mode - - if buf_io is None: - sio.write(b'bb = None\r') - else: - sio.write(b'bb = bytearray(a2b_hex("%s"))\r' % b2a_hex(buf_io)) - - sio.write(b'busy(1)\r') - sio.write(b'rv = ckcc.gate(%d, bb, %d)\r' % (method, arg2)) - sio.write(b'busy(0)\r') - if buf_io is None: - sio.write(b'print("%d," % rv)\r') - else: - sio.write(b'print("%d, %s" % (rv, b2a_hex(bb).decode()))\r') - sio.write(b'\x04\r') # CTRL-D, end paste; start exec - - lines = [] - for retries in range(10): - lines.extend(self.wait_done()) - #print('back: \n' + '\n'.join( f'[{n}] {l}' for n,l in enumerate(lines))) - if len(lines) >= 2 and lines[-1] == lines[-2] == '>>> ': - break - else: - raise RuntimeError("timed out") - - # result is in lines between final === and first >>> ... typically a single - # line, but might overflow into next 'line' - assert '=== ' in lines and '>>> ' in lines - a = -list(reversed(lines)).index('=== ') - b = lines[a:].index('>>> ') - rv = ''.join(lines[a:a+b]).strip() - - assert rv - assert ',' in rv - assert not rv.startswith('===') - - if 1: - # trace output - print(f"Callgate(method={method}, {len(buf_io) if buf_io else 0} bytes, "\ - f"arg2={arg2}) => rv={rv}") - - self.response.write(rv.encode('ascii') + b'\n') + self.mv = sdl2.ext.PixelView(self.sprite) + # 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") + + def new_contents(self, buf): + # got bytes for new update. + buf = buf[-1024:] # ignore backlogs, get final state + assert len(buf) == 1024, len(buf) + + for y in range(0, 64, 8): + line = buf[y*128//8:] + for x in range(128): + val = buf[(y*128//8) + x] + mask = 0x01 + for i in range(8): + self.mv[y+i][x] = self.fg if (val & mask) else self.bg + mask <<= 1 + + if self.movie is not None: + self.new_frame() + + def click_to_key(self, x, y): + # take a click on image => keypad key if valid + col = ((x - self.KEYPAD_LEFT) // self.KEYPAD_PITCH) + row = ((y - self.KEYPAD_TOP) // self.KEYPAD_PITCH) + + #print('rc= %d,%d' % (row,col)) + if not (0 <= row < 4): return None + if not (0 <= col < 3): return None + + return '123456789x0y'[(row*3) + col] + + def draw_leds(self, spriterenderer, active_set=0): + # always draw SE led, since one is always on + GEN_LED = 0x1 + SD_LED = 0x2 + USB_LED = 0x4 + + spriterenderer.render(self.led_green if (active_set & GEN_LED) else self.led_red) + + if active_set & SD_LED: + spriterenderer.render(self.led_sdcard) + if active_set & USB_LED: + spriterenderer.render(self.led_usb) + def start(): print('''\nColdcard Simulator: Commands (over simulated window): - Control-Q to quit - - Z to snapshot screen. - - S/E to start/end movie recording - - N to capture NFC data (tap it) + - ^Z to snapshot screen. + - ^S/^E to start/end movie recording + - ^N to capture NFC data (tap it) ''') sdl2.ext.init() sdl2.SDL_EnableScreenSaver() + is_q1 = ('--q1' in sys.argv) + factory = sdl2.ext.SpriteFactory(sdl2.ext.SOFTWARE) - bg = factory.from_image("background.png") - oled = OLEDSimulator(factory) - - # for genuine/caution lights and other LED's - led_red = factory.from_image("led-red.png") - led_green = factory.from_image("led-green.png") - led_sdcard = factory.from_image("led-sd.png") - led_usb = factory.from_image("led-usb.png") + simdis = OLEDSimulator(factory) + bg = factory.from_image(simdis.background_img) window = sdl2.ext.Window("Coldcard Simulator", size=bg.size, position=(100, 100)) window.show() @@ -321,17 +173,17 @@ def start(): spriterenderer = factory.create_sprite_render_system(window) + # initial state spriterenderer.render(bg) - spriterenderer.render(oled.sprite) - spriterenderer.render(led_red) + spriterenderer.render(simdis.sprite) genuine_state = False - sd_active = False + simdis.draw_leds(spriterenderer) # capture exec path and move into intended working directory env = os.environ.copy() env['MICROPYPATH'] = ':' + os.path.realpath('../shared') - oled_r, oled_w = os.pipe() # fancy OLED display + display_r, display_w = os.pipe() # fancy OLED display led_r, led_w = os.pipe() # genuine LED numpad_r, numpad_w = os.pipe() # keys @@ -349,7 +201,7 @@ def start(): # - open the serial device # - get buffering/non-blocking right # - pass in open fd numbers - pass_fds = [oled_w, numpad_r, led_w] + pass_fds = [display_w, numpad_r, led_w] if '--metal' in sys.argv: # bare-metal access: use a real Coldcard's bootrom+SE. @@ -369,7 +221,7 @@ def start(): cc_cmd = ['../coldcard-mpy', '-X', 'heapsize=9m', '-i', '../sim_boot.py', - str(oled_w), str(numpad_r), str(led_w)] \ + str(display_w), str(numpad_r), str(led_w)] \ + metal_args + sys.argv[1:] xterm = subprocess.Popen(['xterm', '-title', 'Coldcard Simulator REPL', '-geom', '132x40+450+40', '-e'] + cc_cmd, @@ -379,16 +231,16 @@ def start(): # reopen as binary streams - oled_rx = open(oled_r, 'rb', closefd=0, buffering=0) + display_rx = open(display_r, 'rb', closefd=0, buffering=0) led_rx = open(led_r, 'rb', closefd=0, buffering=0) numpad_tx = open(numpad_w, 'wb', closefd=0, buffering=0) # setup no blocking - for r in [oled_rx, led_rx]: + for r in [display_rx, led_rx]: fl = fcntl.fcntl(r, fcntl.F_GETFL) fcntl.fcntl(r, fcntl.F_SETFL, fl | os.O_NONBLOCK) - readables = [oled_rx, led_rx] + readables = [display_rx, led_rx] if bare_metal: readables.append(bare_metal.request) @@ -427,19 +279,14 @@ def start(): else: ch = '\0' - # remap ESC/Enter - if ch == '\x1b': - ch = 'x' - elif ch == '\x0d': - ch = 'y' + # control+KEY + if event.key.keysym.mod == 0x40 and event.type == sdl2.SDL_KEYDOWN: + if ch == 'q': + # control-Q + running = False + break - if ch == 'q' and event.key.keysym.mod == 0x40: - # control-Q - running = False - break - - if ch == 'n': - if event.type == sdl2.SDL_KEYDOWN: + if ch == 'n': # see sim_nfc.py try: nfc = open('nfc-dump.ndef', 'rb').read() @@ -448,44 +295,48 @@ def start(): print(f"Simulated NFC read: {len(nfc)} bytes into {fn}") except FileNotFoundError: print("NFC not ready") - continue - if ch in 'zse': - if event.type == sdl2.SDL_KEYDOWN: + if ch in 'zse': if ch == 'z': - oled.snapshot() + simdis.snapshot() if ch == 's': - oled.movie_start() + simdis.movie_start() if ch == 'e': - oled.movie_end() + simdis.movie_end() + continue + + if ch == 'm': + # do many OK's in a row ... for word nest menu + for i in range(30): + numpad_tx.write(b'y\n') + numpad_tx.write(b'\n') + continue + + if event.key.keysym.mod == 0x40: + # control key releases: ignore continue - if ch == 'm': - # do many OK's in a row ... for word nest menu - for i in range(30): - numpad_tx.write(b'y\n') - numpad_tx.write(b'\n') - continue + # remap ESC/Enter + if not is_q1: + if ch == '\x1b': + ch = 'x' + elif ch == '\x0d': + ch = 'y' - if ch not in '0123456789xy': - if ch.isprintable(): - print("Invalid key: '%s'" % ch) - continue - + if ch not in '0123456789xy': + if ch.isprintable(): + print("Invalid key: '%s'" % ch) + continue + # need this to kill key-repeat - ch = ch.encode('ascii') send_event(ch, event.type == sdl2.SDL_KEYDOWN) if event.type == sdl2.SDL_MOUSEBUTTONDOWN: #print('xy = %d, %d' % (event.button.x, event.button.y)) - col = ((event.button.x - KEYPAD_LEFT) // KEYPAD_PITCH) - row = ((event.button.y - KEYPAD_TOP) // KEYPAD_PITCH) - #print('rc= %d,%d' % (row,col)) - if not (0 <= row < 4): continue - if not (0 <= col < 3): continue - ch = '123456789x0y'[(row*3) + col] - send_event(ch.encode('ascii'), True) + ch = simdis.click_to_key(event.button.x, event.button.y) + if ch is not None: + send_event(ch.encode('ascii'), True) if event.type == sdl2.SDL_MOUSEBUTTONUP: for ch in list(pressed): @@ -498,44 +349,30 @@ def start(): bare_metal.readable() continue - # Cheating: 1024 is size of OLED update, don't change. + # Must be bigger than a full screen update. buf = r.read(1024*1000) if not buf: break - if r is oled_rx: - buf = buf[-1024:] - oled.render(window, buf) - spriterenderer.render(oled.sprite) + if r is display_rx: + simdis.new_contents(buf) + spriterenderer.render(simdis.sprite) window.refresh() elif r is led_rx: + # XXX 8+8 bits for c in buf: #print("LED change: 0x%02x" % c[0]) mask = (c >> 4) & 0xf lset = c & 0xf - GEN_LED = 0x1 - SD_LED = 0x2 - USB_LED = 0x4 - sd_active = usb_active = False - - if mask & GEN_LED: - genuine_state = ((mask & lset) == GEN_LED) - if mask & SD_LED: - sd_active = ((mask & lset) == SD_LED) - if mask & USB_LED: - usb_active = ((mask & lset) == USB_LED) + active_set = (mask & lset) #print("Genuine LED: %r" % genuine_state) spriterenderer.render(bg) - spriterenderer.render(oled.sprite) - spriterenderer.render(led_green if genuine_state else led_red) - if sd_active: - spriterenderer.render(led_sdcard) - if usb_active: - spriterenderer.render(led_usb) + spriterenderer.render(simdis.sprite) + simdis.draw_leds(spriterenderer, active_set) window.refresh() else: diff --git a/unix/variant/ckcc.py b/unix/variant/ckcc.py index ca45aa4d..5f5ed5d7 100644 --- a/unix/variant/ckcc.py +++ b/unix/variant/ckcc.py @@ -228,6 +228,8 @@ def get_cpi_id(): return 0x461 # STM32L496RG6 if ('--mk4' in sys.argv): return 0x470 # STM32L4S5 + if ('--q1' in sys.argv): + return 0x470 # STM32L4S5 #default mk4 return 0x470 # STM32L4S5 diff --git a/unix/variant/version.py b/unix/variant/version.py index 22c66acc..92380566 100644 --- a/unix/variant/version.py +++ b/unix/variant/version.py @@ -32,7 +32,7 @@ def get_header_value(fld_name): return b'\x18\x07\x11\x19S\x08\x00\x00' return 0 -# default is latest hardware +# default is Mk4 hardware hw_label = 'mk4' has_608 = True has_membrane = True @@ -40,6 +40,8 @@ has_fatram = True has_se2 = True has_psram = True has_nfc = True +has_qr = False +num_sd_slots = 1 if '--mk1' in sys.argv: # doubt this works still @@ -67,6 +69,11 @@ if '--mk3' in sys.argv: has_psram = False has_nfc = False +if '--q1' in sys.argv: + hw_label = 'q1' + has_qr = True + num_sd_slots = 2 + mk_num = int(hw_label[2:]) from public_constants import MAX_TXN_LEN, MAX_UPLOAD_LEN