Merge branch 'master' into lotus

This commit is contained in:
Daniel Schaefer
2023-02-01 17:42:57 +08:00
1249 changed files with 49179 additions and 1551 deletions

View File

@@ -71,6 +71,7 @@ subcommands = [
'qmk.cli.list.keymaps',
'qmk.cli.list.layouts',
'qmk.cli.kle2json',
'qmk.cli.mass_compile',
'qmk.cli.multibuild',
'qmk.cli.new.keyboard',
'qmk.cli.new.keymap',

View File

@@ -34,7 +34,7 @@ def bux(cli):
@B _y ]# ,c vUWNWWPsfsssN9WyccnckAfUfWb0DR0&R5RRRddq2_ `@D`jr@2U@#c3@1@Qc- B@
@B !7! .r]` }AE0RdRqNd9dNR9fUIzzosPqqAddNNdER9EE9dPy! BQ!zy@iU@.Q@@y@8x- B@
@B :****>. '7adddDdR&gRNdRbd&dNNbbRdNdd5NdRRD0RSf}- .k0&EW`xR .8Q=NRRx B@
@B =**-rx*r}r~}" ;n2jkzsf3N3zsKsP5dddRddddRddNNqPzy\" '~****" B@
@B =**-rx*r}r~}" ;n2jkzsf3N3zsKsP5dddRddddRddNNqPzy\\" '~****" B@
@B :!!~!;=~r>:*_ `:^vxikylulKfHkyjzzozoIoklix|^!-` B@
@B ```'-_""::::!:_-.`` B@
@B `- .` B@

View File

@@ -33,9 +33,11 @@ from typing import Any, Dict, Iterator, List, Tuple
from milc import cli
import qmk.path
from qmk.commands import dump_lines
from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.keymap import keymap_completer, locate_keymap
from qmk.path import normpath
KC_A = 4
KC_SPC = 0x2c
@@ -63,9 +65,10 @@ def parse_file(file_name: str) -> List[Tuple[str, str]]:
try:
from english_words import english_words_lower_alpha_set as correct_words
except ImportError:
cli.echo('Autocorrection will falsely trigger when a typo is a substring of a correctly spelled word.')
cli.echo('To check for this, install the english_words package and rerun this script:')
cli.echo(' {fg_cyan}python3 -m pip install english_words')
if not cli.args.quiet:
cli.echo('Autocorrection will falsely trigger when a typo is a substring of a correctly spelled word.')
cli.echo('To check for this, install the english_words package and rerun this script:')
cli.echo(' {fg_cyan}python3 -m pip install english_words')
# Use a minimal word list as a fallback.
correct_words = ('information', 'available', 'international', 'language', 'loosest', 'reference', 'wealthier', 'entertainment', 'association', 'provides', 'technology', 'statehood')
@@ -232,58 +235,51 @@ def encode_link(link: Dict[str, Any]) -> List[int]:
return [byte_offset & 255, byte_offset >> 8]
def write_generated_code(autocorrections: List[Tuple[str, str]], data: List[int], file_name: str) -> None:
"""Writes autocorrection data as generated C code to `file_name`.
Args:
autocorrections: List of (typo, correction) tuples.
data: List of ints in 0-255, the serialized trie.
file_name: String, path of the output C file.
"""
assert all(0 <= b <= 255 for b in data)
def typo_len(e: Tuple[str, str]) -> int:
return len(e[0])
min_typo = min(autocorrections, key=typo_len)[0]
max_typo = max(autocorrections, key=typo_len)[0]
generated_code = ''.join([
'// Generated code.\n\n', f'// Autocorrection dictionary ({len(autocorrections)} entries):\n', ''.join(sorted(f'// {typo:<{len(max_typo)}} -> {correction}\n' for typo, correction in autocorrections)),
f'\n#define AUTOCORRECT_MIN_LENGTH {len(min_typo)} // "{min_typo}"\n', f'#define AUTOCORRECT_MAX_LENGTH {len(max_typo)} // "{max_typo}"\n\n', f'#define DICTIONARY_SIZE {len(data)}\n\n',
textwrap.fill('static const uint8_t autocorrect_data[DICTIONARY_SIZE] PROGMEM = {%s};' % (', '.join(map(str, data))), width=120, subsequent_indent=' '), '\n\n'
])
with open(file_name, 'wt') as f:
f.write(generated_code)
def typo_len(e: Tuple[str, str]) -> int:
return len(e[0])
@cli.argument('filename', default='autocorrect_dict.txt', help='The autocorrection database file')
def to_hex(b: int) -> str:
return f'0x{b:02X}'
@cli.argument('filename', type=normpath, help='The autocorrection database file')
@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('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.subcommand('Generate the autocorrection data file from a dictionary file.')
def generate_autocorrect_data(cli):
autocorrections = parse_file(cli.args.filename)
trie = make_trie(autocorrections)
data = serialize_trie(autocorrections, trie)
# Environment processing
if cli.args.output == '-':
cli.args.output = None
if cli.args.output:
cli.args.output.parent.mkdir(parents=True, exist_ok=True)
cli.log.info('Creating autocorrect database at {fg_cyan}%s', cli.args.output)
write_generated_code(autocorrections, data, cli.args.output)
current_keyboard = cli.args.keyboard or cli.config.user.keyboard or cli.config.generate_autocorrect_data.keyboard
current_keymap = cli.args.keymap or cli.config.user.keymap or cli.config.generate_autocorrect_data.keymap
else:
current_keyboard = cli.args.keyboard or cli.config.user.keyboard or cli.config.generate_autocorrect_data.keyboard
current_keymap = cli.args.keymap or cli.config.user.keymap or cli.config.generate_autocorrect_data.keymap
if current_keyboard and current_keymap:
cli.args.output = locate_keymap(current_keyboard, current_keymap).parent / 'autocorrect_data.h'
if current_keyboard and current_keymap:
filename = locate_keymap(current_keyboard, current_keymap).parent / 'autocorrect_data.h'
cli.log.info('Creating autocorrect database at {fg_cyan}%s', filename)
write_generated_code(autocorrections, data, filename)
assert all(0 <= b <= 255 for b in data)
else:
write_generated_code(autocorrections, data, 'autocorrect_data.h')
min_typo = min(autocorrections, key=typo_len)[0]
max_typo = max(autocorrections, key=typo_len)[0]
cli.log.info('Processed %d autocorrection entries to table with %d bytes.', len(autocorrections), len(data))
# Build the autocorrect_data.h file.
autocorrect_data_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '']
autocorrect_data_h_lines.append(f'// Autocorrection dictionary ({len(autocorrections)} entries):')
for typo, correction in autocorrections:
autocorrect_data_h_lines.append(f'// {typo:<{len(max_typo)}} -> {correction}')
autocorrect_data_h_lines.append('')
autocorrect_data_h_lines.append(f'#define AUTOCORRECT_MIN_LENGTH {len(min_typo)} // "{min_typo}"')
autocorrect_data_h_lines.append(f'#define AUTOCORRECT_MAX_LENGTH {len(max_typo)} // "{max_typo}"')
autocorrect_data_h_lines.append(f'#define DICTIONARY_SIZE {len(data)}')
autocorrect_data_h_lines.append('')
autocorrect_data_h_lines.append('static const uint8_t autocorrect_data[DICTIONARY_SIZE] PROGMEM = {')
autocorrect_data_h_lines.append(textwrap.fill(' %s' % (', '.join(map(to_hex, data))), width=100, subsequent_indent=' '))
autocorrect_data_h_lines.append('};')
# Show the results
dump_lines(cli.args.output, autocorrect_data_h_lines, cli.args.quiet)

View File

@@ -62,7 +62,7 @@ def matrix_pins(matrix_pins, postfix=''):
def generate_matrix_size(kb_info_json, config_h_lines):
"""Add the matrix size to the config.h.
"""
if 'matrix_pins' in kb_info_json:
if 'matrix_size' in kb_info_json:
config_h_lines.append(generate_define('MATRIX_COLS', kb_info_json['matrix_size']['cols']))
config_h_lines.append(generate_define('MATRIX_ROWS', kb_info_json['matrix_size']['rows']))

View File

@@ -5,7 +5,9 @@ from argparse import ArgumentTypeError
from milc import cli
import qmk.path
from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
from qmk.commands import dump_lines
from qmk.path import normpath
def breathing_center(value):
@@ -24,17 +26,10 @@ def breathing_max(value):
raise ArgumentTypeError('Breathing max must be between 0 and 255')
@cli.argument('-c', '--center', arg_only=True, type=breathing_center, default=1.85, help='The breathing center value, from 1 to 2.7. Default: 1.85')
@cli.argument('-m', '--max', arg_only=True, type=breathing_max, default=255, help='The breathing maximum value, from 0 to 255. Default: 255')
@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help='Quiet mode, only output error messages')
@cli.subcommand('Generates an RGB Light breathing table header.')
def generate_rgb_breathe_table(cli):
"""Generate a rgblight_breathe_table.h file containing a breathing LUT for RGB Lighting (Underglow) feature.
"""
def _generate_table(lines, center, maximum):
breathe_values = [0] * 256
for pos in range(0, 256):
breathe_values[pos] = (int)((math.exp(math.sin((pos / 255) * math.pi)) - cli.args.center / math.e) * (cli.args.max / (math.e - 1 / math.e)))
breathe_values[pos] = (int)((math.exp(math.sin((pos / 255) * math.pi)) - center / math.e) * (maximum / (math.e - 1 / math.e)))
values_template = ''
for s in range(0, 3):
@@ -51,11 +46,7 @@ def generate_rgb_breathe_table(cli):
values_template += '#endif'
values_template += '\n\n' if s < 2 else ''
table_template = '''#pragma once
#define RGBLIGHT_EFFECT_BREATHE_TABLE
// clang-format off
table_template = '''#define RGBLIGHT_EFFECT_BREATHE_TABLE
// Breathing center: {0:.2f}
// Breathing max: {1:d}
@@ -65,15 +56,23 @@ const uint8_t PROGMEM rgblight_effect_breathe_table[] = {{
}};
static const int table_scale = 256 / sizeof(rgblight_effect_breathe_table);
'''.format(cli.args.center, cli.args.max, values_template)
'''.format(center, maximum, values_template)
lines.append(table_template)
if cli.args.output:
cli.args.output.parent.mkdir(parents=True, exist_ok=True)
if cli.args.output.exists():
cli.args.output.replace(cli.args.output.parent / (cli.args.output.name + '.bak'))
cli.args.output.write_text(table_template)
if not cli.args.quiet:
cli.log.info('Wrote header to %s.', cli.args.output)
else:
print(table_template)
@cli.argument('-c', '--center', arg_only=True, type=breathing_center, default=1.85, help='The breathing center value, from 1 to 2.7. Default: 1.85')
@cli.argument('-m', '--max', arg_only=True, type=breathing_max, default=255, help='The breathing maximum value, from 0 to 255. Default: 255')
@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help='Quiet mode, only output error messages')
@cli.subcommand('Generates an RGB Light breathing table header.')
def generate_rgb_breathe_table(cli):
"""Generate a rgblight_breathe_table.h file containing a breathing LUT for RGB Lighting (Underglow) feature.
"""
# Build the header file.
header_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '// clang-format off']
_generate_table(header_lines, cli.args.center, cli.args.max)
# Show the results
dump_lines(cli.args.output, header_lines, cli.args.quiet)

View File

@@ -12,6 +12,8 @@ from qmk.path import is_keyboard, keyboard
from qmk.git import git_get_ignored_files
from qmk.c_parse import c_source_files
CHIBIOS_CONF_CHECKS = ['chconf.h', 'halconf.h', 'mcuconf.h', 'board.h']
def _list_defaultish_keymaps(kb):
"""Return default like keymaps for a given keyboard
@@ -64,6 +66,15 @@ def _handle_json_errors(kb, info):
return ok
def _chibios_conf_includenext_check(target):
"""Check the ChibiOS conf.h for the correct inclusion of the next conf.h
"""
for i, line in enumerate(target.open()):
if f'#include_next "{target.name}"' in line:
return f'Found `#include_next "{target.name}"` on line {i} of {target}, should be `#include_next <{target.name}>` (use angle brackets, not quotes)'
return None
def _rules_mk_assignment_only(kb):
"""Check the keyboard-level rules.mk to ensure it only has assignments.
"""
@@ -124,6 +135,12 @@ def keymap_check(kb, km):
cli.log.error(f'{kb}/{km}: The file "{file}" does not have a license header!')
ok = False
if file.name in CHIBIOS_CONF_CHECKS:
check_error = _chibios_conf_includenext_check(file)
if check_error is not None:
cli.log.error(f'{kb}/{km}: {check_error}')
ok = False
return ok
@@ -156,6 +173,12 @@ def keyboard_check(kb):
cli.log.error(f'{kb}: The file "{file}" does not have a license header!')
ok = False
if file.name in CHIBIOS_CONF_CHECKS:
check_error = _chibios_conf_includenext_check(file)
if check_error is not None:
cli.log.error(f'{kb}: {check_error}')
ok = False
return ok

View File

@@ -0,0 +1,169 @@
"""Compile all keyboards.
This will compile everything in parallel, for testing purposes.
"""
import fnmatch
import logging
import multiprocessing
import os
import re
from pathlib import Path
from subprocess import DEVNULL
from dotty_dict import dotty
from milc import cli
from qmk.constants import QMK_FIRMWARE
from qmk.commands import _find_make, get_make_parallel_args
from qmk.info import keymap_json
import qmk.keyboard
import qmk.keymap
def _set_log_level(level):
cli.acquire_lock()
old = cli.log_level
cli.log_level = level
cli.log.setLevel(level)
logging.root.setLevel(level)
cli.release_lock()
return old
def _all_keymaps(keyboard):
old = _set_log_level(logging.CRITICAL)
keymaps = qmk.keymap.list_keymaps(keyboard)
_set_log_level(old)
return (keyboard, keymaps)
def _keymap_exists(keyboard, keymap):
old = _set_log_level(logging.CRITICAL)
ret = keyboard if qmk.keymap.locate_keymap(keyboard, keymap) is not None else None
_set_log_level(old)
return ret
def _load_keymap_info(keyboard, keymap):
old = _set_log_level(logging.CRITICAL)
ret = (keyboard, keymap, keymap_json(keyboard, keymap))
_set_log_level(old)
return ret
@cli.argument('-t', '--no-temp', arg_only=True, action='store_true', help="Remove temporary files during build.")
@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs; 0 means unlimited.")
@cli.argument('-c', '--clean', arg_only=True, action='store_true', help="Remove object files before compiling.")
@cli.argument(
'-f',
'--filter',
arg_only=True,
action='append',
default=[],
help= # noqa: `format-python` and `pytest` don't agree here.
"Filter the list of keyboards based on the supplied value in rules.mk. Matches info.json structure, and accepts the format 'features.rgblight=true'. May be passed multiple times, all filters need to match. Value may include wildcards such as '*' and '?'." # noqa: `format-python` and `pytest` don't agree here.
)
@cli.argument('-km', '--keymap', type=str, default='default', help="The keymap name to build. Default is 'default'.")
@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.subcommand('Compile QMK Firmware for all keyboards.', hidden=False if cli.config.user.developer else True)
def mass_compile(cli):
"""Compile QMK Firmware against all keyboards.
"""
make_cmd = _find_make()
if cli.args.clean:
cli.run([make_cmd, 'clean'], capture_output=False, stdin=DEVNULL)
builddir = Path(QMK_FIRMWARE) / '.build'
makefile = builddir / 'parallel_kb_builds.mk'
targets = []
with multiprocessing.Pool() as pool:
cli.log.info(f'Retrieving list of keyboards with keymap "{cli.args.keymap}"...')
target_list = []
if cli.args.keymap == 'all':
kb_to_kms = pool.map(_all_keymaps, qmk.keyboard.list_keyboards())
for targets in kb_to_kms:
keyboard = targets[0]
keymaps = targets[1]
target_list.extend([(keyboard, keymap) for keymap in keymaps])
else:
target_list = [(kb, cli.args.keymap) for kb in filter(lambda kb: kb is not None, pool.starmap(_keymap_exists, [(kb, cli.args.keymap) for kb in qmk.keyboard.list_keyboards()]))]
if len(cli.args.filter) == 0:
targets = target_list
else:
cli.log.info('Parsing data for all matching keyboard/keymap combinations...')
valid_keymaps = [(e[0], e[1], dotty(e[2])) for e in pool.starmap(_load_keymap_info, target_list)]
filter_re = re.compile(r'^(?P<key>[a-zA-Z0-9_\.]+)\s*=\s*(?P<value>[^#]+)$')
for filter_txt in cli.args.filter:
f = filter_re.match(filter_txt)
if f is not None:
key = f.group('key')
value = f.group('value')
cli.log.info(f'Filtering on condition ("{key}" == "{value}")...')
def _make_filter(k, v):
expr = fnmatch.translate(v)
rule = re.compile(expr, re.IGNORECASE)
def f(e):
lhs = e[2].get(k)
lhs = str(False if lhs is None else lhs)
return rule.search(lhs) is not None
return f
valid_keymaps = filter(_make_filter(key, value), valid_keymaps)
targets = [(e[0], e[1]) for e in valid_keymaps]
if len(targets) == 0:
return
builddir.mkdir(parents=True, exist_ok=True)
with open(makefile, "w") as f:
for target in sorted(targets):
keyboard_name = target[0]
keymap_name = target[1]
keyboard_safe = keyboard_name.replace('/', '_')
# yapf: disable
f.write(
f"""\
all: {keyboard_safe}_{keymap_name}_binary
{keyboard_safe}_{keymap_name}_binary:
@rm -f "{QMK_FIRMWARE}/.build/failed.log.{keyboard_safe}.{keymap_name}" || true
@echo "Compiling QMK Firmware for target: '{keyboard_name}:{keymap_name}'..." >>"{QMK_FIRMWARE}/.build/build.log.{os.getpid()}.{keyboard_safe}"
+@$(MAKE) -C "{QMK_FIRMWARE}" -f "{QMK_FIRMWARE}/builddefs/build_keyboard.mk" KEYBOARD="{keyboard_name}" KEYMAP="{keymap_name}" REQUIRE_PLATFORM_KEY= COLOR=true SILENT=false {' '.join(cli.args.env)} \\
>>"{QMK_FIRMWARE}/.build/build.log.{os.getpid()}.{keyboard_safe}.{keymap_name}" 2>&1 \\
|| cp "{QMK_FIRMWARE}/.build/build.log.{os.getpid()}.{keyboard_safe}.{keymap_name}" "{QMK_FIRMWARE}/.build/failed.log.{os.getpid()}.{keyboard_safe}.{keymap_name}"
@{{ grep '\[ERRORS\]' "{QMK_FIRMWARE}/.build/build.log.{os.getpid()}.{keyboard_safe}.{keymap_name}" >/dev/null 2>&1 && printf "Build %-64s \e[1;31m[ERRORS]\e[0m\\n" "{keyboard_name}:{keymap_name}" ; }} \\
|| {{ grep '\[WARNINGS\]' "{QMK_FIRMWARE}/.build/build.log.{os.getpid()}.{keyboard_safe}.{keymap_name}" >/dev/null 2>&1 && printf "Build %-64s \e[1;33m[WARNINGS]\e[0m\\n" "{keyboard_name}:{keymap_name}" ; }} \\
|| printf "Build %-64s \e[1;32m[OK]\e[0m\\n" "{keyboard_name}:{keymap_name}"
@rm -f "{QMK_FIRMWARE}/.build/build.log.{os.getpid()}.{keyboard_safe}.{keymap_name}" || true
"""# noqa
)
# yapf: enable
if cli.args.no_temp:
# yapf: disable
f.write(
f"""\
@rm -rf "{QMK_FIRMWARE}/.build/{keyboard_safe}_{keymap_name}.elf" 2>/dev/null || true
@rm -rf "{QMK_FIRMWARE}/.build/{keyboard_safe}_{keymap_name}.map" 2>/dev/null || true
@rm -rf "{QMK_FIRMWARE}/.build/{keyboard_safe}_{keymap_name}.hex" 2>/dev/null || true
@rm -rf "{QMK_FIRMWARE}/.build/{keyboard_safe}_{keymap_name}.bin" 2>/dev/null || true
@rm -rf "{QMK_FIRMWARE}/.build/{keyboard_safe}_{keymap_name}.uf2" 2>/dev/null || true
@rm -rf "{QMK_FIRMWARE}/.build/obj_{keyboard_safe}" || true
@rm -rf "{QMK_FIRMWARE}/.build/obj_{keyboard_safe}_{keymap_name}" || true
"""# noqa
)
# yapf: enable
f.write('\n')
cli.run([make_cmd, *get_make_parallel_args(cli.args.parallel), '-f', makefile.as_posix(), 'all'], capture_output=False, stdin=DEVNULL)
# Check for failures
failures = [f for f in builddir.glob(f'failed.log.{os.getpid()}.*')]
if len(failures) > 0:
return False

View File

@@ -1,3 +1,4 @@
import platform
import shutil
import time
import os
@@ -56,6 +57,20 @@ def _check_dfu_programmer_version():
return False
def _find_usb_device(vid_hex, pid_hex):
# WSL doesnt have access to USB - use powershell instead...?
if 'microsoft' in platform.uname().release.lower():
ret = cli.run(['powershell.exe', '-command', 'Get-PnpDevice -PresentOnly | Select-Object -Property InstanceId'])
if f'USB\\VID_{vid_hex:04X}&PID_{pid_hex:04X}' in ret.stdout:
return (vid_hex, pid_hex)
else:
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
return usb.core.find(idVendor=vid_hex, idProduct=pid_hex)
def _find_bootloader():
# To avoid running forever in the background, only look for bootloaders for 10min
start_time = time.time()
@@ -64,11 +79,7 @@ def _find_bootloader():
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)
dev = _find_usb_device(vid_hex, pid_hex)
if dev:
if bl == 'atmel-dfu':
details = _PID_TO_MCU[pid]
@@ -178,25 +189,25 @@ def flasher(mcu, file):
# Add a small sleep to avoid race conditions
time.sleep(1)
if bl == 'atmel-dfu':
_flash_atmel_dfu(details, file.name)
_flash_atmel_dfu(details, file)
elif bl == 'caterina':
if _flash_caterina(details, file.name):
if _flash_caterina(details, file):
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):
if _flash_hid_bootloader(mcu, details, file):
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)
_flash_dfu_util(details, file)
elif bl == 'usbasploader' or bl == 'usbtinyisp':
if mcu:
_flash_isp(mcu, bl, file.name)
_flash_isp(mcu, bl, file)
else:
return (True, "Specifying the MCU with '-m' is necessary for ISP flashing!")
elif bl == 'md-boot':
_flash_mdloader(file.name)
_flash_mdloader(file)
else:
return (True, "Known bootloader found but flashing not currently supported!")