diff options
author | Akshay <[email protected]> | 2022-04-10 12:13:40 +0100 |
---|---|---|
committer | Akshay <[email protected]> | 2022-04-10 12:13:40 +0100 |
commit | dc90387ce7d8ba7b607d9c48540bf6d8b560f14d (patch) | |
tree | 4ccb8fa5886b66fa9d480edef74236c27f035e16 /util |
Diffstat (limited to 'util')
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 | |||
19 | AUDIO_DAC_BUFFER_SIZE=256 | ||
20 | AUDIO_DAC_SAMPLE_MAX=4095 | ||
21 | |||
22 | def plot(values): | ||
23 | for v in values: | ||
24 | print('0'* int(v * 80/AUDIO_DAC_SAMPLE_MAX)) | ||
25 | |||
26 | def to_lut(values): | ||
27 | for v in values: | ||
28 | print(hex(int(v)), end=", ") | ||
29 | |||
30 | |||
31 | from math import sin, tau, pi | ||
32 | |||
33 | samples=[] | ||
34 | |||
35 | def 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 | |||
39 | def 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, | ||
47 | def 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() | ||
64 | sampleTrapezoidal() | ||
65 | #print(samples) | ||
66 | plot(samples) | ||
67 | to_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 | |||
3 | set -eEuo pipefail | ||
4 | umask 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 | |||
17 | sinfo() { echo "$@" >&2 ; } | ||
18 | shead() { sinfo "" ; sinfo "---------------------------------" ; sinfo "-- $@" ; sinfo "---------------------------------" ; } | ||
19 | havecmd() { command command type "${1}" >/dev/null 2>&1 || return 1 ; } | ||
20 | |||
21 | this_script="$(realpath "${BASH_SOURCE[0]}")" | ||
22 | script_dir="$(realpath "$(dirname "$this_script")")" | ||
23 | qmk_firmware_dir="$(realpath "$script_dir/../")" | ||
24 | |||
25 | export PATH="$PATH:$script_dir/fmpp/bin" | ||
26 | |||
27 | build_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 | |||
46 | find_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 | |||
57 | upgrade_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 | |||
77 | upgrade_chconf_files() { | ||
78 | upgrade_conf_files_generic chconf.h update_chconf_rt.sh | ||
79 | } | ||
80 | |||
81 | upgrade_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 | |||
93 | upgrade_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 | |||
101 | havecmd fmpp || build_fmpp | ||
102 | |||
103 | upgrade_mcuconf_files | ||
104 | upgrade_chconf_files | ||
105 | upgrade_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 | |||
4 | errcho() { | ||
5 | echo "$@" >&2 | ||
6 | } | ||
7 | |||
8 | USAGE="Usage: $0 [keyboard[:keymap[:target]]]" | ||
9 | |||
10 | # Check preconditions | ||
11 | for arg; do | ||
12 | if [ "$arg" = "--help" ]; then | ||
13 | echo "$USAGE" | ||
14 | exit 0 | ||
15 | fi | ||
16 | done | ||
17 | if [ $# -gt 1 ]; then | ||
18 | errcho "$USAGE" | ||
19 | exit 1 | ||
20 | fi | ||
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 | ||
25 | if [ -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 | ||
38 | fi | ||
39 | |||
40 | |||
41 | # Determine arguments | ||
42 | if [ $# -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 | ||
46 | else | ||
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 | ||
54 | fi | ||
55 | if [ -z "$keyboard" ]; then | ||
56 | keyboard=all | ||
57 | fi | ||
58 | if [ -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 | ||
69 | fi | ||
70 | dir=$(pwd -W 2>/dev/null) || dir=$PWD # Use Windows path if on Windows | ||
71 | |||
72 | if [ "$RUNTIME" = "docker" ]; then | ||
73 | uid_arg="--user $(id -u):$(id -g)" | ||
74 | fi | ||
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 | ||
6 | winusb,STM32 Bootloader,0483,DF11,6d98a87f-4ecf-464d-89ed-8c684d857a75 | ||
7 | winusb,APM32 Bootloader,314B,0106,9ff3cc31-6772-4a3f-a492-a80d91f7a853 | ||
8 | winusb,STM32duino Bootloader,1EAF,0003,746915ec-99d8-4a90-a722-3c85ba31e4fe | ||
9 | libusbk,USBaspLoader,16C0,05DC,e69affdc-0ef0-427c-aefb-4e593c9d2724 | ||
10 | winusb,Kiibohd DFU Bootloader,1C11,B007,aa5a3f86-b81e-4416-89ad-0c1ea1ed63af | ||
11 | libusb,ATmega16U2,03EB,2FEF,007274da-b75f-492e-a288-8fc0aff8339f | ||
12 | libusb,ATmega32U2,03EB,2FF0,ddc2c572-cb6e-4f61-a6cc-1a5de941f063 | ||
13 | libusb,ATmega16U4,03EB,2FF3,3180d426-bf93-4578-a693-2efbc337da8e | ||
14 | libusb,ATmega32U4,03EB,2FF4,5f9726fd-f9de-487a-9fbd-8b3524a7a56a | ||
15 | libusb,AT90USB64,03EB,2FF9,c6a708ad-e97d-43cd-b04a-3180d737a71b | ||
16 | libusb,AT90USB162,03EB,2FFA,ef8546f0-ef09-4e7c-8fc2-ffbae1dcd84a | ||
17 | libusb,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 | |||
3 | set -x | ||
4 | |||
5 | if ! doxygen -v 2>&1 > /dev/null; then | ||
6 | echo "doxygen not found! Please install it!" | ||
7 | exit 1 | ||
8 | elif ! 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 | ||
19 | fi | ||
20 | |||
21 | if [ ! -e Doxyfile ]; then | ||
22 | echo "Error: You must run this from the top-level qmk_firmware directory!" | ||
23 | exit 1 | ||
24 | fi | ||
25 | |||
26 | # Generate the doxygen XML files | ||
27 | rm -rf doxygen | ||
28 | doxygen Doxyfile | ||
29 | |||
30 | # Generate the moxygen Markdown files | ||
31 | moxygen -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 | |||
3 | DEBIAN_FRONTEND=noninteractive | ||
4 | DEBCONF_NONINTERACTIVE_SEEN=true | ||
5 | export 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 | |||
6 | find -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 | ||
18 | done | ||
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. | ||
7 | echo_error() { | ||
8 | echo -e "[\033[0;91mERROR\033[m]: $1" | ||
9 | } | ||
10 | |||
11 | # Print a message in bold. | ||
12 | echo_bold() { | ||
13 | echo -e "\033[1m$1\033[m" | ||
14 | } | ||
15 | |||
16 | # Prompt the user for information, showing the default value in brackets. | ||
17 | prompt() { | ||
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. | ||
29 | set_git_username() { | ||
30 | git_username="$(git config --get user.name)" | ||
31 | } | ||
32 | |||
33 | # Copy the template files to the new keyboard directory. | ||
34 | copy_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. | ||
53 | set_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. | ||
62 | replace_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. | ||
76 | replace_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. | ||
87 | replace_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. | ||
99 | replace_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. | ||
112 | array_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/ | ||
126 | if [ ! -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 | ||
130 | fi | ||
131 | |||
132 | echo_bold "########################################" | ||
133 | echo_bold "# NOTICE #" | ||
134 | echo_bold "# This script has been deprecated. #" | ||
135 | echo_bold "# Please use qmk new-keyboard instead. #" | ||
136 | echo_bold "########################################" | ||
137 | echo | ||
138 | echo_bold "Generating a new QMK keyboard directory" | ||
139 | echo | ||
140 | |||
141 | # Keyboard name is required, so keep prompting until we get one | ||
142 | while [ -z "$keyboard_name" ]; do | ||
143 | prompt "Keyboard Name" "" | ||
144 | keyboard_name=$prompt_return | ||
145 | keyboard_base_name=$(basename $keyboard_name) | ||
146 | done | ||
147 | |||
148 | keyboard_dir="keyboards/$keyboard_name" | ||
149 | |||
150 | if [ -d "$keyboard_dir" ]; then | ||
151 | echo_error "Keyboard $keyboard_name already exists!" | ||
152 | exit 1 | ||
153 | fi | ||
154 | |||
155 | KEYBOARD_TYPES=("avr" "ps2avrgb") | ||
156 | |||
157 | prompt "Keyboard Type" "avr" | ||
158 | keyboard_type=$prompt_return | ||
159 | |||
160 | if ! array_contains "$keyboard_type" "${KEYBOARD_TYPES[@]}"; then | ||
161 | echo_error "Keyboard type must be one of: ${KEYBOARD_TYPES[*]}" | ||
162 | exit 1 | ||
163 | fi | ||
164 | |||
165 | set_git_username | ||
166 | prompt "Your Name" "$git_username" | ||
167 | username=$prompt_return | ||
168 | |||
169 | echo | ||
170 | |||
171 | copy_templates | ||
172 | set_sed_i | ||
173 | replace_year_placeholders | ||
174 | replace_keyboard_placeholders | ||
175 | [ -n "$username" ] && replace_name_placeholders | ||
176 | |||
177 | echo | ||
178 | echo_bold "Created a new keyboard called $keyboard_name." | ||
179 | echo | ||
180 | echo_bold "To start working on things, cd into keyboards/$keyboard_name," | ||
181 | echo_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 | |||
6 | KB_PATH=$(echo "$1" | tr 'A-Z' 'a-z') | ||
7 | USERNAME=$(echo "$2" | tr 'A-Z' 'a-z') | ||
8 | |||
9 | if [ -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 | ||
13 | fi | ||
14 | |||
15 | if [ -z "$USERNAME" ]; then | ||
16 | printf "Usage: %s <keyboard_path> <username>\n" "$0" | ||
17 | printf "Example: %s 1upkeyboards/1up60hse yourname\n" "$0" | ||
18 | exit 1 | ||
19 | fi | ||
20 | |||
21 | cd "$(dirname "$0")/.." | ||
22 | |||
23 | if [ ! -d "keyboards/$KB_PATH" ]; then | ||
24 | printf "Error! keyboards/%s does not exist!\n" "$KB_PATH" | ||
25 | exit 1 | ||
26 | fi | ||
27 | |||
28 | if [ -d "keyboards/$KB_PATH/keymaps/$USERNAME" ]; then | ||
29 | printf "Error! keyboards/%s/keymaps/%s already exists!\n" "$KB_PATH" "$USERNAME" | ||
30 | exit 1 | ||
31 | fi | ||
32 | |||
33 | # Recursively copy the chosen keyboard's default keymap | ||
34 | cp -r keyboards/"$KB_PATH"/keymaps/default keyboards/"$KB_PATH"/keymaps/"$USERNAME" | ||
35 | |||
36 | printf "%s keymap directory created in: qmk_firmware/keyboards/%s/keymaps/\n\n" "$USERNAME" "$KB_PATH" | ||
37 | |||
38 | printf "Compile a firmware file with your new keymap by typing: \n" | ||
39 | printf " make %s:%s\n" "$KB_PATH" "$USERNAME" | ||
40 | printf "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]] | ||
2 | name = "appdirs" | ||
3 | version = "1.4.4" | ||
4 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." | ||
5 | category = "main" | ||
6 | optional = false | ||
7 | python-versions = "*" | ||
8 | |||
9 | [[package]] | ||
10 | name = "argcomplete" | ||
11 | version = "1.12.3" | ||
12 | description = "Bash tab completion for argparse" | ||
13 | category = "main" | ||
14 | optional = false | ||
15 | python-versions = "*" | ||
16 | |||
17 | [package.extras] | ||
18 | test = ["coverage", "flake8", "pexpect", "wheel"] | ||
19 | |||
20 | [[package]] | ||
21 | name = "attrs" | ||
22 | version = "21.2.0" | ||
23 | description = "Classes Without Boilerplate" | ||
24 | category = "main" | ||
25 | optional = false | ||
26 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" | ||
27 | |||
28 | [package.extras] | ||
29 | dev = ["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"] | ||
30 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] | ||
31 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] | ||
32 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] | ||
33 | |||
34 | [[package]] | ||
35 | name = "colorama" | ||
36 | version = "0.4.4" | ||
37 | description = "Cross-platform colored terminal text." | ||
38 | category = "main" | ||
39 | optional = false | ||
40 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" | ||
41 | |||
42 | [[package]] | ||
43 | name = "coverage" | ||
44 | version = "5.5" | ||
45 | description = "Code coverage measurement for Python" | ||
46 | category = "dev" | ||
47 | optional = false | ||
48 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" | ||
49 | |||
50 | [package.extras] | ||
51 | toml = ["toml"] | ||
52 | |||
53 | [[package]] | ||
54 | name = "flake8" | ||
55 | version = "3.9.2" | ||
56 | description = "the modular source code checker: pep8 pyflakes and co" | ||
57 | category = "dev" | ||
58 | optional = false | ||
59 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" | ||
60 | |||
61 | [package.dependencies] | ||
62 | mccabe = ">=0.6.0,<0.7.0" | ||
63 | pycodestyle = ">=2.7.0,<2.8.0" | ||
64 | pyflakes = ">=2.3.0,<2.4.0" | ||
65 | |||
66 | [[package]] | ||
67 | name = "flake8-polyfill" | ||
68 | version = "1.0.2" | ||
69 | description = "Polyfill package for Flake8 plugins" | ||
70 | category = "dev" | ||
71 | optional = false | ||
72 | python-versions = "*" | ||
73 | |||
74 | [package.dependencies] | ||
75 | flake8 = "*" | ||
76 | |||
77 | [[package]] | ||
78 | name = "halo" | ||
79 | version = "0.0.31" | ||
80 | description = "Beautiful terminal spinners in Python" | ||
81 | category = "main" | ||
82 | optional = false | ||
83 | python-versions = ">=3.4" | ||
84 | |||
85 | [package.dependencies] | ||
86 | colorama = ">=0.3.9" | ||
87 | log-symbols = ">=0.0.14" | ||
88 | six = ">=1.12.0" | ||
89 | spinners = ">=0.0.24" | ||
90 | termcolor = ">=1.1.0" | ||
91 | |||
92 | [package.extras] | ||
93 | ipython = ["IPython (==5.7.0)", "ipywidgets (==7.1.0)"] | ||
94 | |||
95 | [[package]] | ||
96 | name = "hid" | ||
97 | version = "1.0.4" | ||
98 | description = "ctypes bindings for hidapi" | ||
99 | category = "main" | ||
100 | optional = false | ||
101 | python-versions = "*" | ||
102 | |||
103 | [[package]] | ||
104 | name = "hjson" | ||
105 | version = "3.0.2" | ||
106 | description = "Hjson, a user interface for JSON." | ||
107 | category = "main" | ||
108 | optional = false | ||
109 | python-versions = "*" | ||
110 | |||
111 | [[package]] | ||
112 | name = "jsonschema" | ||
113 | version = "3.2.0" | ||
114 | description = "An implementation of JSON Schema validation for Python" | ||
115 | category = "main" | ||
116 | optional = false | ||
117 | python-versions = "*" | ||
118 | |||
119 | [package.dependencies] | ||
120 | attrs = ">=17.4.0" | ||
121 | pyrsistent = ">=0.14.0" | ||
122 | six = ">=1.11.0" | ||
123 | |||
124 | [package.extras] | ||
125 | format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] | ||
126 | format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] | ||
127 | |||
128 | [[package]] | ||
129 | name = "log-symbols" | ||
130 | version = "0.0.14" | ||
131 | description = "Colored symbols for various log levels for Python" | ||
132 | category = "main" | ||
133 | optional = false | ||
134 | python-versions = "*" | ||
135 | |||
136 | [package.dependencies] | ||
137 | colorama = ">=0.3.9" | ||
138 | |||
139 | [[package]] | ||
140 | name = "mccabe" | ||
141 | version = "0.6.1" | ||
142 | description = "McCabe checker, plugin for flake8" | ||
143 | category = "dev" | ||
144 | optional = false | ||
145 | python-versions = "*" | ||
146 | |||
147 | [[package]] | ||
148 | name = "milc" | ||
149 | version = "1.6.2" | ||
150 | description = "Opinionated Batteries-Included Python 3 CLI Framework." | ||
151 | category = "main" | ||
152 | optional = false | ||
153 | python-versions = "*" | ||
154 | |||
155 | [package.dependencies] | ||
156 | appdirs = "*" | ||
157 | argcomplete = "*" | ||
158 | colorama = "*" | ||
159 | halo = "*" | ||
160 | spinners = "*" | ||
161 | |||
162 | [[package]] | ||
163 | name = "nose2" | ||
164 | version = "0.10.0" | ||
165 | description = "unittest2 with plugins, the succesor to nose" | ||
166 | category = "dev" | ||
167 | optional = false | ||
168 | python-versions = "*" | ||
169 | |||
170 | [package.dependencies] | ||
171 | coverage = ">=4.4.1" | ||
172 | six = ">=1.7" | ||
173 | |||
174 | [package.extras] | ||
175 | coverage_plugin = ["coverage (>=4.4.1)"] | ||
176 | dev = ["Sphinx (>=1.6.5)", "sphinx-rtd-theme", "mock", "coverage"] | ||
177 | |||
178 | [[package]] | ||
179 | name = "pep8-naming" | ||
180 | version = "0.12.1" | ||
181 | description = "Check PEP-8 naming conventions, plugin for flake8" | ||
182 | category = "dev" | ||
183 | optional = false | ||
184 | python-versions = "*" | ||
185 | |||
186 | [package.dependencies] | ||
187 | flake8 = ">=3.9.1" | ||
188 | flake8-polyfill = ">=1.0.2,<2" | ||
189 | |||
190 | [[package]] | ||
191 | name = "pycodestyle" | ||
192 | version = "2.7.0" | ||
193 | description = "Python style guide checker" | ||
194 | category = "dev" | ||
195 | optional = false | ||
196 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | ||
197 | |||
198 | [[package]] | ||
199 | name = "pyflakes" | ||
200 | version = "2.3.1" | ||
201 | description = "passive checker of Python programs" | ||
202 | category = "dev" | ||
203 | optional = false | ||
204 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | ||
205 | |||
206 | [[package]] | ||
207 | name = "pygments" | ||
208 | version = "2.10.0" | ||
209 | description = "Pygments is a syntax highlighting package written in Python." | ||
210 | category = "main" | ||
211 | optional = false | ||
212 | python-versions = ">=3.5" | ||
213 | |||
214 | [[package]] | ||
215 | name = "pyrsistent" | ||
216 | version = "0.18.0" | ||
217 | description = "Persistent/Functional/Immutable data structures" | ||
218 | category = "main" | ||
219 | optional = false | ||
220 | python-versions = ">=3.6" | ||
221 | |||
222 | [[package]] | ||
223 | name = "pyusb" | ||
224 | version = "1.2.1" | ||
225 | description = "Python USB access module" | ||
226 | category = "main" | ||
227 | optional = false | ||
228 | python-versions = ">=3.6.0" | ||
229 | |||
230 | [[package]] | ||
231 | name = "qmk" | ||
232 | version = "1.0.0" | ||
233 | description = "A program to help users work with QMK Firmware." | ||
234 | category = "main" | ||
235 | optional = false | ||
236 | python-versions = "*" | ||
237 | |||
238 | [package.dependencies] | ||
239 | hid = "*" | ||
240 | hjson = "*" | ||
241 | jsonschema = ">=3" | ||
242 | milc = ">=1.4.2" | ||
243 | pygments = "*" | ||
244 | pyusb = "*" | ||
245 | qmk-dotty-dict = "*" | ||
246 | |||
247 | [[package]] | ||
248 | name = "qmk-dotty-dict" | ||
249 | version = "1.3.0.post1" | ||
250 | description = "Dictionary wrapper for quick access to deeply nested keys." | ||
251 | category = "main" | ||
252 | optional = false | ||
253 | python-versions = "*" | ||
254 | |||
255 | [[package]] | ||
256 | name = "six" | ||
257 | version = "1.16.0" | ||
258 | description = "Python 2 and 3 compatibility utilities" | ||
259 | category = "main" | ||
260 | optional = false | ||
261 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" | ||
262 | |||
263 | [[package]] | ||
264 | name = "spinners" | ||
265 | version = "0.0.24" | ||
266 | description = "Spinners for terminals" | ||
267 | category = "main" | ||
268 | optional = false | ||
269 | python-versions = "*" | ||
270 | |||
271 | [[package]] | ||
272 | name = "termcolor" | ||
273 | version = "1.1.0" | ||
274 | description = "ANSII Color formatting for output in terminal." | ||
275 | category = "main" | ||
276 | optional = false | ||
277 | python-versions = "*" | ||
278 | |||
279 | [[package]] | ||
280 | name = "yapf" | ||
281 | version = "0.31.0" | ||
282 | description = "A formatter for Python code." | ||
283 | category = "dev" | ||
284 | optional = false | ||
285 | python-versions = "*" | ||
286 | |||
287 | [metadata] | ||
288 | lock-version = "1.1" | ||
289 | python-versions = "^3.8" | ||
290 | content-hash = "468ae51aaddfe2ce62938f131c688bbc447e41608d4dd7d30db36391c38bda20" | ||
291 | |||
292 | [metadata.files] | ||
293 | appdirs = [ | ||
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 | ] | ||
297 | argcomplete = [ | ||
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 | ] | ||
301 | attrs = [ | ||
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 | ] | ||
305 | colorama = [ | ||
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 | ] | ||
309 | coverage = [ | ||
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 | ] | ||
363 | flake8 = [ | ||
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 | ] | ||
367 | flake8-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 | ] | ||
371 | halo = [ | ||
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 | ] | ||
375 | hid = [ | ||
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 | ] | ||
379 | hjson = [ | ||
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 | ] | ||
383 | jsonschema = [ | ||
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 | ] | ||
387 | log-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 | ] | ||
391 | mccabe = [ | ||
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 | ] | ||
395 | milc = [ | ||
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 | ] | ||
399 | nose2 = [ | ||
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 | ] | ||
403 | pep8-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 | ] | ||
407 | pycodestyle = [ | ||
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 | ] | ||
411 | pyflakes = [ | ||
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 | ] | ||
415 | pygments = [ | ||
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 | ] | ||
419 | pyrsistent = [ | ||
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 | ] | ||
442 | pyusb = [ | ||
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 | ] | ||
446 | qmk = [ | ||
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 | ] | ||
450 | qmk-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 | ] | ||
454 | six = [ | ||
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 | ] | ||
458 | spinners = [ | ||
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 | ] | ||
462 | termcolor = [ | ||
463 | {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, | ||
464 | ] | ||
465 | yapf = [ | ||
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] | ||
5 | name = "qmk_firmware" | ||
6 | version = "0.1.0" | ||
7 | description = "" | ||
8 | authors = [] | ||
9 | |||
10 | [tool.poetry.dependencies] | ||
11 | python = "^3.8" | ||
12 | appdirs = "*" | ||
13 | argcomplete = "*" | ||
14 | colorama = "*" | ||
15 | hid = "*" | ||
16 | hjson = "*" | ||
17 | jsonschema = ">=3" | ||
18 | milc = ">=1.4.2" | ||
19 | Pygments = "*" | ||
20 | pyusb = "*" | ||
21 | qmk-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. | ||
26 | qmk = "*" | ||
27 | |||
28 | [tool.poetry.dev-dependencies] | ||
29 | nose2 = "*" | ||
30 | flake8 = "*" | ||
31 | pep8-naming = "*" | ||
32 | yapf = "*" | ||
33 | |||
34 | [build-system] | ||
35 | requires = ["poetry-core>=1.0.0"] | ||
36 | build-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 | |||
3 | let | ||
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 | |||
173 | in | ||
174 | mkSources (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 | |||
3 | QMK_FIRMWARE_DIR=$(cd -P -- "$(dirname -- "$0")/.." >/dev/null && pwd -P) | ||
4 | QMK_FIRMWARE_UTIL_DIR=$QMK_FIRMWARE_DIR/util | ||
5 | if [ "$1" = "-y" ]; then | ||
6 | SKIP_PROMPT='-y' | ||
7 | MSYS2_CONFIRM='--noconfirm' | ||
8 | else | ||
9 | SKIP_PROMPT='' | ||
10 | MSYS2_CONFIRM='' | ||
11 | fi | ||
12 | |||
13 | case $(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;; | ||
67 | esac | ||
68 | |||
69 | if type _qmk_install_prepare &>/dev/null; then | ||
70 | _qmk_install_prepare || exit 1 | ||
71 | fi | ||
72 | |||
73 | _qmk_install | ||
74 | |||
75 | if type _qmk_install_bootloadhid &>/dev/null; then | ||
76 | _qmk_install_bootloadhid | ||
77 | fi | ||
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 | ||
2 | eval "$(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. | ||
7 | echo_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/ | ||
15 | if [ ! -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 | ||
19 | fi | ||
20 | |||
21 | # Set the inplace editing parameter for sed. | ||
22 | # macOS/BSD sed expects a file extension immediately following -i. | ||
23 | set_sed_i() { | ||
24 | sed_i=(-i) | ||
25 | |||
26 | case $(uname -a) in | ||
27 | *Darwin*) sed_i=(-i "") | ||
28 | esac | ||
29 | } | ||
30 | set_sed_i | ||
31 | |||
32 | # Exclude keyamps/ directories | ||
33 | files=$(find keyboards -type f -name 'rules.mk' -not \( -path '*/keymaps*' -prune \)) | ||
34 | |||
35 | # Edit rules.mk files | ||
36 | for file in $files; do | ||
37 | sed "${sed_i[@]}" -e "s/(+[0-9].*)$//g" "$file" | ||
38 | done | ||
39 | |||
40 | echo "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 | |||
19 | import wave, struct, sys | ||
20 | |||
21 | waveFile = wave.open(sys.argv[1], 'r') | ||
22 | # print(str(waveFile.getparams())) | ||
23 | # sys.exit() | ||
24 | |||
25 | if (waveFile.getsampwidth() != 2): | ||
26 | raise(Exception("This script currently only works with 16bit audio files")) | ||
27 | |||
28 | length = waveFile.getnframes() | ||
29 | out = "#define DAC_SAMPLE_CUSTOM_LENGTH " + str(length) + "\n\n" | ||
30 | out += "static const dacsample_t dac_sample_custom[" + str(length) + "] = {" | ||
31 | for 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)) + ", " | ||
37 | out = out[:-2] | ||
38 | out += "\n};" | ||
39 | print(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 | |||
6 | set -eEuo pipefail | ||
7 | |||
8 | job_count=$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 2) | ||
9 | source_ref="0.11.0" | ||
10 | dest_ref="develop" | ||
11 | ignore_ref="master" | ||
12 | unset skip_zero | ||
13 | |||
14 | function 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 | |||
25 | if [[ ${#} -eq 0 ]]; then | ||
26 | usage | ||
27 | fi | ||
28 | |||
29 | while 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 | ||
39 | done | ||
40 | |||
41 | # Work out the target board | ||
42 | shift $((OPTIND-1)) | ||
43 | keyboard_target=$1 | ||
44 | |||
45 | last_size=0 | ||
46 | last_line="" | ||
47 | function 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. | ||
79 | build_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 | |||
19 | from __future__ import print_function | ||
20 | |||
21 | import argparse | ||
22 | from struct import pack, unpack | ||
23 | import os, sys | ||
24 | |||
25 | MAGIC_FEEA = '\xea\xff\xfe\xff' | ||
26 | |||
27 | MAGIC_FEE9 = '\x16\x01' | ||
28 | EMPTY_WORD = '\xff\xff' | ||
29 | WORD_ENCODING = 0x8000 | ||
30 | VALUE_NEXT = 0x6000 | ||
31 | VALUE_RESERVED = 0x4000 | ||
32 | VALUE_ENCODED = 0x2000 | ||
33 | BYTE_RANGE = 0x80 | ||
34 | |||
35 | CHUNK_SIZE = 1024 | ||
36 | |||
37 | STRUCT_FMTS = { | ||
38 | 1: 'B', | ||
39 | 2: 'H', | ||
40 | 4: 'I' | ||
41 | } | ||
42 | PRINTABLE='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ' | ||
43 | |||
44 | EECONFIG_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 | ] | ||
64 | VIABASE_V1 = 35 | ||
65 | |||
66 | VERBOSE = False | ||
67 | |||
68 | def 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 | |||
90 | def 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 | |||
105 | def 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 | |||
173 | def 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 | |||
215 | def 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 | |||
222 | def 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 | |||
276 | def 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 | |||
309 | def main(): | ||
310 | global VERBOSE | ||
311 | kwargs = vars(parseArgs()) | ||
312 | VERBOSE = kwargs.pop('verbose') | ||
313 | return decodeSTM32Eeprom(**kwargs) | ||
314 | |||
315 | if __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 | ||
3 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2fef", TAG+="uaccess" | ||
4 | ### ATmega32U2 | ||
5 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff0", TAG+="uaccess" | ||
6 | ### ATmega16U4 | ||
7 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff3", TAG+="uaccess" | ||
8 | ### ATmega32U4 | ||
9 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff4", TAG+="uaccess" | ||
10 | ### AT90USB64 | ||
11 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff9", TAG+="uaccess" | ||
12 | ### AT90USB162 | ||
13 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ffa", TAG+="uaccess" | ||
14 | ### AT90USB128 | ||
15 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ffb", TAG+="uaccess" | ||
16 | |||
17 | # Input Club | ||
18 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="1c11", ATTRS{idProduct}=="b007", TAG+="uaccess" | ||
19 | |||
20 | # STM32duino | ||
21 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="1eaf", ATTRS{idProduct}=="0003", TAG+="uaccess" | ||
22 | # STM32 DFU | ||
23 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", TAG+="uaccess" | ||
24 | |||
25 | # BootloadHID | ||
26 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05df", TAG+="uaccess" | ||
27 | |||
28 | # USBAspLoader | ||
29 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05dc", TAG+="uaccess" | ||
30 | |||
31 | # ModemManager should ignore the following devices | ||
32 | # Atmel SAM-BA (Massdrop) | ||
33 | SUBSYSTEMS=="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 | ||
38 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2302", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" | ||
39 | ## Spark Fun Electronics | ||
40 | ### Pro Micro 3V3/8MHz | ||
41 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9203", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" | ||
42 | ### Pro Micro 5V/16MHz | ||
43 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9205", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" | ||
44 | ### LilyPad 3V3/8MHz (and some Pro Micro clones) | ||
45 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9207", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" | ||
46 | ## Pololu Electronics | ||
47 | ### A-Star 32U4 | ||
48 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="1ffb", ATTRS{idProduct}=="0101", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" | ||
49 | ## Arduino SA | ||
50 | ### Leonardo | ||
51 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0036", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" | ||
52 | ### Micro | ||
53 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0037", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" | ||
54 | ## Adafruit Industries LLC | ||
55 | ### Feather 32U4 | ||
56 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="239a", ATTRS{idProduct}=="000c", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" | ||
57 | ### ItsyBitsy 32U4 3V3/8MHz | ||
58 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="239a", ATTRS{idProduct}=="000d", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" | ||
59 | ### ItsyBitsy 32U4 5V/16MHz | ||
60 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="239a", ATTRS{idProduct}=="000e", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" | ||
61 | ## dog hunter AG | ||
62 | ### Leonardo | ||
63 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="0036", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" | ||
64 | ### Micro | ||
65 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="0037", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" | ||
66 | |||
67 | # hid_listen | ||
68 | KERNEL=="hidraw*", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl" | ||
69 | |||
70 | # hid bootloaders | ||
71 | ## QMK HID | ||
72 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2067", TAG+="uaccess" | ||
73 | ## PJRC's HalfKay | ||
74 | SUBSYSTEMS=="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 | ||
2 | import sys | ||
3 | import struct | ||
4 | import subprocess | ||
5 | import re | ||
6 | import os | ||
7 | import os.path | ||
8 | import argparse | ||
9 | |||
10 | |||
11 | UF2_MAGIC_START0 = 0x0A324655 # "UF2\n" | ||
12 | UF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected | ||
13 | UF2_MAGIC_END = 0x0AB16F30 # Ditto | ||
14 | |||
15 | families = { | ||
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 | |||
43 | INFO_FILE = "/INFO_UF2.TXT" | ||
44 | |||
45 | appstartaddr = 0x2000 | ||
46 | familyid = 0x0 | ||
47 | |||
48 | |||
49 | def 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 | |||
53 | def 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 | |||
62 | def 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 | |||
98 | def 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 | |||
108 | def 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 | |||
131 | class 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 | |||
150 | def 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 | |||
190 | def to_str(b): | ||
191 | return b.decode("utf-8") | ||
192 | |||
193 | def 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 | |||
224 | def 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 | |||
230 | def list_drives(): | ||
231 | for d in get_drives(): | ||
232 | print(d, board_id(d)) | ||
233 | |||
234 | |||
235 | def 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 | |||
241 | def 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 | |||
318 | if __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 | ||
7 | chibios_branches="trunk stable_20.3.x stable_21.6.x" | ||
8 | |||
9 | # The ChibiOS tags to mirror | ||
10 | chibios_tags="ver20.3.1 ver20.3.2 ver20.3.3 ver20.3.4 ver21.6.0" | ||
11 | |||
12 | # The ChibiOS-Contrib branches to mirror | ||
13 | contrib_branches="chibios-20.3.x chibios-21.6.x" | ||
14 | |||
15 | ################################ | ||
16 | # Actions | ||
17 | |||
18 | set -eEuo pipefail | ||
19 | umask 022 | ||
20 | |||
21 | this_script="$(realpath "${BASH_SOURCE[0]}")" | ||
22 | script_dir="$(realpath "$(dirname "$this_script")")" | ||
23 | qmk_firmware_dir="$(realpath "$script_dir/../")" | ||
24 | chibios_dir="$qmk_firmware_dir/lib/chibios" | ||
25 | contrib_dir="$qmk_firmware_dir/lib/chibios-contrib" | ||
26 | |||
27 | chibios_git_location=$(realpath "$chibios_dir/$(cat "$chibios_dir/.git" | awk '/gitdir:/ {print $2}')") | ||
28 | chibios_git_config=$(realpath "$chibios_git_location/config") | ||
29 | contrib_git_location=$(realpath "$contrib_dir/$(cat "$contrib_dir/.git" | awk '/gitdir:/ {print $2}')") | ||
30 | contrib_git_config=$(realpath "$contrib_git_location/config") | ||
31 | |||
32 | cd "$chibios_dir" | ||
33 | |||
34 | if [[ -z "$(cat "$chibios_git_config" | grep '\[svn-remote "svn"\]')" ]] ; then | ||
35 | git svn init --stdlayout --prefix='svn/' http://svn.osdn.net/svnroot/chibios/ | ||
36 | fi | ||
37 | |||
38 | if [[ -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 | ||
41 | else | ||
42 | git remote set-url qmk [email protected]:qmk/ChibiOS.git | ||
43 | git remote set-url qmk [email protected]:qmk/ChibiOS.git --push | ||
44 | fi | ||
45 | |||
46 | echo "Updating remotes..." | ||
47 | git fetch --all --tags --prune | ||
48 | |||
49 | echo "Fetching latest from subversion..." | ||
50 | git svn fetch | ||
51 | |||
52 | echo "Updating ChibiOS branches..." | ||
53 | for 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 | ||
57 | done | ||
58 | |||
59 | echo "Updating ChibiOS tags..." | ||
60 | for 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 | ||
64 | done | ||
65 | |||
66 | cd "$contrib_dir" | ||
67 | |||
68 | if [[ -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 | ||
71 | else | ||
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 | ||
74 | fi | ||
75 | |||
76 | if [[ -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 | ||
79 | else | ||
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 | ||
82 | fi | ||
83 | |||
84 | echo "Updating remotes..." | ||
85 | git fetch --all --tags --prune | ||
86 | |||
87 | echo "Updating ChibiOS-Contrib branches..." | ||
88 | for 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 | ||
92 | done | ||
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++ | ||
2 | CC = gcc | ||
3 | |||
4 | # compiler flags: | ||
5 | # -g adds debugging information to the executable file | ||
6 | # -Wall turns on most, but not all, compiler warnings | ||
7 | CFLAGS = -g -Wall | ||
8 | |||
9 | # the build target executable: | ||
10 | TARGET = usb_detach | ||
11 | |||
12 | all: $(TARGET) | ||
13 | |||
14 | $(TARGET): $(TARGET).c | ||
15 | $(CC) $(CFLAGS) -o $(TARGET) $(TARGET).c | ||
16 | |||
17 | clean: | ||
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 | |||
3 | When 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. | ||
4 | This program can force Linux to give up a device, so that the programming script can reset it. | ||
5 | |||
6 | ## To compile: | ||
7 | ```bash | ||
8 | make clean && make | ||
9 | ``` | ||
10 | |||
11 | ## To run: | ||
12 | 1. Use `lsusb` to discover the Bus and Device numbers for your keyboard. | ||
13 | 2. Run the program: `sudo ./usb_detach /dev/bus/usb/<BUS>/<DEVICE>`. | ||
14 | 3. 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 | |||
10 | int 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 @@ | |||
1 | FROM qmkfm/qmk_cli | ||
2 | |||
3 | # Basic upgrades; install sudo and SSH. | ||
4 | RUN apt-get update && apt-get install --no-install-recommends -y \ | ||
5 | sudo \ | ||
6 | openssh-server \ | ||
7 | && rm -rf /var/lib/apt/lists/* | ||
8 | RUN mkdir /var/run/sshd | ||
9 | RUN sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config | ||
10 | RUN 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. | ||
14 | RUN rm /usr/sbin/policy-rc.d | ||
15 | |||
16 | # Add the Vagrant user and necessary passwords. | ||
17 | RUN groupadd vagrant | ||
18 | RUN useradd -c "Vagrant" -g vagrant -d /home/vagrant -m -s /bin/bash vagrant | ||
19 | RUN echo 'root:vagrant' | chpasswd | ||
20 | RUN echo 'vagrant:vagrant' | chpasswd | ||
21 | |||
22 | # Allow the vagrant user to use sudo without a password. | ||
23 | RUN echo 'vagrant ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/vagrant | ||
24 | |||
25 | # Install Vagrant's insecure public key so provisioning and 'vagrant ssh' work. | ||
26 | RUN mkdir /home/vagrant/.ssh | ||
27 | ADD https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub /home/vagrant/.ssh/authorized_keys | ||
28 | RUN chmod 0600 /home/vagrant/.ssh/authorized_keys | ||
29 | RUN chown -R vagrant:vagrant /home/vagrant/.ssh | ||
30 | RUN chmod 0700 /home/vagrant/.ssh | ||
31 | |||
32 | EXPOSE 22 | ||
33 | CMD ["/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 | ||
4 | Vagrant-friendly `qmkfm/qmk_cli`. | ||
5 | |||
6 | In 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 | |||
19 | import wave, struct, sys | ||
20 | |||
21 | waveFile = wave.open(sys.argv[1], 'r') | ||
22 | |||
23 | length = waveFile.getnframes() | ||
24 | out = "#define DAC_WAVETABLE_CUSTOM_LENGTH " + str(int(length / 256)) + "\n\n" | ||
25 | out += "static const dacsample_t dac_wavetable_custom[" + str(int(length / 256)) + "][256] = {" | ||
26 | for 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 }," | ||
38 | out = out[:-1] | ||
39 | out += "\n};" | ||
40 | print(out) | ||