aboutsummaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorAkshay <[email protected]>2022-04-10 12:13:40 +0100
committerAkshay <[email protected]>2022-04-10 12:13:40 +0100
commitdc90387ce7d8ba7b607d9c48540bf6d8b560f14d (patch)
tree4ccb8fa5886b66fa9d480edef74236c27f035e16 /util
Diffstat (limited to 'util')
-rwxr-xr-xutil/audio_generate_dac_lut.py67
-rwxr-xr-xutil/chibios_conf_updater.sh105
-rwxr-xr-xutil/docker_build.sh85
-rw-r--r--util/drivers.txt17
-rwxr-xr-xutil/generate_internal_docs.sh31
-rwxr-xr-xutil/install/arch.sh17
-rwxr-xr-xutil/install/debian.sh28
-rwxr-xr-xutil/install/fedora.sh14
-rwxr-xr-xutil/install/freebsd.sh18
-rwxr-xr-xutil/install/gentoo.sh34
-rwxr-xr-xutil/install/linux_shared.sh13
-rwxr-xr-xutil/install/macos.sh26
-rwxr-xr-xutil/install/msys2.sh35
-rwxr-xr-xutil/install/slackware.sh25
-rwxr-xr-xutil/install/solus.sh19
-rwxr-xr-xutil/install/void.sh15
-rwxr-xr-xutil/list_keyboards.sh18
-rwxr-xr-xutil/new_keyboard.sh181
-rwxr-xr-xutil/new_keymap.sh40
-rw-r--r--util/nix/poetry.lock468
-rw-r--r--util/nix/pyproject.toml36
-rw-r--r--util/nix/sources.json38
-rw-r--r--util/nix/sources.nix174
-rwxr-xr-xutil/qmk_install.sh77
-rw-r--r--util/qmk_tab_complete.sh2
-rwxr-xr-xutil/rules_cleaner.sh40
-rwxr-xr-xutil/sample_parser.py39
-rwxr-xr-xutil/size_regression.sh79
-rwxr-xr-xutil/stm32eeprom_parser.py316
-rw-r--r--util/udev/50-qmk.rules74
-rwxr-xr-xutil/uf2conv.py319
-rwxr-xr-xutil/update_chibios_mirror.sh92
-rw-r--r--util/usb_detach/Makefile18
-rw-r--r--util/usb_detach/readme.md14
-rw-r--r--util/usb_detach/usb_detach.c34
-rw-r--r--util/vagrant/Dockerfile33
-rw-r--r--util/vagrant/readme.md12
-rwxr-xr-xutil/wavetable_parser.py40
38 files changed, 2693 insertions, 0 deletions
diff --git a/util/audio_generate_dac_lut.py b/util/audio_generate_dac_lut.py
new file mode 100755
index 000000000..c31ba3d7e
--- /dev/null
+++ b/util/audio_generate_dac_lut.py
@@ -0,0 +1,67 @@
1#!/usr/bin/env python3
2#
3# Copyright 2020 JohSchneider
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18
19AUDIO_DAC_BUFFER_SIZE=256
20AUDIO_DAC_SAMPLE_MAX=4095
21
22def plot(values):
23 for v in values:
24 print('0'* int(v * 80/AUDIO_DAC_SAMPLE_MAX))
25
26def to_lut(values):
27 for v in values:
28 print(hex(int(v)), end=", ")
29
30
31from math import sin, tau, pi
32
33samples=[]
34
35def sampleSine():
36 for s in range(AUDIO_DAC_BUFFER_SIZE):
37 samples.append((sin((s/AUDIO_DAC_BUFFER_SIZE)*tau - pi/2) + 1 )/2* AUDIO_DAC_SAMPLE_MAX)
38
39def sampleTriangle():
40 for s in range(AUDIO_DAC_BUFFER_SIZE):
41 if s < AUDIO_DAC_BUFFER_SIZE/2:
42 samples.append(s/(AUDIO_DAC_BUFFER_SIZE/2) * AUDIO_DAC_SAMPLE_MAX)
43 else:
44 samples.append(AUDIO_DAC_SAMPLE_MAX - (s-AUDIO_DAC_BUFFER_SIZE/2)/(AUDIO_DAC_BUFFER_SIZE/2) * AUDIO_DAC_SAMPLE_MAX)
45
46#compromise between square and triangle wave,
47def sampleTrapezoidal():
48 for i in range(AUDIO_DAC_BUFFER_SIZE):
49 a=3 #slope/inclination
50 if (i < AUDIO_DAC_BUFFER_SIZE/2):
51 s = a * (i * AUDIO_DAC_SAMPLE_MAX/(AUDIO_DAC_BUFFER_SIZE/2)) + (1-a)*AUDIO_DAC_SAMPLE_MAX/2
52 else:
53 i = i - AUDIO_DAC_BUFFER_SIZE/2
54 s = AUDIO_DAC_SAMPLE_MAX - a * (i * AUDIO_DAC_SAMPLE_MAX/(AUDIO_DAC_BUFFER_SIZE/2)) - (1-a)*AUDIO_DAC_SAMPLE_MAX/2
55
56 if s < 0:
57 s=0
58 if s> AUDIO_DAC_SAMPLE_MAX:
59 s=AUDIO_DAC_SAMPLE_MAX
60 samples.append(s)
61
62
63#sampleSine()
64sampleTrapezoidal()
65#print(samples)
66plot(samples)
67to_lut(samples)
diff --git a/util/chibios_conf_updater.sh b/util/chibios_conf_updater.sh
new file mode 100755
index 000000000..a5699ca3c
--- /dev/null
+++ b/util/chibios_conf_updater.sh
@@ -0,0 +1,105 @@
1#!/usr/bin/env bash
2
3set -eEuo pipefail
4umask 022
5
6#####################
7# You will need to get an older JDK -- JDK 8
8#
9# !!!!!!!! DO NOT INSTALL THIS IF YOU HAVE AN EXISTING JDK OR JRE INSTALLED !!!!!!!!
10#
11# For Debian 10-ish distro's:
12# wget -qO - https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public | sudo apt-key add -
13# sudo add-apt-repository --yes https://adoptopenjdk.jfrog.io/adoptopenjdk/deb/
14# sudo apt-get update && sudo apt-get install adoptopenjdk-8-hotspot
15
16
17sinfo() { echo "$@" >&2 ; }
18shead() { sinfo "" ; sinfo "---------------------------------" ; sinfo "-- $@" ; sinfo "---------------------------------" ; }
19havecmd() { command command type "${1}" >/dev/null 2>&1 || return 1 ; }
20
21this_script="$(realpath "${BASH_SOURCE[0]}")"
22script_dir="$(realpath "$(dirname "$this_script")")"
23qmk_firmware_dir="$(realpath "$script_dir/../")"
24
25export PATH="$PATH:$script_dir/fmpp/bin"
26
27build_fmpp() {
28 [ -f "$script_dir/fmpp.tar.gz" ] \
29 || wget -O"$script_dir/fmpp.tar.gz" https://github.com/freemarker/fmpp/archive/v0.9.16.tar.gz
30 [ -d "$script_dir/fmpp" ] \
31 || { mkdir "$script_dir/fmpp" && tar xf "$script_dir/fmpp.tar.gz" -C "$script_dir/fmpp" --strip-components=1 ; }
32 pushd "$script_dir/fmpp" >/dev/null 2>&1
33 sed -e "s#bootclasspath.path=.*#bootclasspath.path=$(find /usr/lib/jvm -name 'rt.jar' | sort | tail -n1)#g" \
34 -e "s#ant.jar.path=.*#ant.jar.path=$(find /usr/share/java -name 'ant-1*.jar' | sort | tail -n1)#g" \
35 build.properties.sample > build.properties
36 sed -e 's#source="1.5"#source="1.8"#g' \
37 -e 's#target="1.5"#target="1.8"#g' \
38 build.xml > build.xml.new
39 mv build.xml.new build.xml
40 ant clean
41 ant
42 chmod +x "$script_dir/fmpp/bin/fmpp"
43 popd >/dev/null 2>&1
44}
45
46find_chibi_files() {
47 local search_path="$1"
48 shift
49 local conditions=( "$@" )
50 for file in $(find -L "$search_path" -not -path '*/lib/chibios*' -and -not -path '*/util/*' -and \( "${conditions[@]}" \) | sort) ; do
51 if [ -z "$(grep 'include_next' "$file")" ] ; then
52 echo $file
53 fi
54 done
55}
56
57upgrade_conf_files_generic() {
58 local search_filename="$1"
59 local update_script="$2"
60 shead "Updating $search_filename files ($update_script)..."
61 pushd "$qmk_firmware_dir/lib/chibios/tools/updater" >/dev/null 2>&1
62 for file in $(find_chibi_files "$qmk_firmware_dir" -name "$search_filename") ; do
63 cp -f "$file" "$file.orig"
64 clang-format --style='{IndentPPDirectives: None}' -i "$file"
65 cp -f "$file" "$file.formatted"
66 bash "$update_script" "$file"
67 if ! diff "$file" "$file.formatted" >/dev/null 2>&1 ; then
68 dos2unix "$file" >/dev/null 2>&1
69 else
70 cp -f "$file.orig" "$file"
71 fi
72 rm -f "$file.orig" "$file.formatted"
73 done
74 popd >/dev/null 2>&1
75}
76
77upgrade_chconf_files() {
78 upgrade_conf_files_generic chconf.h update_chconf_rt.sh
79}
80
81upgrade_halconf_files() {
82 upgrade_conf_files_generic halconf.h update_halconf.sh
83
84 OIFS=$IFS
85 IFS=$'\n'
86 for file in $(find_chibi_files "$qmk_firmware_dir" -name halconf.h) ; do
87 echo $file
88 sed -i 's@#include "mcuconf.h"@#include <mcuconf.h>@g' "$file"
89 done
90 IFS=$OIFS
91}
92
93upgrade_mcuconf_files() {
94 pushd "$qmk_firmware_dir/lib/chibios/tools/updater" >/dev/null 2>&1
95 for f in $(find . -name 'update_mcuconf*') ; do
96 upgrade_conf_files_generic mcuconf.h $f
97 done
98 popd >/dev/null 2>&1
99}
100
101havecmd fmpp || build_fmpp
102
103upgrade_mcuconf_files
104upgrade_chconf_files
105upgrade_halconf_files
diff --git a/util/docker_build.sh b/util/docker_build.sh
new file mode 100755
index 000000000..8a6b2d056
--- /dev/null
+++ b/util/docker_build.sh
@@ -0,0 +1,85 @@
1#!/bin/sh
2# NOTE: This script uses tabs for indentation
3
4errcho() {
5 echo "$@" >&2
6}
7
8USAGE="Usage: $0 [keyboard[:keymap[:target]]]"
9
10# Check preconditions
11for arg; do
12 if [ "$arg" = "--help" ]; then
13 echo "$USAGE"
14 exit 0
15 fi
16done
17if [ $# -gt 1 ]; then
18 errcho "$USAGE"
19 exit 1
20fi
21
22# Allow $RUNTIME to be overriden by the user as an environment variable
23# Else check if either docker or podman exit and set them as runtime
24# if none are found error out
25if [ -z "$RUNTIME" ]; then
26 if command -v docker >/dev/null 2>&1; then
27 RUNTIME="docker"
28 elif command -v podman >/dev/null 2>&1; then
29 RUNTIME="podman"
30 else
31 errcho "Error: no compatible container runtime found."
32 errcho "Either podman or docker are required."
33 errcho "See https://podman.io/getting-started/installation"
34 errcho "or https://docs.docker.com/install/#supported-platforms"
35 errcho "for installation instructions."
36 exit 2
37 fi
38fi
39
40
41# Determine arguments
42if [ $# -eq 0 ]; then
43 printf "keyboard=" && read -r keyboard
44 [ -n "$keyboard" ] && printf "keymap=" && read -r keymap
45 [ -n "$keymap" ] && printf "target=" && read -r target
46else
47 IFS=':' read -r keyboard keymap target x <<-EOF
48 $1
49 EOF
50 if [ -n "$x" ]; then
51 errcho "$USAGE"
52 exit 1
53 fi
54fi
55if [ -z "$keyboard" ]; then
56 keyboard=all
57fi
58if [ -n "$target" ]; then
59 # IF we are using docker on non Linux and docker-machine isn't working print an error
60 # ELSE set usb_args
61 if [ ! "$(uname)" = "Linux" ] && [ "$RUNTIME" = "docker" ] && ! docker-machine active >/dev/null 2>&1; then
62 errcho "Error: target requires docker-machine to work on your platform"
63 errcho "See http://gw.tnode.com/docker/docker-machine-with-usb-support-on-windows-macos"
64 errcho "Consider flashing with QMK Toolbox (https://github.com/qmk/qmk_toolbox) instead"
65 exit 3
66 else
67 usb_args="--privileged -v /dev:/dev"
68 fi
69fi
70dir=$(pwd -W 2>/dev/null) || dir=$PWD # Use Windows path if on Windows
71
72if [ "$RUNTIME" = "docker" ]; then
73 uid_arg="--user $(id -u):$(id -g)"
74fi
75
76# Run container and build firmware
77"$RUNTIME" run --rm -it $usb_args \
78 $uid_arg \
79 -w /qmk_firmware \
80 -v "$dir":/qmk_firmware \
81 -e ALT_GET_KEYBOARDS=true \
82 -e SKIP_GIT="$SKIP_GIT" \
83 -e MAKEFLAGS="$MAKEFLAGS" \
84 qmkfm/qmk_cli \
85 make "$keyboard${keymap:+:$keymap}${target:+:$target}"
diff --git a/util/drivers.txt b/util/drivers.txt
new file mode 100644
index 000000000..1f6c67c4c
--- /dev/null
+++ b/util/drivers.txt
@@ -0,0 +1,17 @@
1# The format is
2# driver,desc,vid,pid,guid
3# Use a comma as a separator without spaces
4# Driver can be one of winusb,libusb,libusbk
5# Use Windows Powershell and type [guid]::NewGuid() to generate guids
6winusb,STM32 Bootloader,0483,DF11,6d98a87f-4ecf-464d-89ed-8c684d857a75
7winusb,APM32 Bootloader,314B,0106,9ff3cc31-6772-4a3f-a492-a80d91f7a853
8winusb,STM32duino Bootloader,1EAF,0003,746915ec-99d8-4a90-a722-3c85ba31e4fe
9libusbk,USBaspLoader,16C0,05DC,e69affdc-0ef0-427c-aefb-4e593c9d2724
10winusb,Kiibohd DFU Bootloader,1C11,B007,aa5a3f86-b81e-4416-89ad-0c1ea1ed63af
11libusb,ATmega16U2,03EB,2FEF,007274da-b75f-492e-a288-8fc0aff8339f
12libusb,ATmega32U2,03EB,2FF0,ddc2c572-cb6e-4f61-a6cc-1a5de941f063
13libusb,ATmega16U4,03EB,2FF3,3180d426-bf93-4578-a693-2efbc337da8e
14libusb,ATmega32U4,03EB,2FF4,5f9726fd-f9de-487a-9fbd-8b3524a7a56a
15libusb,AT90USB64,03EB,2FF9,c6a708ad-e97d-43cd-b04a-3180d737a71b
16libusb,AT90USB162,03EB,2FFA,ef8546f0-ef09-4e7c-8fc2-ffbae1dcd84a
17libusb,AT90USB128,03EB,2FFB,fd217df3-59d0-440a-a8f3-4c0c8c84daa3
diff --git a/util/generate_internal_docs.sh b/util/generate_internal_docs.sh
new file mode 100755
index 000000000..b107a37ad
--- /dev/null
+++ b/util/generate_internal_docs.sh
@@ -0,0 +1,31 @@
1#!/usr/bin/env bash
2
3set -x
4
5if ! doxygen -v 2>&1 > /dev/null; then
6 echo "doxygen not found! Please install it!"
7 exit 1
8elif ! moxygen -V 2>&1 > /dev/null; then
9 echo -n "moxygen not found! Would you like to install it? [y/n] "
10 read ANSWER
11 case $ANSWER in
12 y|Y|yes|YES|Yes)
13 npm install -g moxygen
14 ;;
15 *)
16 exit 1
17 ;;
18 esac
19fi
20
21if [ ! -e Doxyfile ]; then
22 echo "Error: You must run this from the top-level qmk_firmware directory!"
23 exit 1
24fi
25
26# Generate the doxygen XML files
27rm -rf doxygen
28doxygen Doxyfile
29
30# Generate the moxygen Markdown files
31moxygen -a -g -o docs/internals_%s.md doxygen/xml
diff --git a/util/install/arch.sh b/util/install/arch.sh
new file mode 100755
index 000000000..33c39212d
--- /dev/null
+++ b/util/install/arch.sh
@@ -0,0 +1,17 @@
1#!/usr/bin/env bash
2
3_qmk_install() {
4 echo "Installing dependencies"
5
6 sudo pacman --needed --noconfirm -S \
7 base-devel clang diffutils gcc git unzip wget zip python-pip \
8 avr-binutils arm-none-eabi-binutils arm-none-eabi-gcc \
9 arm-none-eabi-newlib avrdude dfu-programmer dfu-util \
10 riscv64-elf-binutils riscv64-elf-gcc riscv64-elf-newlib
11 sudo pacman --needed --noconfirm -U https://archive.archlinux.org/packages/a/avr-gcc/avr-gcc-8.3.0-1-x86_64.pkg.tar.xz
12 sudo pacman --needed --noconfirm -S avr-libc # Must be installed after the above, or it will bring in the latest avr-gcc instead
13
14 sudo pacman --needed --noconfirm -S hidapi # This will fail if the community repo isn't enabled
15
16 python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt
17}
diff --git a/util/install/debian.sh b/util/install/debian.sh
new file mode 100755
index 000000000..3e02919bd
--- /dev/null
+++ b/util/install/debian.sh
@@ -0,0 +1,28 @@
1#!/usr/bin/env bash
2
3DEBIAN_FRONTEND=noninteractive
4DEBCONF_NONINTERACTIVE_SEEN=true
5export DEBIAN_FRONTEND DEBCONF_NONINTERACTIVE_SEEN
6
7_qmk_install_prepare() {
8 sudo apt-get update $SKIP_PROMPT
9}
10
11_qmk_install() {
12 echo "Installing dependencies"
13
14 sudo apt-get --quiet --yes install \
15 build-essential clang-format diffutils gcc git unzip wget zip \
16 python3-pip binutils-avr gcc-avr avr-libc binutils-arm-none-eabi \
17 gcc-arm-none-eabi libnewlib-arm-none-eabi avrdude dfu-programmer \
18 dfu-util teensy-loader-cli libhidapi-hidraw0 libusb-dev
19
20 # RISC-V toolchains with picolibc support are only available for distributions based on Debian 11+.
21 if sudo apt-get install --simulate --quiet --yes picolibc-riscv64-unknown-elf gcc-riscv64-unknown-elf binutils-riscv64-unknown-elf > /dev/null 2>&1; then
22 sudo apt-get --quiet --yes install picolibc-riscv64-unknown-elf \
23 gcc-riscv64-unknown-elf \
24 binutils-riscv64-unknown-elf
25 fi
26
27 python3 -m pip install --user -r "$QMK_FIRMWARE_DIR"/requirements.txt
28}
diff --git a/util/install/fedora.sh b/util/install/fedora.sh
new file mode 100755
index 000000000..e123447d3
--- /dev/null
+++ b/util/install/fedora.sh
@@ -0,0 +1,14 @@
1#!/usr/bin/env bash
2
3_qmk_install() {
4 echo "Installing dependencies"
5
6 # TODO: Check whether devel/headers packages are really needed
7 sudo dnf $SKIP_PROMPT install \
8 clang diffutils git gcc glibc-headers kernel-devel kernel-headers \
9 make unzip wget zip python3 avr-binutils avr-gcc avr-libc \
10 arm-none-eabi-binutils-cs arm-none-eabi-gcc-cs arm-none-eabi-newlib \
11 avrdude dfu-programmer dfu-util hidapi
12
13 python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt
14}
diff --git a/util/install/freebsd.sh b/util/install/freebsd.sh
new file mode 100755
index 000000000..595911969
--- /dev/null
+++ b/util/install/freebsd.sh
@@ -0,0 +1,18 @@
1#!/usr/bin/env bash
2
3_qmk_install_prepare() {
4 sudo pkg update $SKIP_PROMPT
5}
6
7_qmk_install() {
8 echo "Installing dependencies"
9
10 sudo pkg install -y \
11 git wget gmake gcc zip unzip diffutils \
12 python3 \
13 avr-binutils avr-gcc avr-libc \
14 arm-none-eabi-binutils arm-none-eabi-gcc arm-none-eabi-newlib \
15 avrdude dfu-programmer dfu-util
16
17 sudo python3 -m pip install -r $QMK_FIRMWARE_DIR/requirements.txt
18}
diff --git a/util/install/gentoo.sh b/util/install/gentoo.sh
new file mode 100755
index 000000000..b031fc762
--- /dev/null
+++ b/util/install/gentoo.sh
@@ -0,0 +1,34 @@
1#!/usr/bin/env bash
2
3_qmk_install_prepare() {
4 echo "This script will make a USE change in order to ensure that that QMK works on your system."
5 echo "All changes will be sent to the file /etc/portage/package.use/qmkfirmware -- please review it, and read Portage's output carefully before installing any packages on your system."
6 echo "You will also need to ensure that your kernel is compiled with support for the microcontroller that you are using (e.g. enable Arduino for the Pro Micro). Further information can be found on the Gentoo wiki."
7
8 read -p "Proceed? [y/N] " res
9 case $res in
10 [Yy]*)
11 return 0;;
12 *)
13 return 1;;
14 esac
15}
16
17_qmk_install() {
18 echo "Installing dependencies"
19
20 sudo touch /etc/portage/package.use/qmkfirmware
21 # tee is used here since sudo doesn't apply to >>
22 echo "sys-devel/gcc multilib" | sudo tee --append /etc/portage/package.use/qmkfirmware >/dev/null
23 sudo emerge -auN sys-devel/gcc
24 sudo emerge -au --noreplace \
25 app-arch/unzip app-arch/zip net-misc/wget sys-devel/clang \
26 sys-devel/crossdev \>=dev-lang/python-3.7 dev-embedded/avrdude \
27 dev-embedded/dfu-programmer app-mobilephone/dfu-util sys-apps/hwloc \
28 dev-libs/hidapi
29
30 sudo crossdev -s4 --stable --g \<9 --portage --verbose --target avr
31 sudo crossdev -s4 --stable --g \<9 --portage --verbose --target arm-none-eabi
32
33 python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt
34}
diff --git a/util/install/linux_shared.sh b/util/install/linux_shared.sh
new file mode 100755
index 000000000..e060f425f
--- /dev/null
+++ b/util/install/linux_shared.sh
@@ -0,0 +1,13 @@
1#!/usr/bin/env bash
2
3# For those distros that do not package bootloadHID
4_qmk_install_bootloadhid() {
5 if ! command -v bootloadHID > /dev/null; then
6 wget https://www.obdev.at/downloads/vusb/bootloadHID.2012-12-08.tar.gz -O - | tar -xz -C /tmp
7 pushd /tmp/bootloadHID.2012-12-08/commandline/ > /dev/null
8 if make; then
9 sudo cp bootloadHID /usr/local/bin
10 fi
11 popd > /dev/null
12 fi
13}
diff --git a/util/install/macos.sh b/util/install/macos.sh
new file mode 100755
index 000000000..870b4bec9
--- /dev/null
+++ b/util/install/macos.sh
@@ -0,0 +1,26 @@
1#!/usr/bin/env bash
2
3_qmk_install_prepare() {
4 echo "Checking Homebrew installation"
5
6 if ! brew --version >/dev/null 2>&1; then
7 echo "Error! Homebrew is broken or not installed."
8 echo "Please run \`brew doctor\` or follow the installation instructions at https://brew.sh/, then re-run this script."
9 return 1
10 fi
11
12 brew update && brew upgrade --formulae --ignore-pinned
13}
14
15_qmk_install() {
16 echo "Installing dependencies"
17
18 # All macOS dependencies are managed in the Homebrew package:
19 # https://github.com/qmk/homebrew-qmk
20 brew install qmk/qmk/qmk
21
22 brew link --force avr-gcc@8
23 brew link --force arm-gcc-bin@8
24
25 python3 -m pip install -r $QMK_FIRMWARE_DIR/requirements.txt
26}
diff --git a/util/install/msys2.sh b/util/install/msys2.sh
new file mode 100755
index 000000000..203f8eff0
--- /dev/null
+++ b/util/install/msys2.sh
@@ -0,0 +1,35 @@
1#!/usr/bin/env bash
2
3_qmk_install_prepare() {
4 pacman -Syu $MSYS2_CONFIRM
5}
6
7_qmk_install() {
8 echo "Installing dependencies"
9
10 pacman --needed --noconfirm --disable-download-timeout -S pactoys-git
11 pacboy sync --needed --noconfirm --disable-download-timeout \
12 base-devel: toolchain:x clang:x git: unzip: python3-pip:x \
13 avr-binutils:x avr-gcc:x avr-libc:x arm-none-eabi-binutils:x \
14 arm-none-eabi-gcc:x arm-none-eabi-newlib:x avrdude:x bootloadhid:x \
15 dfu-programmer:x dfu-util:x teensy-loader-cli:x hidapi:x
16
17 _qmk_install_drivers
18
19 python3 -m pip install -r "$QMK_FIRMWARE_DIR/requirements.txt"
20}
21
22_qmk_install_drivers() {
23 echo "Installing drivers"
24
25 tmpdir=$(mktemp -d)
26 cp "$QMK_FIRMWARE_UTIL_DIR/drivers.txt" $tmpdir
27 pushd $tmpdir > /dev/null
28
29 wget "https://github.com/qmk/qmk_driver_installer/releases/download/v1.01/qmk_driver_installer.exe"
30
31 cmd.exe //c "qmk_driver_installer.exe --all --force drivers.txt"
32
33 popd > /dev/null
34 rm -r $tmpdir
35}
diff --git a/util/install/slackware.sh b/util/install/slackware.sh
new file mode 100755
index 000000000..df4d073e7
--- /dev/null
+++ b/util/install/slackware.sh
@@ -0,0 +1,25 @@
1#!/usr/bin/env bash
2
3_qmk_install_prepare() {
4 echo "Before you continue, please ensure that your user is added to sudoers and that sboinstall is configured."
5 read -p "Proceed? [y/N] " res
6
7 case $res in
8 [Yy]*)
9 ;;
10 *)
11 return 1;;
12 esac
13}
14
15_qmk_install() {
16 echo "Installing dependencies"
17
18 sudo sboinstall \
19 avr-binutils avr-gcc avr-libc \
20 arm-binutils arm-gcc newlib \
21 python3 \
22 avrdude dfu-programmer dfu-util teensy_loader_cli
23
24 python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt
25}
diff --git a/util/install/solus.sh b/util/install/solus.sh
new file mode 100755
index 000000000..fad0605cf
--- /dev/null
+++ b/util/install/solus.sh
@@ -0,0 +1,19 @@
1#!/usr/bin/env bash
2
3_qmk_install_prepare() {
4 sudo eopkg -y update-repo
5 sudo eopkg -y upgrade
6}
7
8_qmk_install() {
9 echo "Installing dependencies"
10
11 sudo eopkg -y install \
12 -c system.devel git wget zip unzip \
13 python3 \
14 avr-binutils avr-gcc avr-libc \
15 arm-none-eabi-binutils arm-none-eabi-gcc arm-none-eabi-newlib \
16 avrdude dfu-programmer dfu-util
17
18 python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt
19}
diff --git a/util/install/void.sh b/util/install/void.sh
new file mode 100755
index 000000000..6aeb8e00a
--- /dev/null
+++ b/util/install/void.sh
@@ -0,0 +1,15 @@
1#!/usr/bin/env bash
2
3_qmk_install() {
4 echo "Installing dependencies"
5
6 sudo xbps-install $SKIP_PROMPT \
7 gcc git make wget unzip zip \
8 python3-pip \
9 avr-binutils avr-gcc avr-libc \
10 cross-arm-none-eabi-binutils cross-arm-none-eabi-gcc cross-arm-none-eabi-newlib \
11 avrdude dfu-programmer dfu-util teensy_loader_cli \
12 libusb-compat-devel
13
14 python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt
15}
diff --git a/util/list_keyboards.sh b/util/list_keyboards.sh
new file mode 100755
index 000000000..aa6ed1c6a
--- /dev/null
+++ b/util/list_keyboards.sh
@@ -0,0 +1,18 @@
1#!/bin/sh
2# Temporary shell script to find keyboards
3#
4# This allows us to exclude keyboards by including a .noci file.
5
6find -L keyboards -type f -name rules.mk | grep -v keymaps | sed 's!keyboards/\(.*\)/rules.mk!\1!' | while read keyboard; do
7 if [ "$1" = "noci" ]; then
8 case "$keyboard" in
9 handwired/*)
10 ;;
11 *)
12 test -e "keyboards/${keyboard}/.noci" || echo "$keyboard"
13 ;;
14 esac
15 else
16 echo "$keyboard"
17 fi
18done
diff --git a/util/new_keyboard.sh b/util/new_keyboard.sh
new file mode 100755
index 000000000..62e8cb9e1
--- /dev/null
+++ b/util/new_keyboard.sh
@@ -0,0 +1,181 @@
1#!/usr/bin/env bash
2
3# This script generates a new keyboard directory under keyboards/,
4# and copies the template files from data/templates/ into it.
5
6# Print an error message with the word "ERROR" in red.
7echo_error() {
8 echo -e "[\033[0;91mERROR\033[m]: $1"
9}
10
11# Print a message in bold.
12echo_bold() {
13 echo -e "\033[1m$1\033[m"
14}
15
16# Prompt the user for information, showing the default value in brackets.
17prompt() {
18 local message="$1"
19 local default="$2"
20
21 [ -n "$default" ] && message+=" [$default]"
22 message+=": "
23
24 read -rp "$message" prompt_return
25 [ -z "$prompt_return" ] && prompt_return="$default"
26}
27
28# Grab a username from Git config.
29set_git_username() {
30 git_username="$(git config --get user.name)"
31}
32
33# Copy the template files to the new keyboard directory.
34copy_templates() {
35 mkdir -p "$keyboard_dir"
36
37 echo -n "Copying base template files..."
38 cp -r "data/templates/base/." "${keyboard_dir}"
39 echo " done"
40
41 echo -n "Copying $keyboard_type template files..."
42 cp -r "data/templates/${keyboard_type}/." "${keyboard_dir}"
43 echo " done"
44
45 echo -n "Renaming keyboard files..."
46 mv "${keyboard_dir}/keyboard.c" "${keyboard_dir}/${keyboard_base_name}.c"
47 mv "${keyboard_dir}/keyboard.h" "${keyboard_dir}/${keyboard_base_name}.h"
48 echo " done"
49}
50
51# Set the inplace editing parameter for sed.
52# macOS/BSD sed expects a file extension immediately following -i.
53set_sed_i() {
54 sed_i=(-i)
55
56 case $(uname -a) in
57 *Darwin*) sed_i=(-i "")
58 esac
59}
60
61# Replace a token with a value in the given list of files.
62replace_placeholders() {
63 local replace_token="$1"
64 local replace_value="$2"
65 shift 2
66 local replace_filenames=("$@")
67
68 echo -n "Replacing $replace_token with $replace_value..."
69 for replace_filename in "${replace_filenames[@]}"; do
70 sed "${sed_i[@]}" -e "s/${replace_token}/${replace_value}/g" "$replace_filename"
71 done
72 echo " done"
73}
74
75# Replace %YEAR% with the current year.
76replace_year_placeholders() {
77 local replace_year_filenames=(
78 "${keyboard_dir}/config.h"
79 "${keyboard_dir}/${keyboard_base_name}.c"
80 "${keyboard_dir}/${keyboard_base_name}.h"
81 "${keyboard_dir}/keymaps/default/keymap.c"
82 )
83 replace_placeholders "%YEAR%" "$(date +%Y)" "${replace_year_filenames[@]}"
84}
85
86# Replace %KEYBOARD% with the keyboard name.
87replace_keyboard_placeholders() {
88 local replace_keyboard_filenames=(
89 "${keyboard_dir}/config.h"
90 "${keyboard_dir}/info.json"
91 "${keyboard_dir}/readme.md"
92 "${keyboard_dir}/${keyboard_base_name}.c"
93 "${keyboard_dir}/keymaps/default/readme.md"
94 )
95 replace_placeholders "%KEYBOARD%" "$keyboard_base_name" "${replace_keyboard_filenames[@]}"
96}
97
98# Replace %YOUR_NAME% with the username.
99replace_name_placeholders() {
100 local replace_name_filenames=(
101 "${keyboard_dir}/config.h"
102 "${keyboard_dir}/info.json"
103 "${keyboard_dir}/readme.md"
104 "${keyboard_dir}/${keyboard_base_name}.c"
105 "${keyboard_dir}/${keyboard_base_name}.h"
106 "${keyboard_dir}/keymaps/default/keymap.c"
107 )
108 replace_placeholders "%YOUR_NAME%" "$username" "${replace_name_filenames[@]}"
109}
110
111# Check if an array contains an element.
112array_contains() {
113 local e match="$1"
114 shift
115 for e; do
116 [[ "$e" == "$match" ]] && return 0;
117 done
118
119 return 1
120}
121
122# If we've been started from util/, we want to be in qmk_firmware/
123[[ "$PWD" == *util ]] && cd ..
124
125# The root qmk_firmware/ directory should have a subdirectory called quantum/
126if [ ! -d "quantum" ]; then
127 echo_error "Could not detect the QMK firmware directory!"
128 echo_error "Are you sure you're in the right place?"
129 exit 1
130fi
131
132echo_bold "########################################"
133echo_bold "# NOTICE #"
134echo_bold "# This script has been deprecated. #"
135echo_bold "# Please use qmk new-keyboard instead. #"
136echo_bold "########################################"
137echo
138echo_bold "Generating a new QMK keyboard directory"
139echo
140
141# Keyboard name is required, so keep prompting until we get one
142while [ -z "$keyboard_name" ]; do
143 prompt "Keyboard Name" ""
144 keyboard_name=$prompt_return
145 keyboard_base_name=$(basename $keyboard_name)
146done
147
148keyboard_dir="keyboards/$keyboard_name"
149
150if [ -d "$keyboard_dir" ]; then
151 echo_error "Keyboard $keyboard_name already exists!"
152 exit 1
153fi
154
155KEYBOARD_TYPES=("avr" "ps2avrgb")
156
157prompt "Keyboard Type" "avr"
158keyboard_type=$prompt_return
159
160if ! array_contains "$keyboard_type" "${KEYBOARD_TYPES[@]}"; then
161 echo_error "Keyboard type must be one of: ${KEYBOARD_TYPES[*]}"
162 exit 1
163fi
164
165set_git_username
166prompt "Your Name" "$git_username"
167username=$prompt_return
168
169echo
170
171copy_templates
172set_sed_i
173replace_year_placeholders
174replace_keyboard_placeholders
175[ -n "$username" ] && replace_name_placeholders
176
177echo
178echo_bold "Created a new keyboard called $keyboard_name."
179echo
180echo_bold "To start working on things, cd into keyboards/$keyboard_name,"
181echo_bold "or open the directory in your favourite text editor."
diff --git a/util/new_keymap.sh b/util/new_keymap.sh
new file mode 100755
index 000000000..c483314fd
--- /dev/null
+++ b/util/new_keymap.sh
@@ -0,0 +1,40 @@
1#!/bin/sh
2# Script to make a new keymap for a keyboard of your choosing
3# This script automates the copying of the default keymap into
4# your own keymap
5
6KB_PATH=$(echo "$1" | tr 'A-Z' 'a-z')
7USERNAME=$(echo "$2" | tr 'A-Z' 'a-z')
8
9if [ -z "$KB_PATH" ]; then
10 printf "Usage: %s <keyboard_path> <username>\n" "$0"
11 printf "Example: %s 1upkeyboards/1up60hse yourname\n" "$0"
12 exit 1
13fi
14
15if [ -z "$USERNAME" ]; then
16 printf "Usage: %s <keyboard_path> <username>\n" "$0"
17 printf "Example: %s 1upkeyboards/1up60hse yourname\n" "$0"
18 exit 1
19fi
20
21cd "$(dirname "$0")/.."
22
23if [ ! -d "keyboards/$KB_PATH" ]; then
24 printf "Error! keyboards/%s does not exist!\n" "$KB_PATH"
25 exit 1
26fi
27
28if [ -d "keyboards/$KB_PATH/keymaps/$USERNAME" ]; then
29 printf "Error! keyboards/%s/keymaps/%s already exists!\n" "$KB_PATH" "$USERNAME"
30 exit 1
31fi
32
33# Recursively copy the chosen keyboard's default keymap
34cp -r keyboards/"$KB_PATH"/keymaps/default keyboards/"$KB_PATH"/keymaps/"$USERNAME"
35
36printf "%s keymap directory created in: qmk_firmware/keyboards/%s/keymaps/\n\n" "$USERNAME" "$KB_PATH"
37
38printf "Compile a firmware file with your new keymap by typing: \n"
39printf " make %s:%s\n" "$KB_PATH" "$USERNAME"
40printf "from the qmk_firmware directory\n"
diff --git a/util/nix/poetry.lock b/util/nix/poetry.lock
new file mode 100644
index 000000000..9bb0f64de
--- /dev/null
+++ b/util/nix/poetry.lock
@@ -0,0 +1,468 @@
1[[package]]
2name = "appdirs"
3version = "1.4.4"
4description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
5category = "main"
6optional = false
7python-versions = "*"
8
9[[package]]
10name = "argcomplete"
11version = "1.12.3"
12description = "Bash tab completion for argparse"
13category = "main"
14optional = false
15python-versions = "*"
16
17[package.extras]
18test = ["coverage", "flake8", "pexpect", "wheel"]
19
20[[package]]
21name = "attrs"
22version = "21.2.0"
23description = "Classes Without Boilerplate"
24category = "main"
25optional = false
26python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
27
28[package.extras]
29dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
30docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
31tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
32tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
33
34[[package]]
35name = "colorama"
36version = "0.4.4"
37description = "Cross-platform colored terminal text."
38category = "main"
39optional = false
40python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
41
42[[package]]
43name = "coverage"
44version = "5.5"
45description = "Code coverage measurement for Python"
46category = "dev"
47optional = false
48python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
49
50[package.extras]
51toml = ["toml"]
52
53[[package]]
54name = "flake8"
55version = "3.9.2"
56description = "the modular source code checker: pep8 pyflakes and co"
57category = "dev"
58optional = false
59python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
60
61[package.dependencies]
62mccabe = ">=0.6.0,<0.7.0"
63pycodestyle = ">=2.7.0,<2.8.0"
64pyflakes = ">=2.3.0,<2.4.0"
65
66[[package]]
67name = "flake8-polyfill"
68version = "1.0.2"
69description = "Polyfill package for Flake8 plugins"
70category = "dev"
71optional = false
72python-versions = "*"
73
74[package.dependencies]
75flake8 = "*"
76
77[[package]]
78name = "halo"
79version = "0.0.31"
80description = "Beautiful terminal spinners in Python"
81category = "main"
82optional = false
83python-versions = ">=3.4"
84
85[package.dependencies]
86colorama = ">=0.3.9"
87log-symbols = ">=0.0.14"
88six = ">=1.12.0"
89spinners = ">=0.0.24"
90termcolor = ">=1.1.0"
91
92[package.extras]
93ipython = ["IPython (==5.7.0)", "ipywidgets (==7.1.0)"]
94
95[[package]]
96name = "hid"
97version = "1.0.4"
98description = "ctypes bindings for hidapi"
99category = "main"
100optional = false
101python-versions = "*"
102
103[[package]]
104name = "hjson"
105version = "3.0.2"
106description = "Hjson, a user interface for JSON."
107category = "main"
108optional = false
109python-versions = "*"
110
111[[package]]
112name = "jsonschema"
113version = "3.2.0"
114description = "An implementation of JSON Schema validation for Python"
115category = "main"
116optional = false
117python-versions = "*"
118
119[package.dependencies]
120attrs = ">=17.4.0"
121pyrsistent = ">=0.14.0"
122six = ">=1.11.0"
123
124[package.extras]
125format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"]
126format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"]
127
128[[package]]
129name = "log-symbols"
130version = "0.0.14"
131description = "Colored symbols for various log levels for Python"
132category = "main"
133optional = false
134python-versions = "*"
135
136[package.dependencies]
137colorama = ">=0.3.9"
138
139[[package]]
140name = "mccabe"
141version = "0.6.1"
142description = "McCabe checker, plugin for flake8"
143category = "dev"
144optional = false
145python-versions = "*"
146
147[[package]]
148name = "milc"
149version = "1.6.2"
150description = "Opinionated Batteries-Included Python 3 CLI Framework."
151category = "main"
152optional = false
153python-versions = "*"
154
155[package.dependencies]
156appdirs = "*"
157argcomplete = "*"
158colorama = "*"
159halo = "*"
160spinners = "*"
161
162[[package]]
163name = "nose2"
164version = "0.10.0"
165description = "unittest2 with plugins, the succesor to nose"
166category = "dev"
167optional = false
168python-versions = "*"
169
170[package.dependencies]
171coverage = ">=4.4.1"
172six = ">=1.7"
173
174[package.extras]
175coverage_plugin = ["coverage (>=4.4.1)"]
176dev = ["Sphinx (>=1.6.5)", "sphinx-rtd-theme", "mock", "coverage"]
177
178[[package]]
179name = "pep8-naming"
180version = "0.12.1"
181description = "Check PEP-8 naming conventions, plugin for flake8"
182category = "dev"
183optional = false
184python-versions = "*"
185
186[package.dependencies]
187flake8 = ">=3.9.1"
188flake8-polyfill = ">=1.0.2,<2"
189
190[[package]]
191name = "pycodestyle"
192version = "2.7.0"
193description = "Python style guide checker"
194category = "dev"
195optional = false
196python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
197
198[[package]]
199name = "pyflakes"
200version = "2.3.1"
201description = "passive checker of Python programs"
202category = "dev"
203optional = false
204python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
205
206[[package]]
207name = "pygments"
208version = "2.10.0"
209description = "Pygments is a syntax highlighting package written in Python."
210category = "main"
211optional = false
212python-versions = ">=3.5"
213
214[[package]]
215name = "pyrsistent"
216version = "0.18.0"
217description = "Persistent/Functional/Immutable data structures"
218category = "main"
219optional = false
220python-versions = ">=3.6"
221
222[[package]]
223name = "pyusb"
224version = "1.2.1"
225description = "Python USB access module"
226category = "main"
227optional = false
228python-versions = ">=3.6.0"
229
230[[package]]
231name = "qmk"
232version = "1.0.0"
233description = "A program to help users work with QMK Firmware."
234category = "main"
235optional = false
236python-versions = "*"
237
238[package.dependencies]
239hid = "*"
240hjson = "*"
241jsonschema = ">=3"
242milc = ">=1.4.2"
243pygments = "*"
244pyusb = "*"
245qmk-dotty-dict = "*"
246
247[[package]]
248name = "qmk-dotty-dict"
249version = "1.3.0.post1"
250description = "Dictionary wrapper for quick access to deeply nested keys."
251category = "main"
252optional = false
253python-versions = "*"
254
255[[package]]
256name = "six"
257version = "1.16.0"
258description = "Python 2 and 3 compatibility utilities"
259category = "main"
260optional = false
261python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
262
263[[package]]
264name = "spinners"
265version = "0.0.24"
266description = "Spinners for terminals"
267category = "main"
268optional = false
269python-versions = "*"
270
271[[package]]
272name = "termcolor"
273version = "1.1.0"
274description = "ANSII Color formatting for output in terminal."
275category = "main"
276optional = false
277python-versions = "*"
278
279[[package]]
280name = "yapf"
281version = "0.31.0"
282description = "A formatter for Python code."
283category = "dev"
284optional = false
285python-versions = "*"
286
287[metadata]
288lock-version = "1.1"
289python-versions = "^3.8"
290content-hash = "468ae51aaddfe2ce62938f131c688bbc447e41608d4dd7d30db36391c38bda20"
291
292[metadata.files]
293appdirs = [
294 {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
295 {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
296]
297argcomplete = [
298 {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"},
299 {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"},
300]
301attrs = [
302 {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
303 {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
304]
305colorama = [
306 {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
307 {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
308]
309coverage = [
310 {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"},
311 {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"},
312 {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"},
313 {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"},
314 {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"},
315 {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"},
316 {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"},
317 {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"},
318 {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"},
319 {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"},
320 {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"},
321 {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"},
322 {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"},
323 {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"},
324 {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"},
325 {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"},
326 {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"},
327 {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"},
328 {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"},
329 {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"},
330 {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"},
331 {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"},
332 {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"},
333 {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"},
334 {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"},
335 {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"},
336 {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"},
337 {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"},
338 {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"},
339 {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"},
340 {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"},
341 {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"},
342 {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"},
343 {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"},
344 {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"},
345 {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"},
346 {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"},
347 {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"},
348 {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"},
349 {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"},
350 {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"},
351 {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"},
352 {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"},
353 {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"},
354 {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"},
355 {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"},
356 {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"},
357 {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"},
358 {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"},
359 {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"},
360 {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"},
361 {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"},
362]
363flake8 = [
364 {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
365 {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
366]
367flake8-polyfill = [
368 {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"},
369 {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"},
370]
371halo = [
372 {file = "halo-0.0.31-py2-none-any.whl", hash = "sha256:5350488fb7d2aa7c31a1344120cee67a872901ce8858f60da7946cef96c208ab"},
373 {file = "halo-0.0.31.tar.gz", hash = "sha256:7b67a3521ee91d53b7152d4ee3452811e1d2a6321975137762eb3d70063cc9d6"},
374]
375hid = [
376 {file = "hid-1.0.4-py2-none-any.whl", hash = "sha256:fba9913f07030b01059b822b24c83b370ca3f444e9e6443bd662f9f1aa3f0780"},
377 {file = "hid-1.0.4.tar.gz", hash = "sha256:f61b0382f37a334bc8ba8604bc84b94875ee4f594fbbaf82b2c3b3e827883fc1"},
378]
379hjson = [
380 {file = "hjson-3.0.2-py3-none-any.whl", hash = "sha256:5546438bf4e1b52bc964c6a47c4ed10fa5fba8a1b264e22efa893e333baad2db"},
381 {file = "hjson-3.0.2.tar.gz", hash = "sha256:2838fd7200e5839ea4516ece953f3a19892c41089f0d933ba3f68e596aacfcd5"},
382]
383jsonschema = [
384 {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"},
385 {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"},
386]
387log-symbols = [
388 {file = "log_symbols-0.0.14-py3-none-any.whl", hash = "sha256:4952106ff8b605ab7d5081dd2c7e6ca7374584eff7086f499c06edd1ce56dcca"},
389 {file = "log_symbols-0.0.14.tar.gz", hash = "sha256:cf0bbc6fe1a8e53f0d174a716bc625c4f87043cc21eb55dd8a740cfe22680556"},
390]
391mccabe = [
392 {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
393 {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
394]
395milc = [
396 {file = "milc-1.6.2-py2.py3-none-any.whl", hash = "sha256:cb26404c7f3d6797c9c42005de732161e45e21294cde85845e914b279321bdad"},
397 {file = "milc-1.6.2.tar.gz", hash = "sha256:779710a0b9300bef3c5748158887e6c734659e147d55548d9e4701d7a7d5dddf"},
398]
399nose2 = [
400 {file = "nose2-0.10.0-py2.py3-none-any.whl", hash = "sha256:aa620e759f2c5018d9ba041340391913e282ecebd3c392027f1575847b093ec6"},
401 {file = "nose2-0.10.0.tar.gz", hash = "sha256:886ba617a96de0130c54b24479bd5c2d74d5c940d40f3809c3a275511a0c4a60"},
402]
403pep8-naming = [
404 {file = "pep8-naming-0.12.1.tar.gz", hash = "sha256:bb2455947757d162aa4cad55dba4ce029005cd1692f2899a21d51d8630ca7841"},
405 {file = "pep8_naming-0.12.1-py2.py3-none-any.whl", hash = "sha256:4a8daeaeb33cfcde779309fc0c9c0a68a3bbe2ad8a8308b763c5068f86eb9f37"},
406]
407pycodestyle = [
408 {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
409 {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
410]
411pyflakes = [
412 {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
413 {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
414]
415pygments = [
416 {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"},
417 {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"},
418]
419pyrsistent = [
420 {file = "pyrsistent-0.18.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f4c8cabb46ff8e5d61f56a037974228e978f26bfefce4f61a4b1ac0ba7a2ab72"},
421 {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:da6e5e818d18459fa46fac0a4a4e543507fe1110e808101277c5a2b5bab0cd2d"},
422 {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5e4395bbf841693eaebaa5bb5c8f5cdbb1d139e07c975c682ec4e4f8126e03d2"},
423 {file = "pyrsistent-0.18.0-cp36-cp36m-win32.whl", hash = "sha256:527be2bfa8dc80f6f8ddd65242ba476a6c4fb4e3aedbf281dfbac1b1ed4165b1"},
424 {file = "pyrsistent-0.18.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2aaf19dc8ce517a8653746d98e962ef480ff34b6bc563fc067be6401ffb457c7"},
425 {file = "pyrsistent-0.18.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58a70d93fb79dc585b21f9d72487b929a6fe58da0754fa4cb9f279bb92369396"},
426 {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4916c10896721e472ee12c95cdc2891ce5890898d2f9907b1b4ae0f53588b710"},
427 {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:73ff61b1411e3fb0ba144b8f08d6749749775fe89688093e1efef9839d2dcc35"},
428 {file = "pyrsistent-0.18.0-cp37-cp37m-win32.whl", hash = "sha256:b29b869cf58412ca5738d23691e96d8aff535e17390128a1a52717c9a109da4f"},
429 {file = "pyrsistent-0.18.0-cp37-cp37m-win_amd64.whl", hash = "sha256:097b96f129dd36a8c9e33594e7ebb151b1515eb52cceb08474c10a5479e799f2"},
430 {file = "pyrsistent-0.18.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:772e94c2c6864f2cd2ffbe58bb3bdefbe2a32afa0acb1a77e472aac831f83427"},
431 {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c1a9ff320fa699337e05edcaae79ef8c2880b52720bc031b219e5b5008ebbdef"},
432 {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cd3caef37a415fd0dae6148a1b6957a8c5f275a62cca02e18474608cb263640c"},
433 {file = "pyrsistent-0.18.0-cp38-cp38-win32.whl", hash = "sha256:e79d94ca58fcafef6395f6352383fa1a76922268fa02caa2272fff501c2fdc78"},
434 {file = "pyrsistent-0.18.0-cp38-cp38-win_amd64.whl", hash = "sha256:a0c772d791c38bbc77be659af29bb14c38ced151433592e326361610250c605b"},
435 {file = "pyrsistent-0.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d5ec194c9c573aafaceebf05fc400656722793dac57f254cd4741f3c27ae57b4"},
436 {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6b5eed00e597b5b5773b4ca30bd48a5774ef1e96f2a45d105db5b4ebb4bca680"},
437 {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:48578680353f41dca1ca3dc48629fb77dfc745128b56fc01096b2530c13fd426"},
438 {file = "pyrsistent-0.18.0-cp39-cp39-win32.whl", hash = "sha256:f3ef98d7b76da5eb19c37fda834d50262ff9167c65658d1d8f974d2e4d90676b"},
439 {file = "pyrsistent-0.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:404e1f1d254d314d55adb8d87f4f465c8693d6f902f67eb6ef5b4526dc58e6ea"},
440 {file = "pyrsistent-0.18.0.tar.gz", hash = "sha256:773c781216f8c2900b42a7b638d5b517bb134ae1acbebe4d1e8f1f41ea60eb4b"},
441]
442pyusb = [
443 {file = "pyusb-1.2.1-py3-none-any.whl", hash = "sha256:2b4c7cb86dbadf044dfb9d3a4ff69fd217013dbe78a792177a3feb172449ea36"},
444 {file = "pyusb-1.2.1.tar.gz", hash = "sha256:a4cc7404a203144754164b8b40994e2849fde1cfff06b08492f12fff9d9de7b9"},
445]
446qmk = [
447 {file = "qmk-1.0.0-py2.py3-none-any.whl", hash = "sha256:63d69b97a533d91b0cfa7887e68cac7df52c6f7bddf4bf44d17ef1241d85ffff"},
448 {file = "qmk-1.0.0.tar.gz", hash = "sha256:da62eec73c4548cc37b0b9be3937202dc3a301dc2f2663610ecca751a610f9ca"},
449]
450qmk-dotty-dict = [
451 {file = "qmk_dotty_dict-1.3.0.post1-py3-none-any.whl", hash = "sha256:a9cb7fc3ff9631190fee0ecac14986a0ac7b4b6892347dc9d7486a4c4ea24492"},
452 {file = "qmk_dotty_dict-1.3.0.post1.tar.gz", hash = "sha256:3b611e393660bfaa6835c68e94784bae80fe07b8490978b5ecab03a0d2fc7ea2"},
453]
454six = [
455 {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
456 {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
457]
458spinners = [
459 {file = "spinners-0.0.24-py3-none-any.whl", hash = "sha256:2fa30d0b72c9650ad12bbe031c9943b8d441e41b4f5602b0ec977a19f3290e98"},
460 {file = "spinners-0.0.24.tar.gz", hash = "sha256:1eb6aeb4781d72ab42ed8a01dcf20f3002bf50740d7154d12fb8c9769bf9e27f"},
461]
462termcolor = [
463 {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"},
464]
465yapf = [
466 {file = "yapf-0.31.0-py2.py3-none-any.whl", hash = "sha256:e3a234ba8455fe201eaa649cdac872d590089a18b661e39bbac7020978dd9c2e"},
467 {file = "yapf-0.31.0.tar.gz", hash = "sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d"},
468]
diff --git a/util/nix/pyproject.toml b/util/nix/pyproject.toml
new file mode 100644
index 000000000..1ec8aacd4
--- /dev/null
+++ b/util/nix/pyproject.toml
@@ -0,0 +1,36 @@
1# This file should be kept in sync with requirements.txt and requirements-dev.txt
2# It is particularly required by the Nix environment (see shell.nix). To update versions,
3# normally one would run "poetry update --lock"
4[tool.poetry]
5name = "qmk_firmware"
6version = "0.1.0"
7description = ""
8authors = []
9
10[tool.poetry.dependencies]
11python = "^3.8"
12appdirs = "*"
13argcomplete = "*"
14colorama = "*"
15hid = "*"
16hjson = "*"
17jsonschema = ">=3"
18milc = ">=1.4.2"
19Pygments = "*"
20pyusb = "*"
21qmk-dotty-dict = "*"
22
23# This dependency is not mentioned in requirements.txt (QMK CLI is not a
24# library package that is required by the Python code in qmk_firmware), but is
25# required to build a proper nix-shell environment.
26qmk = "*"
27
28[tool.poetry.dev-dependencies]
29nose2 = "*"
30flake8 = "*"
31pep8-naming = "*"
32yapf = "*"
33
34[build-system]
35requires = ["poetry-core>=1.0.0"]
36build-backend = "poetry.core.masonry.api"
diff --git a/util/nix/sources.json b/util/nix/sources.json
new file mode 100644
index 000000000..caf5cb7d2
--- /dev/null
+++ b/util/nix/sources.json
@@ -0,0 +1,38 @@
1{
2 "niv": {
3 "branch": "master",
4 "description": "Easy dependency management for Nix projects",
5 "homepage": "https://github.com/nmattia/niv",
6 "owner": "nmattia",
7 "repo": "niv",
8 "rev": "af958e8057f345ee1aca714c1247ef3ba1c15f5e",
9 "sha256": "1qjavxabbrsh73yck5dcq8jggvh3r2jkbr6b5nlz5d9yrqm9255n",
10 "type": "tarball",
11 "url": "https://github.com/nmattia/niv/archive/af958e8057f345ee1aca714c1247ef3ba1c15f5e.tar.gz",
12 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
13 },
14 "nixpkgs": {
15 "branch": "nixpkgs-unstable",
16 "description": "Nix Packages collection",
17 "homepage": "",
18 "owner": "NixOS",
19 "repo": "nixpkgs",
20 "rev": "c0e881852006b132236cbf0301bd1939bb50867e",
21 "sha256": "0fy7z7yxk5n7yslsvx5cyc6h21qwi4bhxf3awhirniszlbvaazy2",
22 "type": "tarball",
23 "url": "https://github.com/NixOS/nixpkgs/archive/c0e881852006b132236cbf0301bd1939bb50867e.tar.gz",
24 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
25 },
26 "poetry2nix": {
27 "branch": "master",
28 "description": "Convert poetry projects to nix automagically [maintainer=@adisbladis] ",
29 "homepage": "",
30 "owner": "nix-community",
31 "repo": "poetry2nix",
32 "rev": "2d27d44397242b28c3f0081e0432e4f6c951f3a1",
33 "sha256": "06syfg150r59m4kksj5547b5kwxjxjaif5hiljcq966kb9hxsvmv",
34 "type": "tarball",
35 "url": "https://github.com/nix-community/poetry2nix/archive/2d27d44397242b28c3f0081e0432e4f6c951f3a1.tar.gz",
36 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
37 }
38}
diff --git a/util/nix/sources.nix b/util/nix/sources.nix
new file mode 100644
index 000000000..1938409dd
--- /dev/null
+++ b/util/nix/sources.nix
@@ -0,0 +1,174 @@
1# This file has been generated by Niv.
2
3let
4
5 #
6 # The fetchers. fetch_<type> fetches specs of type <type>.
7 #
8
9 fetch_file = pkgs: name: spec:
10 let
11 name' = sanitizeName name + "-src";
12 in
13 if spec.builtin or true then
14 builtins_fetchurl { inherit (spec) url sha256; name = name'; }
15 else
16 pkgs.fetchurl { inherit (spec) url sha256; name = name'; };
17
18 fetch_tarball = pkgs: name: spec:
19 let
20 name' = sanitizeName name + "-src";
21 in
22 if spec.builtin or true then
23 builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
24 else
25 pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
26
27 fetch_git = name: spec:
28 let
29 ref =
30 if spec ? ref then spec.ref else
31 if spec ? branch then "refs/heads/${spec.branch}" else
32 if spec ? tag then "refs/tags/${spec.tag}" else
33 abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!";
34 in
35 builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; };
36
37 fetch_local = spec: spec.path;
38
39 fetch_builtin-tarball = name: throw
40 ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
41 $ niv modify ${name} -a type=tarball -a builtin=true'';
42
43 fetch_builtin-url = name: throw
44 ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
45 $ niv modify ${name} -a type=file -a builtin=true'';
46
47 #
48 # Various helpers
49 #
50
51 # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695
52 sanitizeName = name:
53 (
54 concatMapStrings (s: if builtins.isList s then "-" else s)
55 (
56 builtins.split "[^[:alnum:]+._?=-]+"
57 ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name)
58 )
59 );
60
61 # The set of packages used when specs are fetched using non-builtins.
62 mkPkgs = sources: system:
63 let
64 sourcesNixpkgs =
65 import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
66 hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
67 hasThisAsNixpkgsPath = <nixpkgs> == ./.;
68 in
69 if builtins.hasAttr "nixpkgs" sources
70 then sourcesNixpkgs
71 else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
72 import <nixpkgs> {}
73 else
74 abort
75 ''
76 Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
77 add a package called "nixpkgs" to your sources.json.
78 '';
79
80 # The actual fetching function.
81 fetch = pkgs: name: spec:
82
83 if ! builtins.hasAttr "type" spec then
84 abort "ERROR: niv spec ${name} does not have a 'type' attribute"
85 else if spec.type == "file" then fetch_file pkgs name spec
86 else if spec.type == "tarball" then fetch_tarball pkgs name spec
87 else if spec.type == "git" then fetch_git name spec
88 else if spec.type == "local" then fetch_local spec
89 else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
90 else if spec.type == "builtin-url" then fetch_builtin-url name
91 else
92 abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
93
94 # If the environment variable NIV_OVERRIDE_${name} is set, then use
95 # the path directly as opposed to the fetched source.
96 replace = name: drv:
97 let
98 saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name;
99 ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
100 in
101 if ersatz == "" then drv else
102 # this turns the string into an actual Nix path (for both absolute and
103 # relative paths)
104 if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}";
105
106 # Ports of functions for older nix versions
107
108 # a Nix version of mapAttrs if the built-in doesn't exist
109 mapAttrs = builtins.mapAttrs or (
110 f: set: with builtins;
111 listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
112 );
113
114 # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
115 range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1);
116
117 # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
118 stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
119
120 # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
121 stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
122 concatMapStrings = f: list: concatStrings (map f list);
123 concatStrings = builtins.concatStringsSep "";
124
125 # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
126 optionalAttrs = cond: as: if cond then as else {};
127
128 # fetchTarball version that is compatible between all the versions of Nix
129 builtins_fetchTarball = { url, name ? null, sha256 }@attrs:
130 let
131 inherit (builtins) lessThan nixVersion fetchTarball;
132 in
133 if lessThan nixVersion "1.12" then
134 fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
135 else
136 fetchTarball attrs;
137
138 # fetchurl version that is compatible between all the versions of Nix
139 builtins_fetchurl = { url, name ? null, sha256 }@attrs:
140 let
141 inherit (builtins) lessThan nixVersion fetchurl;
142 in
143 if lessThan nixVersion "1.12" then
144 fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
145 else
146 fetchurl attrs;
147
148 # Create the final "sources" from the config
149 mkSources = config:
150 mapAttrs (
151 name: spec:
152 if builtins.hasAttr "outPath" spec
153 then abort
154 "The values in sources.json should not have an 'outPath' attribute"
155 else
156 spec // { outPath = replace name (fetch config.pkgs name spec); }
157 ) config.sources;
158
159 # The "config" used by the fetchers
160 mkConfig =
161 { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
162 , sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile)
163 , system ? builtins.currentSystem
164 , pkgs ? mkPkgs sources system
165 }: rec {
166 # The sources, i.e. the attribute set of spec name to spec
167 inherit sources;
168
169 # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
170 inherit pkgs;
171 };
172
173in
174mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); }
diff --git a/util/qmk_install.sh b/util/qmk_install.sh
new file mode 100755
index 000000000..5f22ba0ad
--- /dev/null
+++ b/util/qmk_install.sh
@@ -0,0 +1,77 @@
1#!/usr/bin/env bash
2
3QMK_FIRMWARE_DIR=$(cd -P -- "$(dirname -- "$0")/.." >/dev/null && pwd -P)
4QMK_FIRMWARE_UTIL_DIR=$QMK_FIRMWARE_DIR/util
5if [ "$1" = "-y" ]; then
6 SKIP_PROMPT='-y'
7 MSYS2_CONFIRM='--noconfirm'
8else
9 SKIP_PROMPT=''
10 MSYS2_CONFIRM=''
11fi
12
13case $(uname -a) in
14 *Darwin*)
15 . "$QMK_FIRMWARE_UTIL_DIR/install/macos.sh";;
16 *FreeBSD*)
17 . "$QMK_FIRMWARE_UTIL_DIR/install/freebsd.sh";;
18 *MINGW64_NT*)
19 . "$QMK_FIRMWARE_UTIL_DIR/install/msys2.sh";;
20 *MSYS_NT*|*MINGW32_NT*)
21 echo "Please open a MinGW64 terminal window and re-run this script."
22 exit 1;;
23 *Linux*)
24 . "$QMK_FIRMWARE_UTIL_DIR/install/linux_shared.sh"
25
26 case $(grep ID /etc/os-release) in
27 *arch*|*manjaro*)
28 . "$QMK_FIRMWARE_UTIL_DIR/install/arch.sh";;
29 *debian*|*ubuntu*)
30 . "$QMK_FIRMWARE_UTIL_DIR/install/debian.sh";;
31 *fedora*)
32 . "$QMK_FIRMWARE_UTIL_DIR/install/fedora.sh";;
33 *gentoo*)
34 . "$QMK_FIRMWARE_UTIL_DIR/install/gentoo.sh";;
35 *slackware*)
36 . "$QMK_FIRMWARE_UTIL_DIR/install/slackware.sh";;
37 *solus*)
38 . "$QMK_FIRMWARE_UTIL_DIR/install/solus.sh";;
39 *void*)
40 . "$QMK_FIRMWARE_UTIL_DIR/install/void.sh";;
41 *)
42 echo "Sorry, we don't recognize your distribution. Try using the docker image instead:"
43 echo
44 echo "https://docs.qmk.fm/#/getting_started_docker"
45 exit 1;;
46 esac
47
48 if uname -a | grep -qi microsoft; then
49 echo "********************************************************************************"
50 echo "* Detected Windows Subsystem for Linux. *"
51 echo "* Currently, WSL has no access to USB devices and so flashing from within the *"
52 echo "* WSL terminal will not work. *"
53 echo "* *"
54 echo "* Please install the QMK Toolbox instead: *"
55 echo "* https://github.com/qmk/qmk_toolbox/releases *"
56 echo "* Then, map your WSL filesystem as a network drive: *"
57 echo "* \\\\\\\\wsl$\\<distro> *"
58 echo "********************************************************************************"
59 echo
60 fi
61 ;;
62 *)
63 echo "Sorry, we don't recognize your environment. Help us by contributing support!"
64 echo
65 echo "https://docs.qmk.fm/#/contributing"
66 exit 1;;
67esac
68
69if type _qmk_install_prepare &>/dev/null; then
70 _qmk_install_prepare || exit 1
71fi
72
73_qmk_install
74
75if type _qmk_install_bootloadhid &>/dev/null; then
76 _qmk_install_bootloadhid
77fi
diff --git a/util/qmk_tab_complete.sh b/util/qmk_tab_complete.sh
new file mode 100644
index 000000000..ebcb5536a
--- /dev/null
+++ b/util/qmk_tab_complete.sh
@@ -0,0 +1,2 @@
1# Register qmk with tab completion
2eval "$(register-python-argcomplete --no-defaults qmk)"
diff --git a/util/rules_cleaner.sh b/util/rules_cleaner.sh
new file mode 100755
index 000000000..0367ae849
--- /dev/null
+++ b/util/rules_cleaner.sh
@@ -0,0 +1,40 @@
1#!/usr/bin/env bash
2
3# This script finds all rules.mk files in keyboards/ subdirectories,
4# and deletes the build option filesize impacts from them.
5
6# Print an error message with the word "ERROR" in red.
7echo_error() {
8 echo -e "[\033[0;91mERROR\033[m]: $1"
9}
10
11# If we've been started from util/, we want to be in qmk_firmware/
12[[ "$PWD" == *util ]] && cd ..
13
14# The root qmk_firmware/ directory should have a subdirectory called quantum/
15if [ ! -d "quantum" ]; then
16 echo_error "Could not detect the QMK firmware directory!"
17 echo_error "Are you sure you're in the right place?"
18 exit 1
19fi
20
21# Set the inplace editing parameter for sed.
22# macOS/BSD sed expects a file extension immediately following -i.
23set_sed_i() {
24 sed_i=(-i)
25
26 case $(uname -a) in
27 *Darwin*) sed_i=(-i "")
28 esac
29}
30set_sed_i
31
32# Exclude keyamps/ directories
33files=$(find keyboards -type f -name 'rules.mk' -not \( -path '*/keymaps*' -prune \))
34
35# Edit rules.mk files
36for file in $files; do
37 sed "${sed_i[@]}" -e "s/(+[0-9].*)$//g" "$file"
38done
39
40echo "Cleaned up rules.mk files."
diff --git a/util/sample_parser.py b/util/sample_parser.py
new file mode 100755
index 000000000..70e193aee
--- /dev/null
+++ b/util/sample_parser.py
@@ -0,0 +1,39 @@
1#!/usr/bin/env python3
2#
3# Copyright 2019 Jack Humbert
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18
19import wave, struct, sys
20
21waveFile = wave.open(sys.argv[1], 'r')
22# print(str(waveFile.getparams()))
23# sys.exit()
24
25if (waveFile.getsampwidth() != 2):
26 raise(Exception("This script currently only works with 16bit audio files"))
27
28length = waveFile.getnframes()
29out = "#define DAC_SAMPLE_CUSTOM_LENGTH " + str(length) + "\n\n"
30out += "static const dacsample_t dac_sample_custom[" + str(length) + "] = {"
31for i in range(0,length):
32 if (i % 8 == 0):
33 out += "\n "
34 waveData = waveFile.readframes(1)
35 data = struct.unpack("<h", waveData)
36 out += str(int((int(data[0]) + 0x8000) / 16)) + ", "
37out = out[:-2]
38out += "\n};"
39print(out)
diff --git a/util/size_regression.sh b/util/size_regression.sh
new file mode 100755
index 000000000..988d1d9b5
--- /dev/null
+++ b/util/size_regression.sh
@@ -0,0 +1,79 @@
1#!/bin/bash
2
3# Copyright 2021 Nick Brassel (@tzarc)
4# SPDX-License-Identifier: GPL-2.0-or-later
5
6set -eEuo pipefail
7
8job_count=$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 2)
9source_ref="0.11.0"
10dest_ref="develop"
11ignore_ref="master"
12unset skip_zero
13
14function usage() {
15 echo "Usage: $(basename "$0") [-h] [-j <jobs>] [-s <source>] [-d <dest>] [-n] planck/rev6:default"
16 echo " -h : Shows this usage page."
17 echo " -j <threads> : Change the number of threads to execute with. Defaults to \`$job_count\`."
18 echo " -s <source> : Use source commit, branch, tag, or sha1 to start the search. Defaults to \`$source_ref\`."
19 echo " -d <dest> : Use destination commit, branch, tag, or sha1 to end the search. Defaults to \`$dest_ref\`."
20 echo " -i <ignore> : The branch to ignore refs from. Defaults to \`$ignore_ref\`."
21 echo " -n : Skips printing changes if the delta is zero."
22 exit 1
23}
24
25if [[ ${#} -eq 0 ]]; then
26 usage
27fi
28
29while getopts "hj:s:d:i:n" opt "$@" ; do
30 case "$opt" in
31 h) usage; exit 0;;
32 j) job_count="${OPTARG:-}";;
33 s) source_ref="${OPTARG:-}";;
34 d) dest_ref="${OPTARG:-}";;
35 i) ignore_ref="${OPTARG:-}";;
36 n) skip_zero=1;;
37 \?) usage >&2; exit 1;;
38 esac
39done
40
41# Work out the target board
42shift $((OPTIND-1))
43keyboard_target=$1
44
45last_size=0
46last_line=""
47function build_executor() {
48 git rev-list --oneline --no-merges ${source_ref}...${dest_ref} ^${ignore_ref} | while IFS= read -r line ; do
49 revision=$(echo $line | cut -d' ' -f1)
50
51 make distclean >/dev/null 2>&1
52 git checkout $revision >/dev/null 2>&1 || { echo "Failed to check out revision ${revision}" >&2 ; exit 1 ; }
53 make -j${job_count} $keyboard_target >/dev/null 2>&1 || true
54 file_size=$(arm-none-eabi-size .build/*.elf 2>/dev/null | awk '/elf/ {print $1}' 2>/dev/null || true)
55
56 if [[ "$last_size" == 0 ]] ; then last_size=$file_size ; fi
57 if [[ -z "$file_size" ]] ; then file_size=0 ; fi
58
59 if [[ -n "$last_line" ]] ; then
60 size_delta=$(( $last_size - $file_size ))
61 if { [[ -n "${skip_zero:-}" ]] && [[ $size_delta -ne 0 ]] ; } || [[ $file_size -eq 0 ]] ; then
62 printf "Size: %8d, delta: %+6d -- %s\n" "$last_size" "$size_delta" "$last_line"
63 fi
64 fi
65
66 last_size=$file_size
67 last_line=$line
68 done
69
70 if [ -n "$last_line" ] ; then
71 size_delta=0
72 printf "Size: %8d, delta: %+6d -- %s\n" "$last_size" "$size_delta" "$last_line"
73 fi
74}
75
76# The actual execution of all the builds needs to be the last command in the entire script
77# - During builds, this script file will disappear, so we need the entire script to be
78# loaded into the script interpreter at the time of execution. Do not refactor.
79build_executor
diff --git a/util/stm32eeprom_parser.py b/util/stm32eeprom_parser.py
new file mode 100755
index 000000000..e08b67064
--- /dev/null
+++ b/util/stm32eeprom_parser.py
@@ -0,0 +1,316 @@
1#!/usr/bin/env python
2#
3# Copyright 2021 Don Kjer
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18
19from __future__ import print_function
20
21import argparse
22from struct import pack, unpack
23import os, sys
24
25MAGIC_FEEA = '\xea\xff\xfe\xff'
26
27MAGIC_FEE9 = '\x16\x01'
28EMPTY_WORD = '\xff\xff'
29WORD_ENCODING = 0x8000
30VALUE_NEXT = 0x6000
31VALUE_RESERVED = 0x4000
32VALUE_ENCODED = 0x2000
33BYTE_RANGE = 0x80
34
35CHUNK_SIZE = 1024
36
37STRUCT_FMTS = {
38 1: 'B',
39 2: 'H',
40 4: 'I'
41}
42PRINTABLE='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ '
43
44EECONFIG_V1 = [
45 ("MAGIC", 0, 2),
46 ("DEBUG", 2, 1),
47 ("DEFAULT_LAYER", 3, 1),
48 ("KEYMAP", 4, 1),
49 ("MOUSEKEY_ACCEL", 5, 1),
50 ("BACKLIGHT", 6, 1),
51 ("AUDIO", 7, 1),
52 ("RGBLIGHT", 8, 4),
53 ("UNICODEMODE", 12, 1),
54 ("STENOMODE", 13, 1),
55 ("HANDEDNESS", 14, 1),
56 ("KEYBOARD", 15, 4),
57 ("USER", 19, 4),
58 ("VELOCIKEY", 23, 1),
59 ("HAPTIC", 24, 4),
60 ("MATRIX", 28, 4),
61 ("MATRIX_EXTENDED", 32, 2),
62 ("KEYMAP_UPPER_BYTE", 34, 1),
63]
64VIABASE_V1 = 35
65
66VERBOSE = False
67
68def parseArgs():
69 parser = argparse.ArgumentParser(description='Decode an STM32 emulated eeprom dump')
70 parser.add_argument('-s', '--size', type=int,
71 help='Size of the emulated eeprom (default: input_size / 2)')
72 parser.add_argument('-o', '--output', help='File to write decoded eeprom to')
73 parser.add_argument('-y', '--layout-options-size', type=int,
74 help='VIA layout options size (default: 1)', default=1)
75 parser.add_argument('-t', '--custom-config-size', type=int,
76 help='VIA custom config size (default: 0)', default=0)
77 parser.add_argument('-l', '--layers', type=int,
78 help='VIA keyboard layers (default: 4)', default=4)
79 parser.add_argument('-r', '--rows', type=int, help='VIA matrix rows')
80 parser.add_argument('-c', '--cols', type=int, help='VIA matrix columns')
81 parser.add_argument('-m', '--macros', type=int,
82 help='VIA macro count (default: 16)', default=16)
83 parser.add_argument('-C', '--canonical', action='store_true',
84 help='Canonical hex+ASCII display.')
85 parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
86 parser.add_argument('input', help='Raw contents of the STM32 flash area used to emulate eeprom')
87 return parser.parse_args()
88
89
90def decodeEepromFEEA(in_file, size):
91 decoded=size*[None]
92 pos = 0
93 while True:
94 chunk = in_file.read(CHUNK_SIZE)
95 for i in range(0, len(chunk), 2):
96 decoded[pos] = unpack('B', chunk[i])[0]
97 pos += 1
98 if pos >= size:
99 break
100
101 if len(chunk) < CHUNK_SIZE or pos >= size:
102 break
103 return decoded
104
105def decodeEepromFEE9(in_file, size):
106 decoded=size*[None]
107 pos = 0
108 # Read compacted flash
109 while True:
110 read_size = min(size - pos, CHUNK_SIZE)
111 chunk = in_file.read(read_size)
112 for i in range(len(chunk)):
113 decoded[pos] = unpack('B', chunk[i])[0] ^ 0xFF
114 pos += 1
115 if pos >= size:
116 break
117
118 if len(chunk) < read_size or pos >= size:
119 break
120 if VERBOSE:
121 print("COMPACTED EEPROM:")
122 dumpBinary(decoded, True)
123 print("WRITE LOG:")
124 # Read write log
125 while True:
126 entry = in_file.read(2)
127 if len(entry) < 2:
128 print("Partial log address at position 0x%04x" % pos, file=sys.stderr)
129 break
130 pos += 2
131
132 if entry == EMPTY_WORD:
133 break
134
135 be_entry = unpack('>H', entry)[0]
136 entry = unpack('H', entry)[0]
137 if not (entry & WORD_ENCODING):
138 address = entry >> 8
139 decoded[address] = entry & 0xFF
140 if VERBOSE:
141 print("[0x%04x]: BYTE 0x%02x = 0x%02x" % (be_entry, address, decoded[address]))
142 else:
143 if (entry & VALUE_NEXT) == VALUE_NEXT:
144 # Read next word as value
145 value = in_file.read(2)
146 if len(value) < 2:
147 print("Partial log value at position 0x%04x" % pos, file=sys.stderr)
148 break
149 pos += 2
150 address = entry & 0x1FFF
151 address <<= 1
152 address += BYTE_RANGE
153 decoded[address] = unpack('B', value[0])[0] ^ 0xFF
154 decoded[address+1] = unpack('B', value[1])[0] ^ 0xFF
155 be_value = unpack('>H', value)[0]
156 if VERBOSE:
157 print("[0x%04x 0x%04x]: WORD 0x%04x = 0x%02x%02x" % (be_entry, be_value, address, decoded[address+1], decoded[address]))
158 else:
159 # Reserved for future use
160 if entry & VALUE_RESERVED:
161 if VERBOSE:
162 print("[0x%04x]: RESERVED 0x%04x" % (be_entry, address))
163 continue
164 address = entry & 0x1FFF
165 address <<= 1
166 decoded[address] = (entry & VALUE_ENCODED) >> 13
167 decoded[address+1] = 0
168 if VERBOSE:
169 print("[0x%04x]: ENCODED 0x%04x = 0x%02x%02x" % (be_entry, address, decoded[address+1], decoded[address]))
170
171 return decoded
172
173def dumpBinary(data, canonical):
174 def display(pos, row):
175 print("%04x" % pos, end='')
176 for i in range(len(row)):
177 if i % 8 == 0:
178 print(" ", end='')
179 char = row[i]
180 if char is None:
181 print(" ", end='')
182 else:
183 print(" %02x" % row[i], end='')
184 if canonical:
185 print(" |", end='')
186 for i in range(len(row)):
187 char = row[i]
188 if char is None:
189 char = " "
190 else:
191 char = chr(char)
192 if char not in PRINTABLE:
193 char = "."
194 print(char, end='')
195 print("|", end='')
196
197 print("")
198
199 size = len(data)
200 prev_row = ''
201 first_repeat = True
202 for pos in range(0, size, 16):
203 row=data[pos:pos+16]
204 row[len(row):16] = (16-len(row))*[None]
205 if row == prev_row:
206 if first_repeat:
207 print("*")
208 first_repeat = False
209 else:
210 first_repeat = True
211 display(pos, row)
212 prev_row = row
213 print("%04x" % (pos+16))
214
215def dumpEeconfig(data, eeconfig):
216 print("EECONFIG:")
217 for (name, pos, length) in eeconfig:
218 fmt = STRUCT_FMTS[length]
219 value = unpack(fmt, ''.join([chr(x) for x in data[pos:pos+length]]))[0]
220 print(("%%04x %%s = 0x%%0%dx" % (length * 2)) % (pos, name, value))
221
222def dumpVia(data, base, layers, cols, rows, macros,
223 layout_options_size, custom_config_size):
224 magicYear = data[base + 0]
225 magicMonth = data[base + 1]
226 magicDay = data[base + 2]
227 # Sanity check
228 if not 10 <= magicYear <= 0x99 or \
229 not 0 <= magicMonth <= 0x12 or \
230 not 0 <= magicDay <= 0x31:
231 print("ERROR: VIA Signature is not valid; Year:%x, Month:%x, Day:%x" % (magicYear, magicMonth, magicDay))
232 return
233 if cols is None or rows is None:
234 print("ERROR: VIA dump requires specifying --rows and --cols", file=sys.stderr)
235 return 2
236 print("VIA:")
237 # Decode magic
238 print("%04x MAGIC = 20%02x-%02x-%02x" % (base, magicYear, magicMonth, magicDay))
239 # Decode layout options
240 options = 0
241 pos = base + 3
242 for i in range(base+3, base+3+layout_options_size):
243 options = options << 8
244 options |= data[i]
245 print(("%%04x LAYOUT_OPTIONS = 0x%%0%dx" % (layout_options_size * 2)) % (pos, options))
246 pos += layout_options_size + custom_config_size
247 # Decode keycodes
248 keymap_size = layers * rows * cols * 2
249 if (pos + keymap_size) >= (len(data) - 1):
250 print("ERROR: VIA keymap requires %d bytes, but only %d available" % (keymap_size, len(data) - pos))
251 return 3
252 for layer in range(layers):
253 print("%s LAYER %d %s" % ('-'*int(cols*2.5), layer, '-'*int(cols*2.5)))
254 for row in range(rows):
255 print("%04x | " % pos, end='')
256 for col in range(cols):
257 keycode = (data[pos] << 8) | (data[pos+1])
258 print(" %04x" % keycode, end='')
259 pos += 2
260 print("")
261 # Decode macros
262 for macro_num in range(macros):
263 macro = ""
264 macro_pos = pos
265 while pos < len(data):
266 char = chr(data[pos])
267 pos += 1
268 if char == '\x00':
269 print("%04x MACRO[%d] = '%s'" % (macro_pos, macro_num, macro))
270 break
271 else:
272 macro += char
273 return 0
274
275
276def decodeSTM32Eeprom(input, canonical, size=None, output=None, **kwargs):
277 input_size = os.path.getsize(input)
278 if size is None:
279 size = input_size >> 1
280
281 # Read the first few bytes to check magic signature
282 with open(input, 'rb') as in_file:
283 magic=in_file.read(4)
284 in_file.seek(0)
285
286 if magic == MAGIC_FEEA:
287 decoded = decodeEepromFEEA(in_file, size)
288 eeconfig = EECONFIG_V1
289 via_base = VIABASE_V1
290 elif magic[:2] == MAGIC_FEE9:
291 decoded = decodeEepromFEE9(in_file, size)
292 eeconfig = EECONFIG_V1
293 via_base = VIABASE_V1
294 else:
295 print("Unknown magic signature: %s" % " ".join(["0x%02x" % ord(x) for x in magic]), file=sys.stderr)
296 return 1
297
298 if output is not None:
299 with open(output, 'wb') as out_file:
300 out_file.write(pack('%dB' % len(decoded), *decoded))
301 print("DECODED EEPROM:")
302 dumpBinary(decoded, canonical)
303 dumpEeconfig(decoded, eeconfig)
304 if kwargs['rows'] is not None and kwargs['cols'] is not None:
305 return dumpVia(decoded, via_base, **kwargs)
306
307 return 0
308
309def main():
310 global VERBOSE
311 kwargs = vars(parseArgs())
312 VERBOSE = kwargs.pop('verbose')
313 return decodeSTM32Eeprom(**kwargs)
314
315if __name__ == '__main__':
316 sys.exit(main())
diff --git a/util/udev/50-qmk.rules b/util/udev/50-qmk.rules
new file mode 100644
index 000000000..57806f9df
--- /dev/null
+++ b/util/udev/50-qmk.rules
@@ -0,0 +1,74 @@
1# Atmel DFU
2### ATmega16U2
3SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2fef", TAG+="uaccess"
4### ATmega32U2
5SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff0", TAG+="uaccess"
6### ATmega16U4
7SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff3", TAG+="uaccess"
8### ATmega32U4
9SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff4", TAG+="uaccess"
10### AT90USB64
11SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff9", TAG+="uaccess"
12### AT90USB162
13SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ffa", TAG+="uaccess"
14### AT90USB128
15SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ffb", TAG+="uaccess"
16
17# Input Club
18SUBSYSTEMS=="usb", ATTRS{idVendor}=="1c11", ATTRS{idProduct}=="b007", TAG+="uaccess"
19
20# STM32duino
21SUBSYSTEMS=="usb", ATTRS{idVendor}=="1eaf", ATTRS{idProduct}=="0003", TAG+="uaccess"
22# STM32 DFU
23SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", TAG+="uaccess"
24
25# BootloadHID
26SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05df", TAG+="uaccess"
27
28# USBAspLoader
29SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05dc", TAG+="uaccess"
30
31# ModemManager should ignore the following devices
32# Atmel SAM-BA (Massdrop)
33SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="6124", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
34
35# Caterina (Pro Micro)
36## pid.codes shared PID
37### Keyboardio Atreus 2 Bootloader
38SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2302", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
39## Spark Fun Electronics
40### Pro Micro 3V3/8MHz
41SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9203", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
42### Pro Micro 5V/16MHz
43SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9205", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
44### LilyPad 3V3/8MHz (and some Pro Micro clones)
45SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9207", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
46## Pololu Electronics
47### A-Star 32U4
48SUBSYSTEMS=="usb", ATTRS{idVendor}=="1ffb", ATTRS{idProduct}=="0101", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
49## Arduino SA
50### Leonardo
51SUBSYSTEMS=="usb", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0036", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
52### Micro
53SUBSYSTEMS=="usb", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0037", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
54## Adafruit Industries LLC
55### Feather 32U4
56SUBSYSTEMS=="usb", ATTRS{idVendor}=="239a", ATTRS{idProduct}=="000c", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
57### ItsyBitsy 32U4 3V3/8MHz
58SUBSYSTEMS=="usb", ATTRS{idVendor}=="239a", ATTRS{idProduct}=="000d", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
59### ItsyBitsy 32U4 5V/16MHz
60SUBSYSTEMS=="usb", ATTRS{idVendor}=="239a", ATTRS{idProduct}=="000e", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
61## dog hunter AG
62### Leonardo
63SUBSYSTEMS=="usb", ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="0036", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
64### Micro
65SUBSYSTEMS=="usb", ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="0037", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
66
67# hid_listen
68KERNEL=="hidraw*", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl"
69
70# hid bootloaders
71## QMK HID
72SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2067", TAG+="uaccess"
73## PJRC's HalfKay
74SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="0478", TAG+="uaccess"
diff --git a/util/uf2conv.py b/util/uf2conv.py
new file mode 100755
index 000000000..8677a828c
--- /dev/null
+++ b/util/uf2conv.py
@@ -0,0 +1,319 @@
1#!/usr/bin/env python3
2import sys
3import struct
4import subprocess
5import re
6import os
7import os.path
8import argparse
9
10
11UF2_MAGIC_START0 = 0x0A324655 # "UF2\n"
12UF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected
13UF2_MAGIC_END = 0x0AB16F30 # Ditto
14
15families = {
16 'SAMD21': 0x68ed2b88,
17 'SAML21': 0x1851780a,
18 'SAMD51': 0x55114460,
19 'NRF52': 0x1b57745f,
20 'STM32F0': 0x647824b6,
21 'STM32F1': 0x5ee21072,
22 'STM32F2': 0x5d1a0a2e,
23 'STM32F3': 0x6b846188,
24 'STM32F4': 0x57755a57,
25 'STM32F7': 0x53b80f00,
26 'STM32G0': 0x300f5633,
27 'STM32G4': 0x4c71240a,
28 'STM32H7': 0x6db66082,
29 'STM32L0': 0x202e3a91,
30 'STM32L1': 0x1e1f432d,
31 'STM32L4': 0x00ff6919,
32 'STM32L5': 0x04240bdf,
33 'STM32WB': 0x70d16653,
34 'STM32WL': 0x21460ff0,
35 'ATMEGA32': 0x16573617,
36 'MIMXRT10XX': 0x4FB2D5BD,
37 'LPC55': 0x2abc77ec,
38 'GD32F350': 0x31D228C6,
39 'ESP32S2': 0xbfdd4eee,
40 'RP2040': 0xe48bff56
41}
42
43INFO_FILE = "/INFO_UF2.TXT"
44
45appstartaddr = 0x2000
46familyid = 0x0
47
48
49def is_uf2(buf):
50 w = struct.unpack("<II", buf[0:8])
51 return w[0] == UF2_MAGIC_START0 and w[1] == UF2_MAGIC_START1
52
53def is_hex(buf):
54 try:
55 w = buf[0:30].decode("utf-8")
56 except UnicodeDecodeError:
57 return False
58 if w[0] == ':' and re.match(b"^[:0-9a-fA-F\r\n]+$", buf):
59 return True
60 return False
61
62def convert_from_uf2(buf):
63 global appstartaddr
64 numblocks = len(buf) // 512
65 curraddr = None
66 outp = []
67 for blockno in range(numblocks):
68 ptr = blockno * 512
69 block = buf[ptr:ptr + 512]
70 hd = struct.unpack(b"<IIIIIIII", block[0:32])
71 if hd[0] != UF2_MAGIC_START0 or hd[1] != UF2_MAGIC_START1:
72 print("Skipping block at " + ptr + "; bad magic")
73 continue
74 if hd[2] & 1:
75 # NO-flash flag set; skip block
76 continue
77 datalen = hd[4]
78 if datalen > 476:
79 assert False, "Invalid UF2 data size at " + ptr
80 newaddr = hd[3]
81 if curraddr is None:
82 appstartaddr = newaddr
83 curraddr = newaddr
84 padding = newaddr - curraddr
85 if padding < 0:
86 assert False, "Block out of order at " + ptr
87 if padding > 10*1024*1024:
88 assert False, "More than 10M of padding needed at " + ptr
89 if padding % 4 != 0:
90 assert False, "Non-word padding size at " + ptr
91 while padding > 0:
92 padding -= 4
93 outp += b"\x00\x00\x00\x00"
94 outp.append(block[32 : 32 + datalen])
95 curraddr = newaddr + datalen
96 return b"".join(outp)
97
98def convert_to_carray(file_content):
99 outp = "const unsigned long bindata_len = %d;\n" % len(file_content)
100 outp += "const unsigned char bindata[] __attribute__((aligned(16))) = {"
101 for i in range(len(file_content)):
102 if i % 16 == 0:
103 outp += "\n"
104 outp += "0x%02x, " % file_content[i]
105 outp += "\n};\n"
106 return bytes(outp, "utf-8")
107
108def convert_to_uf2(file_content):
109 global familyid
110 datapadding = b""
111 while len(datapadding) < 512 - 256 - 32 - 4:
112 datapadding += b"\x00\x00\x00\x00"
113 numblocks = (len(file_content) + 255) // 256
114 outp = []
115 for blockno in range(numblocks):
116 ptr = 256 * blockno
117 chunk = file_content[ptr:ptr + 256]
118 flags = 0x0
119 if familyid:
120 flags |= 0x2000
121 hd = struct.pack(b"<IIIIIIII",
122 UF2_MAGIC_START0, UF2_MAGIC_START1,
123 flags, ptr + appstartaddr, 256, blockno, numblocks, familyid)
124 while len(chunk) < 256:
125 chunk += b"\x00"
126 block = hd + chunk + datapadding + struct.pack(b"<I", UF2_MAGIC_END)
127 assert len(block) == 512
128 outp.append(block)
129 return b"".join(outp)
130
131class Block:
132 def __init__(self, addr):
133 self.addr = addr
134 self.bytes = bytearray(256)
135
136 def encode(self, blockno, numblocks):
137 global familyid
138 flags = 0x0
139 if familyid:
140 flags |= 0x2000
141 hd = struct.pack("<IIIIIIII",
142 UF2_MAGIC_START0, UF2_MAGIC_START1,
143 flags, self.addr, 256, blockno, numblocks, familyid)
144 hd += self.bytes[0:256]
145 while len(hd) < 512 - 4:
146 hd += b"\x00"
147 hd += struct.pack("<I", UF2_MAGIC_END)
148 return hd
149
150def convert_from_hex_to_uf2(buf):
151 global appstartaddr
152 appstartaddr = None
153 upper = 0
154 currblock = None
155 blocks = []
156 for line in buf.split('\n'):
157 if line[0] != ":":
158 continue
159 i = 1
160 rec = []
161 while i < len(line) - 1:
162 rec.append(int(line[i:i+2], 16))
163 i += 2
164 tp = rec[3]
165 if tp == 4:
166 upper = ((rec[4] << 8) | rec[5]) << 16
167 elif tp == 2:
168 upper = ((rec[4] << 8) | rec[5]) << 4
169 assert (upper & 0xffff) == 0
170 elif tp == 1:
171 break
172 elif tp == 0:
173 addr = upper | (rec[1] << 8) | rec[2]
174 if appstartaddr is None:
175 appstartaddr = addr
176 i = 4
177 while i < len(rec) - 1:
178 if not currblock or currblock.addr & ~0xff != addr & ~0xff:
179 currblock = Block(addr & ~0xff)
180 blocks.append(currblock)
181 currblock.bytes[addr & 0xff] = rec[i]
182 addr += 1
183 i += 1
184 numblocks = len(blocks)
185 resfile = b""
186 for i in range(0, numblocks):
187 resfile += blocks[i].encode(i, numblocks)
188 return resfile
189
190def to_str(b):
191 return b.decode("utf-8")
192
193def get_drives():
194 drives = []
195 if sys.platform == "win32":
196 r = subprocess.check_output(["wmic", "PATH", "Win32_LogicalDisk",
197 "get", "DeviceID,", "VolumeName,",
198 "FileSystem,", "DriveType"])
199 for line in to_str(r).split('\n'):
200 words = re.split('\s+', line)
201 if len(words) >= 3 and words[1] == "2" and words[2] == "FAT":
202 drives.append(words[0])
203 else:
204 rootpath = "/media"
205 if sys.platform == "darwin":
206 rootpath = "/Volumes"
207 elif sys.platform == "linux":
208 tmp = rootpath + "/" + os.environ["USER"]
209 if os.path.isdir(tmp):
210 rootpath = tmp
211 for d in os.listdir(rootpath):
212 drives.append(os.path.join(rootpath, d))
213
214
215 def has_info(d):
216 try:
217 return os.path.isfile(d + INFO_FILE)
218 except Exception:
219 return False
220
221 return list(filter(has_info, drives))
222
223
224def board_id(path):
225 with open(path + INFO_FILE, mode='r') as file:
226 file_content = file.read()
227 return re.search("Board-ID: ([^\r\n]*)", file_content).group(1)
228
229
230def list_drives():
231 for d in get_drives():
232 print(d, board_id(d))
233
234
235def write_file(name, buf):
236 with open(name, "wb") as f:
237 f.write(buf)
238 print("Wrote %d bytes to %s" % (len(buf), name))
239
240
241def main():
242 global appstartaddr, familyid
243 def error(msg):
244 print(msg)
245 sys.exit(1)
246 parser = argparse.ArgumentParser(description='Convert to UF2 or flash directly.')
247 parser.add_argument('input', metavar='INPUT', type=str, nargs='?',
248 help='input file (HEX, BIN or UF2)')
249 parser.add_argument('-b' , '--base', dest='base', type=str,
250 default="0x2000",
251 help='set base address of application for BIN format (default: 0x2000)')
252 parser.add_argument('-o' , '--output', metavar="FILE", dest='output', type=str,
253 help='write output to named file; defaults to "flash.uf2" or "flash.bin" where sensible')
254 parser.add_argument('-d' , '--device', dest="device_path",
255 help='select a device path to flash')
256 parser.add_argument('-l' , '--list', action='store_true',
257 help='list connected devices')
258 parser.add_argument('-c' , '--convert', action='store_true',
259 help='do not flash, just convert')
260 parser.add_argument('-D' , '--deploy', action='store_true',
261 help='just flash, do not convert')
262 parser.add_argument('-f' , '--family', dest='family', type=str,
263 default="0x0",
264 help='specify familyID - number or name (default: 0x0)')
265 parser.add_argument('-C' , '--carray', action='store_true',
266 help='convert binary file to a C array, not UF2')
267 args = parser.parse_args()
268 appstartaddr = int(args.base, 0)
269
270 if args.family.upper() in families:
271 familyid = families[args.family.upper()]
272 else:
273 try:
274 familyid = int(args.family, 0)
275 except ValueError:
276 error("Family ID needs to be a number or one of: " + ", ".join(families.keys()))
277
278 if args.list:
279 list_drives()
280 else:
281 if not args.input:
282 error("Need input file")
283 with open(args.input, mode='rb') as f:
284 inpbuf = f.read()
285 from_uf2 = is_uf2(inpbuf)
286 ext = "uf2"
287 if args.deploy:
288 outbuf = inpbuf
289 elif from_uf2:
290 outbuf = convert_from_uf2(inpbuf)
291 ext = "bin"
292 elif is_hex(inpbuf):
293 outbuf = convert_from_hex_to_uf2(inpbuf.decode("utf-8"))
294 elif args.carray:
295 outbuf = convert_to_carray(inpbuf)
296 ext = "h"
297 else:
298 outbuf = convert_to_uf2(inpbuf)
299 print("Converting to %s, output size: %d, start address: 0x%x" %
300 (ext, len(outbuf), appstartaddr))
301 if args.convert or ext != "uf2":
302 drives = []
303 if args.output is None:
304 args.output = "flash." + ext
305 else:
306 drives = get_drives()
307
308 if args.output:
309 write_file(args.output, outbuf)
310 else:
311 if len(drives) == 0:
312 error("No drive to deploy.")
313 for d in drives:
314 print("Flashing %s (%s)" % (d, board_id(d)))
315 write_file(d + "/NEW.UF2", outbuf)
316
317
318if __name__ == "__main__":
319 main()
diff --git a/util/update_chibios_mirror.sh b/util/update_chibios_mirror.sh
new file mode 100755
index 000000000..0bf648ebf
--- /dev/null
+++ b/util/update_chibios_mirror.sh
@@ -0,0 +1,92 @@
1#!/bin/bash
2
3################################
4# Configuration
5
6# The ChibiOS branches to mirror
7chibios_branches="trunk stable_20.3.x stable_21.6.x"
8
9# The ChibiOS tags to mirror
10chibios_tags="ver20.3.1 ver20.3.2 ver20.3.3 ver20.3.4 ver21.6.0"
11
12# The ChibiOS-Contrib branches to mirror
13contrib_branches="chibios-20.3.x chibios-21.6.x"
14
15################################
16# Actions
17
18set -eEuo pipefail
19umask 022
20
21this_script="$(realpath "${BASH_SOURCE[0]}")"
22script_dir="$(realpath "$(dirname "$this_script")")"
23qmk_firmware_dir="$(realpath "$script_dir/../")"
24chibios_dir="$qmk_firmware_dir/lib/chibios"
25contrib_dir="$qmk_firmware_dir/lib/chibios-contrib"
26
27chibios_git_location=$(realpath "$chibios_dir/$(cat "$chibios_dir/.git" | awk '/gitdir:/ {print $2}')")
28chibios_git_config=$(realpath "$chibios_git_location/config")
29contrib_git_location=$(realpath "$contrib_dir/$(cat "$contrib_dir/.git" | awk '/gitdir:/ {print $2}')")
30contrib_git_config=$(realpath "$contrib_git_location/config")
31
32cd "$chibios_dir"
33
34if [[ -z "$(cat "$chibios_git_config" | grep '\[svn-remote "svn"\]')" ]] ; then
35 git svn init --stdlayout --prefix='svn/' http://svn.osdn.net/svnroot/chibios/
36fi
37
38if [[ -z "$(cat "$chibios_git_config" | grep '\[remote "qmk"\]')" ]] ; then
39 git remote add qmk [email protected]:qmk/ChibiOS.git
40 git remote set-url qmk [email protected]:qmk/ChibiOS.git --push
41else
42 git remote set-url qmk [email protected]:qmk/ChibiOS.git
43 git remote set-url qmk [email protected]:qmk/ChibiOS.git --push
44fi
45
46echo "Updating remotes..."
47git fetch --all --tags --prune
48
49echo "Fetching latest from subversion..."
50git svn fetch
51
52echo "Updating ChibiOS branches..."
53for branch in $chibios_branches ; do
54 echo "Creating branch 'svn-mirror/$branch' from 'svn/$branch'..."
55 git branch -f svn-mirror/$branch svn/$branch \
56 && git push qmk svn-mirror/$branch
57done
58
59echo "Updating ChibiOS tags..."
60for tagname in $chibios_tags ; do
61 echo "Creating tag 'svn-mirror/$tagname' from 'svn/tags/$tagname'..."
62 GIT_COMMITTER_DATE="$(git log -n1 --pretty=format:'%ad' svn/tags/$tagname)" git tag -f -a -m "Tagging $tagname" svn-mirror/$tagname svn/tags/$tagname
63 git push qmk svn-mirror/$tagname
64done
65
66cd "$contrib_dir"
67
68if [[ -z "$(cat "$contrib_git_config" | grep '\[remote "qmk"\]')" ]] ; then
69 git remote add qmk [email protected]:qmk/ChibiOS-Contrib.git
70 git remote set-url qmk [email protected]:qmk/ChibiOS-Contrib.git --push
71else
72 git remote set-url qmk [email protected]:qmk/ChibiOS-Contrib.git
73 git remote set-url qmk [email protected]:qmk/ChibiOS-Contrib.git --push
74fi
75
76if [[ -z "$(cat "$contrib_git_config" | grep '\[remote "upstream"\]')" ]] ; then
77 git remote add upstream [email protected]:ChibiOS/ChibiOS-Contrib.git
78 git remote set-url upstream [email protected]:ChibiOS/ChibiOS-Contrib.git --push
79else
80 git remote set-url upstream [email protected]:ChibiOS/ChibiOS-Contrib.git
81 git remote set-url upstream [email protected]:ChibiOS/ChibiOS-Contrib.git --push
82fi
83
84echo "Updating remotes..."
85git fetch --all --tags --prune
86
87echo "Updating ChibiOS-Contrib branches..."
88for branch in $contrib_branches ; do
89 echo "Creating branch 'mirror/$branch' from 'upstream/$branch'..."
90 git branch -f mirror/$branch upstream/$branch \
91 && git push qmk mirror/$branch
92done
diff --git a/util/usb_detach/Makefile b/util/usb_detach/Makefile
new file mode 100644
index 000000000..533c1928f
--- /dev/null
+++ b/util/usb_detach/Makefile
@@ -0,0 +1,18 @@
1# the compiler: gcc for C program, define as g++ for C++
2CC = gcc
3
4# compiler flags:
5# -g adds debugging information to the executable file
6# -Wall turns on most, but not all, compiler warnings
7CFLAGS = -g -Wall
8
9# the build target executable:
10TARGET = usb_detach
11
12all: $(TARGET)
13
14$(TARGET): $(TARGET).c
15 $(CC) $(CFLAGS) -o $(TARGET) $(TARGET).c
16
17clean:
18 $(RM) $(TARGET)
diff --git a/util/usb_detach/readme.md b/util/usb_detach/readme.md
new file mode 100644
index 000000000..ac42944ee
--- /dev/null
+++ b/util/usb_detach/readme.md
@@ -0,0 +1,14 @@
1# usb_detach
2
3When trying to flash on Linux, you may encounter a "Resource Unavailable" error. This means that Linux's HID driver has taken exclusive control of the keyboard, and the program script can't flash it.
4This program can force Linux to give up a device, so that the programming script can reset it.
5
6## To compile:
7```bash
8make clean && make
9```
10
11## To run:
121. Use `lsusb` to discover the Bus and Device numbers for your keyboard.
132. Run the program: `sudo ./usb_detach /dev/bus/usb/<BUS>/<DEVICE>`.
143. Build and program the firmware as normal.
diff --git a/util/usb_detach/usb_detach.c b/util/usb_detach/usb_detach.c
new file mode 100644
index 000000000..786ab5e67
--- /dev/null
+++ b/util/usb_detach/usb_detach.c
@@ -0,0 +1,34 @@
1/* Found at https://www.linuxquestions.org/questions/linux-hardware-18/how-to-unclaim-usb-device-558138/#post3406986 */
2#include <stdio.h>
3#include <sys/ioctl.h>
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <fcntl.h>
7#include <linux/ioctl.h>
8#include <linux/usbdevice_fs.h>
9
10int main(int argc, char**argv)
11{
12 struct usbdevfs_ioctl command;
13 int ret;
14 int fd;
15 int i;
16 if (argc>1) {
17 fd = open(argv[1],O_RDWR);
18 if (fd<1){
19 perror("unable to open file");
20 return 1;
21 }
22 for (i=0;i<255;i++){ // hack: should fetch how many interface there is.
23 command.ifno = i;
24 command.ioctl_code = USBDEVFS_DISCONNECT;
25 command.data = NULL;
26 ret = ioctl(fd, USBDEVFS_IOCTL, &command);
27 if(ret!=-1)
28 printf("un claimed interface %d %d\n",i,ret);
29 }
30 } else {
31 printf ("usage: %s /dev/bus/usb/BUS/DEVICE\n",argv[0]);
32 printf("Release all interfaces of this usb device for usage in virtualisation\n");
33 }
34} \ No newline at end of file
diff --git a/util/vagrant/Dockerfile b/util/vagrant/Dockerfile
new file mode 100644
index 000000000..951d4fc40
--- /dev/null
+++ b/util/vagrant/Dockerfile
@@ -0,0 +1,33 @@
1FROM qmkfm/qmk_cli
2
3# Basic upgrades; install sudo and SSH.
4RUN apt-get update && apt-get install --no-install-recommends -y \
5 sudo \
6 openssh-server \
7 && rm -rf /var/lib/apt/lists/*
8RUN mkdir /var/run/sshd
9RUN sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
10RUN echo 'UseDNS no' >> /etc/ssh/sshd_config
11
12# Remove the policy file once we're finished installing software.
13# This allows invoke-rc.d and friends to work as expected.
14RUN rm /usr/sbin/policy-rc.d
15
16# Add the Vagrant user and necessary passwords.
17RUN groupadd vagrant
18RUN useradd -c "Vagrant" -g vagrant -d /home/vagrant -m -s /bin/bash vagrant
19RUN echo 'root:vagrant' | chpasswd
20RUN echo 'vagrant:vagrant' | chpasswd
21
22# Allow the vagrant user to use sudo without a password.
23RUN echo 'vagrant ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/vagrant
24
25# Install Vagrant's insecure public key so provisioning and 'vagrant ssh' work.
26RUN mkdir /home/vagrant/.ssh
27ADD https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub /home/vagrant/.ssh/authorized_keys
28RUN chmod 0600 /home/vagrant/.ssh/authorized_keys
29RUN chown -R vagrant:vagrant /home/vagrant/.ssh
30RUN chmod 0700 /home/vagrant/.ssh
31
32EXPOSE 22
33CMD ["/usr/sbin/sshd", "-D"]
diff --git a/util/vagrant/readme.md b/util/vagrant/readme.md
new file mode 100644
index 000000000..a8396007e
--- /dev/null
+++ b/util/vagrant/readme.md
@@ -0,0 +1,12 @@
1# QMK Vagrant Utilities
2
3## Dockerfile
4Vagrant-friendly `qmkfm/qmk_cli`.
5
6In order for the Docker provider and `vagrant ssh` to function the container has a few extra requirements.
7
8* vagrant user
9* ssh server
10 * configured with expected public key
11* sudo
12 * passwordless for vagrant user
diff --git a/util/wavetable_parser.py b/util/wavetable_parser.py
new file mode 100755
index 000000000..be0f01f7b
--- /dev/null
+++ b/util/wavetable_parser.py
@@ -0,0 +1,40 @@
1#!/usr/bin/env python3
2#
3# Copyright 2019 Jack Humbert
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18
19import wave, struct, sys
20
21waveFile = wave.open(sys.argv[1], 'r')
22
23length = waveFile.getnframes()
24out = "#define DAC_WAVETABLE_CUSTOM_LENGTH " + str(int(length / 256)) + "\n\n"
25out += "static const dacsample_t dac_wavetable_custom[" + str(int(length / 256)) + "][256] = {"
26for i in range(0,length):
27 if (i % 8 == 0):
28 out += "\n "
29 if (i % 256 == 0):
30 out = out[:-2]
31 out += "{\n "
32 waveData = waveFile.readframes(1)
33 data = struct.unpack("<h", waveData)
34 out += str(int((int(data[0]) + 0x8000) / 16)) + ", "
35 if (i % 256 == 255):
36 out = out[:-2]
37 out += "\n },"
38out = out[:-1]
39out += "\n};"
40print(out)