Allow CLI to flash .uf2 files (#19462)

This commit is contained in:
Joel Challis 2023-01-01 04:51:29 +00:00 committed by GitHub
parent cd1f05a23a
commit e4cfbd2532
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 21 deletions

View file

@ -11,12 +11,14 @@ 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, build_environment from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json, build_environment
from qmk.keyboard import keyboard_completer, keyboard_folder from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.keymap import keymap_completer
from qmk.flashers import flasher from qmk.flashers import flasher
def print_bootloader_help(): def _list_bootloaders():
"""Prints the available bootloaders listed in docs.qmk.fm. """Prints the available bootloaders listed in docs.qmk.fm.
""" """
cli.print_help()
cli.log.info('Here are the available bootloaders:') cli.log.info('Here are the available bootloaders:')
cli.echo('\tavrdude') cli.echo('\tavrdude')
cli.echo('\tbootloadhid') cli.echo('\tbootloadhid')
@ -36,14 +38,29 @@ def print_bootloader_help():
cli.echo('\tuf2-split-left') cli.echo('\tuf2-split-left')
cli.echo('\tuf2-split-right') cli.echo('\tuf2-split-right')
cli.echo('For more info, visit https://docs.qmk.fm/#/flashing') cli.echo('For more info, visit https://docs.qmk.fm/#/flashing')
return False
def _flash_binary(filename, mcu):
"""Try to flash binary firmware
"""
cli.echo('Flashing binary firmware...\nPlease reset your keyboard into bootloader mode now!\nPress Ctrl-C to exit.\n')
try:
err, msg = flasher(mcu, filename)
if err:
cli.log.error(msg)
return False
except KeyboardInterrupt:
cli.log.info('Ctrl-C was pressed, exiting...')
return True
@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('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('-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('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export 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('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export 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.")
@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs; 0 means unlimited.") @cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs; 0 means unlimited.")
@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.") @cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.")
@ -56,30 +73,17 @@ def flash(cli):
If a binary firmware is supplied, try to flash that. 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 export is supplied this command will create a new keymap, overwriting an existing keymap if one exists.
will be ignored.
If no file is supplied, keymap and keyboard are expected. If a keyboard and keymap are provided this command will build a firmware based on that.
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.filename and cli.args.filename.suffix in ['.bin', '.hex']: if cli.args.filename and cli.args.filename.suffix in ['.bin', '.hex', '.uf2']:
# Try to flash binary firmware return _flash_binary(cli.args.filename, cli.args.mcu)
cli.echo('Flashing binary firmware...\nPlease reset your keyboard into bootloader mode now!\nPress Ctrl-C to exit.\n')
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...')
return True
if cli.args.bootloaders: if cli.args.bootloaders:
# Provide usage and list bootloaders return _list_bootloaders()
cli.print_help()
print_bootloader_help()
return False
# Build the environment vars # Build the environment vars
envs = build_environment(cli.args.env) envs = build_environment(cli.args.env)

View file

@ -71,6 +71,12 @@ def _find_usb_device(vid_hex, pid_hex):
return usb.core.find(idVendor=vid_hex, idProduct=pid_hex) return usb.core.find(idVendor=vid_hex, idProduct=pid_hex)
def _find_uf2_devices():
"""Delegate to uf2conv.py as VID:PID pairs can potentially fluctuate more than other bootloaders
"""
return cli.run(['util/uf2conv.py', '--list']).stdout.splitlines()
def _find_bootloader(): def _find_bootloader():
# To avoid running forever in the background, only look for bootloaders for 10min # To avoid running forever in the background, only look for bootloaders for 10min
start_time = time.time() start_time = time.time()
@ -95,6 +101,8 @@ def _find_bootloader():
else: else:
details = None details = None
return (bl, details) return (bl, details)
if _find_uf2_devices():
return ('_uf2_compatible_', None)
time.sleep(0.1) time.sleep(0.1)
return (None, None) return (None, None)
@ -184,6 +192,10 @@ def _flash_mdloader(file):
cli.run(['mdloader', '--first', '--download', file, '--restart'], capture_output=False) cli.run(['mdloader', '--first', '--download', file, '--restart'], capture_output=False)
def _flash_uf2(file):
cli.run(['util/uf2conv.py', '--deploy', file], capture_output=False)
def flasher(mcu, file): def flasher(mcu, file):
bl, details = _find_bootloader() bl, details = _find_bootloader()
# Add a small sleep to avoid race conditions # Add a small sleep to avoid race conditions
@ -208,6 +220,8 @@ def flasher(mcu, file):
return (True, "Specifying the MCU with '-m' is necessary for ISP flashing!") return (True, "Specifying the MCU with '-m' is necessary for ISP flashing!")
elif bl == 'md-boot': elif bl == 'md-boot':
_flash_mdloader(file) _flash_mdloader(file)
elif bl == '_uf2_compatible_':
_flash_uf2(file)
else: else:
return (True, "Known bootloader found but flashing not currently supported!") return (True, "Known bootloader found but flashing not currently supported!")