firmware_flasher: automatically restart keyboard in bootloader mode and flash

main
Ilya Zhuravlev 2020-12-26 21:51:48 -05:00
parent d6b0a6cb54
commit 7795ad5d07
3 changed files with 78 additions and 16 deletions

View File

@ -6,13 +6,16 @@ import struct
import time import time
import threading import threading
from PyQt5.QtCore import pyqtSignal from PyQt5.QtCore import pyqtSignal, QCoreApplication
from PyQt5.QtGui import QFontDatabase from PyQt5.QtGui import QFontDatabase
from PyQt5.QtWidgets import QHBoxLayout, QLineEdit, QToolButton, QPlainTextEdit, QProgressBar,QFileDialog, QDialog from PyQt5.QtWidgets import QHBoxLayout, QLineEdit, QToolButton, QPlainTextEdit, QProgressBar,QFileDialog, QDialog
from basic_editor import BasicEditor from basic_editor import BasicEditor
from util import tr, chunks from util import tr, chunks, find_vial_devices
from vial_device import VialBootloader from vial_device import VialBootloader, VialKeyboard
BL_SUPPORTED_VERSION = 0
def send_retries(dev, data, retries=20): def send_retries(dev, data, retries=20):
@ -32,6 +35,18 @@ def send_retries(dev, data, retries=20):
CHUNK = 64 CHUNK = 64
def bl_get_version(dev):
dev.send(b"VC\x00")
data = dev.recv(8)
return data[0]
def bl_get_uid(dev):
dev.send(b"VC\x01")
data = dev.recv(8)
return data
def cmd_flash(device, firmware, log_cb, progress_cb, complete_cb, error_cb): def cmd_flash(device, firmware, log_cb, progress_cb, complete_cb, error_cb):
if firmware[0:8] != b"VIALFW00": if firmware[0:8] != b"VIALFW00":
return error_cb("Error: Invalid signature") return error_cb("Error: Invalid signature")
@ -50,24 +65,22 @@ def cmd_flash(device, firmware, log_cb, progress_cb, complete_cb, error_cb):
)) ))
# Check bootloader is correct version # Check bootloader is correct version
device.send(b"VC\x00") ver = bl_get_version(device)
data = device.recv(8) log_cb("* Bootloader version: {}".format(ver))
log_cb("* Bootloader version: {}".format(data[0])) if ver != BL_SUPPORTED_VERSION:
if data[0] != 0:
return error_cb("Error: Unsupported bootloader version") return error_cb("Error: Unsupported bootloader version")
device.send(b"VC\x01") uid = bl_get_uid(device)
data = device.recv(8) log_cb("* Vial ID: {}".format(uid.hex()))
log_cb("* Vial ID: {}".format(data.hex()))
if data == b"\xFF" * 8: if uid == b"\xFF" * 8:
log_cb("\n\n\n!!! WARNING !!!\nBootloader UID is not set, make sure to configure it" log_cb("\n\n\n!!! WARNING !!!\nBootloader UID is not set, make sure to configure it"
" before releasing production firmware\n!!! WARNING !!!\n\n") " before releasing production firmware\n!!! WARNING !!!\n\n")
if data != fw_uid: if uid != fw_uid:
return error_cb("Error: Firmware package was built for different device\n\texpected={}\n\tgot={}".format( return error_cb("Error: Firmware package was built for different device\n\texpected={}\n\tgot={}".format(
fw_uid.hex(), fw_uid.hex(),
data.hex() uid.hex()
)) ))
# OK all checks complete, we can flash now # OK all checks complete, we can flash now
@ -139,12 +152,18 @@ class FirmwareFlasher(BasicEditor):
def rebuild(self, device): def rebuild(self, device):
super().rebuild(device) super().rebuild(device)
self.txt_logger.clear() self.txt_logger.clear()
if not self.valid():
return
if isinstance(self.device, VialBootloader): if isinstance(self.device, VialBootloader):
self.log("Valid Vial Bootloader device at {}".format(self.device.desc["path"].decode("utf-8"))) self.log("Valid Vial Bootloader device at {}".format(self.device.desc["path"].decode("utf-8")))
elif isinstance(self.device, VialKeyboard):
self.log("Vial keyboard detected")
def valid(self): def valid(self):
# TODO: it is also valid to flash a VialKeyboard which supports optional "vibl-integration" feature return isinstance(self.device, VialBootloader) or\
return isinstance(self.device, VialBootloader) isinstance(self.device, VialKeyboard) and self.device.keyboard.vibl
def on_click_select_file(self): def on_click_select_file(self):
dialog = QFileDialog() dialog = QFileDialog()
@ -170,6 +189,34 @@ class FirmwareFlasher(BasicEditor):
self.log("Preparing to flash...") self.log("Preparing to flash...")
self.lock_ui() self.lock_ui()
# TODO: this needs to switch to the secure assisted-reset feature before public release
if isinstance(self.device, VialKeyboard):
uid = self.device.keyboard.get_uid()
self.log("Restarting in bootloader mode...")
self.device.keyboard.reset()
# watch for bootloaders to appear and ask them for their UID, return one that matches the keyboard
while True:
self.log("Looking for devices...")
QCoreApplication.processEvents()
time.sleep(1)
devices = find_vial_devices()
found = None
for dev in devices:
if isinstance(dev, VialBootloader):
dev.open()
# TODO: update version check before release
if bl_get_version(dev) != BL_SUPPORTED_VERSION or bl_get_uid(dev) != uid:
dev.close()
found = dev
break
if found:
self.log("Found Vial Bootloader device at {}".format(found.desc["path"].decode("utf-8")))
self.device = found
break
threading.Thread(target=lambda: cmd_flash( threading.Thread(target=lambda: cmd_flash(
self.device, firmware, self.on_log, self.on_progress, self.on_complete, self.on_error)).start() self.device, firmware, self.on_log, self.on_progress, self.on_complete, self.on_error)).start()

View File

@ -54,6 +54,7 @@ class Keyboard:
self.macro_count = 0 self.macro_count = 0
self.macro_memory = 0 self.macro_memory = 0
self.macro = b"" self.macro = b""
self.vibl = False
self.vial_protocol = self.keyboard_id = -1 self.vial_protocol = self.keyboard_id = -1
@ -103,6 +104,10 @@ class Keyboard:
payload = json.loads(lzma.decompress(payload)) payload = json.loads(lzma.decompress(payload))
if "vial" in payload:
vial = payload["vial"]
self.vibl = vial.get("vibl", False)
self.layouts = payload.get("layouts") self.layouts = payload.get("layouts")
self.rows = payload["matrix"]["rows"] self.rows = payload["matrix"]["rows"]
@ -275,3 +280,13 @@ class Keyboard:
self.set_layout_options(data["layout_options"]) self.set_layout_options(data["layout_options"])
self.set_macro(base64.b64decode(data["macro"])) self.set_macro(base64.b64decode(data["macro"]))
def reset(self):
self.usb_send(self.dev, struct.pack("B", 0xB))
self.dev.close()
def get_uid(self):
""" Retrieve UID from the keyboard, explicitly sending a query packet """
data = self.usb_send(self.dev, struct.pack("BB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_GET_KEYBOARD_ID))
keyboard_id = data[4:12]
return keyboard_id

View File

@ -32,7 +32,7 @@ def is_rawhid(dev):
return dev["interface_number"] == 1 return dev["interface_number"] == 1
def find_vial_devices(sideload_vid, sideload_pid): def find_vial_devices(sideload_vid=None, sideload_pid=None):
from vial_device import VialBootloader, VialKeyboard from vial_device import VialBootloader, VialKeyboard
filtered = [] filtered = []