CLI: Teaching the CLI to flash binaries (#16584)

Co-authored-by: Ryan <fauxpark@gmail.com>
Co-authored-by: Sergey Vlasov <sigprof@gmail.com>
Co-authored-by: Joel Challis <git@zvecr.com>
Co-authored-by: Nick Brassel <nick@tzarc.org>
This commit is contained in:
Erovia 2022-08-20 06:39:19 +01:00 committed by GitHub
parent 3bf36e8b04
commit 5e2ffe7d8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 361 additions and 91 deletions

View File

@ -90,6 +90,8 @@ This command is similar to `qmk compile`, but can also target a bootloader. The
This command is directory aware. It will automatically fill in KEYBOARD and/or KEYMAP if you are in a keyboard or keymap directory. This command is directory aware. It will automatically fill in KEYBOARD and/or KEYMAP if you are in a keyboard or keymap directory.
This command can also flash binary firmware files (hex or bin) such as the ones produced by [Configurator](https://config.qmk.fm).
**Usage for Configurator Exports**: **Usage for Configurator Exports**:
``` ```
@ -102,6 +104,21 @@ qmk flash [-bl <bootloader>] [-c] [-e <var>=<value>] [-j <num_jobs>] <configurat
qmk flash -kb <keyboard_name> -km <keymap_name> [-bl <bootloader>] [-c] [-e <var>=<value>] [-j <num_jobs>] qmk flash -kb <keyboard_name> -km <keymap_name> [-bl <bootloader>] [-c] [-e <var>=<value>] [-j <num_jobs>]
``` ```
**Usage for pre-compiled firmwares**:
**Note**: The microcontroller needs to be specified (`-m` argument) for keyboards with the following bootloaders:
* HalfKay
* QMK HID
* USBaspLoader
ISP flashing is also supported with the following flashers and require the microcontroller to be specified:
* USBasp
* USBtinyISP
```
qmk flash [-m <microcontroller>] <compiledFirmware.[bin|hex]>
```
**Listing the Bootloaders** **Listing the Bootloaders**
``` ```

View File

@ -15,6 +15,7 @@ from milc.questions import yesno
import_names = { import_names = {
# A mapping of package name to importable name # A mapping of package name to importable name
'pep8-naming': 'pep8ext_naming', 'pep8-naming': 'pep8ext_naming',
'pyserial': 'serial',
'pyusb': 'usb.core', 'pyusb': 'usb.core',
'qmk-dotty-dict': 'dotty_dict', 'qmk-dotty-dict': 'dotty_dict',
'pillow': 'PIL' 'pillow': 'PIL'

View File

@ -6,7 +6,7 @@ from pathlib import Path
from milc import cli from milc import cli
from qmk.constants import QMK_FIRMWARE from qmk.constants import QMK_FIRMWARE, BOOTLOADER_VIDS_PIDS
from .check import CheckStatus from .check import CheckStatus
@ -26,6 +26,18 @@ def _udev_rule(vid, pid=None, *args):
return rule return rule
def _generate_desired_rules(bootloader_vids_pids):
rules = dict()
for bl in bootloader_vids_pids.keys():
rules[bl] = set()
for vid_pid in bootloader_vids_pids[bl]:
if bl == 'caterina' or bl == 'md-boot':
rules[bl].add(_udev_rule(vid_pid[0], vid_pid[1], 'ENV{ID_MM_DEVICE_IGNORE}="1"'))
else:
rules[bl].add(_udev_rule(vid_pid[0], vid_pid[1]))
return rules
def _deprecated_udev_rule(vid, pid=None): def _deprecated_udev_rule(vid, pid=None):
""" Helper function that return udev rules """ Helper function that return udev rules
@ -47,47 +59,8 @@ def check_udev_rules():
Path("/run/udev/rules.d/"), Path("/run/udev/rules.d/"),
Path("/etc/udev/rules.d/"), Path("/etc/udev/rules.d/"),
] ]
desired_rules = {
'atmel-dfu': { desired_rules = _generate_desired_rules(BOOTLOADER_VIDS_PIDS)
_udev_rule("03eb", "2fef"), # ATmega16U2
_udev_rule("03eb", "2ff0"), # ATmega32U2
_udev_rule("03eb", "2ff3"), # ATmega16U4
_udev_rule("03eb", "2ff4"), # ATmega32U4
_udev_rule("03eb", "2ff9"), # AT90USB64
_udev_rule("03eb", "2ffa"), # AT90USB162
_udev_rule("03eb", "2ffb") # AT90USB128
},
'kiibohd': {_udev_rule("1c11", "b007")},
'stm32': {
_udev_rule("1eaf", "0003"), # STM32duino
_udev_rule("0483", "df11") # STM32 DFU
},
'bootloadhid': {_udev_rule("16c0", "05df")},
'usbasploader': {_udev_rule("16c0", "05dc")},
'massdrop': {_udev_rule("03eb", "6124", 'ENV{ID_MM_DEVICE_IGNORE}="1"')},
'caterina': {
# Spark Fun Electronics
_udev_rule("1b4f", "9203", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Pro Micro 3V3/8MHz
_udev_rule("1b4f", "9205", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Pro Micro 5V/16MHz
_udev_rule("1b4f", "9207", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # LilyPad 3V3/8MHz (and some Pro Micro clones)
# Pololu Electronics
_udev_rule("1ffb", "0101", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # A-Star 32U4
# Arduino SA
_udev_rule("2341", "0036", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Leonardo
_udev_rule("2341", "0037", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Micro
# Adafruit Industries LLC
_udev_rule("239a", "000c", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Feather 32U4
_udev_rule("239a", "000d", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # ItsyBitsy 32U4 3V3/8MHz
_udev_rule("239a", "000e", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # ItsyBitsy 32U4 5V/16MHz
# dog hunter AG
_udev_rule("2a03", "0036", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Leonardo
_udev_rule("2a03", "0037", 'ENV{ID_MM_DEVICE_IGNORE}="1"') # Micro
},
'hid-bootloader': {
_udev_rule("03eb", "2067"), # QMK HID
_udev_rule("16c0", "0478") # PJRC halfkay
}
}
# These rules are no longer recommended, only use them to check for their presence. # These rules are no longer recommended, only use them to check for their presence.
deprecated_rules = { deprecated_rules = {

View File

@ -4,6 +4,7 @@ You can compile a keymap already in the repo or using a QMK Configurator export.
A bootloader must be specified. A bootloader must be specified.
""" """
from subprocess import DEVNULL from subprocess import DEVNULL
import sys
from argcomplete.completers import FilesCompleter from argcomplete.completers import FilesCompleter
from milc import cli from milc import cli
@ -12,6 +13,7 @@ import qmk.path
from qmk.decorators import automagic_keyboard, automagic_keymap from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json
from qmk.keyboard import keyboard_completer, keyboard_folder from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.flashers import flasher
def print_bootloader_help(): def print_bootloader_help():
@ -38,9 +40,10 @@ def print_bootloader_help():
cli.echo('For more info, visit https://docs.qmk.fm/#/flashing') cli.echo('For more info, visit https://docs.qmk.fm/#/flashing')
@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), completer=FilesCompleter('.json'), help='The configurator export JSON to compile.') @cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), completer=FilesCompleter('.json'), help='A configurator export JSON to be compiled and flashed or a pre-compiled binary firmware file (bin/hex) to be flashed.')
@cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.') @cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.')
@cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\'s make options of bootloaders.') @cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\'s make options of bootloaders.')
@cli.argument('-m', '--mcu', help='The MCU name. Required for HalfKay, HID, USBAspLoader and ISP flashing.')
@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.') @cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.') @cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.") @cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.")
@ -53,6 +56,8 @@ def print_bootloader_help():
def flash(cli): def flash(cli):
"""Compile and or flash QMK Firmware or keyboard/layout """Compile and or flash QMK Firmware or keyboard/layout
If a binary firmware is supplied, try to flash that.
If a Configurator JSON export is supplied this command will create a new keymap. Keymap and Keyboard arguments If a Configurator JSON export is supplied this command will create a new keymap. Keymap and Keyboard arguments
will be ignored. will be ignored.
@ -60,56 +65,69 @@ def flash(cli):
If bootloader is omitted the make system will use the configured bootloader for that keyboard. If bootloader is omitted the make system will use the configured bootloader for that keyboard.
""" """
if cli.args.clean and not cli.args.filename and not cli.args.dry_run: if cli.args.filename and cli.args.filename.suffix in ['.bin', '.hex']:
if cli.config.flash.keyboard and cli.config.flash.keymap: # Try to flash binary firmware
command = create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, 'clean') cli.echo('Flashing binary firmware...\nPlease reset your keyboard into bootloader mode now!\nPress Ctrl-C to exit.\n')
cli.run(command, capture_output=False, stdin=DEVNULL) try:
err, msg = flasher(cli.args.mcu, cli.args.filename)
if err:
cli.log.error(msg)
return False
except KeyboardInterrupt:
cli.log.info('Ctrl-C was pressed, exiting...')
sys.exit(0)
else:
if cli.args.clean and not cli.args.filename and not cli.args.dry_run:
if cli.config.flash.keyboard and cli.config.flash.keymap:
command = create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, 'clean')
cli.run(command, capture_output=False, stdin=DEVNULL)
# Build the environment vars
envs = {}
for env in cli.args.env:
if '=' in env:
key, value = env.split('=', 1)
envs[key] = value
else:
cli.log.warning('Invalid environment variable: %s', env)
# Determine the compile command
command = ''
if cli.args.bootloaders:
# Provide usage and list bootloaders
cli.echo('usage: qmk flash [-h] [-b] [-n] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
print_bootloader_help()
return False
if cli.args.filename:
# Handle compiling a configurator JSON
user_keymap = parse_configurator_json(cli.args.filename)
keymap_path = qmk.path.keymap(user_keymap['keyboard'])
command = compile_configurator_json(user_keymap, cli.args.bootloader, parallel=cli.config.flash.parallel, **envs)
cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
# Build the environment vars
envs = {}
for env in cli.args.env:
if '=' in env:
key, value = env.split('=', 1)
envs[key] = value
else: else:
cli.log.warning('Invalid environment variable: %s', env) if cli.config.flash.keyboard and cli.config.flash.keymap:
# Generate the make command for a specific keyboard/keymap.
command = create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, cli.args.bootloader, parallel=cli.config.flash.parallel, **envs)
# Determine the compile command elif not cli.config.flash.keyboard:
command = '' cli.log.error('Could not determine keyboard!')
elif not cli.config.flash.keymap:
cli.log.error('Could not determine keymap!')
if cli.args.bootloaders: # Compile the firmware, if we're able to
# Provide usage and list bootloaders if command:
cli.echo('usage: qmk flash [-h] [-b] [-n] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]') cli.log.info('Compiling keymap with {fg_cyan}%s', ' '.join(command))
print_bootloader_help() if not cli.args.dry_run:
return False cli.echo('\n')
compile = cli.run(command, capture_output=False, stdin=DEVNULL)
return compile.returncode
if cli.args.filename: else:
# Handle compiling a configurator JSON cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')
user_keymap = parse_configurator_json(cli.args.filename) cli.echo('usage: qmk flash [-h] [-b] [-n] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
keymap_path = qmk.path.keymap(user_keymap['keyboard']) return False
command = compile_configurator_json(user_keymap, cli.args.bootloader, parallel=cli.config.flash.parallel, **envs)
cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
else:
if cli.config.flash.keyboard and cli.config.flash.keymap:
# Generate the make command for a specific keyboard/keymap.
command = create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, cli.args.bootloader, parallel=cli.config.flash.parallel, **envs)
elif not cli.config.flash.keyboard:
cli.log.error('Could not determine keyboard!')
elif not cli.config.flash.keymap:
cli.log.error('Could not determine keymap!')
# Compile the firmware, if we're able to
if command:
cli.log.info('Compiling keymap with {fg_cyan}%s', ' '.join(command))
if not cli.args.dry_run:
cli.echo('\n')
compile = cli.run(command, capture_output=False, stdin=DEVNULL)
return compile.returncode
else:
cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')
cli.echo('usage: qmk flash [-h] [-b] [-n] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
return False

View File

@ -64,6 +64,54 @@ LEGACY_KEYCODES = { # Comment here is to force multiline formatting
'RESET': 'QK_BOOT' 'RESET': 'QK_BOOT'
} }
# Map VID:PID values to bootloaders
BOOTLOADER_VIDS_PIDS = {
'atmel-dfu': {
("03eb", "2fef"), # ATmega16U2
("03eb", "2ff0"), # ATmega32U2
("03eb", "2ff3"), # ATmega16U4
("03eb", "2ff4"), # ATmega32U4
("03eb", "2ff9"), # AT90USB64
("03eb", "2ffa"), # AT90USB162
("03eb", "2ffb") # AT90USB128
},
'kiibohd': {("1c11", "b007")},
'stm32-dfu': {
("1eaf", "0003"), # STM32duino
("0483", "df11") # STM32 DFU
},
'apm32-dfu': {("314b", "0106")},
'gd32v-dfu': {("28e9", "0189")},
'bootloadhid': {("16c0", "05df")},
'usbasploader': {("16c0", "05dc")},
'usbtinyisp': {("1782", "0c9f")},
'md-boot': {("03eb", "6124")},
'caterina': {
# pid.codes shared PID
("1209", "9203"), # Keyboardio Atreus 2 Bootloader
# Spark Fun Electronics
("1b4f", "9203"), # Pro Micro 3V3/8MHz
("1b4f", "9205"), # Pro Micro 5V/16MHz
("1b4f", "9207"), # LilyPad 3V3/8MHz (and some Pro Micro clones)
# Pololu Electronics
("1ffb", "0101"), # A-Star 32U4
# Arduino SA
("2341", "0036"), # Leonardo
("2341", "0037"), # Micro
# Adafruit Industries LLC
("239a", "000c"), # Feather 32U4
("239a", "000d"), # ItsyBitsy 32U4 3V3/8MHz
("239a", "000e"), # ItsyBitsy 32U4 5V/16MHz
# dog hunter AG
("2a03", "0036"), # Leonardo
("2a03", "0037") # Micro
},
'hid-bootloader': {
("03eb", "2067"), # QMK HID
("16c0", "0478") # PJRC halfkay
}
}
# Common format strings # Common format strings
DATE_FORMAT = '%Y-%m-%d' DATE_FORMAT = '%Y-%m-%d'
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S %Z' DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S %Z'

203
lib/python/qmk/flashers.py Normal file
View File

@ -0,0 +1,203 @@
import shutil
import time
import os
import signal
import usb.core
from qmk.constants import BOOTLOADER_VIDS_PIDS
from milc import cli
# yapf: disable
_PID_TO_MCU = {
'2fef': 'atmega16u2',
'2ff0': 'atmega32u2',
'2ff3': 'atmega16u4',
'2ff4': 'atmega32u4',
'2ff9': 'at90usb64',
'2ffa': 'at90usb162',
'2ffb': 'at90usb128'
}
AVRDUDE_MCU = {
'atmega32a': 'm32',
'atmega328p': 'm328p',
'atmega328': 'm328',
}
# yapf: enable
class DelayedKeyboardInterrupt:
# Custom interrupt handler to delay the processing of Ctrl-C
# https://stackoverflow.com/a/21919644
def __enter__(self):
self.signal_received = False
self.old_handler = signal.signal(signal.SIGINT, self.handler)
def handler(self, sig, frame):
self.signal_received = (sig, frame)
def __exit__(self, type, value, traceback):
signal.signal(signal.SIGINT, self.old_handler)
if self.signal_received:
self.old_handler(*self.signal_received)
# TODO: Make this more generic, so cli/doctor/check.py and flashers.py can share the code
def _check_dfu_programmer_version():
# Return True if version is higher than 0.7.0: supports '--force'
check = cli.run(['dfu-programmer', '--version'], combined_output=True, timeout=5)
first_line = check.stdout.split('\n')[0]
version_number = first_line.split()[1]
maj, min_, bug = version_number.split('.')
if int(maj) >= 0 and int(min_) >= 7:
return True
else:
return False
def _find_bootloader():
# To avoid running forever in the background, only look for bootloaders for 10min
start_time = time.time()
while time.time() - start_time < 600:
for bl in BOOTLOADER_VIDS_PIDS:
for vid, pid in BOOTLOADER_VIDS_PIDS[bl]:
vid_hex = int(f'0x{vid}', 0)
pid_hex = int(f'0x{pid}', 0)
with DelayedKeyboardInterrupt():
# PyUSB does not like to be interrupted by Ctrl-C
# therefore we catch the interrupt with a custom handler
# and only process it once pyusb finished
dev = usb.core.find(idVendor=vid_hex, idProduct=pid_hex)
if dev:
if bl == 'atmel-dfu':
details = _PID_TO_MCU[pid]
elif bl == 'caterina':
details = (vid_hex, pid_hex)
elif bl == 'hid-bootloader':
if vid == '16c0' and pid == '0478':
details = 'halfkay'
else:
details = 'qmk-hid'
elif bl == 'stm32-dfu' or bl == 'apm32-dfu' or bl == 'gd32v-dfu' or bl == 'kiibohd':
details = (vid, pid)
else:
details = None
return (bl, details)
time.sleep(0.1)
return (None, None)
def _find_serial_port(vid, pid):
if 'windows' in cli.platform.lower():
from serial.tools.list_ports_windows import comports
platform = 'windows'
else:
from serial.tools.list_ports_posix import comports
platform = 'posix'
start_time = time.time()
# Caterina times out after 8 seconds
while time.time() - start_time < 8:
for port in comports():
port, desc, hwid = port
if f'{vid:04x}:{pid:04x}' in hwid.casefold():
if platform == 'windows':
time.sleep(1)
return port
else:
start_time = time.time()
# Wait until the port becomes writable before returning
while time.time() - start_time < 8:
if os.access(port, os.W_OK):
return port
else:
time.sleep(0.5)
return None
return None
def _flash_caterina(details, file):
port = _find_serial_port(details[0], details[1])
if port:
cli.run(['avrdude', '-p', 'atmega32u4', '-c', 'avr109', '-U', f'flash:w:{file}:i', '-P', port], capture_output=False)
return False
else:
return True
def _flash_atmel_dfu(mcu, file):
force = '--force' if _check_dfu_programmer_version() else ''
cli.run(['dfu-programmer', mcu, 'erase', force], capture_output=False)
cli.run(['dfu-programmer', mcu, 'flash', force, file], capture_output=False)
cli.run(['dfu-programmer', mcu, 'reset'], capture_output=False)
def _flash_hid_bootloader(mcu, details, file):
if details == 'halfkay':
if shutil.which('teensy-loader-cli'):
cmd = 'teensy-loader-cli'
elif shutil.which('teensy_loader_cli'):
cmd = 'teensy_loader_cli'
# Use 'hid_bootloader_cli' for QMK HID and as a fallback for HalfKay
if not cmd:
if shutil.which('hid_bootloader_cli'):
cmd = 'hid_bootloader_cli'
else:
return True
cli.run([cmd, f'-mmcu={mcu}', '-w', '-v', file], capture_output=False)
def _flash_dfu_util(details, file):
# STM32duino
if details[0] == '1eaf' and details[1] == '0003':
cli.run(['dfu-util', '-a', '2', '-d', f'{details[0]}:{details[1]}', '-R', '-D', file], capture_output=False)
# kiibohd
elif details[0] == '1c11' and details[1] == 'b007':
cli.run(['dfu-util', '-a', '0', '-d', f'{details[0]}:{details[1]}', '-D', file], capture_output=False)
# STM32, APM32, or GD32V DFU
else:
cli.run(['dfu-util', '-a', '0', '-d', f'{details[0]}:{details[1]}', '-s', '0x08000000:leave', '-D', file], capture_output=False)
def _flash_isp(mcu, programmer, file):
programmer = 'usbasp' if programmer == 'usbasploader' else 'usbtiny'
# Check if the provide mcu has an avrdude-specific name, otherwise pass on what the user provided
mcu = AVRDUDE_MCU.get(mcu, mcu)
cli.run(['avrdude', '-p', mcu, '-c', programmer, '-U', f'flash:w:{file}:i'], capture_output=False)
def _flash_mdloader(file):
cli.run(['mdloader', '--first', '--download', file, '--restart'], capture_output=False)
def flasher(mcu, file):
bl, details = _find_bootloader()
# Add a small sleep to avoid race conditions
time.sleep(1)
if bl == 'atmel-dfu':
_flash_atmel_dfu(details, file.name)
elif bl == 'caterina':
if _flash_caterina(details, file.name):
return (True, "The Caterina bootloader was found but is not writable. Check 'qmk doctor' output for advice.")
elif bl == 'hid-bootloader':
if mcu:
if _flash_hid_bootloader(mcu, details, file.name):
return (True, "Please make sure 'teensy_loader_cli' or 'hid_bootloader_cli' is available on your system.")
else:
return (True, "Specifying the MCU with '-m' is necessary for HalfKay/HID bootloaders!")
elif bl == 'stm32-dfu' or bl == 'apm32-dfu' or bl == 'gd32v-dfu' or bl == 'kiibohd':
_flash_dfu_util(details, file.name)
elif bl == 'usbasploader' or bl == 'usbtinyisp':
if mcu:
_flash_isp(mcu, bl, file.name)
else:
return (True, "Specifying the MCU with '-m' is necessary for ISP flashing!")
elif bl == 'md-boot':
_flash_mdloader(file.name)
else:
return (True, "Known bootloader found but flashing not currently supported!")
return (False, None)

View File

@ -7,6 +7,7 @@ hjson
jsonschema>=4 jsonschema>=4
milc>=1.4.2 milc>=1.4.2
pygments pygments
pyserial
pyusb pyusb
qmk-dotty-dict qmk-dotty-dict
pillow pillow

View File

@ -28,6 +28,9 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05df", TAG+="uacc
# USBAspLoader # USBAspLoader
SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05dc", TAG+="uaccess" SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05dc", TAG+="uaccess"
# USBtinyISP
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1782", ATTRS{idProduct}=="0c9f", TAG+="uaccess"
# ModemManager should ignore the following devices # ModemManager should ignore the following devices
# Atmel SAM-BA (Massdrop) # Atmel SAM-BA (Massdrop)
SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="6124", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="6124", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
@ -72,3 +75,9 @@ KERNEL=="hidraw*", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2067", TAG+="uaccess" SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2067", TAG+="uaccess"
## PJRC's HalfKay ## PJRC's HalfKay
SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="0478", TAG+="uaccess" SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="0478", TAG+="uaccess"
# APM32 DFU
SUBSYSTEMS=="usb", ATTRS{idVendor}=="314b", ATTRS{idProduct}=="0106", TAG+="uaccess"
# GD32V DFU
SUBSYSTEMS=="usb", ATTRS{idVendor}=="28e9", ATTRS{idProduct}=="0189", TAG+="uaccess"