From e4cfbd2532aa173fe8389de5f2935989c0be804d Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sun, 1 Jan 2023 04:51:29 +0000 Subject: [PATCH] Allow CLI to flash .uf2 files (#19462) --- lib/python/qmk/cli/flash.py | 46 ++++++++++++++++++++----------------- lib/python/qmk/flashers.py | 14 +++++++++++ 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/lib/python/qmk/cli/flash.py b/lib/python/qmk/cli/flash.py index 40bfbdab5688..52defb5f0d3c 100644 --- a/lib/python/qmk/cli/flash.py +++ b/lib/python/qmk/cli/flash.py @@ -11,12 +11,14 @@ 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.keyboard import keyboard_completer, keyboard_folder +from qmk.keymap import keymap_completer from qmk.flashers import flasher -def print_bootloader_help(): +def _list_bootloaders(): """Prints the available bootloaders listed in docs.qmk.fm. """ + cli.print_help() cli.log.info('Here are the available bootloaders:') cli.echo('\tavrdude') cli.echo('\tbootloadhid') @@ -36,14 +38,29 @@ def print_bootloader_help(): cli.echo('\tuf2-split-left') cli.echo('\tuf2-split-right') 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('-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('-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. 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('-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('-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.") @@ -56,30 +73,17 @@ def flash(cli): 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 - will be ignored. + If a Configurator export is supplied this command will create a new keymap, overwriting an existing keymap if one exists. - 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 cli.args.filename and cli.args.filename.suffix in ['.bin', '.hex']: - # 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(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.filename and cli.args.filename.suffix in ['.bin', '.hex', '.uf2']: + return _flash_binary(cli.args.filename, cli.args.mcu) if cli.args.bootloaders: - # Provide usage and list bootloaders - cli.print_help() - print_bootloader_help() - return False + return _list_bootloaders() # Build the environment vars envs = build_environment(cli.args.env) diff --git a/lib/python/qmk/flashers.py b/lib/python/qmk/flashers.py index e902e5072fe1..f83665d9accf 100644 --- a/lib/python/qmk/flashers.py +++ b/lib/python/qmk/flashers.py @@ -71,6 +71,12 @@ def _find_usb_device(vid_hex, 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(): # To avoid running forever in the background, only look for bootloaders for 10min start_time = time.time() @@ -95,6 +101,8 @@ def _find_bootloader(): else: details = None return (bl, details) + if _find_uf2_devices(): + return ('_uf2_compatible_', None) time.sleep(0.1) return (None, None) @@ -184,6 +192,10 @@ def _flash_mdloader(file): 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): bl, details = _find_bootloader() # 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!") elif bl == 'md-boot': _flash_mdloader(file) + elif bl == '_uf2_compatible_': + _flash_uf2(file) else: return (True, "Known bootloader found but flashing not currently supported!")