From d7173967087e022d20d1f9c812b1b668e9d3f71b Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Thu, 30 Jun 2022 13:19:27 +0200 Subject: [Core] Add Raspberry Pi RP2040 support (#14877) * Disable RESET keycode because of naming conflicts * Add Pico SDK as submodule * Add RP2040 build support to QMK * Adjust USB endpoint structs for RP2040 * Add RP2040 bootloader and double-tap reset routine * Add generic and pro micro RP2040 boards * Add RP2040 onekey keyboard * Add WS2812 PIO DMA enabled driver and documentation Supports regular and open-drain output configuration. RP2040 GPIOs are sadly not 5V tolerant, so this is a bit use-less or needs extra hardware or you take the risk to fry your hardware. * Adjust SIO Driver for RP2040 * Adjust I2C Driver for RP2040 * Adjust SPI Driver for RP2040 * Add PIO serial driver and documentation * Add general RP2040 documentation * Apply suggestions from code review Co-authored-by: Nick Brassel Co-authored-by: Nick Brassel --- lib/python/qmk/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/python/qmk/constants.py') diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py index a54d9058bc..be85a1dbff 100644 --- a/lib/python/qmk/constants.py +++ b/lib/python/qmk/constants.py @@ -14,7 +14,7 @@ QMK_FIRMWARE_UPSTREAM = 'qmk/qmk_firmware' MAX_KEYBOARD_SUBFOLDERS = 5 # Supported processor types -CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK66FX1M0', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F405', 'STM32F407', 'STM32F411', 'STM32F446', 'STM32G431', 'STM32G474', 'STM32L412', 'STM32L422', 'STM32L432', 'STM32L433', 'STM32L442', 'STM32L443', 'GD32VF103', 'WB32F3G71', 'WB32FQ95' +CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK66FX1M0', 'RP2040', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F405', 'STM32F407', 'STM32F411', 'STM32F446', 'STM32G431', 'STM32G474', 'STM32L412', 'STM32L422', 'STM32L432', 'STM32L433', 'STM32L442', 'STM32L443', 'GD32VF103', 'WB32F3G71', 'WB32FQ95' LUFA_PROCESSORS = 'at90usb162', 'atmega16u2', 'atmega32u2', 'atmega16u4', 'atmega32u4', 'at90usb646', 'at90usb647', 'at90usb1286', 'at90usb1287', None VUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85' -- cgit v1.2.3 From ac5e6b6a3bfad12ab7d9786a18fcc90f8bf4caf7 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 3 Jul 2022 00:12:45 +1000 Subject: Tentative Teensy 3.5 support (#14420) * Tentative Teensy 3.5 support * Set firmware format to .hex for ARM Teensys * Got to "device descriptor failed" by comparing with Teensy 3.6 code * Drop down to 96MHz... * Bump back up to 120MHz --- builddefs/bootloader.mk | 4 +-- builddefs/mcu_selection.mk | 27 ++++++++++++++ data/schemas/keyboard.jsonschema | 2 +- docs/compatible_microcontrollers.md | 2 ++ keyboards/handwired/onekey/teensy_35/chconf.h | 28 +++++++++++++++ keyboards/handwired/onekey/teensy_35/config.h | 32 +++++++++++++++++ keyboards/handwired/onekey/teensy_35/halconf.h | 28 +++++++++++++++ keyboards/handwired/onekey/teensy_35/mcuconf.h | 50 ++++++++++++++++++++++++++ keyboards/handwired/onekey/teensy_35/rules.mk | 8 +++++ lib/python/qmk/constants.py | 2 +- platforms/chibios/chibios_config.h | 2 +- 11 files changed, 180 insertions(+), 5 deletions(-) create mode 100644 keyboards/handwired/onekey/teensy_35/chconf.h create mode 100644 keyboards/handwired/onekey/teensy_35/config.h create mode 100644 keyboards/handwired/onekey/teensy_35/halconf.h create mode 100644 keyboards/handwired/onekey/teensy_35/mcuconf.h create mode 100644 keyboards/handwired/onekey/teensy_35/rules.mk (limited to 'lib/python/qmk/constants.py') diff --git a/builddefs/bootloader.mk b/builddefs/bootloader.mk index 3d4c03b01c..51e9b7d558 100644 --- a/builddefs/bootloader.mk +++ b/builddefs/bootloader.mk @@ -105,8 +105,8 @@ ifeq ($(strip $(BOOTLOADER)), halfkay) ifeq ($(strip $(MCU)), at90usb1286) BOOTLOADER_SIZE = 1024 endif - # Teensy LC, 3.x - ifneq (,$(filter $(MCU_ORIG), MKL26Z64 MK20DX128 MK20DX256 MK66FX1M0)) + # Teensy LC, 3.0, 3.1/2, 3.5, 3.6 + ifneq (,$(filter $(MCU_ORIG), MKL26Z64 MK20DX128 MK20DX256 MK64FX512 MK66FX1M0)) FIRMWARE_FORMAT = hex endif endif diff --git a/builddefs/mcu_selection.mk b/builddefs/mcu_selection.mk index 135f663c14..7528aa6842 100644 --- a/builddefs/mcu_selection.mk +++ b/builddefs/mcu_selection.mk @@ -87,6 +87,33 @@ ifneq ($(findstring MK20DX256, $(MCU)),) BOARD ?= PJRC_TEENSY_3_1 endif +ifneq ($(findstring MK64FX512, $(MCU)),) + # Cortex version + MCU = cortex-m4 + + # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 + ARMV = 7 + + ## chip/board settings + # - the next two should match the directories in + # /os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) + MCU_FAMILY = KINETIS + MCU_SERIES = K60x + + # Linker script to use + # - it should exist either in /os/common/ports/ARMCMx/compilers/GCC/ld/ + # or /ld/ + MCU_LDSCRIPT ?= MK64FX512 + + # Startup code to use + # - it should exist in /os/common/startup/ARMCMx/compilers/GCC/mk/ + MCU_STARTUP ?= k60x + + # Board: it should exist either in /os/hal/boards/, + # /boards/, or drivers/boards/ + BOARD ?= PJRC_TEENSY_3_5 +endif + ifneq ($(findstring MK66FX1M0, $(MCU)),) # Cortex version MCU = cortex-m4 diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema index 8aca807e38..320974f0c4 100644 --- a/data/schemas/keyboard.jsonschema +++ b/data/schemas/keyboard.jsonschema @@ -42,7 +42,7 @@ }, "processor": { "type": "string", - "enum": ["cortex-m0", "cortex-m0plus", "cortex-m3", "cortex-m4", "MKL26Z64", "MK20DX128", "MK20DX256", "MK66FX1M0", "RP2040", "STM32F042", "STM32F072", "STM32F103", "STM32F303", "STM32F401", "STM32F405", "STM32F407", "STM32F411", "STM32F446", "STM32G431", "STM32G474", "STM32L412", "STM32L422", "STM32L432", "STM32L433", "STM32L442", "STM32L443", "GD32VF103", "WB32F3G71", "WB32FQ95", "atmega16u2", "atmega32u2", "atmega16u4", "atmega32u4", "at90usb162", "at90usb646", "at90usb647", "at90usb1286", "at90usb1287", "atmega32a", "atmega328p", "atmega328", "attiny85", "unknown"] + "enum": ["cortex-m0", "cortex-m0plus", "cortex-m3", "cortex-m4", "MKL26Z64", "MK20DX128", "MK20DX256", "MK64FX512", "MK66FX1M0", "RP2040", "STM32F042", "STM32F072", "STM32F103", "STM32F303", "STM32F401", "STM32F405", "STM32F407", "STM32F411", "STM32F446", "STM32G431", "STM32G474", "STM32L412", "STM32L422", "STM32L432", "STM32L433", "STM32L442", "STM32L443", "GD32VF103", "WB32F3G71", "WB32FQ95", "atmega16u2", "atmega32u2", "atmega16u4", "atmega32u4", "at90usb162", "at90usb646", "at90usb647", "at90usb1286", "at90usb1287", "atmega32a", "atmega328p", "atmega328", "attiny85", "unknown"] }, "audio": { "type": "object", diff --git a/docs/compatible_microcontrollers.md b/docs/compatible_microcontrollers.md index a594fe0620..cc9c0b7f92 100644 --- a/docs/compatible_microcontrollers.md +++ b/docs/compatible_microcontrollers.md @@ -62,6 +62,8 @@ You can also use any ARM chip with USB that [ChibiOS](https://www.chibios.org) s * [MK20DX128](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/k-series-cortex-m4/k2x-usb/kinetis-k20-50-mhz-full-speed-usb-mixed-signal-integration-microcontrollers-based-on-arm-cortex-m4-core:K20_50) * [MK20DX256](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/k-series-cortex-m4/k2x-usb/kinetis-k20-72-mhz-full-speed-usb-mixed-signal-integration-microcontrollers-mcus-based-on-arm-cortex-m4-core:K20_72) * PJRC Teensy 3.2 + * [MK64FX512](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/k-series-cortex-m4/k6x-ethernet/kinetis-k64-120-mhz-256-kb-sram-microcontrollers-mcus-based-on-arm-cortex-m4-core:K64_120) + * PJRC Teensy 3.5 * [MK66FX1M0](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/k-series-cortex-m4/k6x-ethernet/kinetis-k66-180-mhz-dual-high-speed-full-speed-usbs-2mb-flash-microcontrollers-mcus-based-on-arm-cortex-m4-core:K66_180) * PJRC Teensy 3.6 diff --git a/keyboards/handwired/onekey/teensy_35/chconf.h b/keyboards/handwired/onekey/teensy_35/chconf.h new file mode 100644 index 0000000000..ee8312c526 --- /dev/null +++ b/keyboards/handwired/onekey/teensy_35/chconf.h @@ -0,0 +1,28 @@ +/* Copyright 2020 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * This file was auto-generated by: + * `qmk chibios-confmigrate -i keyboards/handwired/onekey/teensy_35/chconf.h -r platforms/chibios/common/configs/chconf.h` + */ + +#pragma once + +#define CH_CFG_ST_TIMEDELTA 0 + +#define CH_CFG_TIME_QUANTUM 20 + +#include_next diff --git a/keyboards/handwired/onekey/teensy_35/config.h b/keyboards/handwired/onekey/teensy_35/config.h new file mode 100644 index 0000000000..a5f2c945c4 --- /dev/null +++ b/keyboards/handwired/onekey/teensy_35/config.h @@ -0,0 +1,32 @@ +/* Copyright 2019 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +// TODO: including this causes "error: expected identifier before '(' token" errors +//#include "config_common.h" + +#define PRODUCT Onekey Teensy 3.5 + +#define MATRIX_COL_PINS { D5 } // 20/A6 +#define MATRIX_ROW_PINS { B2 } // 19/A5 +#define UNUSED_PINS + +// i2c_master defines +#define I2C1_SCL_PIN B0 // 16/A2 on pinout +#define I2C1_SDA_PIN B1 // 17/A3 on pinout +#define I2C1_SCL_PAL_MODE PAL_MODE_ALTERNATIVE_2 +#define I2C1_SDA_PAL_MODE PAL_MODE_ALTERNATIVE_2 diff --git a/keyboards/handwired/onekey/teensy_35/halconf.h b/keyboards/handwired/onekey/teensy_35/halconf.h new file mode 100644 index 0000000000..6ac756d0f2 --- /dev/null +++ b/keyboards/handwired/onekey/teensy_35/halconf.h @@ -0,0 +1,28 @@ +/* Copyright 2020 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * This file was auto-generated by: + * `qmk chibios-confmigrate -i keyboards/handwired/onekey/teensy_35/halconf.h -r platforms/chibios/common/configs/halconf.h` + */ + +#pragma once + +#define HAL_USE_SERIAL TRUE + +#define SERIAL_USB_BUFFERS_SIZE 256 + +#include_next diff --git a/keyboards/handwired/onekey/teensy_35/mcuconf.h b/keyboards/handwired/onekey/teensy_35/mcuconf.h new file mode 100644 index 0000000000..dc508eee7d --- /dev/null +++ b/keyboards/handwired/onekey/teensy_35/mcuconf.h @@ -0,0 +1,50 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef _MCUCONF_H_ +#define _MCUCONF_H_ + +#define K60x_MCUCONF + +/* + * HAL driver system settings. + */ + +/* Select the MCU clocking mode below by enabling the appropriate block. */ + +/* PEE mode - 120MHz system clock driven by external crystal. */ +#define KINETIS_MCG_MODE KINETIS_MCG_MODE_PEE +#define KINETIS_PLLCLK_FREQUENCY 120000000UL // 120 MHz (RUN) +#define KINETIS_SYSCLK_FREQUENCY KINETIS_PLLCLK_FREQUENCY +#define KINETIS_BUSCLK_FREQUENCY 60000000UL +#define KINETIS_FLASHCLK_FREQUENCY 24000000UL // 24 MHz (RUN) + +#define KINETIS_CLKDIV1_OUTDIV1 1 // -> 0 +#define KINETIS_CLKDIV1_OUTDIV2 2 // -> 1 +#define KINETIS_CLKDIV1_OUTDIV4 5 // -> 4 + +/* + * SERIAL driver system settings. + */ +#define KINETIS_SERIAL_USE_UART0 TRUE + +/* + * USB driver settings + */ +#define KINETIS_USB_USE_USB0 TRUE +#define KINETIS_USB_USB0_IRQ_PRIORITY 5 + +#endif /* _MCUCONF_H_ */ diff --git a/keyboards/handwired/onekey/teensy_35/rules.mk b/keyboards/handwired/onekey/teensy_35/rules.mk new file mode 100644 index 0000000000..8ebefd03f5 --- /dev/null +++ b/keyboards/handwired/onekey/teensy_35/rules.mk @@ -0,0 +1,8 @@ +# MCU name +MCU = MK64FX512 + +# Bootloader selection +BOOTLOADER = halfkay + +# Enter lower-power sleep mode when on the ChibiOS idle thread +OPT_DEFS += -DCORTEX_ENABLE_WFI_IDLE=TRUE diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py index be85a1dbff..95fe9a61d0 100644 --- a/lib/python/qmk/constants.py +++ b/lib/python/qmk/constants.py @@ -14,7 +14,7 @@ QMK_FIRMWARE_UPSTREAM = 'qmk/qmk_firmware' MAX_KEYBOARD_SUBFOLDERS = 5 # Supported processor types -CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK66FX1M0', 'RP2040', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F405', 'STM32F407', 'STM32F411', 'STM32F446', 'STM32G431', 'STM32G474', 'STM32L412', 'STM32L422', 'STM32L432', 'STM32L433', 'STM32L442', 'STM32L443', 'GD32VF103', 'WB32F3G71', 'WB32FQ95' +CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK64FX512', 'MK66FX1M0', 'RP2040', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F405', 'STM32F407', 'STM32F411', 'STM32F446', 'STM32G431', 'STM32G474', 'STM32L412', 'STM32L422', 'STM32L432', 'STM32L433', 'STM32L442', 'STM32L443', 'GD32VF103', 'WB32F3G71', 'WB32FQ95' LUFA_PROCESSORS = 'at90usb162', 'atmega16u2', 'atmega32u2', 'atmega16u4', 'atmega32u4', 'at90usb646', 'at90usb647', 'at90usb1286', 'at90usb1287', None VUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85' diff --git a/platforms/chibios/chibios_config.h b/platforms/chibios/chibios_config.h index 1571bd5cd3..e5aa8e3d05 100644 --- a/platforms/chibios/chibios_config.h +++ b/platforms/chibios/chibios_config.h @@ -95,7 +95,7 @@ #if defined(MCU_KINETIS) # define CPU_CLOCK KINETIS_SYSCLK_FREQUENCY -# if defined(K20x) || defined(KL2x) +# if defined(K20x) || defined(K60x) || defined(KL2x) # define USE_I2CV1 # define USE_I2CV1_CONTRIB // for some reason a bunch of ChibiOS-Contrib boards only have clock_speed # define USE_GPIOV1 -- cgit v1.2.3 From fc7e9efd215fee7ec8acd3e2740a6ab9d4633818 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sat, 13 Aug 2022 14:39:56 +0100 Subject: Improve importer workflow (#17707) --- docs/hand_wire.md | 33 +++++----- lib/python/qmk/constants.py | 5 ++ lib/python/qmk/importers.py | 147 +++++++++++++++++++++++++++++--------------- 3 files changed, 120 insertions(+), 65 deletions(-) (limited to 'lib/python/qmk/constants.py') diff --git a/docs/hand_wire.md b/docs/hand_wire.md index e79a80375a..06809254df 100644 --- a/docs/hand_wire.md +++ b/docs/hand_wire.md @@ -177,20 +177,25 @@ From here, you should have a working keyboard once you program a firmware. Simple firmware can be created easily using the [Keyboard Firmware Builder](https://kbfirmware.com/) website. Recreate your layout using [Keyboard Layout Editor](https://www.keyboard-layout-editor.com), import it and recreate the matrix (if not already done as part of [planning the matrix](#planning-the-matrix). -Go through the rest of the tabs, assigning keys until you get to the last one where you can compile and download your firmware. The .hex file can be flashed straight onto your keyboard, and the .zip of source files can be modified for advanced functionality and compiled locally using the method described in [Building Your First Firmware](newbs_building_firmware?id=build-your-firmware). - -The source given by Keyboard Firmware Builder is QMK, but is based on a version of QMK from early 2017. To compile the code from your .zip file in a modern version of QMK Firmware, you'll need to open the .zip and follow these instructions: - -1. Extract the `kb` folder to `qmk_firmware/keyboards/handwired/`. -2. Open the extracted `kb` folder, then proceed to the `keymaps/default/` folder, and open `keymap.c`. -3. Locate and delete the `action_get_macro` code block: - ``` - const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { - ... - return MACRO_NONE; - } - ``` -4. Save and close `keymap.c`. +Go through the rest of the tabs, assigning keys until you get to the last one where you can compile and download your firmware. The .hex file can be flashed straight onto your keyboard, or for advanced functionality, compiled locally after [Setting up Your Environment](newbs_getting_started.md). + +The source given by Keyboard Firmware Builder is QMK, but is based on a version of QMK from early 2017. To compile the firmware in a modern version of QMK Firmware, you'll need to export via the `Save Configuration` button, then run: + + qmk import-kbfirmware /path/to/export.json + +For example: + +``` +$ qmk import-kbfirmware ~/Downloads/gh62.json +Ψ Importing gh62.json. + +⚠ Support here is basic - Consider using 'qmk new-keyboard' instead +Ψ Imported a new keyboard named gh62. +Ψ To start working on things, `cd` into keyboards/gh62, +Ψ or open the directory in your preferred text editor. +Ψ And build with qmk compile -kb gh62 -km default. +``` + ## Flashing the Firmware diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py index 95fe9a61d0..5fe8326daf 100644 --- a/lib/python/qmk/constants.py +++ b/lib/python/qmk/constants.py @@ -58,6 +58,11 @@ MCU2BOOTLOADER = { "atmega328": "usbasploader", } +# Map of legacy keycodes that can be automatically updated +LEGACY_KEYCODES = { # Comment here is to force multiline formatting + 'RESET': 'QK_BOOT' +} + # Common format strings DATE_FORMAT = '%Y-%m-%d' DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S %Z' diff --git a/lib/python/qmk/importers.py b/lib/python/qmk/importers.py index f9ecac02ae..307c66ee3c 100644 --- a/lib/python/qmk/importers.py +++ b/lib/python/qmk/importers.py @@ -1,10 +1,26 @@ from dotty_dict import dotty +from datetime import date +from pathlib import Path import json +from qmk.git import git_get_username from qmk.json_schema import validate from qmk.path import keyboard, keymap -from qmk.constants import MCU2BOOTLOADER +from qmk.constants import MCU2BOOTLOADER, LEGACY_KEYCODES from qmk.json_encoders import InfoJSONEncoder, KeymapJSONEncoder +from qmk.json_schema import deep_update, json_load + +TEMPLATE = Path('data/templates/keyboard/') + + +def replace_placeholders(src, dest, tokens): + """Replaces the given placeholders in each template file. + """ + content = src.read_text() + for key, value in tokens.items(): + content = content.replace(f'%{key}%', value) + + dest.write_text(content) def _gen_dummy_keymap(name, info_data): @@ -18,7 +34,47 @@ def _gen_dummy_keymap(name, info_data): "layers": [["KC_NO" for _ in range(0, layout_length)]], } - return json.dumps(keymap_data, cls=KeymapJSONEncoder) + return keymap_data + + +def _extract_kbfirmware_layout(kbf_data): + layout = [] + for key in kbf_data['keyboard.keys']: + item = { + 'matrix': [key['row'], key['col']], + 'x': key['state']['x'], + 'y': key['state']['y'], + } + if key['state']['w'] != 1: + item['w'] = key['state']['w'] + if key['state']['h'] != 1: + item['h'] = key['state']['h'] + layout.append(item) + + return layout + + +def _extract_kbfirmware_keymap(kbf_data): + keymap_data = { + 'keyboard': kbf_data['keyboard.settings.name'].lower(), + 'layout': 'LAYOUT', + 'layers': [], + } + + for i in range(15): + layer = [] + for key in kbf_data['keyboard.keys']: + keycode = key['keycodes'][i]['id'] + keycode = LEGACY_KEYCODES.get(keycode, keycode) + if '()' in keycode: + fields = key['keycodes'][i]['fields'] + keycode = f'{keycode.split(")")[0]}{",".join(map(str, fields))})' + layer.append(keycode) + if set(layer) == {'KC_TRNS'}: + break + keymap_data['layers'].append(layer) + + return keymap_data def import_keymap(keymap_data): @@ -40,7 +96,7 @@ def import_keymap(keymap_data): return (kb_name, km_name) -def import_keyboard(info_data): +def import_keyboard(info_data, keymap_data=None): # Validate to ensure we don't have to deal with bad data - handles stdin/file validate(info_data, 'qmk.api.keyboard.v1') @@ -55,17 +111,36 @@ def import_keyboard(info_data): if kb_folder.exists(): raise ValueError(f'Keyboard {{fg_cyan}}{kb_name}{{fg_reset}} already exists! Please choose a different name.') + if not keymap_data: + # TODO: if supports community then grab that instead + keymap_data = _gen_dummy_keymap(kb_name, info_data) + keyboard_info = kb_folder / 'info.json' - keyboard_rules = kb_folder / 'rules.mk' keyboard_keymap = kb_folder / 'keymaps' / 'default' / 'keymap.json' - # This is the deepest folder in the expected tree + # begin with making the deepest folder in the tree keyboard_keymap.parent.mkdir(parents=True, exist_ok=True) + user_name = git_get_username() + if not user_name: + user_name = 'TODO' + + tokens = { # Comment here is to force multiline formatting + 'YEAR': str(date.today().year), + 'KEYBOARD': kb_name, + 'USER_NAME': user_name, + 'REAL_NAME': user_name, + } + # Dump out all those lovely files - keyboard_info.write_text(json.dumps(info_data, cls=InfoJSONEncoder)) - keyboard_rules.write_text("# This file intentionally left blank") - keyboard_keymap.write_text(_gen_dummy_keymap(kb_name, info_data)) + for file in list(TEMPLATE.iterdir()): + replace_placeholders(file, kb_folder / file.name, tokens) + + temp = json_load(keyboard_info) + deep_update(temp, info_data) + + keyboard_info.write_text(json.dumps(temp, cls=InfoJSONEncoder)) + keyboard_keymap.write_text(json.dumps(keymap_data, cls=KeymapJSONEncoder)) return kb_name @@ -77,21 +152,12 @@ def import_kbfirmware(kbfirmware_data): mcu = ["atmega32u2", "atmega32u4", "at90usb1286"][kbf_data['keyboard.controller']] bootloader = MCU2BOOTLOADER.get(mcu, "custom") - layout = [] - for key in kbf_data['keyboard.keys']: - layout.append({ - "matrix": [key["row"], key["col"]], - "x": key["state"]["x"], - "y": key["state"]["y"], - "w": key["state"]["w"], - "h": key["state"]["h"], - }) + layout = _extract_kbfirmware_layout(kbf_data) + keymap_data = _extract_kbfirmware_keymap(kbf_data) # convert to d/d info.json - info_data = { + info_data = dotty({ "keyboard_name": kbf_data['keyboard.settings.name'].lower(), - "manufacturer": "TODO", - "maintainer": "TODO", "processor": mcu, "bootloader": bootloader, "diode_direction": diode_direction, @@ -99,50 +165,29 @@ def import_kbfirmware(kbfirmware_data): "cols": kbf_data['keyboard.pins.col'], "rows": kbf_data['keyboard.pins.row'], }, - "usb": { - "vid": "0xFEED", - "pid": "0x0000", - "device_version": "0.0.1", - }, - "features": { - "bootmagic": True, - "command": False, - "console": False, - "extrakey": True, - "mousekey": True, - "nkro": True, - }, "layouts": { "LAYOUT": { "layout": layout, } } - } + }) if kbf_data['keyboard.pins.num'] or kbf_data['keyboard.pins.caps'] or kbf_data['keyboard.pins.scroll']: - indicators = {} if kbf_data['keyboard.pins.num']: - indicators['num_lock'] = kbf_data['keyboard.pins.num'] + info_data['indicators.num_lock'] = kbf_data['keyboard.pins.num'] if kbf_data['keyboard.pins.caps']: - indicators['caps_lock'] = kbf_data['keyboard.pins.caps'] + info_data['indicators.caps_lock'] = kbf_data['keyboard.pins.caps'] if kbf_data['keyboard.pins.scroll']: - indicators['scroll_lock'] = kbf_data['keyboard.pins.scroll'] - info_data['indicators'] = indicators + info_data['indicators.scroll_lock'] = kbf_data['keyboard.pins.scroll'] if kbf_data['keyboard.pins.rgb']: - info_data['rgblight'] = { - 'animations': { - 'all': True - }, - 'led_count': kbf_data['keyboard.settings.rgbNum'], - 'pin': kbf_data['keyboard.pins.rgb'], - } + info_data['rgblight.animations.all'] = True + info_data['rgblight.led_count'] = kbf_data['keyboard.settings.rgbNum'] + info_data['rgblight.pin'] = kbf_data['keyboard.pins.rgb'] if kbf_data['keyboard.pins.led']: - info_data['backlight'] = { - 'levels': kbf_data['keyboard.settings.backlightLevels'], - 'pin': kbf_data['keyboard.pins.led'], - } + info_data['backlight.levels'] = kbf_data['keyboard.settings.backlightLevels'] + info_data['backlight.pin'] = kbf_data['keyboard.pins.led'] # delegate as if it were a regular keyboard import - return import_keyboard(info_data) + return import_keyboard(info_data.to_dict(), keymap_data) -- cgit v1.2.3 From 9550cc464cd0579071cf490b6f3c9d151d336bd9 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Fri, 19 Aug 2022 01:48:33 +0100 Subject: Fix new-keyboard default for RP2040 bootloader (#18100) --- lib/python/qmk/constants.py | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/python/qmk/constants.py') diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py index 5fe8326daf..7da9df1d8a 100644 --- a/lib/python/qmk/constants.py +++ b/lib/python/qmk/constants.py @@ -20,6 +20,7 @@ VUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85' # Bootloaders of the supported processors MCU2BOOTLOADER = { + "RP2040": "rp2040", "MKL26Z64": "halfkay", "MK20DX128": "halfkay", "MK20DX256": "halfkay", -- cgit v1.2.3 From 5e2ffe7d8f4187109514147469d7db93e075f6f0 Mon Sep 17 00:00:00 2001 From: Erovia Date: Sat, 20 Aug 2022 06:39:19 +0100 Subject: CLI: Teaching the CLI to flash binaries (#16584) Co-authored-by: Ryan Co-authored-by: Sergey Vlasov Co-authored-by: Joel Challis Co-authored-by: Nick Brassel --- docs/cli_commands.md | 17 ++++ lib/python/qmk/cli/__init__.py | 1 + lib/python/qmk/cli/doctor/linux.py | 57 +++-------- lib/python/qmk/cli/flash.py | 120 ++++++++++++---------- lib/python/qmk/constants.py | 48 +++++++++ lib/python/qmk/flashers.py | 203 +++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + util/udev/50-qmk.rules | 9 ++ 8 files changed, 363 insertions(+), 93 deletions(-) create mode 100644 lib/python/qmk/flashers.py (limited to 'lib/python/qmk/constants.py') diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 56767d962b..4608ed85b6 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -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 can also flash binary firmware files (hex or bin) such as the ones produced by [Configurator](https://config.qmk.fm). + **Usage for Configurator Exports**: ``` @@ -102,6 +104,21 @@ qmk flash [-bl ] [-c] [-e =] [-j ] -km [-bl ] [-c] [-e =] [-j ] ``` +**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 ] +``` + **Listing the Bootloaders** ``` diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 8a507677ef..f05b2a746e 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -15,6 +15,7 @@ from milc.questions import yesno import_names = { # A mapping of package name to importable name 'pep8-naming': 'pep8ext_naming', + 'pyserial': 'serial', 'pyusb': 'usb.core', 'qmk-dotty-dict': 'dotty_dict', 'pillow': 'PIL' diff --git a/lib/python/qmk/cli/doctor/linux.py b/lib/python/qmk/cli/doctor/linux.py index 94683d3307..a803305c0d 100644 --- a/lib/python/qmk/cli/doctor/linux.py +++ b/lib/python/qmk/cli/doctor/linux.py @@ -6,7 +6,7 @@ from pathlib import Path from milc import cli -from qmk.constants import QMK_FIRMWARE +from qmk.constants import QMK_FIRMWARE, BOOTLOADER_VIDS_PIDS from .check import CheckStatus @@ -26,6 +26,18 @@ def _udev_rule(vid, pid=None, *args): 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): """ Helper function that return udev rules @@ -47,47 +59,8 @@ def check_udev_rules(): Path("/run/udev/rules.d/"), Path("/etc/udev/rules.d/"), ] - desired_rules = { - 'atmel-dfu': { - _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 - } - } + + desired_rules = _generate_desired_rules(BOOTLOADER_VIDS_PIDS) # These rules are no longer recommended, only use them to check for their presence. deprecated_rules = { diff --git a/lib/python/qmk/cli/flash.py b/lib/python/qmk/cli/flash.py index ebe739c50e..c39f4b36d4 100644 --- a/lib/python/qmk/cli/flash.py +++ b/lib/python/qmk/cli/flash.py @@ -4,6 +4,7 @@ You can compile a keymap already in the repo or using a QMK Configurator export. A bootloader must be specified. """ from subprocess import DEVNULL +import sys from argcomplete.completers import FilesCompleter from milc import cli @@ -12,6 +13,7 @@ import qmk.path from qmk.decorators import automagic_keyboard, automagic_keymap from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json from qmk.keyboard import keyboard_completer, keyboard_folder +from qmk.flashers import flasher def print_bootloader_help(): @@ -38,9 +40,10 @@ def print_bootloader_help(): 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('-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('-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): """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 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 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']) + 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...') + sys.exit(0) 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 + 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']) - 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 + 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 diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py index 7da9df1d8a..10da5e7e8e 100644 --- a/lib/python/qmk/constants.py +++ b/lib/python/qmk/constants.py @@ -64,6 +64,54 @@ LEGACY_KEYCODES = { # Comment here is to force multiline formatting '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 DATE_FORMAT = '%Y-%m-%d' DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S %Z' diff --git a/lib/python/qmk/flashers.py b/lib/python/qmk/flashers.py new file mode 100644 index 0000000000..a9cf726b44 --- /dev/null +++ b/lib/python/qmk/flashers.py @@ -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) diff --git a/requirements.txt b/requirements.txt index e09d58d829..9c192b0f0f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ hjson jsonschema>=4 milc>=1.4.2 pygments +pyserial pyusb qmk-dotty-dict pillow diff --git a/util/udev/50-qmk.rules b/util/udev/50-qmk.rules index 57806f9df0..86f1dc9004 100644 --- a/util/udev/50-qmk.rules +++ b/util/udev/50-qmk.rules @@ -28,6 +28,9 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05df", TAG+="uacc # USBAspLoader 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 # Atmel SAM-BA (Massdrop) 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" ## PJRC's HalfKay 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" -- cgit v1.2.3 From 7ee55b17540f1ec1dfae95e870cf29d74dcea8ad Mon Sep 17 00:00:00 2001 From: Sergey Vlasov Date: Sat, 20 Aug 2022 17:59:17 +0300 Subject: Fix PID value for the Keyboardio Atreus 2 bootloader (#18116) Copy the correct PID from `util/udev/50-qmk.rules`. --- lib/python/qmk/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/python/qmk/constants.py') diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py index 10da5e7e8e..622199e46e 100644 --- a/lib/python/qmk/constants.py +++ b/lib/python/qmk/constants.py @@ -88,7 +88,7 @@ BOOTLOADER_VIDS_PIDS = { 'md-boot': {("03eb", "6124")}, 'caterina': { # pid.codes shared PID - ("1209", "9203"), # Keyboardio Atreus 2 Bootloader + ("1209", "2302"), # Keyboardio Atreus 2 Bootloader # Spark Fun Electronics ("1b4f", "9203"), # Pro Micro 3V3/8MHz ("1b4f", "9205"), # Pro Micro 5V/16MHz -- cgit v1.2.3