summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Didron <fdidron@users.noreply.github.com>2019-12-06 09:25:39 +0900
committerGitHub <noreply@github.com>2019-12-06 09:25:39 +0900
commit13e1dd3d53a75840cce9c7fa52fd6d8d26a36a9a (patch)
treea51a6bd431d848279d8c03357ac05ecfb25312ce
parent1d600f09a8b813f77d8b6ff4cc55484b7154ae25 (diff)
parent038cef3054a816021a797c217d147057c8edd926 (diff)
Merge pull request #222 from zsa/staging
Firmware 15
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules2
-rw-r--r--Makefile13
-rwxr-xr-xbin/qmk94
l---------bin/qmk-compile-json1
l---------bin/qmk-doctor1
l---------bin/qmk-hello1
l---------bin/qmk-json-keymap1
-rw-r--r--build_json.mk2
-rw-r--r--common_features.mk36
-rw-r--r--drivers/arm/ws2812.c1
-rw-r--r--drivers/arm/ws2812_pwm.c1
-rw-r--r--drivers/arm/ws2812_spi.c1
-rw-r--r--drivers/avr/ws2812.c33
-rw-r--r--drivers/avr/ws2812.h35
-rw-r--r--drivers/avr/ws2812_i2c.c31
-rw-r--r--drivers/haptic/DRV2605L.c11
-rw-r--r--drivers/haptic/DRV2605L.h2
-rw-r--r--drivers/haptic/haptic.c67
-rw-r--r--drivers/haptic/haptic.h17
-rw-r--r--keyboards/ergodox_ez/config.h5
-rw-r--r--keyboards/ergodox_ez/ergodox_ez.c65
-rw-r--r--keyboards/ergodox_ez/keymaps/webusb/default.png.md1
-rw-r--r--keyboards/ergodox_ez/keymaps/webusb/default_highres.png.md1
-rw-r--r--keyboards/ergodox_ez/keymaps/webusb/keymap.c147
-rw-r--r--keyboards/ergodox_ez/keymaps/webusb/readme.md15
-rw-r--r--keyboards/ergodox_ez/keymaps/webusb/rules.mk1
-rw-r--r--keyboards/ergodox_ez/matrix.c39
-rw-r--r--keyboards/planck/ez/config.h5
-rw-r--r--keyboards/planck/ez/ez.c59
-rw-r--r--keyboards/planck/keymaps/webusb/config.h39
-rw-r--r--keyboards/planck/keymaps/webusb/keymap.c136
-rw-r--r--keyboards/planck/keymaps/webusb/readme.md2
-rw-r--r--keyboards/planck/keymaps/webusb/rules.mk3
m---------lib/lufa0
-rw-r--r--lib/python/milc.py120
-rw-r--r--lib/python/qmk/cli/__init__.py14
-rw-r--r--lib/python/qmk/cli/cformat.py6
-rwxr-xr-xlib/python/qmk/cli/compile.py10
-rw-r--r--lib/python/qmk/cli/config.py96
-rwxr-xr-xlib/python/qmk/cli/doctor.py11
-rwxr-xr-xlib/python/qmk/cli/hello.py6
-rw-r--r--lib/python/qmk/cli/json/__init__.py5
-rwxr-xr-xlib/python/qmk/cli/json/keymap.py20
-rw-r--r--lib/python/qmk/cli/list/__init__.py1
-rw-r--r--lib/python/qmk/cli/list/keyboards.py26
-rw-r--r--lib/python/qmk/cli/new/__init__.py1
-rwxr-xr-xlib/python/qmk/cli/new/keymap.py21
-rwxr-xr-xlib/python/qmk/cli/pyformat.py5
-rw-r--r--lib/python/qmk/cli/pytest.py (renamed from lib/python/qmk/cli/nose2.py)8
-rw-r--r--lib/python/qmk/path.py1
-rw-r--r--lib/python/qmk/tests/test_cli_commands.py47
-rw-r--r--message.mk5
-rw-r--r--quantum/backlight/backlight.c (renamed from tmk_core/common/backlight.c)0
-rw-r--r--quantum/backlight/backlight.h (renamed from tmk_core/common/backlight.h)0
-rw-r--r--quantum/color.c10
-rw-r--r--quantum/config_common.h21
-rw-r--r--quantum/encoder.c42
-rw-r--r--quantum/keymap_common.c5
-rw-r--r--quantum/keymap_extras/keymap_italian.h55
-rw-r--r--quantum/keymap_extras/keymap_italian_osx_ansi.h113
-rw-r--r--quantum/keymap_extras/keymap_italian_osx_iso.h113
-rw-r--r--quantum/led_tables.c44
-rw-r--r--quantum/process_keycode/process_tap_dance.c2
-rw-r--r--quantum/process_keycode/process_tap_dance.h10
-rw-r--r--quantum/quantum.c86
-rw-r--r--quantum/quantum_keycodes.h6
-rw-r--r--quantum/rgb_matrix_drivers.c22
-rw-r--r--quantum/rgblight.c3
-rw-r--r--quantum/template/ps2avrgb/rules.mk6
-rw-r--r--quantum/template/ps2avrgb/template.c44
-rw-r--r--requirements.txt2
-rw-r--r--shell.nix2
-rw-r--r--tmk_core/common.mk9
-rw-r--r--tmk_core/common/action.c35
-rw-r--r--tmk_core/common/action.h2
-rw-r--r--tmk_core/common/avr/suspend.c5
-rw-r--r--tmk_core/common/chibios/printf.c9
-rw-r--r--tmk_core/common/chibios/suspend.c5
-rw-r--r--tmk_core/common/command.c5
-rw-r--r--tmk_core/common/keyboard.c28
-rw-r--r--tmk_core/common/timer.h4
-rw-r--r--tmk_core/common/util.h8
-rw-r--r--tmk_core/common/webusb.c72
-rw-r--r--tmk_core/common/webusb.h44
-rw-r--r--tmk_core/protocol/chibios/main.c7
-rw-r--r--tmk_core/protocol/chibios/usb_main.c78
-rw-r--r--tmk_core/protocol/lufa/lufa.c118
-rw-r--r--tmk_core/protocol/usb_descriptor.c74
-rw-r--r--tmk_core/protocol/usb_descriptor.h37
-rw-r--r--tmk_core/protocol/vusb/main.c9
-rw-r--r--tmk_core/protocol/webusb_descriptor.h264
-rw-r--r--util/drivers.txt37
-rwxr-xr-xutil/linux_install.sh26
94 files changed, 2277 insertions, 458 deletions
diff --git a/.gitignore b/.gitignore
index 2611c99bf5..ff7e80b074 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,7 +25,7 @@ quantum/version.h
CMakeLists.txt
cmake-build-debug
doxygen/
-.DS_STORE
+.DS_Store
/util/wsl_downloaded
/util/win_downloaded
/users/
diff --git a/.gitmodules b/.gitmodules
index f2e96bedc8..4137c84d39 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -13,4 +13,4 @@
url = https://github.com/google/googletest
[submodule "lib/lufa"]
path = lib/lufa
- url = https://github.com/qmk/lufa
+ url = https://github.com/zsa/lufa
diff --git a/Makefile b/Makefile
index 37f5f23dbe..508691f45f 100644
--- a/Makefile
+++ b/Makefile
@@ -371,6 +371,9 @@ define PARSE_KEYBOARD
# The same if all was specified
else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all),true)
$$(eval $$(call PARSE_ALL_KEYMAPS))
+ # List all keymaps for the given keyboard
+ else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,list-keymaps),true)
+ $$(eval $$(call LIST_ALL_KEYMAPS))
# Try to match the specified keyamp with the list of known keymaps
else ifeq ($$(call TRY_TO_MATCH_RULE_FROM_LIST,$$(KEYMAPS)),true)
$$(eval $$(call PARSE_KEYMAP,$$(MATCHED_ITEM)))
@@ -407,6 +410,16 @@ endef
# endif
# endef
+# Prints a list of all known keymaps for the given keyboard
+define LIST_ALL_KEYMAPS
+ COMMAND_true_LIST_KEYMAPS := \
+ printf "$$(KEYMAPS)\n";
+ COMMAND_false_LIST_KEYMAPS := \
+ printf "$$(MSG_AVAILABLE_KEYMAPS)\n"; \
+ printf "$$(KEYMAPS)\n";
+ COMMANDS += LIST_KEYMAPS
+endef
+
# $1 Keymap
# This is the meat of compiling a keyboard, when entering this, everything is known
# keyboard, subproject, and keymap
diff --git a/bin/qmk b/bin/qmk
index 06eed6359c..876473fa43 100755
--- a/bin/qmk
+++ b/bin/qmk
@@ -4,10 +4,8 @@
import os
import subprocess
import sys
-from glob import glob
-from time import strftime
-from importlib import import_module
from importlib.util import find_spec
+from time import strftime
# Add the QMK python libs to our path
script_dir = os.path.dirname(os.path.realpath(__file__))
@@ -15,12 +13,8 @@ qmk_dir = os.path.abspath(os.path.join(script_dir, '..'))
python_lib_dir = os.path.abspath(os.path.join(qmk_dir, 'lib', 'python'))
sys.path.append(python_lib_dir)
-# Change to the root of our checkout
-os.environ['ORIG_CWD'] = os.getcwd()
-os.chdir(qmk_dir)
-
# Make sure our modules have been setup
-with open('requirements.txt', 'r') as fd:
+with open(os.path.join(qmk_dir, 'requirements.txt'), 'r') as fd:
for line in fd.readlines():
line = line.strip().replace('<', '=').replace('>', '=')
@@ -32,72 +26,58 @@ with open('requirements.txt', 'r') as fd:
module = line.split('=')[0] if '=' in line else line
if not find_spec(module):
- print('Your QMK build environment is not fully setup!\n')
- print('Please run `./util/qmk_install.sh` to setup QMK.')
+ print('Could not find module %s!', module)
+ print('Please run `pip3 install -r requirements.txt` to install the python dependencies.')
exit(255)
# Figure out our version
+# TODO(skullydazed/anyone): Find a method that doesn't involve git. This is slow in docker and on windows.
command = ['git', 'describe', '--abbrev=6', '--dirty', '--always', '--tags']
-result = subprocess.run(command, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+result = subprocess.run(command, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if result.returncode == 0:
- os.environ['QMK_VERSION'] = 'QMK ' + result.stdout.strip()
+ os.environ['QMK_VERSION'] = result.stdout.strip()
else:
- os.environ['QMK_VERSION'] = 'QMK ' + strftime('%Y-%m-%d-%H:%M:%S')
+ os.environ['QMK_VERSION'] = 'nogit-' + strftime('%Y-%m-%d-%H:%M:%S') + '-dirty'
# Setup the CLI
import milc
-milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}ψ{style_reset_all}'
-
-# If we were invoked as `qmk <cmd>` massage sys.argv into `qmk-<cmd>`.
-# This means we can't accept arguments to the qmk script itself.
-script_name = os.path.basename(sys.argv[0])
-if script_name == 'qmk':
- if len(sys.argv) == 1:
- milc.cli.log.error('No subcommand specified!\n')
-
- if len(sys.argv) == 1 or sys.argv[1] in ['-h', '--help']:
- milc.cli.echo('usage: qmk <subcommand> [...]')
- milc.cli.echo('\nsubcommands:')
- subcommands = glob(os.path.join(qmk_dir, 'bin', 'qmk-*'))
- for subcommand in sorted(subcommands):
- subcommand = os.path.basename(subcommand).split('-', 1)[1]
- milc.cli.echo('\t%s', subcommand)
- milc.cli.echo('\nqmk <subcommand> --help for more information')
- exit(1)
- if sys.argv[1] in ['-V', '--version']:
- milc.cli.echo(os.environ['QMK_VERSION'])
- exit(0)
+milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}Ψ{style_reset_all}'
- sys.argv[0] = script_name = '-'.join((script_name, sys.argv[1]))
- del sys.argv[1]
-# Look for which module to import
-if script_name == 'qmk':
- milc.cli.print_help()
- exit(0)
-elif not script_name.startswith('qmk-'):
- milc.cli.log.error('Invalid symlink, must start with "qmk-": %s', script_name)
-else:
- subcommand = script_name.replace('-', '.').replace('_', '.').split('.')
- subcommand.insert(1, 'cli')
- subcommand = '.'.join(subcommand)
+@milc.cli.entrypoint('QMK Helper Script')
+def qmk_main(cli):
+ """The function that gets run when no subcommand is provided.
+ """
+ cli.print_help()
- try:
- import_module(subcommand)
- except ModuleNotFoundError as e:
- if e.__class__.__name__ != subcommand:
- raise
- milc.cli.log.error('Invalid subcommand! Could not import %s.', subcommand)
- exit(1)
+def main():
+ """Setup our environment and then call the CLI entrypoint.
+ """
+ # Change to the root of our checkout
+ os.environ['ORIG_CWD'] = os.getcwd()
+ os.chdir(qmk_dir)
-if __name__ == '__main__':
+ # Import the subcommands
+ import qmk.cli
+
+ # Execute
return_code = milc.cli()
+
if return_code is False:
exit(1)
- elif return_code is not True and isinstance(return_code, int) and return_code < 256:
+
+ elif return_code is not True and isinstance(return_code, int):
+ if return_code < 0 or return_code > 255:
+ milc.cli.log.error('Invalid return_code: %d', return_code)
+ exit(255)
+
exit(return_code)
- else:
- exit(0)
+
+ exit(0)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/bin/qmk-compile-json b/bin/qmk-compile-json
deleted file mode 120000
index c92dce8a10..0000000000
--- a/bin/qmk-compile-json
+++ /dev/null
@@ -1 +0,0 @@
-qmk \ No newline at end of file
diff --git a/bin/qmk-doctor b/bin/qmk-doctor
deleted file mode 120000
index c92dce8a10..0000000000
--- a/bin/qmk-doctor
+++ /dev/null
@@ -1 +0,0 @@
-qmk \ No newline at end of file
diff --git a/bin/qmk-hello b/bin/qmk-hello
deleted file mode 120000
index c92dce8a10..0000000000
--- a/bin/qmk-hello
+++ /dev/null
@@ -1 +0,0 @@
-qmk \ No newline at end of file
diff --git a/bin/qmk-json-keymap b/bin/qmk-json-keymap
deleted file mode 120000
index c92dce8a10..0000000000
--- a/bin/qmk-json-keymap
+++ /dev/null
@@ -1 +0,0 @@
-qmk \ No newline at end of file
diff --git a/build_json.mk b/build_json.mk
index 8820a8f4a6..e2a33e3b6a 100644
--- a/build_json.mk
+++ b/build_json.mk
@@ -23,5 +23,5 @@ endif
# Generate the keymap.c
ifneq ("$(KEYMAP_JSON)","")
- _ = $(shell test -e $(KEYMAP_C) || bin/qmk-json-keymap $(KEYMAP_JSON) -o $(KEYMAP_C))
+ _ = $(shell test -e $(KEYMAP_C) || bin/qmk json-keymap $(KEYMAP_JSON) -o $(KEYMAP_C))
endif
diff --git a/common_features.mk b/common_features.mk
index 3bc6f1c73b..401c8f43aa 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -112,7 +112,7 @@ ifeq ($(strip $(RGBLIGHT_ENABLE)), yes)
ifeq ($(strip $(RGBLIGHT_CUSTOM_DRIVER)), yes)
OPT_DEFS += -DRGBLIGHT_CUSTOM_DRIVER
else
- SRC += ws2812.c
+ WS2812_DRIVER_REQUIRED = yes
endif
endif
@@ -176,7 +176,7 @@ endif
ifeq ($(strip $(RGB_MATRIX_ENABLE)), WS2812)
OPT_DEFS += -DWS2812
- SRC += ws2812.c
+ WS2812_DRIVER_REQUIRED = yes
endif
ifeq ($(strip $(RGB_MATRIX_CUSTOM_KB)), yes)
@@ -233,11 +233,37 @@ ifeq ($(strip $(BACKLIGHT_ENABLE)), yes)
ifeq ($(strip $(VISUALIZER_ENABLE)), yes)
CIE1931_CURVE = yes
endif
- ifeq ($(strip $(BACKLIGHT_CUSTOM_DRIVER)), yes)
+
+
+ COMMON_VPATH += $(QUANTUM_DIR)/backlight
+ SRC += $(QUANTUM_DIR)/backlight/backlight.c
+ OPT_DEFS += -DBACKLIGHT_ENABLE
+
+ ifeq ($(strip $(BACKLIGHT_ENABLE)), custom)
OPT_DEFS += -DBACKLIGHT_CUSTOM_DRIVER
endif
endif
+VALID_WS2812_DRIVER_TYPES := bitbang pwm spi i2c
+
+WS2812_DRIVER ?= bitbang
+ifeq ($(strip $(WS2812_DRIVER_REQUIRED)), yes)
+ ifeq ($(filter $(WS2812_DRIVER),$(VALID_WS2812_DRIVER_TYPES)),)
+ $(error WS2812_DRIVER="$(WS2812_DRIVER)" is not a valid WS2812 driver)
+ endif
+
+ ifeq ($(strip $(WS2812_DRIVER)), bitbang)
+ SRC += ws2812.c
+ else
+ SRC += ws2812_$(strip $(WS2812_DRIVER)).c
+ endif
+
+ # add extra deps
+ ifeq ($(strip $(WS2812_DRIVER)), i2c)
+ QUANTUM_LIB_SRC += i2c_master.c
+ endif
+endif
+
ifeq ($(strip $(CIE1931_CURVE)), yes)
OPT_DEFS += -DUSE_CIE1931_CURVE
LED_TABLES = yes
@@ -262,6 +288,10 @@ ifeq ($(strip $(USB_HID_ENABLE)), yes)
include $(TMK_DIR)/protocol/usb_hid.mk
endif
+ifeq ($(strip $(WEBUSB_ENABLE)), yes)
+ SRC += $(TMK_DIR)/common/webusb.c
+endif
+
ifeq ($(strip $(ENCODER_ENABLE)), yes)
SRC += $(QUANTUM_DIR)/encoder.c
OPT_DEFS += -DENCODER_ENABLE
diff --git a/drivers/arm/ws2812.c b/drivers/arm/ws2812.c
new file mode 100644
index 0000000000..2094e50098
--- /dev/null
+++ b/drivers/arm/ws2812.c
@@ -0,0 +1 @@
+#error("NOT SUPPORTED") \ No newline at end of file
diff --git a/drivers/arm/ws2812_pwm.c b/drivers/arm/ws2812_pwm.c
new file mode 100644
index 0000000000..2094e50098
--- /dev/null
+++ b/drivers/arm/ws2812_pwm.c
@@ -0,0 +1 @@
+#error("NOT SUPPORTED") \ No newline at end of file
diff --git a/drivers/arm/ws2812_spi.c b/drivers/arm/ws2812_spi.c
new file mode 100644
index 0000000000..2094e50098
--- /dev/null
+++ b/drivers/arm/ws2812_spi.c
@@ -0,0 +1 @@
+#error("NOT SUPPORTED") \ No newline at end of file
diff --git a/drivers/avr/ws2812.c b/drivers/avr/ws2812.c
index 0a02c6f7fd..5c733c4ab0 100644
--- a/drivers/avr/ws2812.c
+++ b/drivers/avr/ws2812.c
@@ -25,13 +25,17 @@
#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>
-#include "debug.h"
-#if !defined(LED_ARRAY) && defined(RGB_MATRIX_ENABLE)
-// LED color buffer
-LED_TYPE led[DRIVER_LED_TOTAL];
-# define LED_ARRAY led
-#endif
+/*
+ * Forward declare internal functions
+ *
+ * The functions take a byte-array and send to the data output as WS2812 bitstream.
+ * The length is the number of bytes to send - three per LED.
+ */
+
+void ws2812_sendarray(uint8_t *array, uint16_t length);
+void ws2812_sendarray_mask(uint8_t *array, uint16_t length, uint8_t pinmask);
+
#ifdef RGBW_BB_TWI
@@ -135,23 +139,6 @@ unsigned char I2C_Write(unsigned char c) {
#endif
-#ifdef RGB_MATRIX_ENABLE
-// Set an led in the buffer to a color
-void inline ws2812_setled(int i, uint8_t r, uint8_t g, uint8_t b) {
- led[i].r = r;
- led[i].g = g;
- led[i].b = b;
-}
-
-void ws2812_setled_all(uint8_t r, uint8_t g, uint8_t b) {
- for (int i = 0; i < sizeof(led) / sizeof(led[0]); i++) {
- led[i].r = r;
- led[i].g = g;
- led[i].b = b;
- }
-}
-#endif
-
// Setleds for standard RGB
void inline ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) {
// ws2812_setleds_pin(ledarray,leds, _BV(ws2812_pin));
diff --git a/drivers/avr/ws2812.h b/drivers/avr/ws2812.h
index a9dd897185..9652b94bbe 100644
--- a/drivers/avr/ws2812.h
+++ b/drivers/avr/ws2812.h
@@ -20,13 +20,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef LIGHT_WS2812_H_
-#define LIGHT_WS2812_H_
-
-#include <avr/io.h>
-#include <avr/interrupt.h>
-//#include "ws2812_config.h"
-//#include "i2cmaster.h"
+#pragma once
#include "quantum/color.h"
@@ -42,33 +36,6 @@
* - Send out the LED data
* - Wait 50�s to reset the LEDs
*/
-#ifdef RGB_MATRIX_ENABLE
-void ws2812_setled(int index, uint8_t r, uint8_t g, uint8_t b);
-void ws2812_setled_all(uint8_t r, uint8_t g, uint8_t b);
-#endif
-
void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds);
void ws2812_setleds_pin(LED_TYPE *ledarray, uint16_t number_of_leds, uint8_t pinmask);
void ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t number_of_leds);
-
-/*
- * Old interface / Internal functions
- *
- * The functions take a byte-array and send to the data output as WS2812 bitstream.
- * The length is the number of bytes to send - three per LED.
- */
-
-void ws2812_sendarray(uint8_t *array, uint16_t length);
-void ws2812_sendarray_mask(uint8_t *array, uint16_t length, uint8_t pinmask);
-
-/*
- * Internal defines
- */
-#ifndef CONCAT
-# define CONCAT(a, b) a##b
-#endif
-#ifndef CONCAT_EXP
-# define CONCAT_EXP(a, b) CONCAT(a, b)
-#endif
-
-#endif /* LIGHT_WS2812_H_ */
diff --git a/drivers/avr/ws2812_i2c.c b/drivers/avr/ws2812_i2c.c
new file mode 100644
index 0000000000..8525a026c7
--- /dev/null
+++ b/drivers/avr/ws2812_i2c.c
@@ -0,0 +1,31 @@
+#include "ws2812.h"
+#include "i2c_master.h"
+
+#ifndef WS2812_ADDRESS
+# define WS2812_ADDRESS 0xb0
+#endif
+
+#ifndef WS2812_TIMEOUT
+# define WS2812_TIMEOUT 100
+#endif
+
+void ws2812_init(void) { i2c_init(); }
+
+// Setleds for standard RGB
+void ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) {
+ static bool s_init = false;
+ if (!s_init) {
+ ws2812_init();
+ s_init = true;
+ }
+
+ i2c_transmit(WS2812_ADDRESS, (uint8_t *)ledarray, sizeof(LED_TYPE) * leds, WS2812_TIMEOUT);
+}
+
+// Setleds for SK6812RGBW
+void ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t leds) {
+// not supported - for now error out if its enabled
+#ifdef RGBW
+# error "RGBW not supported"
+#endif
+}
diff --git a/drivers/haptic/DRV2605L.c b/drivers/haptic/DRV2605L.c
index f74c965253..728554b01e 100644
--- a/drivers/haptic/DRV2605L.c
+++ b/drivers/haptic/DRV2605L.c
@@ -114,6 +114,17 @@ void DRV_init(void) {
DRV_write(DRV_GO, 0x01);
}
+void DRV_rtp_init(void) {
+ DRV_write(DRV_GO, 0x00);
+ DRV_write(DRV_RTP_INPUT, 20); //20 is the lowest value I've found where haptics can still be felt.
+ DRV_write(DRV_MODE, 0x05);
+ DRV_write(DRV_GO, 0x01);
+}
+
+void DRV_amplitude(uint8_t amplitude) {
+ DRV_write(DRV_RTP_INPUT, amplitude);
+}
+
void DRV_pulse(uint8_t sequence) {
DRV_write(DRV_GO, 0x00);
DRV_write(DRV_WAVEFORM_SEQ_1, sequence);
diff --git a/drivers/haptic/DRV2605L.h b/drivers/haptic/DRV2605L.h
index f550b859f7..535c777658 100644
--- a/drivers/haptic/DRV2605L.h
+++ b/drivers/haptic/DRV2605L.h
@@ -170,6 +170,8 @@
void DRV_init(void);
void DRV_write(const uint8_t drv_register, const uint8_t settings);
uint8_t DRV_read(const uint8_t regaddress);
+void DRV_rtp_init(void);
+void DRV_amplitude(const uint8_t amplitude);
void DRV_pulse(const uint8_t sequence);
typedef enum DRV_EFFECT {
diff --git a/drivers/haptic/haptic.c b/drivers/haptic/haptic.c
index ded6d8a44a..989970beee 100644
--- a/drivers/haptic/haptic.c
+++ b/drivers/haptic/haptic.c
@@ -168,6 +168,15 @@ void haptic_set_mode(uint8_t mode) {
xprintf("haptic_config.mode = %u\n", haptic_config.mode);
}
+void haptic_set_amplitude(uint8_t amp) {
+ haptic_config.amplitude = amp;
+ eeconfig_update_haptic(haptic_config.raw);
+ xprintf("haptic_config.amplitude = %u\n", haptic_config.amplitude);
+ #ifdef DRV2605L
+ DRV_amplitude(amp);
+ #endif
+}
+
void haptic_set_buzz(uint8_t buzz) {
haptic_config.buzz = buzz;
eeconfig_update_haptic(haptic_config.raw);
@@ -201,6 +210,53 @@ uint8_t haptic_get_dwell(void) {
return haptic_config.dwell;
}
+void haptic_enable_continuous(void) {
+ haptic_config.cont = 1;
+ xprintf("haptic_config.cont = %u\n", haptic_config.cont);
+ eeconfig_update_haptic(haptic_config.raw);
+ #ifdef DRV2605L
+ DRV_rtp_init();
+ #endif
+}
+
+void haptic_disable_continuous(void) {
+ haptic_config.cont = 0;
+ xprintf("haptic_config.cont = %u\n", haptic_config.cont);
+ eeconfig_update_haptic(haptic_config.raw);
+ #ifdef DRV2605L
+ DRV_write(DRV_MODE,0x00);
+ #endif
+}
+
+void haptic_toggle_continuous(void) {
+#ifdef DRV2605L
+if (haptic_config.cont) {
+ haptic_disable_continuous();
+ } else {
+ haptic_enable_continuous();
+ }
+ eeconfig_update_haptic(haptic_config.raw);
+#endif
+}
+
+
+void haptic_cont_increase(void) {
+ uint8_t amp = haptic_config.amplitude + 10;
+ if (haptic_config.amplitude >= 120) {
+ amp = 120;
+ }
+ haptic_set_amplitude(amp);
+}
+
+void haptic_cont_decrease(void) {
+ uint8_t amp = haptic_config.amplitude - 10;
+ if (haptic_config.amplitude < 20) {
+ amp = 20;
+ }
+ haptic_set_amplitude(amp);
+}
+
+
void haptic_play(void) {
#ifdef DRV2605L
uint8_t play_eff = 0;
@@ -213,6 +269,7 @@ void haptic_play(void) {
}
bool process_haptic(uint16_t keycode, keyrecord_t *record) {
+
if (keycode == HPT_ON && record->event.pressed) {
haptic_enable();
}
@@ -243,6 +300,16 @@ bool process_haptic(uint16_t keycode, keyrecord_t *record) {
if (keycode == HPT_DWLD && record->event.pressed) {
haptic_dwell_decrease();
}
+ if (keycode == HPT_CONT && record->event.pressed) {
+ haptic_toggle_continuous();
+ }
+ if (keycode == HPT_CONI && record->event.pressed) {
+ haptic_cont_increase();
+ }
+ if (keycode == HPT_COND && record->event.pressed) {
+ haptic_cont_decrease();
+ }
+
if (haptic_config.enable) {
if (record->event.pressed) {
// keypress
diff --git a/drivers/haptic/haptic.h b/drivers/haptic/haptic.h
index 8135d0d439..2f6eb31fc2 100644
--- a/drivers/haptic/haptic.h
+++ b/drivers/haptic/haptic.h
@@ -34,12 +34,14 @@
typedef union {
uint32_t raw;
struct {
- bool enable : 1;
- uint8_t feedback : 2;
- uint8_t mode : 7;
- bool buzz : 1;
- uint8_t dwell : 7;
- uint16_t reserved : 16;
+ bool enable :1;
+ uint8_t feedback :2;
+ uint8_t mode :7;
+ bool buzz :1;
+ uint8_t dwell :7;
+ bool cont :1;
+ uint8_t amplitude :8;
+ uint16_t reserved :7;
};
} haptic_config_t;
@@ -71,6 +73,9 @@ uint8_t haptic_get_mode(void);
uint8_t haptic_get_feedback(void);
void haptic_dwell_increase(void);
void haptic_dwell_decrease(void);
+void haptic_toggle_continuous(void);
+void haptic_cont_increase(void);
+void haptic_cont_decrease(void);
void haptic_play(void);
void haptic_shutdown(void);
diff --git a/keyboards/ergodox_ez/config.h b/keyboards/ergodox_ez/config.h
index 2ff452c400..0c74bc4e67 100644
--- a/keyboards/ergodox_ez/config.h
+++ b/keyboards/ergodox_ez/config.h
@@ -25,9 +25,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define VENDOR_ID 0xFEED
#define PRODUCT_ID 0x1307
#define DEVICE_VER 0x0001
-#define MANUFACTURER ErgoDox EZ
-#define PRODUCT ErgoDox EZ
+#define MANUFACTURER ZSA
+#define PRODUCT Ergodox EZ
#define DESCRIPTION QMK keyboard firmware for Ergodox EZ
+#define WEBUSB_LANDING_PAGE_URL u8"configure.ergodox-ez.com"
/* key matrix size */
#define MATRIX_ROWS 14
diff --git a/keyboards/ergodox_ez/ergodox_ez.c b/keyboards/ergodox_ez/ergodox_ez.c
index 3fd4e30833..6f56fd8e12 100644
--- a/keyboards/ergodox_ez/ergodox_ez.c
+++ b/keyboards/ergodox_ez/ergodox_ez.c
@@ -1,5 +1,9 @@
#include QMK_KEYBOARD_H
+#ifdef WEBUSB_ENABLE
+#include "webusb.h"
+#endif
+
extern inline void ergodox_board_led_on(void);
extern inline void ergodox_right_led_1_on(void);
extern inline void ergodox_right_led_2_on(void);
@@ -336,6 +340,17 @@ void keyboard_post_init_kb(void) {
bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
+#ifdef WEBUSB_ENABLE
+ if(webusb_state.paired == true) {
+ uint8_t event[5];
+ event[0] = WEBUSB_STATUS_OK;
+ event[1] = record->event.pressed ? WEBUSB_EVT_KEYDOWN : WEBUSB_EVT_KEYUP;
+ event[2] = record->event.key.col;
+ event[3] = record->event.key.row;
+ event[4] = WEBUSB_STOP_BIT;
+ webusb_send(event, sizeof(event));
+ }
+#endif
switch (keycode) {
case LED_LEVEL:
if (record->event.pressed) {
@@ -387,3 +402,53 @@ void eeconfig_init_kb(void) { // EEPROM is getting reset!
eeconfig_update_kb(keyboard_config.raw);
eeconfig_init_user();
}
+
+#ifdef WEBUSB_ENABLE
+static uint16_t loops = 0;
+static bool is_on = false;
+
+void matrix_scan_kb(void) {
+ if(webusb_state.pairing == true) {
+ if(loops == 0) {
+ ergodox_right_led_1_off();
+ ergodox_right_led_2_off();
+ ergodox_right_led_3_off();
+ }
+ if(loops % WEBUSB_BLINK_STEPS == 0) {
+ if(is_on) {
+ ergodox_right_led_2_off();
+ }
+ else {
+ ergodox_right_led_2_on();
+ }
+ is_on ^= 1;
+ }
+ if(loops > WEBUSB_BLINK_END) {
+ webusb_state.pairing = false;
+ layer_state_set_user(layer_state);
+ loops = 0;
+ }
+ loops++;
+ }
+ else if(loops > 0) {
+ loops = 0;
+ layer_state_set_user(layer_state);
+ }
+ matrix_scan_user();
+}
+
+uint32_t layer_state_set_kb(uint32_t state) {
+ state = layer_state_set_user(state);
+ uint8_t layer = biton32(state);
+ if(webusb_state.paired == true) {
+ uint8_t event[4];
+ event[0] = WEBUSB_STATUS_OK;
+ event[1] = WEBUSB_EVT_LAYER;
+ event[2] = layer;
+ event[3] = WEBUSB_STOP_BIT;
+ webusb_send(event, sizeof(event));
+ }
+ return state;
+}
+#endif
+
diff --git a/keyboards/ergodox_ez/keymaps/webusb/default.png.md b/keyboards/ergodox_ez/keymaps/webusb/default.png.md
new file mode 100644
index 0000000000..744e7d172a
--- /dev/null
+++ b/keyboards/ergodox_ez/keymaps/webusb/default.png.md
@@ -0,0 +1 @@
+https://i.imgur.com/fKX0Zbs.png
diff --git a/keyboards/ergodox_ez/keymaps/webusb/default_highres.png.md b/keyboards/ergodox_ez/keymaps/webusb/default_highres.png.md
new file mode 100644
index 0000000000..074e0634d2
--- /dev/null
+++ b/keyboards/ergodox_ez/keymaps/webusb/default_highres.png.md
@@ -0,0 +1 @@
+https://i.imgur.com/giAc3M9.jpg
diff --git a/keyboards/ergodox_ez/keymaps/webusb/keymap.c b/keyboards/ergodox_ez/keymaps/webusb/keymap.c
new file mode 100644
index 0000000000..58d281de6e
--- /dev/null
+++ b/keyboards/ergodox_ez/keymaps/webusb/keymap.c
@@ -0,0 +1,147 @@
+#include QMK_KEYBOARD_H
+#include "version.h"
+#include "keymap_german.h"
+#include "keymap_nordic.h"
+#include "keymap_french.h"
+#include "keymap_spanish.h"
+#include "keymap_hungarian.h"
+#include "keymap_swedish.h"
+#include "keymap_br_abnt2.h"
+#include "keymap_canadian_multilingual.h"
+#include "keymap_german_ch.h"
+#include "keymap_jp.h"
+
+#define KC_MAC_UNDO LGUI(KC_Z)
+#define KC_MAC_CUT LGUI(KC_X)
+#define KC_MAC_COPY LGUI(KC_C)
+#define KC_MAC_PASTE LGUI(KC_V)
+#define KC_PC_UNDO LCTL(KC_Z)
+#define KC_PC_CUT LCTL(KC_X)
+#define KC_PC_COPY LCTL(KC_C)
+#define KC_PC_PASTE LCTL(KC_V)
+#define ES_LESS_MAC KC_GRAVE
+#define ES_GRTR_MAC LSFT(KC_GRAVE)
+#define ES_BSLS_MAC ALGR(KC_6)
+
+enum custom_keycodes {
+ RGB_SLD = EZ_SAFE_RANGE,
+ HSV_172_255_255,
+ HSV_86_255_128,
+ HSV_27_255_255,
+};
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+ [0] = LAYOUT_ergodox_pretty(
+ WEBUSB_PAIR, KC_1, KC_2, KC_3, KC_4, KC_5, KC_LEFT, KC_RIGHT, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINUS,
+ KC_DELETE, KC_Q, KC_W, KC_E, KC_R, KC_T, TG(1), TG(1), KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSLASH,
+ KC_BSPACE, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, LT(2,KC_SCOLON),LGUI_T(KC_QUOTE),
+ KC_LSHIFT, LCTL_T(KC_Z), KC_X, KC_C, KC_V, KC_B, KC_HYPR, KC_MEH, KC_N, KC_M, KC_COMMA, KC_DOT, RCTL_T(KC_SLASH),KC_RSHIFT,
+ LT(1,KC_GRAVE), KC_QUOTE, LALT(KC_LSHIFT),KC_LEFT, KC_RIGHT, KC_UP, KC_DOWN, KC_LBRACKET, KC_RBRACKET, MO(1),
+ LALT_T(KC_APPLICATION),KC_LGUI, KC_LALT, LCTL_T(KC_ESCAPE),
+ KC_HOME, KC_PGUP,
+ KC_SPACE, KC_BSPACE, KC_END, KC_PGDOWN, KC_TAB, KC_ENTER
+ ),
+ [1] = LAYOUT_ergodox_pretty(
+ KC_ESCAPE, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_TRANSPARENT, KC_TRANSPARENT, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11,
+ KC_TRANSPARENT, KC_EXLM, KC_AT, KC_LCBR, KC_RCBR, KC_PIPE, KC_TRANSPARENT, KC_TRANSPARENT, KC_UP, KC_7, KC_8, KC_9, KC_ASTR, KC_F12,
+ KC_TRANSPARENT, KC_HASH, KC_DLR, KC_LPRN, KC_RPRN, KC_GRAVE, KC_DOWN, KC_4, KC_5, KC_6, KC_PLUS, KC_TRANSPARENT,
+ KC_TRANSPARENT, KC_PERC, KC_CIRC, KC_LBRACKET, KC_RBRACKET, KC_TILD, KC_TRANSPARENT, KC_TRANSPARENT, KC_AMPR, KC_1, KC_2, KC_3, KC_BSLASH, KC_TRANSPARENT,
+ KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_DOT, KC_0, KC_EQUAL, KC_TRANSPARENT,
+ RGB_MOD, HSV_172_255_255,RGB_TOG, RGB_SLD,
+ HSV_86_255_128, KC_TRANSPARENT,
+ RGB_VAD, RGB_VAI, HSV_27_255_255, KC_TRANSPARENT, RGB_HUD, RGB_HUI
+ ),
+ [2] = LAYOUT_ergodox_pretty(
+ KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+ KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_MS_UP, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+ KC_TRANSPARENT, KC_TRANSPARENT, KC_MS_LEFT, KC_MS_DOWN, KC_MS_RIGHT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_MEDIA_PLAY_PAUSE,
+ KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_MEDIA_PREV_TRACK,KC_MEDIA_NEXT_TRACK,KC_TRANSPARENT, KC_TRANSPARENT,
+ KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_MS_BTN1, KC_MS_BTN2, KC_AUDIO_VOL_UP,KC_AUDIO_VOL_DOWN,KC_AUDIO_MUTE, KC_TRANSPARENT, KC_TRANSPARENT,
+ KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+ KC_TRANSPARENT, KC_TRANSPARENT,
+ KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_WWW_BACK
+ ),
+};
+
+
+bool suspended = false;
+
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+ switch (keycode) {
+ case RGB_SLD:
+ if (record->event.pressed) {
+ rgblight_mode(1);
+ }
+ return false;
+ case HSV_172_255_255:
+ if (record->event.pressed) {
+ #ifdef RGBLIGHT_ENABLE
+ rgblight_enable();
+ rgblight_mode(1);
+ rgblight_sethsv(172,255,255);
+ #endif
+ }
+ return false;
+ case HSV_86_255_128:
+ if (record->event.pressed) {
+ #ifdef RGBLIGHT_ENABLE
+ rgblight_enable();
+ rgblight_mode(1);
+ rgblight_sethsv(86,255,128);
+ #endif
+ }
+ return false;
+ case HSV_27_255_255:
+ if (record->event.pressed) {
+ #ifdef RGBLIGHT_ENABLE
+ rgblight_enable();
+ rgblight_mode(1);
+ rgblight_sethsv(27,255,255);
+ #endif
+ }
+ return false;
+ }
+ return true;
+}
+
+uint32_t layer_state_set_user(uint32_t state) {
+
+ uint8_t layer = biton32(state);
+
+ ergodox_board_led_off();
+ ergodox_right_led_1_off();
+ ergodox_right_led_2_off();
+ ergodox_right_led_3_off();
+ switch (layer) {
+ case 1:
+ ergodox_right_led_1_on();
+ break;
+ case 2:
+ ergodox_right_led_2_on();
+ break;
+ case 3:
+ ergodox_right_led_3_on();
+ break;
+ case 4:
+ ergodox_right_led_1_on();
+ ergodox_right_led_2_on();
+ break;
+ case 5:
+ ergodox_right_led_1_on();
+ ergodox_right_led_3_on();
+ break;
+ case 6:
+ ergodox_right_led_2_on();
+ ergodox_right_led_3_on();
+ break;
+ case 7:
+ ergodox_right_led_1_on();
+ ergodox_right_led_2_on();
+ ergodox_right_led_3_on();
+ break;
+ default:
+ break;
+ }
+ return state;
+
+};
diff --git a/keyboards/ergodox_ez/keymaps/webusb/readme.md b/keyboards/ergodox_ez/keymaps/webusb/readme.md
new file mode 100644
index 0000000000..1150a4f709
--- /dev/null
+++ b/keyboards/ergodox_ez/keymaps/webusb/readme.md
@@ -0,0 +1,15 @@
+# ErgoDox EZ Default Configuration
+
+## Changelog
+
+* Dec 2016:
+ * Added LED keys
+ * Refreshed layout graphic, comes from http://configure.ergodox-ez.com now.
+* Sep 22, 2016:
+ * Created a new key in layer 1 (bottom-corner key) that resets the EEPROM.
+* Feb 2, 2016 (V1.1):
+ * Made the right-hand quote key double as Cmd/Win on hold. So you get ' when you tap it, " when you tap it with Shift, and Cmd or Win when you hold it. You can then use it as a modifier, or just press and hold it for a moment (and then let go) to send a single Cmd or Win keystroke (handy for opening the Start menu on Windows).
+
+This is what we ship with out of the factory. :) The image says it all:
+
+![Default](https://i.imgur.com/Be53jH7.png) \ No newline at end of file
diff --git a/keyboards/ergodox_ez/keymaps/webusb/rules.mk b/keyboards/ergodox_ez/keymaps/webusb/rules.mk
new file mode 100644
index 0000000000..fbc60a0a15
--- /dev/null
+++ b/keyboards/ergodox_ez/keymaps/webusb/rules.mk
@@ -0,0 +1 @@
+WEBUSB_ENABLE = yes
diff --git a/keyboards/ergodox_ez/matrix.c b/keyboards/ergodox_ez/matrix.c
index 80a2d1d95b..1ab2e5502f 100644
--- a/keyboards/ergodox_ez/matrix.c
+++ b/keyboards/ergodox_ez/matrix.c
@@ -35,9 +35,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "matrix.h"
#include "debounce.h"
#include QMK_KEYBOARD_H
-#ifdef DEBUG_MATRIX_SCAN_RATE
-# include "timer.h"
-#endif
/*
* This constant define not debouncing time in msecs, assuming eager_pr.
@@ -67,11 +64,6 @@ static void select_row(uint8_t row);
static uint8_t mcp23018_reset_loop;
// static uint16_t mcp23018_reset_loop;
-#ifdef DEBUG_MATRIX_SCAN_RATE
-uint32_t matrix_timer;
-uint32_t matrix_scan_count;
-#endif
-
__attribute__((weak)) void matrix_init_user(void) {}
__attribute__((weak)) void matrix_scan_user(void) {}
@@ -95,13 +87,9 @@ void matrix_init(void) {
// initialize matrix state: all keys off
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
matrix[i] = 0;
- raw_matrix[i] = 0;
+ raw_matrix[i] = 0;
}
-#ifdef DEBUG_MATRIX_SCAN_RATE
- matrix_timer = timer_read32();
- matrix_scan_count = 0;
-#endif
debounce_init(MATRIX_ROWS);
matrix_init_quantum();
}
@@ -116,11 +104,6 @@ void matrix_power_up(void) {
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
matrix[i] = 0;
}
-
-#ifdef DEBUG_MATRIX_SCAN_RATE
- matrix_timer = timer_read32();
- matrix_scan_count = 0;
-#endif
}
// Reads and stores a row, returning
@@ -151,24 +134,10 @@ uint8_t matrix_scan(void) {
}
}
-#ifdef DEBUG_MATRIX_SCAN_RATE
- matrix_scan_count++;
-
- uint32_t timer_now = timer_read32();
- if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) {
- print("matrix scan frequency: ");
- pdec(matrix_scan_count);
- print("\n");
-
- matrix_timer = timer_now;
- matrix_scan_count = 0;
- }
-#endif
-
#ifdef LEFT_LEDS
mcp23018_status = ergodox_left_leds_update();
#endif // LEFT_LEDS
- bool changed = false;
+ bool changed = false;
for (uint8_t i = 0; i < MATRIX_ROWS_PER_SIDE; i++) {
// select rows from left and right hands
uint8_t left_index = i;
@@ -178,13 +147,13 @@ uint8_t matrix_scan(void) {
// we don't need a 30us delay anymore, because selecting a
// left-hand row requires more than 30us for i2c.
-
+
changed |= store_raw_matrix_row(left_index);
changed |= store_raw_matrix_row(right_index);
unselect_rows();
}
-
+
debounce(raw_matrix, matrix, MATRIX_ROWS, changed);
matrix_scan_quantum();
diff --git a/keyboards/planck/ez/config.h b/keyboards/planck/ez/config.h
index df27e7b629..d017e97d17 100644
--- a/keyboards/planck/ez/config.h
+++ b/keyboards/planck/ez/config.h
@@ -18,7 +18,12 @@
#pragma once
/* USB Device descriptor parameter */
+#undef MANUFACTURER
+#define MANUFACTURER ZSA
+#undef PRODUCT
+#define PRODUCT Planck EZ
#define DEVICE_VER 0x0000
+#define WEBUSB_LANDING_PAGE_URL u8"configure.ergodox-ez.com"
#undef MATRIX_ROWS
#undef MATRIX_COLS
diff --git a/keyboards/planck/ez/ez.c b/keyboards/planck/ez/ez.c
index 79310da10a..f34af7a290 100644
--- a/keyboards/planck/ez/ez.c
+++ b/keyboards/planck/ez/ez.c
@@ -16,6 +16,9 @@
#include "ez.h"
#include "ch.h"
#include "hal.h"
+#ifdef WEBUSB_ENABLE
+#include "webusb.h"
+#endif
keyboard_config_t keyboard_config;
@@ -247,6 +250,16 @@ uint32_t layer_state_set_kb(uint32_t state) {
planck_ez_right_led_off();
state = layer_state_set_user(state);
uint8_t layer = biton32(state);
+#ifdef WEBUSB_ENABLE
+ if(webusb_state.paired == true) {
+ uint8_t event[4];
+ event[0] = WEBUSB_STATUS_OK;
+ event[1] = WEBUSB_EVT_LAYER;
+ event[2] = layer;
+ event[3] = WEBUSB_STOP_BIT;
+ webusb_send(event, sizeof(event));
+ }
+#endif
switch (layer) {
case 1:
planck_ez_left_led_on();
@@ -265,6 +278,17 @@ uint32_t layer_state_set_kb(uint32_t state) {
}
bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
+#ifdef WEBUSB_ENABLE
+ if(webusb_state.paired == true) {
+ uint8_t event[5];
+ event[0] = WEBUSB_STATUS_OK;
+ event[1] = record->event.pressed ? WEBUSB_EVT_KEYDOWN : WEBUSB_EVT_KEYUP;
+ event[2] = record->event.key.col;
+ event[3] = record->event.key.row;
+ event[4] = WEBUSB_STOP_BIT;
+ webusb_send(event, sizeof(event));
+ }
+#endif
switch (keycode) {
case LED_LEVEL:
if (record->event.pressed) {
@@ -325,3 +349,38 @@ bool music_mask_kb(uint16_t keycode) {
}
}
#endif
+#ifdef WEBUSB_ENABLE
+static uint16_t loops = 0;
+static bool is_on = false;
+
+void matrix_scan_kb(void) {
+ if(webusb_state.pairing == true) {
+ if(loops == 0) {
+ //lights off
+ }
+ if(loops % WEBUSB_BLINK_STEPS == 0) {
+ if(is_on) {
+ planck_ez_left_led_on();
+ planck_ez_right_led_off();
+ }
+ else {
+ planck_ez_left_led_off();
+ planck_ez_right_led_on();
+ }
+ is_on ^= 1;
+ }
+ if(loops > WEBUSB_BLINK_END * 2) {
+ webusb_state.pairing = false;
+ loops = 0;
+ planck_ez_left_led_off();
+ planck_ez_right_led_off();
+ }
+ loops++;
+ }
+ else if(loops > 0) {
+ loops = 0;
+ planck_ez_left_led_off();
+ planck_ez_right_led_off();
+ }
+}
+#endif
diff --git a/keyboards/planck/keymaps/webusb/config.h b/keyboards/planck/keymaps/webusb/config.h
new file mode 100644
index 0000000000..6fa31cc8a7
--- /dev/null
+++ b/keyboards/planck/keymaps/webusb/config.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#ifdef AUDIO_ENABLE
+ #define STARTUP_SONG SONG(PLANCK_SOUND)
+ // #define STARTUP_SONG SONG(NO_SOUND)
+
+ #define DEFAULT_LAYER_SONGS { SONG(QWERTY_SOUND), \
+ SONG(COLEMAK_SOUND), \
+ SONG(DVORAK_SOUND) \
+ }
+#endif
+
+/*
+ * MIDI options
+ */
+
+/* Prevent use of disabled MIDI features in the keymap */
+//#define MIDI_ENABLE_STRICT 1
+
+/* enable basic MIDI features:
+ - MIDI notes can be sent when in Music mode is on
+*/
+
+#define MIDI_BASIC
+
+/* enable advanced MIDI features:
+ - MIDI notes can be added to the keymap
+ - Octave shift and transpose
+ - Virtual sustain, portamento, and modulation wheel
+ - etc.
+*/
+//#define MIDI_ADVANCED
+
+/* override number of MIDI tone keycodes (each octave adds 12 keycodes and allocates 12 bytes) */
+//#define MIDI_TONE_KEYCODE_OCTAVES 2
+
+// Most tactile encoders have detents every 4 stages
+#define ENCODER_RESOLUTION 4
+
diff --git a/keyboards/planck/keymaps/webusb/keymap.c b/keyboards/planck/keymaps/webusb/keymap.c
new file mode 100644
index 0000000000..9a7c2f4d85
--- /dev/null
+++ b/keyboards/planck/keymaps/webusb/keymap.c
@@ -0,0 +1,136 @@
+#include QMK_KEYBOARD_H
+#ifdef AUDIO_ENABLE
+#include "muse.h"
+#endif
+#include "eeprom.h"
+#include "keymap_german.h"
+#include "keymap_nordic.h"
+#include "keymap_french.h"
+#include "keymap_spanish.h"
+#include "keymap_hungarian.h"
+#include "keymap_swedish.h"
+#include "keymap_br_abnt2.h"
+#include "keymap_canadian_multilingual.h"
+#include "keymap_german_ch.h"
+#include "keymap_jp.h"
+
+#define KC_MAC_UNDO LGUI(KC_Z)
+#define KC_MAC_CUT LGUI(KC_X)
+#define KC_MAC_COPY LGUI(KC_C)
+#define KC_MAC_PASTE LGUI(KC_V)
+#define KC_PC_UNDO LCTL(KC_Z)
+#define KC_PC_CUT LCTL(KC_X)
+#define KC_PC_COPY LCTL(KC_C)
+#define KC_PC_PASTE LCTL(KC_V)
+#define ES_LESS_MAC KC_GRAVE
+#define ES_GRTR_MAC LSFT(KC_GRAVE)
+#define ES_BSLS_MAC ALGR(KC_6)
+
+enum planck_keycodes {
+ RGB_SLD = EZ_SAFE_RANGE,
+};
+
+enum planck_layers {
+ _BASE,
+ _LOWER,
+ _RAISE,
+ _ADJUST,
+};
+
+
+#define LOWER MO(_LOWER)
+#define RAISE MO(_RAISE)
+
+#define LOWER MO(_LOWER)
+#define RAISE MO(_RAISE)
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+ [_BASE] = LAYOUT_planck_grid(KC_TAB,KC_Q,KC_W,KC_E,KC_R,KC_T,KC_Y,KC_U,KC_I,KC_O,KC_P,KC_BSPACE,KC_ESCAPE,KC_A,KC_S,KC_D,KC_F,KC_G,KC_H,KC_J,KC_K,KC_L,KC_SCOLON,KC_QUOTE,KC_LSHIFT,KC_Z,KC_X,KC_C,KC_V,KC_B,KC_N,KC_M,KC_COMMA,KC_DOT,KC_SLASH,KC_ENTER,KC_HYPR,KC_LCTRL,KC_LALT,KC_LGUI,LOWER,KC_SPACE,KC_NO,RAISE,KC_LEFT,KC_DOWN,KC_UP,KC_RIGHT),
+
+ [_LOWER] = LAYOUT_planck_grid(KC_TILD,KC_EXLM,KC_AT,KC_HASH,KC_DLR,KC_PERC,KC_CIRC,KC_AMPR,KC_ASTR,KC_LPRN,KC_RPRN,KC_TRANSPARENT,KC_DELETE,KC_F1,KC_F2,KC_F3,KC_F4,KC_F5,KC_F6,KC_UNDS,KC_PLUS,KC_LCBR,KC_RCBR,KC_PIPE,KC_TRANSPARENT,KC_F7,KC_F8,KC_F9,KC_F10,KC_F11,KC_F12,KC_NONUS_HASH,KC_NONUS_BSLASH,KC_HOME,KC_END,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_NO,KC_TRANSPARENT,KC_MEDIA_NEXT_TRACK,KC_AUDIO_VOL_DOWN,KC_AUDIO_VOL_UP,KC_MEDIA_PLAY_PAUSE),
+
+ [_RAISE] = LAYOUT_planck_grid(KC_GRAVE,KC_1,KC_2,KC_3,KC_4,KC_5,KC_6,KC_7,KC_8,KC_9,KC_0,KC_TRANSPARENT,KC_DELETE,KC_F1,KC_F2,KC_F3,KC_F4,KC_F5,KC_F6,KC_MINUS,KC_EQUAL,KC_LBRACKET,KC_RBRACKET,KC_BSLASH,KC_TRANSPARENT,KC_F7,KC_F8,KC_F9,KC_F10,KC_F11,KC_F12,KC_NONUS_HASH,KC_NONUS_BSLASH,KC_PGUP,KC_PGDOWN,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_NO,KC_TRANSPARENT,KC_MEDIA_NEXT_TRACK,KC_AUDIO_VOL_DOWN,KC_AUDIO_VOL_UP,KC_MEDIA_PLAY_PAUSE),
+
+ [_ADJUST] = LAYOUT_planck_grid(WEBUSB_PAIR,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_DELETE,KC_TRANSPARENT,AU_ON,AU_OFF,AU_TOG,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,RESET,KC_TRANSPARENT,KC_TRANSPARENT,MU_ON,MU_OFF,MU_TOG,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_NO,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT),
+
+};
+
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+ switch (keycode) {
+ }
+ return true;
+}
+
+#ifdef AUDIO_ENABLE
+bool muse_mode = false;
+uint8_t last_muse_note = 0;
+uint16_t muse_counter = 0;
+uint8_t muse_offset = 70;
+uint16_t muse_tempo = 50;
+
+void encoder_update(bool clockwise) {
+ if (muse_mode) {
+ if (IS_LAYER_ON(_RAISE)) {
+ if (clockwise) {
+ muse_offset++;
+ } else {
+ muse_offset--;
+ }
+ } else {
+ if (clockwise) {
+ muse_tempo+=1;
+ } else {
+ muse_tempo-=1;
+ }
+ }
+ } else {
+ if (clockwise) {
+#ifdef MOUSEKEY_ENABLE
+ register_code(KC_MS_WH_DOWN);
+ unregister_code(KC_MS_WH_DOWN);
+#else
+ register_code(KC_PGDN);
+ unregister_code(KC_PGDN);
+#endif
+ } else {
+#ifdef MOUSEKEY_ENABLE
+ register_code(KC_MS_WH_UP);
+ unregister_code(KC_MS_WH_UP);
+#else
+ register_code(KC_PGUP);
+ unregister_code(KC_PGUP);
+#endif
+ }
+ }
+}
+
+void matrix_scan_user(void) {
+#ifdef AUDIO_ENABLE
+ if (muse_mode) {
+ if (muse_counter == 0) {
+ uint8_t muse_note = muse_offset + SCALE[muse_clock_pulse()];
+ if (muse_note != last_muse_note) {
+ stop_note(compute_freq_for_midi_note(last_muse_note));
+ play_note(compute_freq_for_midi_note(muse_note), 0xF);
+ last_muse_note = muse_note;
+ }
+ }
+ muse_counter = (muse_counter + 1) % muse_tempo;
+ }
+#endif
+}
+
+bool music_mask_user(uint16_t keycode) {
+ switch (keycode) {
+ case RAISE:
+ case LOWER:
+ return false;
+ default:
+ return true;
+ }
+}
+#endif
+
+uint32_t layer_state_set_user(uint32_t state) {
+ return update_tri_layer_state(state, _LOWER, _RAISE, _ADJUST);
+}
diff --git a/keyboards/planck/keymaps/webusb/readme.md b/keyboards/planck/keymaps/webusb/readme.md
new file mode 100644
index 0000000000..de9680b498
--- /dev/null
+++ b/keyboards/planck/keymaps/webusb/readme.md
@@ -0,0 +1,2 @@
+# The Default Planck Layout
+
diff --git a/keyboards/planck/keymaps/webusb/rules.mk b/keyboards/planck/keymaps/webusb/rules.mk
new file mode 100644
index 0000000000..2985843fbc
--- /dev/null
+++ b/keyboards/planck/keymaps/webusb/rules.mk
@@ -0,0 +1,3 @@
+SRC += muse.c
+RGB_MATRIX_ENABLE = no
+WEBUSB_ENABLE = yes
diff --git a/lib/lufa b/lib/lufa
-Subproject ce10f7642b0459e409839b23cc91498945119b4
+Subproject 94e43e49777eaf76ea984adc486752bcd663a89
diff --git a/lib/python/milc.py b/lib/python/milc.py
index c62c1b166c..7b130bdea6 100644
--- a/lib/python/milc.py
+++ b/lib/python/milc.py
@@ -17,6 +17,7 @@ import argparse
import logging
import os
import re
+import shlex
import sys
from decimal import Decimal
from tempfile import NamedTemporaryFile
@@ -35,6 +36,10 @@ except ImportError:
import argcomplete
import colorama
+from appdirs import user_config_dir
+
+# Disable logging until we can configure it how the user wants
+logging.basicConfig(filename='/dev/null')
# Log Level Representations
EMOJI_LOGLEVELS = {
@@ -47,6 +52,7 @@ EMOJI_LOGLEVELS = {
}
EMOJI_LOGLEVELS['FATAL'] = EMOJI_LOGLEVELS['CRITICAL']
EMOJI_LOGLEVELS['WARN'] = EMOJI_LOGLEVELS['WARNING']
+UNICODE_SUPPORT = sys.stdout.encoding.lower().startswith('utf')
# ANSI Color setup
# Regex was gratefully borrowed from kfir on stackoverflow:
@@ -97,11 +103,12 @@ class ANSIFormatter(logging.Formatter):
class ANSIEmojiLoglevelFormatter(ANSIFormatter):
- """A log formatter that makes the loglevel an emoji.
+ """A log formatter that makes the loglevel an emoji on UTF capable terminals.
"""
def format(self, record):
- record.levelname = EMOJI_LOGLEVELS[record.levelname].format(**ansi_colors)
+ if UNICODE_SUPPORT:
+ record.levelname = EMOJI_LOGLEVELS[record.levelname].format(**ansi_colors)
return super(ANSIEmojiLoglevelFormatter, self).format(record)
@@ -144,13 +151,15 @@ class Configuration(object):
def __init__(self, *args, **kwargs):
self._config = {}
- self.default_container = ConfigurationOption
+
+ def __getattr__(self, key):
+ return self.__getitem__(key)
def __getitem__(self, key):
"""Returns a config section, creating it if it doesn't exist yet.
"""
if key not in self._config:
- self.__dict__[key] = self._config[key] = ConfigurationOption()
+ self.__dict__[key] = self._config[key] = ConfigurationSection(self)
return self._config[key]
@@ -161,30 +170,34 @@ class Configuration(object):
def __delitem__(self, key):
if key in self.__dict__ and key[0] != '_':
del self.__dict__[key]
- del self._config[key]
+ if key in self._config:
+ del self._config[key]
-class ConfigurationOption(Configuration):
- def __init__(self, *args, **kwargs):
- super(ConfigurationOption, self).__init__(*args, **kwargs)
- self.default_container = dict
+class ConfigurationSection(Configuration):
+ def __init__(self, parent, *args, **kwargs):
+ super(ConfigurationSection, self).__init__(*args, **kwargs)
+ self.parent = parent
def __getitem__(self, key):
- """Returns a config section, creating it if it doesn't exist yet.
+ """Returns a config value, pulling from the `user` section as a fallback.
"""
- if key not in self._config:
- self.__dict__[key] = self._config[key] = None
+ if key in self._config:
+ return self._config[key]
- return self._config[key]
+ elif key in self.parent.user:
+ return self.parent.user[key]
+
+ return None
def handle_store_boolean(self, *args, **kwargs):
"""Does the add_argument for action='store_boolean'.
"""
- kwargs['add_dest'] = False
disabled_args = None
disabled_kwargs = kwargs.copy()
disabled_kwargs['action'] = 'store_false'
+ disabled_kwargs['dest'] = self.get_argument_name(*args, **kwargs)
disabled_kwargs['help'] = 'Disable ' + kwargs['help']
kwargs['action'] = 'store_true'
kwargs['help'] = 'Enable ' + kwargs['help']
@@ -219,11 +232,6 @@ class SubparserWrapper(object):
self.subparser.completer = completer
def add_argument(self, *args, **kwargs):
- if kwargs.get('add_dest', True):
- kwargs['dest'] = self.submodule + '_' + self.cli.get_argument_name(*args, **kwargs)
- if 'add_dest' in kwargs:
- del kwargs['add_dest']
-
if 'action' in kwargs and kwargs['action'] == 'store_boolean':
return handle_store_boolean(self, *args, **kwargs)
@@ -254,12 +262,16 @@ class MILC(object):
self._entrypoint = None
self._inside_context_manager = False
self.ansi = ansi_colors
+ self.arg_only = []
self.config = Configuration()
self.config_file = None
- self.prog_name = sys.argv[0][:-3] if sys.argv[0].endswith('.py') else sys.argv[0]
self.version = os.environ.get('QMK_VERSION', 'unknown')
self.release_lock()
+ # Figure out our program name
+ self.prog_name = sys.argv[0][:-3] if sys.argv[0].endswith('.py') else sys.argv[0]
+ self.prog_name = self.prog_name.split('/')[-1]
+
# Initialize all the things
self.initialize_argparse()
self.initialize_logging()
@@ -273,7 +285,7 @@ class MILC(object):
self._description = self._arg_parser.description = self._arg_defaults.description = value
def echo(self, text, *args, **kwargs):
- """Print colorized text to stdout, as long as stdout is a tty.
+ """Print colorized text to stdout.
ANSI color strings (such as {fg-blue}) will be converted into ANSI
escape sequences, and the ANSI reset sequence will be added to all
@@ -284,11 +296,10 @@ class MILC(object):
if args and kwargs:
raise RuntimeError('You can only specify *args or **kwargs, not both!')
- if sys.stdout.isatty():
- args = args or kwargs
- text = format_ansi(text)
+ args = args or kwargs
+ text = format_ansi(text)
- print(text % args)
+ print(text % args)
def initialize_argparse(self):
"""Prepare to process arguments from sys.argv.
@@ -313,21 +324,21 @@ class MILC(object):
self.release_lock()
def completer(self, completer):
- """Add an arpcomplete completer to this subcommand.
+ """Add an argcomplete completer to this subcommand.
"""
self._arg_parser.completer = completer
def add_argument(self, *args, **kwargs):
"""Wrapper to add arguments to both the main and the shadow argparser.
"""
+ if 'action' in kwargs and kwargs['action'] == 'store_boolean':
+ return handle_store_boolean(self, *args, **kwargs)
+
if kwargs.get('add_dest', True) and args[0][0] == '-':
kwargs['dest'] = 'general_' + self.get_argument_name(*args, **kwargs)
if 'add_dest' in kwargs:
del kwargs['add_dest']
- if 'action' in kwargs and kwargs['action'] == 'store_boolean':
- return handle_store_boolean(self, *args, **kwargs)
-
self.acquire_lock()
self._arg_parser.add_argument(*args, **kwargs)
@@ -396,7 +407,7 @@ class MILC(object):
if self.args and self.args.general_config_file:
return self.args.general_config_file
- return os.path.abspath(os.path.expanduser('~/.%s.ini' % self.prog_name))
+ return os.path.join(user_config_dir(appname='qmk', appauthor='QMK'), '%s.ini' % self.prog_name)
def get_argument_name(self, *args, **kwargs):
"""Takes argparse arguments and returns the dest name.
@@ -413,11 +424,17 @@ class MILC(object):
raise RuntimeError('You must run this before the with statement!')
def argument_function(handler):
+ if 'arg_only' in kwargs and kwargs['arg_only']:
+ arg_name = self.get_argument_name(*args, **kwargs)
+ self.arg_only.append(arg_name)
+ del kwargs['arg_only']
+
+ name = handler.__name__.replace("_", "-")
if handler is self._entrypoint:
self.add_argument(*args, **kwargs)
- elif handler.__name__ in self.subcommands:
- self.subcommands[handler.__name__].add_argument(*args, **kwargs)
+ elif name in self.subcommands:
+ self.subcommands[name].add_argument(*args, **kwargs)
else:
raise RuntimeError('Decorated function is not entrypoint or subcommand!')
@@ -485,15 +502,20 @@ class MILC(object):
if argument in ('subparsers', 'entrypoint'):
continue
- if '_' not in argument:
- continue
-
- section, option = argument.split('_', 1)
- if hasattr(self.args_passed, argument):
- self.config[section][option] = getattr(self.args, argument)
+ if '_' in argument:
+ section, option = argument.split('_', 1)
else:
- if option not in self.config[section]:
- self.config[section][option] = getattr(self.args, argument)
+ section = self._entrypoint.__name__
+ option = argument
+
+ if option not in self.arg_only:
+ if hasattr(self.args_passed, argument):
+ arg_value = getattr(self.args, argument)
+ if arg_value:
+ self.config[section][option] = arg_value
+ else:
+ if option not in self.config[section]:
+ self.config[section][option] = getattr(self.args, argument)
self.release_lock()
@@ -509,6 +531,8 @@ class MILC(object):
self.acquire_lock()
config = RawConfigParser()
+ config_dir = os.path.dirname(self.config_file)
+
for section_name, section in self.config._config.items():
config.add_section(section_name)
for option_name, value in section.items():
@@ -517,7 +541,10 @@ class MILC(object):
continue
config.set(section_name, option_name, str(value))
- with NamedTemporaryFile(mode='w', dir=os.path.dirname(self.config_file), delete=False) as tmpfile:
+ if not os.path.exists(config_dir):
+ os.makedirs(config_dir)
+
+ with NamedTemporaryFile(mode='w', dir=config_dir, delete=False) as tmpfile:
config.write(tmpfile)
# Move the new config file into place atomically
@@ -527,6 +554,7 @@ class MILC(object):
self.log.warning('Config file saving failed, not replacing %s with %s.', self.config_file, tmpfile.name)
self.release_lock()
+ cli.log.info('Wrote configuration to %s', shlex.quote(self.config_file))
def __call__(self):
"""Execute the entrypoint function.
@@ -572,7 +600,7 @@ class MILC(object):
self.add_subparsers()
if not name:
- name = handler.__name__
+ name = handler.__name__.replace("_", "-")
self.acquire_lock()
kwargs['help'] = description
@@ -602,8 +630,8 @@ class MILC(object):
"""Called by __enter__() to setup the logging configuration.
"""
if len(logging.root.handlers) != 0:
- # This is not a design decision. This is what I'm doing for now until I can examine and think about this situation in more detail.
- raise RuntimeError('MILC should be the only system installing root log handlers!')
+ # MILC is the only thing that should have root log handlers
+ logging.root.handlers = []
self.acquire_lock()
@@ -648,8 +676,9 @@ class MILC(object):
self.read_config()
self.setup_logging()
- if self.config.general.save_config:
+ if 'save_config' in self.config.general and self.config.general.save_config:
self.save_config()
+ exit(0)
return self
@@ -712,4 +741,3 @@ if __name__ == '__main__':
cli.goodbye.add_argument('-n', '--name', help='Name to bid farewell to', default='World')
cli() # Automatically picks between main(), hello() and goodbye()
- print(sorted(ansi_colors.keys()))
diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py
index e69de29bb2..e982a75fc8 100644
--- a/lib/python/qmk/cli/__init__.py
+++ b/lib/python/qmk/cli/__init__.py
@@ -0,0 +1,14 @@
+"""QMK CLI Subcommands
+
+We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup.
+"""
+from . import cformat
+from . import compile
+from . import config
+from . import doctor
+from . import hello
+from . import json
+from . import list
+from . import new
+from . import pyformat
+from . import pytest
diff --git a/lib/python/qmk/cli/cformat.py b/lib/python/qmk/cli/cformat.py
index 91e650368b..d2382bdbde 100644
--- a/lib/python/qmk/cli/cformat.py
+++ b/lib/python/qmk/cli/cformat.py
@@ -6,9 +6,9 @@ import subprocess
from milc import cli
-@cli.argument('files', nargs='*', help='Filename(s) to format.')
-@cli.entrypoint("Format C code according to QMK's style.")
-def main(cli):
+@cli.argument('files', nargs='*', arg_only=True, help='Filename(s) to format.')
+@cli.subcommand("Format C code according to QMK's style.")
+def cformat(cli):
"""Format C code according to QMK's style.
"""
clang_format = ['clang-format', '-i']
diff --git a/lib/python/qmk/cli/compile.py b/lib/python/qmk/cli/compile.py
index 7e14ad8fbf..6646891b30 100755
--- a/lib/python/qmk/cli/compile.py
+++ b/lib/python/qmk/cli/compile.py
@@ -14,11 +14,11 @@ import qmk.keymap
import qmk.path
-@cli.argument('filename', nargs='?', type=FileType('r'), help='The configurator export to compile')
+@cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), help='The configurator export to compile')
@cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')
@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')
-@cli.entrypoint('Compile a QMK Firmware.')
-def main(cli):
+@cli.subcommand('Compile a QMK Firmware.')
+def compile(cli):
"""Compile a QMK Firmware.
If a Configurator export is supplied this command will create a new keymap, overwriting an existing keymap if one exists.
@@ -41,9 +41,9 @@ def main(cli):
# Compile the keymap
command = ['make', ':'.join((user_keymap['keyboard'], user_keymap['keymap']))]
- elif cli.config.general.keyboard and cli.config.general.keymap:
+ elif cli.config.compile.keyboard and cli.config.compile.keymap:
# Generate the make command for a specific keyboard/keymap.
- command = ['make', ':'.join((cli.config.general.keyboard, cli.config.general.keymap))]
+ command = ['make', ':'.join((cli.config.compile.keyboard, cli.config.compile.keymap))]
else:
cli.log.error('You must supply a configurator export or both `--keyboard` and `--keymap`.')
diff --git a/lib/python/qmk/cli/config.py b/lib/python/qmk/cli/config.py
new file mode 100644
index 0000000000..d6c774e651
--- /dev/null
+++ b/lib/python/qmk/cli/config.py
@@ -0,0 +1,96 @@
+"""Read and write configuration settings
+"""
+import os
+import subprocess
+
+from milc import cli
+
+
+def print_config(section, key):
+ """Print a single config setting to stdout.
+ """
+ cli.echo('%s.%s{fg_cyan}={fg_reset}%s', section, key, cli.config[section][key])
+
+
+@cli.argument('-ro', '--read-only', action='store_true', help='Operate in read-only mode.')
+@cli.argument('configs', nargs='*', arg_only=True, help='Configuration options to read or write.')
+@cli.subcommand("Read and write configuration settings.")
+def config(cli):
+ """Read and write config settings.
+
+ This script iterates over the config_tokens supplied as argument. Each config_token has the following form:
+
+ section[.key][=value]
+
+ If only a section (EG 'compile') is supplied all keys for that section will be displayed.
+
+ If section.key is supplied the value for that single key will be displayed.
+
+ If section.key=value is supplied the value for that single key will be set.
+
+ If section.key=None is supplied the key will be deleted.
+
+ No validation is done to ensure that the supplied section.key is actually used by qmk scripts.
+ """
+ if not cli.args.configs:
+ # Walk the config tree
+ for section in cli.config:
+ for key in cli.config[section]:
+ print_config(section, key)
+
+ return True
+
+ # Process config_tokens
+ save_config = False
+
+ for argument in cli.args.configs:
+ # Split on space in case they quoted multiple config tokens
+ for config_token in argument.split(' '):
+ # Extract the section, config_key, and value to write from the supplied config_token.
+ if '=' in config_token:
+ key, value = config_token.split('=')
+ else:
+ key = config_token
+ value = None
+
+ if '.' in key:
+ section, config_key = key.split('.', 1)
+ else:
+ section = key
+ config_key = None
+
+ # Validation
+ if config_key and '.' in config_key:
+ cli.log.error('Config keys may not have more than one period! "%s" is not valid.', key)
+ return False
+
+ # Do what the user wants
+ if section and config_key and value:
+ # Write a config key
+ log_string = '%s.%s{fg_cyan}:{fg_reset} %s {fg_cyan}->{fg_reset} %s'
+ if cli.args.read_only:
+ log_string += ' {fg_red}(change not written)'
+
+ cli.echo(log_string, section, config_key, cli.config[section][config_key], value)
+
+ if not cli.args.read_only:
+ if value == 'None':
+ del cli.config[section][config_key]
+ else:
+ cli.config[section][config_key] = value
+ save_config = True
+
+ elif section and config_key:
+ # Display a single key
+ print_config(section, config_key)
+
+ elif section:
+ # Display an entire section
+ for key in cli.config[section]:
+ print_config(section, key)
+
+ # Ending actions
+ if save_config:
+ cli.save_config()
+
+ return True
diff --git a/lib/python/qmk/cli/doctor.py b/lib/python/qmk/cli/doctor.py
index 5a713b20f5..309de0c671 100755
--- a/lib/python/qmk/cli/doctor.py
+++ b/lib/python/qmk/cli/doctor.py
@@ -11,8 +11,8 @@ from glob import glob
from milc import cli
-@cli.entrypoint('Basic QMK environment checks')
-def main(cli):
+@cli.subcommand('Basic QMK environment checks')
+def doctor(cli):
"""Basic QMK environment checks.
This is currently very simple, it just checks that all the expected binaries are on your system.
@@ -36,6 +36,7 @@ def main(cli):
else:
try:
subprocess.run([binary, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5, check=True)
+ cli.log.info('Found {fg_cyan}%s', binary)
except subprocess.CalledProcessError:
cli.log.error("{fg_red}Can't run `%s --version`", binary)
ok = False
@@ -49,12 +50,12 @@ def main(cli):
elif OS == "Linux":
cli.log.info("Detected {fg_cyan}Linux.")
if shutil.which('systemctl'):
- mm_check = subprocess.run(['systemctl', 'list-unit-files'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=10)
+ mm_check = subprocess.run(['systemctl', 'list-unit-files'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=10, universal_newlines=True)
if mm_check.returncode == 0:
- mm = True
+ mm = False
for line in mm_check.stdout.split('\n'):
if 'ModemManager' in line and 'enabled' in line:
- mm = False
+ mm = True
if mm:
cli.log.warn("{bg_yellow}Detected ModemManager. Please disable it if you are using a Pro-Micro.")
diff --git a/lib/python/qmk/cli/hello.py b/lib/python/qmk/cli/hello.py
index bc0cb6de18..bee28c3013 100755
--- a/lib/python/qmk/cli/hello.py
+++ b/lib/python/qmk/cli/hello.py
@@ -6,8 +6,8 @@ from milc import cli
@cli.argument('-n', '--name', default='World', help='Name to greet.')
-@cli.entrypoint('QMK Hello World.')
-def main(cli):
+@cli.subcommand('QMK Hello World.')
+def hello(cli):
"""Log a friendly greeting.
"""
- cli.log.info('Hello, %s!', cli.config.general.name)
+ cli.log.info('Hello, %s!', cli.config.hello.name)
diff --git a/lib/python/qmk/cli/json/__init__.py b/lib/python/qmk/cli/json/__init__.py
index e69de29bb2..f4ebfc45b4 100644
--- a/lib/python/qmk/cli/json/__init__.py
+++ b/lib/python/qmk/cli/json/__init__.py
@@ -0,0 +1,5 @@
+"""QMK CLI JSON Subcommands
+
+We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup.
+"""
+from . import keymap
diff --git a/lib/python/qmk/cli/json/keymap.py b/lib/python/qmk/cli/json/keymap.py
index e2d0b58093..a65acd6197 100755
--- a/lib/python/qmk/cli/json/keymap.py
+++ b/lib/python/qmk/cli/json/keymap.py
@@ -9,10 +9,10 @@ from milc import cli
import qmk.keymap
-@cli.argument('-o', '--output', help='File to write to')
-@cli.argument('filename', help='Configurator JSON file')
-@cli.entrypoint('Create a keymap.c from a QMK Configurator export.')
-def main(cli):
+@cli.argument('-o', '--output', arg_only=True, help='File to write to')
+@cli.argument('filename', arg_only=True, help='Configurator JSON file')
+@cli.subcommand('Create a keymap.c from a QMK Configurator export.')
+def json_keymap(cli):
"""Generate a keymap.c from a configurator export.
This command uses the `qmk.keymap` module to generate a keymap.c from a configurator export. The generated keymap is written to stdout, or to a file if -o is provided.
@@ -28,8 +28,8 @@ def main(cli):
exit(1)
# Environment processing
- if cli.config.general.output == ('-'):
- cli.config.general.output = None
+ if cli.args.output == ('-'):
+ cli.args.output = None
# Parse the configurator json
with open(qmk.path.normpath(cli.args.filename), 'r') as fd:
@@ -38,17 +38,17 @@ def main(cli):
# Generate the keymap
keymap_c = qmk.keymap.generate(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers'])
- if cli.config.general.output:
- output_dir = os.path.dirname(cli.config.general.output)
+ if cli.args.output:
+ output_dir = os.path.dirname(cli.args.output)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
- output_file = qmk.path.normpath(cli.config.general.output)
+ output_file = qmk.path.normpath(cli.args.output)
with open(output_file, 'w') as keymap_fd:
keymap_fd.write(keymap_c)
- cli.log.info('Wrote keymap to %s.', cli.config.general.output)
+ cli.log.info('Wrote keymap to %s.', cli.args.output)
else:
print(keymap_c)
diff --git a/lib/python/qmk/cli/list/__init__.py b/lib/python/qmk/cli/list/__init__.py
new file mode 100644
index 0000000000..c36ba69548
--- /dev/null
+++ b/lib/python/qmk/cli/list/__init__.py
@@ -0,0 +1 @@
+from . import keyboards
diff --git a/lib/python/qmk/cli/list/keyboards.py b/lib/python/qmk/cli/list/keyboards.py
new file mode 100644
index 0000000000..53a7af75c6
--- /dev/null
+++ b/lib/python/qmk/cli/list/keyboards.py
@@ -0,0 +1,26 @@
+"""List the keyboards currently defined within QMK
+"""
+import os
+import re
+import glob
+
+from milc import cli
+
+@cli.subcommand("List the keyboards currently defined within QMK")
+def list_keyboards(cli):
+ """List the keyboards currently defined within QMK
+ """
+
+ base_path = os.path.join(os.getcwd(), "keyboards") + os.path.sep
+ kb_path_wildcard = os.path.join(base_path, "**", "rules.mk")
+
+ # find everywhere we have rules.mk where keymaps isn't in the path
+ paths = [path for path in glob.iglob(kb_path_wildcard, recursive=True) if 'keymaps' not in path]
+
+ # strip the keyboard directory path prefix and rules.mk suffix and alphabetize
+ find_name = lambda path: path.replace(base_path, "").replace(os.path.sep + "rules.mk", "")
+ names = sorted(map(find_name, paths))
+
+ for name in names:
+ # We echo instead of cli.log.info to allow easier piping of this output
+ cli.echo(name)
diff --git a/lib/python/qmk/cli/new/__init__.py b/lib/python/qmk/cli/new/__init__.py
index e69de29bb2..c6a26939b8 100644
--- a/lib/python/qmk/cli/new/__init__.py
+++ b/lib/python/qmk/cli/new/__init__.py
@@ -0,0 +1 @@
+from . import keymap
diff --git a/lib/python/qmk/cli/new/keymap.py b/lib/python/qmk/cli/new/keymap.py
index b378e5ab43..96525e28e1 100755
--- a/lib/python/qmk/cli/new/keymap.py
+++ b/lib/python/qmk/cli/new/keymap.py
@@ -6,20 +6,20 @@ import shutil
from milc import cli
-@cli.argument('-k', '--keyboard', help='Specify keyboard name. Example: 1upkeyboards/1up60hse')
-@cli.argument('-u', '--username', help='Specify any name for the new keymap directory')
-@cli.entrypoint('Creates a new keymap for the keyboard of your choosing')
-def main(cli):
+@cli.argument('-kb', '--keyboard', help='Specify keyboard name. Example: 1upkeyboards/1up60hse')
+@cli.argument('-km', '--keymap', help='Specify the name for the new keymap directory')
+@cli.subcommand('Creates a new keymap for the keyboard of your choosing')
+def new_keymap(cli):
"""Creates a new keymap for the keyboard of your choosing.
"""
- # ask for user input if keyboard or username was not provided in the command line
- keyboard = cli.config.general.keyboard if cli.config.general.keyboard else input("Keyboard Name: ")
- username = cli.config.general.username if cli.config.general.username else input("Username: ")
+ # ask for user input if keyboard or keymap was not provided in the command line
+ keyboard = cli.config.new_keymap.keyboard if cli.config.new_keymap.keyboard else input("Keyboard Name: ")
+ keymap = cli.config.new_keymap.keymap if cli.config.new_keymap.keymap else input("Keymap Name: ")
# generate keymap paths
kb_path = os.path.join(os.getcwd(), "keyboards", keyboard)
keymap_path_default = os.path.join(kb_path, "keymaps/default")
- keymap_path = os.path.join(kb_path, "keymaps/%s" % username)
+ keymap_path = os.path.join(kb_path, "keymaps/%s" % keymap)
# check directories
if not os.path.exists(kb_path):
@@ -36,6 +36,5 @@ def main(cli):
shutil.copytree(keymap_path_default, keymap_path, symlinks=True)
# end message to user
- cli.log.info("%s keymap directory created in: %s\n" +
- "Compile a firmware file with your new keymap by typing: \n" +
- "qmk compile -kb %s -km %s", username, keymap_path, keyboard, username)
+ cli.log.info("%s keymap directory created in: %s", keymap, keymap_path)
+ cli.log.info("Compile a firmware with your new keymap by typing: \n" + "qmk compile -kb %s -km %s", keyboard, keymap)
diff --git a/lib/python/qmk/cli/pyformat.py b/lib/python/qmk/cli/pyformat.py
index b1f8c02b28..a53ba40c0a 100755
--- a/lib/python/qmk/cli/pyformat.py
+++ b/lib/python/qmk/cli/pyformat.py
@@ -5,12 +5,13 @@ from milc import cli
import subprocess
-@cli.entrypoint("Format python code according to QMK's style.")
-def main(cli):
+@cli.subcommand("Format python code according to QMK's style.")
+def pyformat(cli):
"""Format python code according to QMK's style.
"""
try:
subprocess.run(['yapf', '-vv', '-ri', 'bin/qmk', 'lib/python'], check=True)
cli.log.info('Successfully formatted the python code in `bin/qmk` and `lib/python`.')
+
except subprocess.CalledProcessError:
cli.log.error('Error formatting python code!')
diff --git a/lib/python/qmk/cli/nose2.py b/lib/python/qmk/cli/pytest.py
index c6c9c67b30..14613e1d96 100644
--- a/lib/python/qmk/cli/nose2.py
+++ b/lib/python/qmk/cli/pytest.py
@@ -2,17 +2,19 @@
QMK script to run unit and integration tests against our python code.
"""
+import sys
from milc import cli
-@cli.entrypoint('QMK Python Unit Tests')
-def main(cli):
+@cli.subcommand('QMK Python Unit Tests')
+def pytest(cli):
"""Use nose2 to run unittests
"""
try:
import nose2
+
except ImportError:
cli.log.error('Could not import nose2! Please install it with {fg_cyan}pip3 install nose2')
return False
- nose2.discover()
+ nose2.discover(argv=['nose2', '-v'])
diff --git a/lib/python/qmk/path.py b/lib/python/qmk/path.py
index cf087265fb..2149625cc6 100644
--- a/lib/python/qmk/path.py
+++ b/lib/python/qmk/path.py
@@ -2,6 +2,7 @@
"""
import logging
import os
+from pkgutil import walk_packages
from qmk.errors import NoSuchKeyboardError
diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py
new file mode 100644
index 0000000000..9a9dc4b958
--- /dev/null
+++ b/lib/python/qmk/tests/test_cli_commands.py
@@ -0,0 +1,47 @@
+import subprocess
+
+
+def check_subcommand(command, *args):
+ cmd = ['bin/qmk', command] + list(args)
+ return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
+
+
+def test_cformat():
+ assert check_subcommand('cformat', 'tmk_core/common/backlight.c').returncode == 0
+
+
+def test_compile():
+ assert check_subcommand('compile', '-kb', 'handwired/onekey/pytest', '-km', 'default').returncode == 0
+
+
+def test_config():
+ result = check_subcommand('config')
+ assert result.returncode == 0
+ assert 'general.color' in result.stdout
+
+
+def test_doctor():
+ result = check_subcommand('doctor')
+ assert result.returncode == 0
+ assert 'QMK Doctor is checking your environment.' in result.stderr
+ assert 'QMK is ready to go' in result.stderr
+
+
+def test_hello():
+ result = check_subcommand('hello')
+ assert result.returncode == 0
+ assert 'Hello,' in result.stderr
+
+
+def test_pyformat():
+ result = check_subcommand('pyformat')
+ assert result.returncode == 0
+ assert 'Successfully formatted the python code' in result.stderr
+
+
+def test_list_keyboards():
+ result = check_subcommand('list-keyboards')
+ assert result.returncode == 0
+ # check to see if a known keyboard is returned
+ # this will fail if handwired/onekey/pytest is removed
+ assert 'handwired/onekey/pytest' in result.stdout
diff --git a/message.mk b/message.mk
index dfbde2845b..53afba2065 100644
--- a/message.mk
+++ b/message.mk
@@ -76,6 +76,11 @@ define GENERATE_MSG_MAKE_TEST
endef
MSG_MAKE_TEST = $(eval $(call GENERATE_MSG_MAKE_TEST))$(MSG_MAKE_TEST_ACTUAL)
MSG_TEST = Testing $(BOLD)$(TEST_NAME)$(NO_COLOR)
+define GENERATE_MSG_AVAILABLE_KEYMAPS
+ MSG_AVAILABLE_KEYMAPS_ACTUAL := Available keymaps for $(BOLD)$$(CURRENT_KB)$(NO_COLOR):
+endef
+MSG_AVAILABLE_KEYMAPS = $(eval $(call GENERATE_MSG_AVAILABLE_KEYMAPS))$(MSG_AVAILABLE_KEYMAPS_ACTUAL)
+
MSG_CHECK_FILESIZE = Checking file size of $(TARGET).hex
MSG_FILE_TOO_BIG = $(ERROR_COLOR)The firmware is too large!$(NO_COLOR) $(CURRENT_SIZE)/$(MAX_SIZE) ($(OVER_SIZE) bytes over)\n
MSG_FILE_TOO_SMALL = The firmware is too small! $(CURRENT_SIZE)/$(MAX_SIZE)\n
diff --git a/tmk_core/common/backlight.c b/quantum/backlight/backlight.c
index 708022f68f..708022f68f 100644
--- a/tmk_core/common/backlight.c
+++ b/quantum/backlight/backlight.c
diff --git a/tmk_core/common/backlight.h b/quantum/backlight/backlight.h
index bb1f897ee8..bb1f897ee8 100644
--- a/tmk_core/common/backlight.h
+++ b/quantum/backlight/backlight.h
diff --git a/quantum/color.c b/quantum/color.c
index 847129736d..1f398e2403 100644
--- a/quantum/color.c
+++ b/quantum/color.c
@@ -36,7 +36,11 @@ RGB hsv_to_rgb(HSV hsv) {
h = hsv.h;
s = hsv.s;
+#ifdef USE_CIE1931_CURVE
+ v = pgm_read_byte(&CIE1931_CURVE[hsv.v]);
+#else
v = hsv.v;
+#endif
region = h * 6 / 255;
remainder = (h * 2 - region * 85) * 3;
@@ -79,11 +83,5 @@ RGB hsv_to_rgb(HSV hsv) {
break;
}
-#ifdef USE_CIE1931_CURVE
- rgb.r = pgm_read_byte(&CIE1931_CURVE[rgb.r]);
- rgb.g = pgm_read_byte(&CIE1931_CURVE[rgb.g]);
- rgb.b = pgm_read_byte(&CIE1931_CURVE[rgb.b]);
-#endif
-
return rgb;
}
diff --git a/quantum/config_common.h b/quantum/config_common.h
index fb9f1fd00a..f42df6357d 100644
--- a/quantum/config_common.h
+++ b/quantum/config_common.h
@@ -175,7 +175,7 @@
// LEDs (only D5/C13 uses an actual LED)
# ifdef CONVERT_TO_PROTON_C_RXLED
-# define D5 PAL_LINE(GPIOC, 13)
+# define D5 PAL_LINE(GPIOC, 14)
# define B0 PAL_LINE(GPIOC, 13)
# else
# define D5 PAL_LINE(GPIOC, 13)
@@ -303,6 +303,25 @@
UCSR1C = _BV(UCSZ11) | _BV(UCSZ10); \
sei(); \
} while (0)
+# elif (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__))
+# define SERIAL_UART_BAUD 115200
+# define SERIAL_UART_DATA UDR1
+ /* UBRR should result in ~16 and set UCSR1A = _BV(U2X1) as per rn42 documentation. HC05 needs baudrate configured accordingly */
+# define SERIAL_UART_UBRR (F_CPU / (8UL * SERIAL_UART_BAUD) - 1)
+# define SERIAL_UART_RXD_VECT USART1_RX_vect
+# define SERIAL_UART_TXD_READY (UCSR1A & _BV(UDRE1))
+# define SERIAL_UART_INIT() do { \
+ UCSR1A = _BV(U2X1); \
+ /* baud rate */ \
+ UBRR1L = SERIAL_UART_UBRR; \
+ /* baud rate */ \
+ UBRR1H = SERIAL_UART_UBRR >> 8; \
+ /* enable TX */ \
+ UCSR1B = _BV(TXEN1); \
+ /* 8-bit data */ \
+ UCSR1C = _BV(UCSZ11) | _BV(UCSZ10); \
+ sei(); \
+ } while(0)
# else
# error "USART configuration is needed."
# endif
diff --git a/quantum/encoder.c b/quantum/encoder.c
index b3b1cd9f2b..36a6403b36 100644
--- a/quantum/encoder.c
+++ b/quantum/encoder.c
@@ -40,8 +40,10 @@ static int8_t encoder_LUT[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1,
static uint8_t encoder_state[NUMBER_OF_ENCODERS] = {0};
#ifdef SPLIT_KEYBOARD
-// slave half encoders come over as second set of encoders
+// right half encoders come over as second set of encoders
static int8_t encoder_value[NUMBER_OF_ENCODERS * 2] = {0};
+// row offsets for each hand
+static uint8_t thisHand, thatHand;
#else
static int8_t encoder_value[NUMBER_OF_ENCODERS] = {0};
#endif
@@ -68,20 +70,33 @@ void encoder_init(void) {
encoder_state[i] = (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
}
+
+#ifdef SPLIT_KEYBOARD
+ thisHand = isLeftHand ? 0 : NUMBER_OF_ENCODERS;
+ thatHand = NUMBER_OF_ENCODERS - thisHand;
+#endif
+}
+
+static void encoder_update(int8_t index, uint8_t state) {
+ encoder_value[index] += encoder_LUT[state & 0xF];
+ if (encoder_value[index] >= ENCODER_RESOLUTION) {
+ encoder_update_kb(index, false);
+ }
+ if (encoder_value[index] <= -ENCODER_RESOLUTION) { // direction is arbitrary here, but this clockwise
+ encoder_update_kb(index, true);
+ }
+ encoder_value[index] %= ENCODER_RESOLUTION;
}
void encoder_read(void) {
for (int i = 0; i < NUMBER_OF_ENCODERS; i++) {
encoder_state[i] <<= 2;
encoder_state[i] |= (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
- encoder_value[i] += encoder_LUT[encoder_state[i] & 0xF];
- if (encoder_value[i] >= ENCODER_RESOLUTION) {
- encoder_update_kb(i, false);
- }
- if (encoder_value[i] <= -ENCODER_RESOLUTION) { // direction is arbitrary here, but this clockwise
- encoder_update_kb(i, true);
- }
- encoder_value[i] %= ENCODER_RESOLUTION;
+#if SPLIT_KEYBOARD
+ encoder_update(i + thisHand, encoder_state[i]);
+#else
+ encoder_update(i, encoder_state[i]);
+#endif
}
}
@@ -90,14 +105,7 @@ void encoder_state_raw(uint8_t* slave_state) { memcpy(slave_state, encoder_state
void encoder_update_raw(uint8_t* slave_state) {
for (int i = 0; i < NUMBER_OF_ENCODERS; i++) {
- encoder_value[NUMBER_OF_ENCODERS + i] += encoder_LUT[slave_state[i] & 0xF];
- if (encoder_value[NUMBER_OF_ENCODERS + i] >= ENCODER_RESOLUTION) {
- encoder_update_kb(NUMBER_OF_ENCODERS + i, false);
- }
- if (encoder_value[NUMBER_OF_ENCODERS + i] <= -ENCODER_RESOLUTION) { // direction is arbitrary here, but this clockwise
- encoder_update_kb(NUMBER_OF_ENCODERS + i, true);
- }
- encoder_value[NUMBER_OF_ENCODERS + i] %= ENCODER_RESOLUTION;
+ encoder_update(i + thatHand, slave_state[i]);
}
}
#endif
diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c
index 9af9510081..4fa45ac37b 100644
--- a/quantum/keymap_common.c
+++ b/quantum/keymap_common.c
@@ -26,9 +26,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "action.h"
#include "action_macro.h"
#include "debug.h"
-#include "backlight.h"
#include "quantum.h"
+#ifdef BACKLIGHT_ENABLE
+# include "backlight.h"
+#endif
+
#ifdef MIDI_ENABLE
# include "process_midi.h"
#endif
diff --git a/quantum/keymap_extras/keymap_italian.h b/quantum/keymap_extras/keymap_italian.h
index 544ae94086..a8c03b884f 100644
--- a/quantum/keymap_extras/keymap_italian.h
+++ b/quantum/keymap_extras/keymap_italian.h
@@ -78,35 +78,36 @@
#define IT_MINS KC_SLSH // - and _
// shifted characters
-#define IT_PIPE LSFT(IT_BKSL) // °
-#define IT_EXLM LSFT(KC_1) // !
-#define IT_DQOT LSFT(KC_2) // "
-#define IT_STRL LSFT(KC_3) // £
-#define IT_DLR LSFT(KC_4) // $
-#define IT_PERC LSFT(KC_5) // %
-#define IT_AMPR LSFT(KC_6) // &
-#define IT_SLSH LSFT(KC_7) // /
-#define IT_LPRN LSFT(KC_8) // (
-#define IT_RPRN LSFT(KC_9) // )
-#define IT_EQL LSFT(KC_0) // =
-#define IT_QST LSFT(IT_APOS) // ?
-#define IT_CRC LSFT(IT_IACC) // ^
-#define IT_ASTR LSFT(IT_PLUS) // *
-#define IT_MORE LSFT(IT_LESS) // >
-#define IT_COLN LSFT(IT_DOT) // :
-#define IT_SCLN LSFT(IT_COMM) // ;
-#define IT_UNDS LSFT(IT_MINS) // _
+#define IT_DEGR LSFT(IT_AACC) // °
+#define IT_EXLM LSFT(KC_1) // !
+#define IT_DQOT LSFT(KC_2) // "
+#define IT_STRL LSFT(KC_3) // £
+#define IT_DLR LSFT(KC_4) // $
+#define IT_PERC LSFT(KC_5) // %
+#define IT_AMPR LSFT(KC_6) // &
+#define IT_SLSH LSFT(KC_7) // /
+#define IT_LPRN LSFT(KC_8) // (
+#define IT_RPRN LSFT(KC_9) // )
+#define IT_EQL LSFT(KC_0) // =
+#define IT_QST LSFT(IT_APOS) // ?
+#define IT_CRC LSFT(IT_IACC) // ^
+#define IT_ASTR LSFT(IT_PLUS) // *
+#define IT_MORE LSFT(IT_LESS) // >
+#define IT_COLN LSFT(IT_DOT) // :
+#define IT_SCLN LSFT(IT_COMM) // ;
+#define IT_UNDS LSFT(IT_MINS) // _
// Alt Gr-ed characters
-#define IT_LCBR ALGR(KC_7) // {
-#define IT_LBRC ALGR(IT_EACC) // [
-#define IT_RBRC ALGR(IT_PLUS) // ]
-#define IT_RCBR ALGR(KC_0) // }
-#define IT_AT ALGR(IT_OACC) // @
-#define IT_EURO ALGR(KC_E) // €
-#define IT_PIPE LSFT(IT_BKSL) // |
-#define IT_SHRP ALGR(IT_AACC) // #
+#define IT_LCBR ALGR(KC_7) // {
+#define IT_LBRC ALGR(IT_EACC) // [
+#define IT_RBRC ALGR(IT_PLUS) // ]
+#define IT_RCBR ALGR(KC_0) // }
+#define IT_AT ALGR(IT_OACC) // @
+#define IT_EURO ALGR(KC_E) // €
+#define IT_PIPE LSFT(IT_BSLS) // |
+#define IT_SHRP ALGR(IT_AACC) // #
-#define IT_X_PLUS X_RBRACKET // #
+// Deprecated
+#define IT_X_PLUS X_RBRACKET // #
#endif
diff --git a/quantum/keymap_extras/keymap_italian_osx_ansi.h b/quantum/keymap_extras/keymap_italian_osx_ansi.h
new file mode 100644
index 0000000000..2b7160ff33
--- /dev/null
+++ b/quantum/keymap_extras/keymap_italian_osx_ansi.h
@@ -0,0 +1,113 @@
+/* Copyright 2015-2016 Matthias Schmidtt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// This is a clone of quantum/keymap_extra/keymap_italian.h intended to be used with Apple devices
+
+#ifndef KEYMAP_ITALIAN
+#define KEYMAP_ITALIAN
+
+#include "keymap.h"
+
+// normal characters
+#define IT_A KC_A
+#define IT_B KC_B
+#define IT_C KC_C
+#define IT_D KC_D
+#define IT_E KC_E
+#define IT_F KC_F
+#define IT_G KC_G
+#define IT_H KC_H
+#define IT_I KC_I
+#define IT_J KC_J
+#define IT_K KC_K
+#define IT_L KC_L
+#define IT_M KC_M
+#define IT_N KC_N
+#define IT_O KC_O
+#define IT_P KC_P
+#define IT_Q KC_Q
+#define IT_R KC_R
+#define IT_S KC_S
+#define IT_T KC_T
+#define IT_U KC_U
+#define IT_V KC_V
+#define IT_W KC_W
+#define IT_X KC_X
+#define IT_Y KC_Y
+#define IT_Z KC_Z
+
+#define IT_0 KC_0
+#define IT_1 KC_1
+#define IT_2 KC_2
+#define IT_3 KC_3
+#define IT_4 KC_4
+#define IT_5 KC_5
+#define IT_6 KC_6
+#define IT_7 KC_7
+#define IT_8 KC_8
+#define IT_9 KC_9
+
+// punctuation
+#define IT_DOT KC_DOT // . and :
+#define IT_COMM KC_COMM // , and ;
+#define IT_APOS KC_MINS // ' and ?
+#define IT_BSLS KC_NUBS // \ and |
+#define IT_LESS KC_GRV // < and >
+#define IT_MINS KC_SLSH // - and _
+
+// accented vowels (regular, with shift, with option, with option and shift)
+#define IT_EACC KC_LBRC // è, é, [, {
+#define IT_PLUS KC_RBRC // +, *, ], }
+#define IT_OACC KC_SCLN // ò, ç, @, Ç
+#define IT_AACC KC_QUOT // à, °, #, ∞
+#define IT_UACC KC_BSLS // ù, §, ¶, ◊
+#define IT_IACC KC_EQL // ì, ^, ˆ, ±
+
+// shifted characters
+#define IT_EXLM LSFT(KC_1) // !
+#define IT_DQOT LSFT(KC_2) // "
+#define IT_STRL LSFT(KC_3) // £
+#define IT_DLR LSFT(KC_4) // $
+#define IT_PERC LSFT(KC_5) // %
+#define IT_AMPR LSFT(KC_6) // &
+#define IT_SLSH LSFT(KC_7) // /
+#define IT_LPRN LSFT(KC_8) // (
+#define IT_RPRN LSFT(KC_9) // )
+#define IT_EQL LSFT(KC_0) // =
+#define IT_DEGR LSFT(IT_AACC) // °
+#define IT_QST LSFT(IT_APOS) // ?
+#define IT_CRC LSFT(IT_IACC) // ^
+#define IT_ASTR LSFT(IT_PLUS) // *
+#define IT_MORE LSFT(IT_LESS) // >
+#define IT_COLN LSFT(IT_DOT) // :
+#define IT_SCLN LSFT(IT_COMM) // ;
+#define IT_UNDS LSFT(IT_MINS) // _
+#define IT_LCBR LSFT(IT_LBRC) // {
+#define IT_RCBR LSFT(IT_RBRC) // }
+#define IT_PIPE LSFT(IT_BSLS) // |
+
+// Alt -ed characters
+#define IT_LBRC LALT(IT_EACC) // [
+#define IT_RBRC LALT(IT_PLUS) // ]
+#define IT_AT LALT(IT_OACC) // @
+#define IT_EURO LALT(KC_E) // €
+#define IT_SHRP LALT(IT_AACC ) // #
+#define IT_ACUT LALT(KC_8) // ´
+#define IT_GRAVE LALT(KC_9) // `
+#define IT_TILDE LALT(KC_5) // ~
+#define IT_PLMN LALT(LSFT(IT_IACC)) // ±
+
+#endif
diff --git a/quantum/keymap_extras/keymap_italian_osx_iso.h b/quantum/keymap_extras/keymap_italian_osx_iso.h
new file mode 100644
index 0000000000..5c920014a1
--- /dev/null
+++ b/quantum/keymap_extras/keymap_italian_osx_iso.h
@@ -0,0 +1,113 @@
+/* Copyright 2015-2016 Matthias Schmidtt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// This is a clone of quantum/keymap_extra/keymap_italian.h intended to be used with Apple devices
+
+#ifndef KEYMAP_ITALIAN
+#define KEYMAP_ITALIAN
+
+#include "keymap.h"
+
+// normal characters
+#define IT_A KC_A
+#define IT_B KC_B
+#define IT_C KC_C
+#define IT_D KC_D
+#define IT_E KC_E
+#define IT_F KC_F
+#define IT_G KC_G
+#define IT_H KC_H
+#define IT_I KC_I
+#define IT_J KC_J
+#define IT_K KC_K
+#define IT_L KC_L
+#define IT_M KC_M
+#define IT_N KC_N
+#define IT_O KC_O
+#define IT_P KC_P
+#define IT_Q KC_Q
+#define IT_R KC_R
+#define IT_S KC_S
+#define IT_T KC_T
+#define IT_U KC_U
+#define IT_V KC_V
+#define IT_W KC_W
+#define IT_X KC_X
+#define IT_Y KC_Y
+#define IT_Z KC_Z
+
+#define IT_0 KC_0
+#define IT_1 KC_1
+#define IT_2 KC_2
+#define IT_3 KC_3
+#define IT_4 KC_4
+#define IT_5 KC_5
+#define IT_6 KC_6
+#define IT_7 KC_7
+#define IT_8 KC_8
+#define IT_9 KC_9
+
+// punctuation
+#define IT_DOT KC_DOT // . and :
+#define IT_COMM KC_COMM // , and ;
+#define IT_APOS KC_MINS // ' and ?
+#define IT_BSLS KC_GRV // \ and |
+#define IT_LESS KC_NUBS// < and >
+#define IT_MINS KC_SLSH // - and _
+
+// accented vowels (regular, with shift, with option, with option and shift)
+#define IT_EACC KC_LBRC // è, é, [, {
+#define IT_PLUS KC_RBRC // +, *, ], }
+#define IT_OACC KC_SCLN // ò, ç, @, Ç
+#define IT_AACC KC_QUOT // à, °, #, ∞
+#define IT_UACC KC_BSLS // ù, §, ¶, ◊
+#define IT_IACC KC_EQL // ì, ^, ˆ, ±
+
+// shifted characters
+#define IT_EXLM LSFT(KC_1) // !
+#define IT_DQOT LSFT(KC_2) // "
+#define IT_STRL LSFT(KC_3) // £
+#define IT_DLR LSFT(KC_4) // $
+#define IT_PERC LSFT(KC_5) // %
+#define IT_AMPR LSFT(KC_6) // &
+#define IT_SLSH LSFT(KC_7) // /
+#define IT_LPRN LSFT(KC_8) // (
+#define IT_RPRN LSFT(KC_9) // )
+#define IT_EQL LSFT(KC_0) // =
+#define IT_DEGR LSFT(IT_AACC) // °
+#define IT_QST LSFT(IT_APOS) // ?
+#define IT_CRC LSFT(IT_IACC) // ^
+#define IT_ASTR LSFT(IT_PLUS) // *
+#define IT_MORE LSFT(IT_LESS) // >
+#define IT_COLN LSFT(IT_DOT) // :
+#define IT_SCLN LSFT(IT_COMM) // ;
+#define IT_UNDS LSFT(IT_MINS) // _
+#define IT_LCBR LSFT(IT_LBRC) // {
+#define IT_RCBR LSFT(IT_RBRC) // }
+#define IT_PIPE LSFT(IT_BSLS) // |
+
+// Alt -ed characters
+#define IT_LBRC LALT(IT_EACC) // [
+#define IT_RBRC LALT(IT_PLUS) // ]
+#define IT_AT LALT(IT_OACC) // @
+#define IT_EURO LALT(KC_E) // €
+#define IT_SHRP LALT(IT_AACC ) // #
+#define IT_ACUT LALT(KC_8) // ´
+#define IT_GRAVE LALT(KC_9) // `
+#define IT_TILDE LALT(KC_5) // ~
+#define IT_PLMN LALT(LSFT(IT_IACC)) // ±
+
+#endif
diff --git a/quantum/led_tables.c b/quantum/led_tables.c
index 8cbf6f4c02..6eb2c09dc8 100644
--- a/quantum/led_tables.c
+++ b/quantum/led_tables.c
@@ -15,14 +15,50 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "led_tables.h"
+// clang-format off
+
#ifdef USE_CIE1931_CURVE
// Lightness curve using the CIE 1931 lightness formula
// Generated by the python script provided in http://jared.geek.nz/2013/feb/linear-led-pwm
-const uint8_t CIE1931_CURVE[256] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 28, 28, 29, 29, 30, 31, 31, 32, 33, 33, 34, 35, 35, 36, 37, 37, 38, 39, 40, 40, 41, 42, 43, 44, 44, 45, 46,
- 47, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 79, 80, 82, 83, 84, 85, 87, 88, 89, 90, 92, 93, 94, 96, 97, 99, 100, 101, 103, 104, 106, 107, 108, 110, 111, 113, 114, 116, 118, 119, 121, 122, 124, 125, 127, 129, 130, 132, 134, 135, 137, 139, 141, 142, 144, 146, 148, 149, 151, 153, 155, 157, 159, 161, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 185, 187, 189, 191, 193, 195, 197, 200, 202, 204, 206, 208, 211, 213, 215, 218, 220, 222, 225, 227, 230, 232, 234, 237, 239, 242, 244, 247, 249, 252, 255};
+const uint8_t CIE1931_CURVE[256] PROGMEM = {
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
+ 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7,
+ 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12,
+ 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 17, 17, 17,
+ 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25,
+ 26, 26, 27, 27, 28, 29, 29, 30, 30, 31, 32, 32, 33, 34, 34, 35,
+ 36, 36, 37, 38, 38, 39, 40, 41, 41, 42, 43, 44, 45, 45, 46, 47,
+ 48, 49, 50, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76, 77, 78, 79,
+ 80, 81, 83, 84, 85, 86, 88, 89, 90, 91, 93, 94, 95, 97, 98, 100,
+ 101, 102, 104, 105, 107, 108, 109, 111, 112, 114, 115, 117, 119, 120, 122, 123,
+ 125, 126, 128, 130, 131, 133, 135, 136, 138, 140, 142, 143, 145, 147, 149, 150,
+ 152, 154, 156, 158, 160, 162, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181,
+ 183, 186, 188, 190, 192, 194, 196, 198, 201, 203, 205, 207, 209, 212, 214, 216,
+ 219, 221, 223, 226, 228, 231, 233, 235, 238, 240, 243, 245, 248, 250, 253, 255
+};
#endif
#ifdef USE_LED_BREATHING_TABLE
-const uint8_t LED_BREATHING_TABLE[256] PROGMEM = {0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, 79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, 127, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170, 173, 176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211, 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240, 241, 243, 244, 245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254, 254, 255, 255, 255,
- 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251, 250, 250, 249, 248, 246, 245, 244, 243, 241, 240, 238, 237, 235, 234, 232, 230, 228, 226, 224, 222, 220, 218, 215, 213, 211, 208, 206, 203, 201, 198, 196, 193, 190, 188, 185, 182, 179, 176, 173, 170, 167, 165, 162, 158, 155, 152, 149, 146, 143, 140, 137, 134, 131, 128, 124, 121, 118, 115, 112, 109, 106, 103, 100, 97, 93, 90, 88, 85, 82, 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, 37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0};
+const uint8_t LED_BREATHING_TABLE[256] PROGMEM = {
+ 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9,
+ 10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
+ 37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
+ 79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124,
+ 127, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170, 173,
+ 176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211, 213, 215,
+ 218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240, 241, 243, 244,
+ 245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254, 254, 255, 255, 255,
+ 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251, 250, 250, 249, 248, 246,
+ 245, 244, 243, 241, 240, 238, 237, 235, 234, 232, 230, 228, 226, 224, 222, 220,
+ 218, 215, 213, 211, 208, 206, 203, 201, 198, 196, 193, 190, 188, 185, 182, 179,
+ 176, 173, 170, 167, 165, 162, 158, 155, 152, 149, 146, 143, 140, 137, 134, 131,
+ 128, 124, 121, 118, 115, 112, 109, 106, 103, 100, 97, 93, 90, 88, 85, 82,
+ 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40,
+ 37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11,
+ 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0
+};
#endif
+
+// clang-format on
diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c
index c27fe48347..16756e59c2 100644
--- a/quantum/process_keycode/process_tap_dance.c
+++ b/quantum/process_keycode/process_tap_dance.c
@@ -71,7 +71,7 @@ void qk_tap_dance_dual_role_finished(qk_tap_dance_state_t *state, void *user_dat
if (state->count == 1) {
register_code16(pair->kc);
} else if (state->count == 2) {
- layer_move(pair->layer);
+ pair->layer_function(pair->layer);
}
}
diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h
index b2d0cb8297..8d227dfd70 100644
--- a/quantum/process_keycode/process_tap_dance.h
+++ b/quantum/process_keycode/process_tap_dance.h
@@ -56,13 +56,19 @@ typedef struct {
typedef struct {
uint16_t kc;
uint8_t layer;
+ void (*layer_function)(uint8_t);
} qk_tap_dance_dual_role_t;
# define ACTION_TAP_DANCE_DOUBLE(kc1, kc2) \
{ .fn = {qk_tap_dance_pair_on_each_tap, qk_tap_dance_pair_finished, qk_tap_dance_pair_reset}, .user_data = (void *)&((qk_tap_dance_pair_t){kc1, kc2}), }
# define ACTION_TAP_DANCE_DUAL_ROLE(kc, layer) \
- { .fn = {qk_tap_dance_dual_role_on_each_tap, qk_tap_dance_dual_role_finished, qk_tap_dance_dual_role_reset}, .user_data = (void *)&((qk_tap_dance_dual_role_t){kc, layer}), }
+ { .fn = { qk_tap_dance_dual_role_on_each_tap, qk_tap_dance_dual_role_finished, qk_tap_dance_dual_role_reset }, .user_data = (void *)&((qk_tap_dance_dual_role_t) { kc, layer, layer_move }), }
+
+# define ACTION_TAP_DANCE_TOGGLE_LAYER(kc, layer) \
+ { .fn = { NULL, qk_tap_dance_dual_role_finished, qk_tap_dance_dual_role_reset }, .user_data = (void *)&((qk_tap_dance_dual_role_t) { kc, layer, layer_invert }), }
+
+# define ACTION_TAP_DANCE_LAYER_MOVE(kc, layer) ACTION_TAP_DANCE_DUAL_ROLE(kc, layer)
# define ACTION_TAP_DANCE_FN(user_fn) \
{ .fn = {NULL, user_fn, NULL}, .user_data = NULL, }
@@ -73,6 +79,8 @@ typedef struct {
# define ACTION_TAP_DANCE_FN_ADVANCED_TIME(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset, tap_specific_tapping_term) \
{ .fn = {user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset}, .user_data = NULL, .custom_tapping_term = tap_specific_tapping_term, }
+
+
extern qk_tap_dance_action_t tap_dance_actions[];
/* To be used internally */
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 90df0293b7..82c7a5265b 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -28,8 +28,10 @@
# define BREATHING_PERIOD 6
#endif
-#include "backlight.h"
-extern backlight_config_t backlight_config;
+#ifdef BACKLIGHT_ENABLE
+# include "backlight.h"
+ extern backlight_config_t backlight_config;
+#endif
#ifdef FAUXCLICKY_ENABLE
# include "fauxclicky.h"
@@ -55,6 +57,10 @@ extern backlight_config_t backlight_config;
# include "encoder.h"
#endif
+#ifdef WEBUSB_ENABLE
+# include "webusb.h"
+#endif
+
#ifdef AUDIO_ENABLE
# ifndef GOODBYE_SONG
# define GOODBYE_SONG SONG(GOODBYE_SOUND)
@@ -89,44 +95,28 @@ static void do_code16(uint16_t code, void (*f)(uint8_t)) {
return;
}
- if (code & QK_LCTL) f(KC_LCTL);
- if (code & QK_LSFT) f(KC_LSFT);
- if (code & QK_LALT) f(KC_LALT);
- if (code & QK_LGUI) f(KC_LGUI);
-
- if (code < QK_RMODS_MIN) return;
-
- if (code & QK_RCTL) f(KC_RCTL);
- if (code & QK_RSFT) f(KC_RSFT);
- if (code & QK_RALT) f(KC_RALT);
- if (code & QK_RGUI) f(KC_RGUI);
-}
-
-static inline void qk_register_weak_mods(uint8_t kc) {
- add_weak_mods(MOD_BIT(kc));
- send_keyboard_report();
-}
-
-static inline void qk_unregister_weak_mods(uint8_t kc) {
- del_weak_mods(MOD_BIT(kc));
- send_keyboard_report();
-}
+ uint8_t mods_to_send = 0;
-static inline void qk_register_mods(uint8_t kc) {
- add_weak_mods(MOD_BIT(kc));
- send_keyboard_report();
-}
+ if (code & QK_RMODS_MIN) { // Right mod flag is set
+ if (code & QK_LCTL) mods_to_send |= MOD_BIT(KC_RCTL);
+ if (code & QK_LSFT) mods_to_send |= MOD_BIT(KC_RSFT);
+ if (code & QK_LALT) mods_to_send |= MOD_BIT(KC_RALT);
+ if (code & QK_LGUI) mods_to_send |= MOD_BIT(KC_RGUI);
+ } else {
+ if (code & QK_LCTL) mods_to_send |= MOD_BIT(KC_LCTL);
+ if (code & QK_LSFT) mods_to_send |= MOD_BIT(KC_LSFT);
+ if (code & QK_LALT) mods_to_send |= MOD_BIT(KC_LALT);
+ if (code & QK_LGUI) mods_to_send |= MOD_BIT(KC_LGUI);
+ }
-static inline void qk_unregister_mods(uint8_t kc) {
- del_weak_mods(MOD_BIT(kc));
- send_keyboard_report();
+ f(mods_to_send);
}
void register_code16(uint16_t code) {
if (IS_MOD(code) || code == KC_NO) {
- do_code16(code, qk_register_mods);
+ do_code16(code, register_mods);
} else {
- do_code16(code, qk_register_weak_mods);
+ do_code16(code, register_weak_mods);
}
register_code(code);
}
@@ -134,9 +124,9 @@ void register_code16(uint16_t code) {
void unregister_code16(uint16_t code) {
unregister_code(code);
if (IS_MOD(code) || code == KC_NO) {
- do_code16(code, qk_unregister_mods);
+ do_code16(code, unregister_mods);
} else {
- do_code16(code, qk_unregister_weak_mods);
+ do_code16(code, unregister_weak_mods);
}
}
@@ -581,6 +571,7 @@ bool process_record_quantum(keyrecord_t *record) {
keymap_config.swap_backslash_backspace = true;
break;
case MAGIC_HOST_NKRO:
+ clear_keyboard(); // clear first buffer to prevent stuck keys
keymap_config.nkro = true;
break;
case MAGIC_SWAP_ALT_GUI:
@@ -623,6 +614,7 @@ bool process_record_quantum(keyrecord_t *record) {
keymap_config.swap_backslash_backspace = false;
break;
case MAGIC_UNHOST_NKRO:
+ clear_keyboard(); // clear first buffer to prevent stuck keys
keymap_config.nkro = false;
break;
case MAGIC_UNSWAP_ALT_GUI:
@@ -660,6 +652,7 @@ bool process_record_quantum(keyrecord_t *record) {
#endif
break;
case MAGIC_TOGGLE_NKRO:
+ clear_keyboard(); // clear first buffer to prevent stuck keys
keymap_config.nkro = !keymap_config.nkro;
break;
default:
@@ -724,6 +717,13 @@ bool process_record_quantum(keyrecord_t *record) {
return false;
}
#endif
+#ifdef WEBUSB_ENABLE
+ case WEBUSB_PAIR:
+ if (record->event.pressed) {
+ webusb_state.pairing = true;
+ }
+ return false;
+#endif
}
return process_action_kb(record);
@@ -1104,6 +1104,22 @@ void matrix_scan_quantum() {
# define COMxx1 COM1A1
# define OCRxx OCR1A
# endif
+# elif defined(__AVR_ATmega328P__) && (BACKLIGHT_PIN == B1 || BACKLIGHT_PIN == B2)
+# define HARDWARE_PWM
+# define ICRx ICR1
+# define TCCRxA TCCR1A
+# define TCCRxB TCCR1B
+# define TIMERx_OVF_vect TIMER1_OVF_vect
+# define TIMSKx TIMSK1
+# define TOIEx TOIE1
+
+# if BACKLIGHT_PIN == B1
+# define COMxx1 COM1A1
+# define OCRxx OCR1A
+# elif BACKLIGHT_PIN == B2
+# define COMxx1 COM1B1
+# define OCRxx OCR1B
+# endif
# else
# if !defined(BACKLIGHT_CUSTOM_DRIVER)
# if !defined(B5_AUDIO) && !defined(B6_AUDIO) && !defined(B7_AUDIO)
diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h
index f5dca02e6b..872aa89bc4 100644
--- a/quantum/quantum_keycodes.h
+++ b/quantum/quantum_keycodes.h
@@ -473,6 +473,9 @@ enum quantum_keycodes {
HPT_BUZ,
HPT_MODI,
HPT_MODD,
+ HPT_CONT,
+ HPT_CONI,
+ HPT_COND,
HPT_DWLI,
HPT_DWLD,
@@ -500,6 +503,9 @@ enum quantum_keycodes {
MAGIC_UNSWAP_CTL_GUI,
MAGIC_TOGGLE_CTL_GUI,
+#ifdef WEBUSB_ENABLE
+ WEBUSB_PAIR,
+#endif
// always leave at the end
SAFE_RANGE
};
diff --git a/quantum/rgb_matrix_drivers.c b/quantum/rgb_matrix_drivers.c
index 5b54bd5956..503f97014f 100644
--- a/quantum/rgb_matrix_drivers.c
+++ b/quantum/rgb_matrix_drivers.c
@@ -97,19 +97,33 @@ const rgb_matrix_driver_t rgb_matrix_driver = {
#elif defined(WS2812)
-extern LED_TYPE led[DRIVER_LED_TOTAL];
+// LED color buffer
+LED_TYPE led[DRIVER_LED_TOTAL];
+
+static void init(void) {}
static void flush(void) {
// Assumes use of RGB_DI_PIN
ws2812_setleds(led, DRIVER_LED_TOTAL);
}
-static void init(void) {}
+// Set an led in the buffer to a color
+static inline void setled(int i, uint8_t r, uint8_t g, uint8_t b) {
+ led[i].r = r;
+ led[i].g = g;
+ led[i].b = b;
+}
+
+static void setled_all(uint8_t r, uint8_t g, uint8_t b) {
+ for (int i = 0; i < sizeof(led) / sizeof(led[0]); i++) {
+ setled(i, r, g, b);
+ }
+}
const rgb_matrix_driver_t rgb_matrix_driver = {
.init = init,
.flush = flush,
- .set_color = ws2812_setled,
- .set_color_all = ws2812_setled_all,
+ .set_color = setled,
+ .set_color_all = setled_all,
};
#endif
diff --git a/quantum/rgblight.c b/quantum/rgblight.c
index a094863fe9..1c197827f2 100644
--- a/quantum/rgblight.c
+++ b/quantum/rgblight.c
@@ -910,6 +910,9 @@ void rgblight_effect_snake(animation_status_t *anim) {
ledp->b = 0;
for (j = 0; j < RGBLIGHT_EFFECT_SNAKE_LENGTH; j++) {
k = pos + j * increment;
+ if (k > RGBLED_NUM) {
+ k = k % RGBLED_NUM;
+ }
if (k < 0) {
k = k + effect_num_leds;
}
diff --git a/quantum/template/ps2avrgb/rules.mk b/quantum/template/ps2avrgb/rules.mk
index 69554cd308..52d9988125 100644
--- a/quantum/template/ps2avrgb/rules.mk
+++ b/quantum/template/ps2avrgb/rules.mk
@@ -14,9 +14,7 @@ EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = yes
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = no
-RGBLIGHT_ENABLE = no
-RGBLIGHT_CUSTOM_DRIVER = yes
+RGBLIGHT_ENABLE = yes
+WS2812_DRIVER = i2c
OPT_DEFS = -DDEBUG_LEVEL=0
-
-SRC += i2c_master.c
diff --git a/quantum/template/ps2avrgb/template.c b/quantum/template/ps2avrgb/template.c
index acc8698f56..503da7ca71 100644
--- a/quantum/template/ps2avrgb/template.c
+++ b/quantum/template/ps2avrgb/template.c
@@ -15,44 +15,30 @@
*/
#include "%KEYBOARD%.h"
-#ifdef RGBLIGHT_ENABLE
-# include <string.h>
-# include "i2c_master.h"
-# include "rgblight.h"
+// Optional override functions below.
+// You can leave any or all of these undefined.
+// These are only required if you want to perform custom actions.
-extern rgblight_config_t rgblight_config;
+/*
void matrix_init_kb(void) {
- i2c_init();
- // call user level keymaps, if any
- matrix_init_user();
-}
-
-// custom RGB driver
-void rgblight_set(void) {
- if (!rgblight_config.enable) {
- memset(led, 0, 3 * RGBLED_NUM);
- }
+ // put your keyboard start-up code here
+ // runs once when the firmware starts up
- i2c_transmit(0xb0, (uint8_t*)led, 3 * RGBLED_NUM, 100);
+ matrix_init_user();
}
-bool rgb_init = false;
-
void matrix_scan_kb(void) {
- // if LEDs were previously on before poweroff, turn them back on
- if (rgb_init == false && rgblight_config.enable) {
- i2c_transmit(0xb0, (uint8_t*)led, 3 * RGBLED_NUM, 100);
- rgb_init = true;
- }
-
- rgblight_task();
- matrix_scan_user();
+ // put your looping keyboard code here
+ // runs every cycle (a lot)
+
+ matrix_scan_user();
}
-#endif
+bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
+ // put your per-action keyboard code here
+ // runs for every action, just before processing by the firmware
-__attribute__ ((weak))
-void matrix_scan_user(void) {
+ return process_record_user(keycode, record);
}
diff --git a/requirements.txt b/requirements.txt
index 351dc2524e..f6257e399e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
# Python requirements
# milc FIXME(skullydazed): Included in the repo for now.
+appdirs
argcomplete
colorama
-#halo
diff --git a/shell.nix b/shell.nix
index e85221975d..df5aff3508 100644
--- a/shell.nix
+++ b/shell.nix
@@ -32,7 +32,7 @@ in
stdenv.mkDerivation {
name = "qmk-firmware";
- buildInputs = [ dfu-programmer dfu-util diffutils git ]
+ buildInputs = [ dfu-programmer dfu-util diffutils git python3 ]
++ lib.optional avr [ avrbinutils avrgcc avrlibc avrdude ]
++ lib.optional arm [ gcc-arm-embedded ]
++ lib.optional teensy [ teensy-loader-cli ];
diff --git a/tmk_core/common.mk b/tmk_core/common.mk
index 2216887555..a32abd8ebc 100644
--- a/tmk_core/common.mk
+++ b/tmk_core/common.mk
@@ -118,6 +118,10 @@ ifeq ($(strip $(RAW_ENABLE)), yes)
TMK_COMMON_DEFS += -DRAW_ENABLE
endif
+ifeq ($(strip $(WEBUSB_ENABLE)), yes)
+ TMK_COMMON_DEFS += -DWEBUSB_ENABLE
+endif
+
ifeq ($(strip $(CONSOLE_ENABLE)), yes)
TMK_COMMON_DEFS += -DCONSOLE_ENABLE
else
@@ -153,11 +157,6 @@ ifeq ($(strip $(NO_SUSPEND_POWER_DOWN)), yes)
TMK_COMMON_DEFS += -DNO_SUSPEND_POWER_DOWN
endif
-ifeq ($(strip $(BACKLIGHT_ENABLE)), yes)
- TMK_COMMON_SRC += $(COMMON_DIR)/backlight.c
- TMK_COMMON_DEFS += -DBACKLIGHT_ENABLE
-endif
-
ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)
TMK_COMMON_DEFS += -DBLUETOOTH_ENABLE
TMK_COMMON_DEFS += -DNO_USB_STARTUP_CHECK
diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c
index 1ba38a285e..bd6aeba4f8 100644
--- a/tmk_core/common/action.c
+++ b/tmk_core/common/action.c
@@ -20,7 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "mousekey.h"
#include "command.h"
#include "led.h"
-#include "backlight.h"
#include "action_layer.h"
#include "action_tapping.h"
#include "action_macro.h"
@@ -28,6 +27,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "action.h"
#include "wait.h"
+#ifdef BACKLIGHT_ENABLE
+# include "backlight.h"
+#endif
+
#ifdef DEBUG_ACTION
# include "debug.h"
#else
@@ -868,9 +871,9 @@ void tap_code(uint8_t code) {
unregister_code(code);
}
-/** \brief Utilities for actions. (FIXME: Needs better description)
+/** \brief Adds the given physically pressed modifiers and sends a keyboard report immediately.
*
- * FIXME: Needs documentation.
+ * \param mods A bitfield of modifiers to unregister.
*/
void register_mods(uint8_t mods) {
if (mods) {
@@ -879,9 +882,9 @@ void register_mods(uint8_t mods) {
}
}
-/** \brief Utilities for actions. (FIXME: Needs better description)
+/** \brief Removes the given physically pressed modifiers and sends a keyboard report immediately.
*
- * FIXME: Needs documentation.
+ * \param mods A bitfield of modifiers to unregister.
*/
void unregister_mods(uint8_t mods) {
if (mods) {
@@ -890,6 +893,28 @@ void unregister_mods(uint8_t mods) {
}
}
+/** \brief Adds the given weak modifiers and sends a keyboard report immediately.
+ *
+ * \param mods A bitfield of modifiers to register.
+ */
+void register_weak_mods(uint8_t mods) {
+ if (mods) {
+ add_weak_mods(mods);
+ send_keyboard_report();
+ }
+}
+
+/** \brief Removes the given weak modifiers and sends a keyboard report immediately.
+ *
+ * \param mods A bitfield of modifiers to unregister.
+ */
+void unregister_weak_mods(uint8_t mods) {
+ if (mods) {
+ del_weak_mods(mods);
+ send_keyboard_report();
+ }
+}
+
/** \brief Utilities for actions. (FIXME: Needs better description)
*
* FIXME: Needs documentation.
diff --git a/tmk_core/common/action.h b/tmk_core/common/action.h
index 633cedb06b..15f4ce15c0 100644
--- a/tmk_core/common/action.h
+++ b/tmk_core/common/action.h
@@ -90,6 +90,8 @@ void unregister_code(uint8_t code);
void tap_code(uint8_t code);
void register_mods(uint8_t mods);
void unregister_mods(uint8_t mods);
+void register_weak_mods(uint8_t mods);
+void unregister_weak_mods(uint8_t mods);
// void set_mods(uint8_t mods);
void clear_keyboard(void);
void clear_keyboard_but_mods(void);
diff --git a/tmk_core/common/avr/suspend.c b/tmk_core/common/avr/suspend.c
index 574000fcd8..c59c196880 100644
--- a/tmk_core/common/avr/suspend.c
+++ b/tmk_core/common/avr/suspend.c
@@ -4,7 +4,6 @@
#include <avr/interrupt.h>
#include "matrix.h"
#include "action.h"
-#include "backlight.h"
#include "suspend_avr.h"
#include "suspend.h"
#include "timer.h"
@@ -16,6 +15,10 @@
# include "lufa.h"
#endif
+#ifdef BACKLIGHT_ENABLE
+# include "backlight.h"
+#endif
+
#ifdef AUDIO_ENABLE
# include "audio.h"
#endif /* AUDIO_ENABLE */
diff --git a/tmk_core/common/chibios/printf.c b/tmk_core/common/chibios/printf.c
index dbd059448c..dcf33f35f8 100644
--- a/tmk_core/common/chibios/printf.c
+++ b/tmk_core/common/chibios/printf.c
@@ -186,6 +186,15 @@ void tfp_format(void* putp, putcf putf, char* fmt, va_list va) {
case 's':
putchw(putp, putf, w, 0, va_arg(va, char*));
break;
+ case 'b':
+#ifdef PRINTF_LONG_SUPPORT
+ if (lng)
+ uli2a(va_arg(va, unsigned long int), 2, 0, bf);
+ else
+#endif
+ ui2a(va_arg(va, unsigned int), 2, 0, bf);
+ putchw(putp, putf, w, lz, bf);
+ break;
case '%':
putf(putp, ch);
default:
diff --git a/tmk_core/common/chibios/suspend.c b/tmk_core/common/chibios/suspend.c
index ae1c6f53e2..c0f9c28d44 100644
--- a/tmk_core/common/chibios/suspend.c
+++ b/tmk_core/common/chibios/suspend.c
@@ -8,10 +8,13 @@
#include "action_util.h"
#include "mousekey.h"
#include "host.h"
-#include "backlight.h"
#include "suspend.h"
#include "wait.h"
+#ifdef BACKLIGHT_ENABLE
+# include "backlight.h"
+#endif
+
/** \brief suspend idle
*
* FIXME: needs doc
diff --git a/tmk_core/common/command.c b/tmk_core/common/command.c
index 8bf72ef258..82cd806091 100644
--- a/tmk_core/common/command.c
+++ b/tmk_core/common/command.c
@@ -32,10 +32,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "sleep_led.h"
#include "led.h"
#include "command.h"
-#include "backlight.h"
#include "quantum.h"
#include "version.h"
+#ifdef BACKLIGHT_ENABLE
+# include "backlight.h"
+#endif
+
#ifdef MOUSEKEY_ENABLE
# include "mousekey.h"
#endif
diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c
index 060b45f1bc..8746b2c124 100644
--- a/tmk_core/common/keyboard.c
+++ b/tmk_core/common/keyboard.c
@@ -29,8 +29,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "util.h"
#include "sendchar.h"
#include "eeconfig.h"
-#include "backlight.h"
#include "action_layer.h"
+#ifdef BACKLIGHT_ENABLE
+# include "backlight.h"
+#endif
#ifdef BOOTMAGIC_ENABLE
# include "bootmagic.h"
#else
@@ -82,6 +84,26 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
# include "velocikey.h"
#endif
+// Only enable this if console is enabled to print to
+#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE)
+static uint32_t matrix_timer = 0;
+static uint32_t matrix_scan_count = 0;
+
+void matrix_scan_perf_task(void) {
+ matrix_scan_count++;
+
+ uint32_t timer_now = timer_read32();
+ if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) {
+ dprintf("matrix scan frequency: %d\n", matrix_scan_count);
+
+ matrix_timer = timer_now;
+ matrix_scan_count = 0;
+ }
+}
+#else
+# define matrix_scan_perf_task()
+#endif
+
#ifdef MATRIX_HAS_GHOST
extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS];
static matrix_row_t get_real_keys(uint8_t row, matrix_row_t rowdata) {
@@ -296,6 +318,10 @@ void keyboard_task(void) {
MATRIX_LOOP_END:
+#ifdef DEBUG_MATRIX_SCAN_RATE
+ matrix_scan_perf_task();
+#endif
+
#ifdef QWIIC_ENABLE
qwiic_task();
#endif
diff --git a/tmk_core/common/timer.h b/tmk_core/common/timer.h
index 853cb98396..378cf7892c 100644
--- a/tmk_core/common/timer.h
+++ b/tmk_core/common/timer.h
@@ -45,9 +45,9 @@ uint16_t timer_elapsed(uint16_t last);
uint32_t timer_elapsed32(uint32_t last);
// Utility functions to check if a future time has expired & autmatically handle time wrapping if checked / reset frequently (half of max value)
-inline bool timer_expired(uint16_t current, uint16_t last) { return current - last < 0x8000; }
+inline bool timer_expired(uint16_t current, uint16_t future) { return (uint16_t)(current - future) < 0x8000; }
-inline bool timer_expired32(uint32_t current, uint32_t future) { return current - future < 0x80000000; }
+inline bool timer_expired32(uint32_t current, uint32_t future) { return (uint32_t)(current - future) < 0x80000000; }
#ifdef __cplusplus
}
diff --git a/tmk_core/common/util.h b/tmk_core/common/util.h
index 5706b047d0..68642e7fd3 100644
--- a/tmk_core/common/util.h
+++ b/tmk_core/common/util.h
@@ -27,6 +27,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define STR(s) XSTR(s)
#define XSTR(s) #s
+#ifdef __cplusplus
+extern "C" {
+#endif
+
uint8_t bitpop(uint8_t bits);
uint8_t bitpop16(uint16_t bits);
uint8_t bitpop32(uint32_t bits);
@@ -39,4 +43,8 @@ uint8_t bitrev(uint8_t bits);
uint16_t bitrev16(uint16_t bits);
uint32_t bitrev32(uint32_t bits);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/tmk_core/common/webusb.c b/tmk_core/common/webusb.c
new file mode 100644
index 0000000000..2488e75de7
--- /dev/null
+++ b/tmk_core/common/webusb.c
@@ -0,0 +1,72 @@
+#include QMK_KEYBOARD_H
+#include <string.h>
+#include "webusb.h"
+#include "wait.h"
+webusb_state_t webusb_state = {
+ .paired = false,
+ .pairing = false,
+};
+
+void webusb_receive(uint8_t *data, uint8_t length) {
+ uint8_t command = data[0];
+
+ if(command == WEBUSB_CMD_PAIR && webusb_state.pairing == true) {
+ uint8_t event[3];
+ webusb_state.pairing = false;
+ webusb_state.paired = true;
+ event[0] = WEBUSB_STATUS_OK;
+ event[1] = WEBUSB_EVT_PAIRED;
+ event[2] = WEBUSB_STOP_BIT;
+ webusb_send(event, sizeof(event));
+ return;
+ }
+
+ if(command == WEBUSB_GET_FW_VERSION) {
+ // Landing page + packet headers(2) + stop bit(1)
+ uint8_t lp_size = sizeof(FIRMWARE_VERSION) + 3;
+ uint8_t url[lp_size];
+
+ uint8_t event[2];
+ event[0] = WEBUSB_STATUS_OK;
+ event[1] = WEBUSB_EVT_FW_VERSION;
+
+ uint8_t stop[1];
+ stop[0] = WEBUSB_STOP_BIT;
+
+ memcpy(url, event, 2);
+ memcpy(url + 2, FIRMWARE_VERSION, sizeof(FIRMWARE_VERSION));
+ memcpy(url + 2 + sizeof(FIRMWARE_VERSION), stop, 1);
+ webusb_send(url, lp_size);
+ return;
+ }
+
+ if(webusb_state.paired == true) {
+ switch(command) {
+ //Handle commands in here
+ case WEBUSB_GET_LAYER:
+ webusb_layer_event();
+ break;
+ default:
+ break;
+ }
+ } else {
+ webusb_error(WEBUSB_STATUS_NOT_PAIRED);
+ }
+};
+
+void webusb_layer_event() {
+ uint8_t layer;
+ uint8_t event[4];
+ layer = biton32(layer_state);
+ event[0] = WEBUSB_STATUS_OK;
+ event[1] = WEBUSB_EVT_LAYER;
+ event[2] = layer;
+ event[3] = WEBUSB_STOP_BIT;
+ webusb_send(event, sizeof(event));
+}
+
+void webusb_error(uint8_t code) {
+ uint8_t buffer[1];
+ buffer[0] = code;
+ webusb_send(buffer, 1);
+}
diff --git a/tmk_core/common/webusb.h b/tmk_core/common/webusb.h
new file mode 100644
index 0000000000..18e884f1ab
--- /dev/null
+++ b/tmk_core/common/webusb.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifndef FIRMWARE_VERSION
+#define FIRMWARE_VERSION u8"default"
+#endif
+#define WEBUSB_STOP_BIT -2
+#define WEBUSB_BLINK_STEPS 512
+#define WEBUSB_BLINK_END WEBUSB_BLINK_STEPS * 60
+
+void webusb_receive(uint8_t *data, uint8_t length);
+void webusb_send(uint8_t *data, uint8_t length);
+void webusb_layer_event(void);
+void webusb_error(uint8_t);
+void webusb_set_pairing_state(void);
+
+typedef struct{
+ bool paired;
+ bool pairing;
+} webusb_state_t;
+
+extern webusb_state_t webusb_state;
+
+enum Webusb_Status_Code {
+ WEBUSB_STATUS_NOT_PAIRED = -1,
+ WEBUSB_STATUS_OK,
+ WEBUSB_STATUS_UNKNOWN_COMMAND,
+};
+
+enum Webusb_Command_Code {
+ WEBUSB_CMD_PAIR,
+ WEBUSB_GET_FW_VERSION,
+ WEBUSB_GET_LAYER
+};
+
+enum Webusb_Event_Code {
+ WEBUSB_EVT_PAIRED,
+ WEBUSB_EVT_KEYDOWN,
+ WEBUSB_EVT_KEYUP,
+ WEBUSB_EVT_LAYER,
+ WEBUSB_EVT_FW_VERSION
+};
diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c
index de2b493b84..16f7f31b47 100644
--- a/tmk_core/protocol/chibios/main.c
+++ b/tmk_core/protocol/chibios/main.c
@@ -77,6 +77,10 @@ void raw_hid_task(void);
void console_task(void);
#endif
+#ifdef WEBUSB_ENABLE
+void webusb_task(void);
+#endif
+
/* TESTING
* Amber LED blinker thread, times are in milliseconds.
*/
@@ -215,5 +219,8 @@ int main(void) {
#ifdef RAW_ENABLE
raw_hid_task();
#endif
+#ifdef WEBUSB_ENABLE
+ webusb_task();
+#endif
}
}
diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c
index c8a6bbb43f..0acb78c9ad 100644
--- a/tmk_core/protocol/chibios/usb_main.c
+++ b/tmk_core/protocol/chibios/usb_main.c
@@ -37,6 +37,9 @@
extern keymap_config_t keymap_config;
#endif
+#ifdef WEBUSB_ENABLE
+#include "webusb.h"
+#endif
/* ---------------------------------------------------------
* Global interface variables and declarations
* ---------------------------------------------------------
@@ -151,6 +154,22 @@ static const USBEndpointConfig shared_ep_config = {
};
#endif
+#ifdef WEBUSB_ENABLE
+/** Microsoft OS 2.0 Descriptor. This is used by Windows to select the USB driver for the device.
+ *
+ * For WebUSB in Chrome, the correct driver is WinUSB, which is selected via CompatibleID.
+ *
+ * Additionally, while Chrome is built using libusb, a magic registry key needs to be set containing a GUID for
+ * the device.
+ */
+const MS_OS_20_Descriptor_t PROGMEM MS_OS_20_Descriptor = MS_OS_20_DESCRIPTOR;
+
+/** URL descriptor string. This is a UTF-8 string containing a URL excluding the prefix. At least one of these must be
+ * defined and returned when the Landing Page descriptor index is requested.
+ */
+const WebUSB_URL_Descriptor_t PROGMEM WebUSB_LandingPage = WEBUSB_URL_DESCRIPTOR(WEBUSB_LANDING_PAGE_URL);
+#endif
+
typedef struct {
size_t queue_capacity_in;
size_t queue_capacity_out;
@@ -233,6 +252,9 @@ typedef struct {
#ifdef VIRTSER_ENABLE
usb_driver_config_t serial_driver;
#endif
+#ifdef WEBUSB_ENABLE
+ usb_driver_config_t webusb_driver;
+#endif
};
usb_driver_config_t array[0];
};
@@ -269,6 +291,14 @@ static usb_driver_configs_t drivers = {
# define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK
.serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false),
#endif
+
+#ifdef WEBUSB_ENABLE
+# define WEBUSB_IN_CAPACITY 4
+# define WEBUSB_OUT_CAPACITY 4
+# define WEBUSB_IN_MODE USB_EP_MODE_TYPE_INTR
+# define WEBUSB_OUT_MODE USB_EP_MODE_TYPE_INTR
+ .webusb_driver = QMK_USB_DRIVER_CONFIG(WEBUSB, 0, false),
+#endif
};
#define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t))
@@ -496,6 +526,27 @@ static bool usb_request_hook_cb(USBDriver *usbp) {
}
}
+#ifdef WEBUSB_ENABLE
+ switch (usbp->setup[1]) {
+ case WEBUSB_VENDOR_CODE:
+ if (usbp->setup[4] == WebUSB_RTYPE_GetURL) {
+ if (usbp->setup[2] == WEBUSB_LANDING_PAGE_INDEX) {
+ usbSetupTransfer(usbp, (uint8_t *)&WebUSB_LandingPage, WebUSB_LandingPage.Header.Size, NULL);
+ return TRUE;
+ break;
+ }
+ }
+ break;
+
+ case MS_OS_20_VENDOR_CODE:
+ if (usbp->setup[4] == MS_OS_20_DESCRIPTOR_INDEX) {
+ usbSetupTransfer(usbp, (uint8_t *)&MS_OS_20_Descriptor, MS_OS_20_Descriptor.Header.TotalLength, NULL);
+ return TRUE;
+ break;
+ }
+ break;
+ }
+#endif
/* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */
if ((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) {
dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4]));
@@ -817,6 +868,33 @@ void raw_hid_task(void) {
#endif
+#ifdef WEBUSB_ENABLE
+void webusb_send(uint8_t *data, uint8_t length) {
+ if(chnWriteTimeout(&drivers.webusb_driver.driver, data, length, TIME_IMMEDIATE) != length){
+ webusb_state.paired = false;
+ webusb_state.pairing = false;
+ }
+}
+
+__attribute__((weak)) void webusb_receive_kb(uint8_t *data, uint8_t length) {
+ // Users should #include "raw_hid.h" in their own code
+ // and implement this function there. Leave this as weak linkage
+ // so users can opt to not handle data coming in.
+}
+
+void webusb_task(void) {
+ uint8_t buffer[WEBUSB_EPSIZE];
+ size_t size = 0;
+ do {
+ size_t size = chnReadTimeout(&drivers.webusb_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
+ if (size > 0) {
+ webusb_receive(buffer, size);
+ }
+ } while (size > 0);
+}
+
+#endif
+
#ifdef MIDI_ENABLE
void send_midi_packet(MIDI_EventPacket_t *event) { chnWrite(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t)); }
diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c
index 12a6924fd0..a6ac44c597 100644
--- a/tmk_core/protocol/lufa/lufa.c
+++ b/tmk_core/protocol/lufa/lufa.c
@@ -90,6 +90,10 @@ extern keymap_config_t keymap_config;
# include "raw_hid.h"
#endif
+#ifdef WEBUSB_ENABLE
+#include "webusb.h"
+#endif
+
uint8_t keyboard_idle = 0;
/* 0: Boot Protocol, 1: Report Protocol(default) */
uint8_t keyboard_protocol = 1;
@@ -268,6 +272,68 @@ static void Console_Task(void) {
}
#endif
+#ifdef WEBUSB_ENABLE
+void webusb_send(uint8_t *data, uint8_t length) {
+ if (USB_DeviceState != DEVICE_STATE_Configured) {
+ return;
+ }
+
+ Endpoint_SelectEndpoint(WEBUSB_IN_EPNUM);
+
+ if(Endpoint_Write_Stream_LE(data, length, NULL)) {
+ // Stream failed to complete, resetting WEBUSB's state
+ webusb_state.paired = false;
+ webusb_state.pairing = false;
+ }
+ Endpoint_ClearIN();
+}
+
+__attribute__((weak)) void webusb_receive_kb(uint8_t *data, uint8_t length) { }
+
+static void webusb_task(void) {
+ // Create a temporary buffer to hold the read in data from the host
+ uint8_t data[WEBUSB_EPSIZE];
+ bool data_read = false;
+
+
+ // Device must be connected and configured for the task to run
+ if (USB_DeviceState != DEVICE_STATE_Configured) return;
+
+ Endpoint_SelectEndpoint(WEBUSB_OUT_EPNUM);
+
+ // Check to see if a packet has been sent from the host
+ if (Endpoint_IsOUTReceived()) {
+ // Check to see if the packet contains data
+ if (Endpoint_IsReadWriteAllowed()) {
+ /* Read data */
+ Endpoint_Read_Stream_LE(data, sizeof(data), NULL);
+ data_read = true;
+ }
+
+ // Finalize the stream transfer to receive the last packet
+ Endpoint_ClearOUT();
+
+ if (data_read) {
+ webusb_receive(data, sizeof(data));
+ }
+ }
+}
+
+/** Microsoft OS 2.0 Descriptor. This is used by Windows to select the USB driver for the device.
+ *
+ * For WebUSB in Chrome, the correct driver is WinUSB, which is selected via CompatibleID.
+ *
+ * Additionally, while Chrome is built using libusb, a magic registry key needs to be set containing a GUID for
+ * the device.
+ */
+const MS_OS_20_Descriptor_t PROGMEM MS_OS_20_Descriptor = MS_OS_20_DESCRIPTOR;
+
+/** URL descriptor string. This is a UTF-8 string containing a URL excluding the prefix. At least one of these must be
+ * defined and returned when the Landing Page descriptor index is requested.
+ */
+const WebUSB_URL_Descriptor_t PROGMEM WebUSB_LandingPage = WEBUSB_URL_DESCRIPTOR(WEBUSB_LANDING_PAGE_URL);
+#endif
+
/*******************************************************************************
* USB Events
******************************************************************************/
@@ -405,6 +471,12 @@ void EVENT_USB_Device_ConfigurationChanged(void) {
# endif
#endif
+#ifdef WEBUSB_ENABLE
+ /* Setup Webusb Endpoints */
+ ConfigSuccess &= Endpoint_ConfigureEndpoint(WEBUSB_IN_EPADDR, EP_TYPE_INTERRUPT, WEBUSB_EPSIZE, 1);
+ ConfigSuccess &= Endpoint_ConfigureEndpoint(WEBUSB_OUT_EPADDR, EP_TYPE_INTERRUPT, WEBUSB_EPSIZE, 1);
+#endif
+
#ifdef MIDI_ENABLE
ConfigSuccess &= Endpoint_ConfigureEndpoint(MIDI_STREAM_IN_EPADDR, EP_TYPE_BULK, MIDI_STREAM_EPSIZE, ENDPOINT_BANK_SINGLE);
ConfigSuccess &= Endpoint_ConfigureEndpoint(MIDI_STREAM_OUT_EPADDR, EP_TYPE_BULK, MIDI_STREAM_EPSIZE, ENDPOINT_BANK_SINGLE);
@@ -536,6 +608,48 @@ void EVENT_USB_Device_ControlRequest(void) {
}
break;
+#ifdef WEBUSB_ENABLE
+ case WEBUSB_VENDOR_CODE:
+ if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_VENDOR | REQREC_DEVICE)) {
+ switch (USB_ControlRequest.wIndex) {
+ case WebUSB_RTYPE_GetURL:
+ switch (USB_ControlRequest.wValue) {
+ case WEBUSB_LANDING_PAGE_INDEX:
+ Endpoint_ClearSETUP();
+ /* Write the descriptor data to the control endpoint */
+ Endpoint_Write_Control_PStream_LE(&WebUSB_LandingPage, WebUSB_LandingPage.Header.Size);
+ /* Release the endpoint after transaction. */
+ Endpoint_ClearStatusStage();
+ break;
+ default: /* Stall transfer on invalid index. */
+ Endpoint_StallTransaction();
+ break;
+ }
+ break;
+ default: /* Stall on unknown WebUSB request */
+ Endpoint_StallTransaction();
+ break;
+ }
+ }
+
+ break;
+ case MS_OS_20_VENDOR_CODE:
+ if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_VENDOR | REQREC_DEVICE)) {
+ switch (USB_ControlRequest.wIndex) {
+ case MS_OS_20_DESCRIPTOR_INDEX:
+ Endpoint_ClearSETUP();
+ /* Write the descriptor data to the control endpoint */
+ Endpoint_Write_Control_PStream_LE(&MS_OS_20_Descriptor, MS_OS_20_Descriptor.Header.TotalLength);
+ /* Release the endpoint after transaction. */
+ Endpoint_ClearStatusStage();
+ break;
+ default: /* Stall on unknown MS OS 2.0 request */
+ Endpoint_StallTransaction();
+ break;
+ }
+ }
+ break;
+#endif
}
#ifdef VIRTSER_ENABLE
@@ -1018,6 +1132,10 @@ int main(void) {
raw_hid_task();
#endif
+#ifdef WEBUSB_ENABLE
+ webusb_task();
+#endif
+
#if !defined(INTERRUPT_CONTROL_ENDPOINT)
USB_USBTask();
#endif
diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c
index 7d509f4ef4..4dd2dfdc9e 100644
--- a/tmk_core/protocol/usb_descriptor.c
+++ b/tmk_core/protocol/usb_descriptor.c
@@ -39,7 +39,9 @@
#include "util.h"
#include "report.h"
#include "usb_descriptor.h"
-
+#ifdef WEBUSB_ENABLE
+#include "webusb_descriptor.h"
+#endif
/*
* HID report descriptors
*/
@@ -274,11 +276,22 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = {
};
#endif
+#ifdef WEBUSB_ENABLE
+const USB_Descriptor_BOS_t PROGMEM BOSDescriptor = BOS_DESCRIPTOR(
+ (MS_OS_20_PLATFORM_DESCRIPTOR(MS_OS_20_VENDOR_CODE, MS_OS_20_DESCRIPTOR_SET_TOTAL_LENGTH))
+ (WEBUSB_PLATFORM_DESCRIPTOR(WEBUSB_VENDOR_CODE, WEBUSB_LANDING_PAGE_INDEX))
+);
+#endif
+
/*
* Device descriptor
*/
const USB_Descriptor_Device_t PROGMEM DeviceDescriptor = {.Header = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device},
+#if WEBUSB_ENABLE
+ .USBSpecification = VERSION_BCD(2, 1, 0),
+#else
.USBSpecification = VERSION_BCD(1, 1, 0),
+#endif
#if VIRTSER_ENABLE
.Class = USB_CSCP_IADDeviceClass,
.SubClass = USB_CSCP_IADDeviceSubclass,
@@ -331,6 +344,16 @@ const USB_Descriptor_Configuration_t PROGMEM
.Keyboard_INEndpoint = {.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, .EndpointAddress = (ENDPOINT_DIR_IN | KEYBOARD_IN_EPNUM), .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), .EndpointSize = KEYBOARD_EPSIZE, .PollingIntervalMS = USB_POLLING_INTERVAL_MS},
#endif
+#ifdef RAW_ENABLE
+ /*
+ * Raw HID
+ */
+ .Raw_Interface = {.Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, .InterfaceNumber = RAW_INTERFACE, .AlternateSetting = 0x00, .TotalEndpoints = 2, .Class = HID_CSCP_HIDClass, .SubClass = HID_CSCP_NonBootSubclass, .Protocol = HID_CSCP_NonBootProtocol, .InterfaceStrIndex = NO_DESCRIPTOR},
+ .Raw_HID = {.Header = {.Size = sizeof(USB_HID_Descriptor_HID_t), .Type = HID_DTYPE_HID}, .HIDSpec = VERSION_BCD(1, 1, 1), .CountryCode = 0x00, .TotalReportDescriptors = 1, .HIDReportType = HID_DTYPE_Report, .HIDReportLength = sizeof(RawReport)},
+ .Raw_INEndpoint = {.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, .EndpointAddress = (ENDPOINT_DIR_IN | RAW_IN_EPNUM), .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), .EndpointSize = RAW_EPSIZE, .PollingIntervalMS = 0x01},
+ .Raw_OUTEndpoint = {.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, .EndpointAddress = (ENDPOINT_DIR_OUT | RAW_OUT_EPNUM), .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), .EndpointSize = RAW_EPSIZE, .PollingIntervalMS = 0x01},
+#endif
+
#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
/*
* Mouse
@@ -361,16 +384,6 @@ const USB_Descriptor_Configuration_t PROGMEM
.Shared_INEndpoint = {.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, .EndpointAddress = (ENDPOINT_DIR_IN | SHARED_IN_EPNUM), .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), .EndpointSize = SHARED_EPSIZE, .PollingIntervalMS = USB_POLLING_INTERVAL_MS},
#endif
-#ifdef RAW_ENABLE
- /*
- * Raw HID
- */
- .Raw_Interface = {.Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, .InterfaceNumber = RAW_INTERFACE, .AlternateSetting = 0x00, .TotalEndpoints = 2, .Class = HID_CSCP_HIDClass, .SubClass = HID_CSCP_NonBootSubclass, .Protocol = HID_CSCP_NonBootProtocol, .InterfaceStrIndex = NO_DESCRIPTOR},
- .Raw_HID = {.Header = {.Size = sizeof(USB_HID_Descriptor_HID_t), .Type = HID_DTYPE_HID}, .HIDSpec = VERSION_BCD(1, 1, 1), .CountryCode = 0x00, .TotalReportDescriptors = 1, .HIDReportType = HID_DTYPE_Report, .HIDReportLength = sizeof(RawReport)},
- .Raw_INEndpoint = {.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, .EndpointAddress = (ENDPOINT_DIR_IN | RAW_IN_EPNUM), .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), .EndpointSize = RAW_EPSIZE, .PollingIntervalMS = 0x01},
- .Raw_OUTEndpoint = {.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, .EndpointAddress = (ENDPOINT_DIR_OUT | RAW_OUT_EPNUM), .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), .EndpointSize = RAW_EPSIZE, .PollingIntervalMS = 0x01},
-#endif
-
#ifdef CONSOLE_ENABLE
/*
* Console
@@ -381,6 +394,38 @@ const USB_Descriptor_Configuration_t PROGMEM
.Console_OUTEndpoint = {.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, .EndpointAddress = (ENDPOINT_DIR_OUT | CONSOLE_OUT_EPNUM), .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), .EndpointSize = CONSOLE_EPSIZE, .PollingIntervalMS = 0x01},
#endif
+#ifdef WEBUSB_ENABLE
+ /*
+ * Webusb
+ */
+ .WebUSB_Interface = {.Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},
+
+ .InterfaceNumber = INTERFACE_ID_WebUSB,
+ .AlternateSetting = 0x00,
+
+ .TotalEndpoints = 2,
+
+ .Class = USB_CSCP_VendorSpecificClass,
+ .SubClass = 0x00,
+ .Protocol = 0x00,
+
+ .InterfaceStrIndex = NO_DESCRIPTOR},
+
+ .WebUSB_DataInEndpoint = {.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},
+
+ .EndpointAddress = WEBUSB_IN_EPADDR,
+ .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
+ .EndpointSize = WEBUSB_EPSIZE,
+ .PollingIntervalMS = 0x05},
+
+ .WebUSB_DataOutEndpoint = {.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},
+
+ .EndpointAddress = WEBUSB_OUT_EPADDR,
+ .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
+ .EndpointSize = WEBUSB_EPSIZE,
+ .PollingIntervalMS = 0x05},
+#endif
+
#ifdef MIDI_ENABLE
/*
* MIDI
@@ -520,6 +565,13 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
Size = sizeof(USB_Descriptor_Device_t);
break;
+#ifdef WEBUSB_ENABLE
+ case DTYPE_BOS:
+ Address = &BOSDescriptor;
+ Size = pgm_read_byte(&BOSDescriptor.TotalLength);
+
+ break;
+#endif
case DTYPE_Configuration:
Address = &ConfigurationDescriptor;
Size = sizeof(USB_Descriptor_Configuration_t);
diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h
index e922edc452..8ee576cc0d 100644
--- a/tmk_core/protocol/usb_descriptor.h
+++ b/tmk_core/protocol/usb_descriptor.h
@@ -48,6 +48,9 @@
#ifdef PROTOCOL_CHIBIOS
# include "hal.h"
#endif
+#ifdef WEBUSB_ENABLE
+#include "webusb_descriptor.h"
+#endif
/*
* USB descriptor structure
@@ -62,6 +65,14 @@ typedef struct {
USB_Descriptor_Endpoint_t Keyboard_INEndpoint;
#endif
+#ifdef RAW_ENABLE
+ // Raw HID Interface
+ USB_Descriptor_Interface_t Raw_Interface;
+ USB_HID_Descriptor_HID_t Raw_HID;
+ USB_Descriptor_Endpoint_t Raw_INEndpoint;
+ USB_Descriptor_Endpoint_t Raw_OUTEndpoint;
+#endif
+
#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
// Mouse HID Interface
USB_Descriptor_Interface_t Mouse_Interface;
@@ -76,14 +87,6 @@ typedef struct {
USB_Descriptor_Endpoint_t Shared_INEndpoint;
#endif
-#ifdef RAW_ENABLE
- // Raw HID Interface
- USB_Descriptor_Interface_t Raw_Interface;
- USB_HID_Descriptor_HID_t Raw_HID;
- USB_Descriptor_Endpoint_t Raw_INEndpoint;
- USB_Descriptor_Endpoint_t Raw_OUTEndpoint;
-#endif
-
#ifdef CONSOLE_ENABLE
// Console HID Interface
USB_Descriptor_Interface_t Console_Interface;
@@ -92,6 +95,12 @@ typedef struct {
USB_Descriptor_Endpoint_t Console_OUTEndpoint;
#endif
+#ifdef WEBUSB_ENABLE
+ USB_Descriptor_Interface_t WebUSB_Interface;
+ USB_Descriptor_Endpoint_t WebUSB_DataInEndpoint;
+ USB_Descriptor_Endpoint_t WebUSB_DataOutEndpoint;
+#endif
+
#ifdef MIDI_ENABLE
USB_Descriptor_Interface_Association_t Audio_Interface_Association;
// MIDI Audio Control Interface
@@ -164,6 +173,10 @@ enum usb_interfaces {
CDI_INTERFACE,
#endif
+#ifdef WEBUSB_ENABLE
+ INTERFACE_ID_WebUSB,
+#endif
+
TOTAL_INTERFACES
};
@@ -224,6 +237,13 @@ enum usb_endpoints {
# define CDC_IN_EPADDR (ENDPOINT_DIR_IN | CDC_IN_EPNUM)
# define CDC_OUT_EPADDR (ENDPOINT_DIR_OUT | CDC_OUT_EPNUM)
#endif
+
+#ifdef WEBUSB_ENABLE
+ WEBUSB_IN_EPNUM = NEXT_EPNUM,
+ WEBUSB_OUT_EPNUM = NEXT_EPNUM,
+# define WEBUSB_IN_EPADDR (ENDPOINT_DIR_IN | WEBUSB_IN_EPNUM)
+# define WEBUSB_OUT_EPADDR (ENDPOINT_DIR_OUT | WEBUSB_OUT_EPNUM)
+#endif
};
#ifdef PROTOCOL_LUFA
@@ -248,6 +268,7 @@ enum usb_endpoints {
#define MIDI_STREAM_EPSIZE 64
#define CDC_NOTIFICATION_EPSIZE 8
#define CDC_EPSIZE 16
+#define WEBUSB_EPSIZE 64
uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const void** const DescriptorAddress);
#endif
diff --git a/tmk_core/protocol/vusb/main.c b/tmk_core/protocol/vusb/main.c
index f8322d94ac..8cc736497d 100644
--- a/tmk_core/protocol/vusb/main.c
+++ b/tmk_core/protocol/vusb/main.c
@@ -20,6 +20,11 @@
#include "timer.h"
#include "uart.h"
#include "debug.h"
+#include "rgblight_reconfig.h"
+
+#if (defined(RGB_MIDI) | defined(RGBLIGHT_ANIMATIONS)) & defined(RGBLIGHT_ENABLE)
+# include "rgblight.h"
+#endif
#define UART_BAUD_RATE 115200
@@ -94,6 +99,10 @@ int main(void) {
// To prevent failing to configure NOT scan keyboard during configuration
if (usbConfiguration && usbInterruptIsReady()) {
keyboard_task();
+
+#if defined(RGBLIGHT_ANIMATIONS) && defined(RGBLIGHT_ENABLE)
+ rgblight_task();
+#endif
}
vusb_transfer_keyboard();
}
diff --git a/tmk_core/protocol/webusb_descriptor.h b/tmk_core/protocol/webusb_descriptor.h
new file mode 100644
index 0000000000..a2385b8c05
--- /dev/null
+++ b/tmk_core/protocol/webusb_descriptor.h
@@ -0,0 +1,264 @@
+#pragma once
+
+#ifndef WORD_TO_BYTES_LE
+# define WORD_TO_BYTES_LE(n) n % 256, (n / 256) % 256
+#endif
+#ifndef LONG_TO_BYTES_LE
+# define LONG_TO_BYTES_LE(n) n % 256, (n / 256) % 256, (n / 65536) % 256, (n / 16777216) % 256
+#endif
+
+#define WEBUSB_VENDOR_CODE 0x42
+
+#ifndef WEBUSB_LANDING_PAGE_URL
+# define WEBUSB_LANDING_PAGE_URL u8"docs.qmk.fm"
+#endif
+
+#define WEBUSB_LANDING_PAGE_PROTOCOL 1 /* 0: http 1: https forced to 1 since https is a requirement to connect over webusb */
+
+#define WEBUSB_LANDING_PAGE_INDEX 1
+
+#define WEBUSB_VERSION VERSION_BCD(1, 0, 0)
+
+/* $ python -c "import uuid;print(', '.join(map(hex, uuid.UUID('3408b638-09a9-47a0-8bfd-a0768815b665').bytes_le)))" */
+#define WEBUSB_PLATFORM_UUID 0x38, 0xb6, 0x8, 0x34, 0xa9, 0x9, 0xa0, 0x47, 0x8b, 0xfd, 0xa0, 0x76, 0x88, 0x15, 0xb6, 0x65
+
+/** \brief Convenience macro to easily create device capability platform descriptors for the WebUSB platform.
+ *
+ * \note This macro is designed to be wrapped in parentheses and included in a sequence to the \ref BOS_DESCRIPTOR macro.
+ *
+ * \param[in] VendorCode Vendor Code that all control requests coming from the browser must use.
+ *
+ * \param[in] LandingPageIndex Index of the URL Descriptor to use as the Landing Page for the device.
+ *
+ */
+#define WEBUSB_PLATFORM_DESCRIPTOR(VendorCode, LandingPageIndex) /* WebUSB Platform Descriptor size */ 24, DTYPE_DeviceCapability, DCTYPE_Platform, /* Reserved */ 0, WEBUSB_PLATFORM_UUID, WORD_TO_BYTES_LE(WEBUSB_VERSION), VendorCode, LandingPageIndex
+
+/** \brief Convenience macro to easily create \ref WebUSB_URL_Descriptor_t instances from a wide character string.
+ *
+ * \note This macro is for little-endian systems only.
+ *
+ * \param[in] URL URL string to initialize a URL Descriptor structure with.
+ *
+ * \note Prefix String literal with u8 to ensure proper conversion: e.g. WEBUSB_URL_DESCRIPTOR(u8"www.google.com")
+ */
+#define WEBUSB_URL_DESCRIPTOR(URL) \
+ { .Header = {.Size = sizeof(WebUSB_URL_Descriptor_t) + (sizeof(URL) - 1), .Type = WebUSB_DTYPE_URL}, .Scheme = (WEBUSB_LANDING_PAGE_PROTOCOL), .UTF8_URL = (URL) }
+
+/* WebUSB Protocol Data Structures */
+enum WebUSB_Request_t {
+ WebUSB_RTYPE_GetURL = 2, /**< Indicates the device should return the indicated WebUSB_URL descriptor. */
+};
+
+enum WebUSB_Descriptor_t {
+ WebUSB_DTYPE_URL = 3, /**< Indicates that the descriptor is a URL descriptor. */
+};
+
+/** \brief WebUSB URL Descriptor (LUFA naming convention).
+ *
+ * Type define for a WebUSB URL Descriptor. This structure uses LUFA-specific element names
+ * to make each element's purpose clearer.
+ *
+ * \note Regardless of CPU architecture, these values should be stored as little endian.
+ */
+typedef struct {
+ USB_Descriptor_Header_t Header; /**< Descriptor header, including type (WebUSB_DTYPE_URL) and size. */
+
+ uint8_t Scheme; /**< URL scheme prefix: 0 means http://, 1 means https://, 255 means included in URL */
+ uint8_t UTF8_URL[]; /**< UTF-8 encoded URL (excluding scheme prefix). */
+} ATTR_PACKED WebUSB_URL_Descriptor_t;
+
+#define MS_OS_20_VENDOR_CODE 0x45 // Must be different than WEBUSB_VENDOR_CODE
+
+#define MS_OS_20_DESCRIPTOR_CONFIGURATION_HEADER_LENGTH 168
+#define MS_OS_20_DESCRIPTOR_FUNCTION_HEADER_LENGTH 160
+#define MS_OS_20_DESCRIPTOR_SET_TOTAL_LENGTH 178 // Sum of `.Length`s in MS_OS_20_Descriptor in WebUSB.c
+
+#define MS_OS_20_DESCRIPTOR_COMPATIBILITY_ID { 'W', 'I', 'N', 'U', 'S', 'B', 0, 0 }
+#define MS_OS_20_DESCRIPTOR_SUB_COMPATIBILITY_ID {0, 0, 0, 0, 0, 0, 0, 0}
+
+#define MS_OS_20_PROPERTY_NAME_LENGTH 42
+#define MS_OS_20_PROPERTY_NAME { \
+ 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, \
+ 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, \
+ 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, \
+ 'D', 0x00, 's', 0x00, 0x00, 0x00 \
+ }
+#define MS_OS_20_PROPERTY_DATA_LENGTH 80
+#define MS_OS_20_PROPERTY_DATA { \
+ '{', 0x00, '9', 0x00, 'D', 0x00, 'B', 0x00, '7', 0x00, 'F', 0x00, \
+ '1', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00, '7', 0x00, 'E', 0x00, \
+ '7', 0x00, 'A', 0x00, '-', 0x00, '4', 0x00, '0', 0x00, '0', 0x00, \
+ '8', 0x00, '-', 0x00, '9', 0x00, '3', 0x00, '6', 0x00, 'B', 0x00, \
+ '-', 0x00, '6', 0x00, 'B', 0x00, 'A', 0x00, '6', 0x00, 'C', 0x00, \
+ '1', 0x00, '9', 0x00, 'A', 0x00, '3', 0x00, '0', 0x00, '8', 0x00, \
+ 'c', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 \
+ }
+
+#define MS_OS_20_PLATFORM_UUID 0xdf, 0x60, 0xdd, 0xd8, 0x89, 0x45, 0xc7, 0x4c, 0x9c, 0xd2, 0x65, 0x9d, 0x9e, 0x64, 0x8a, 0x9f
+
+#define MS_OS_20_WINDOWS_VERSION_8_1 0x06030000 // Windows version (8.1)
+
+#ifndef MS_OS_20_ALTERNATE_ENUMERATION_CODE
+# define MS_OS_20_ALTERNATE_ENUMERATION_CODE 0 /**< Set to non-zero to enable Windows to allow device to return alternate USB descriptors. */
+#endif
+
+/** \brief Convenience macro to easily create device capability platform descriptors for the MS OS 2.0 platform.
+ *
+ * \note This macro is designed to be wrapped in parentheses and included in a sequence to the \ref BOS_DESCRIPTOR macro.
+ *
+ * \param[in] VendorCode Vendor Code that all control requests coming from Windows must use.
+ *
+ * \param[in] TotalLength The length, in bytes, of the MS OS 2.0 descriptor set to be retrieved by Windows.
+ */
+#define MS_OS_20_PLATFORM_DESCRIPTOR(VendorCode, TotalLength) /* Total size of this descriptor */ 28, DTYPE_DeviceCapability, DCTYPE_Platform, /* Reserved */ 0, MS_OS_20_PLATFORM_UUID, LONG_TO_BYTES_LE(MS_OS_20_WINDOWS_VERSION_8_1), WORD_TO_BYTES_LE(TotalLength), VendorCode, MS_OS_20_ALTERNATE_ENUMERATION_CODE
+
+ /* MS OS 2.0 Descriptors Data Structures */
+ enum MS_OS_20_wIndex_t {
+ MS_OS_20_DESCRIPTOR_INDEX = 0x07, /**< Indicates the device should return MS OS 2.0 Descriptor Set. */
+ MS_OS_20_SET_ALT_ENUMERATION = 0x08, /**< Indicates the device may "subsequently return alternate USB descriptors when Windows requests the information." */
+ };
+
+enum MS_OS_20_Descriptor_Types {
+ MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00,
+ MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01,
+ MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02,
+ MS_OS_20_FEATURE_COMPATBLE_ID = 0x03,
+ MS_OS_20_FEATURE_REG_PROPERTY = 0x04,
+ // MS_OS_20_FEATURE_MIN_RESUME_TIME = 0x05,
+ // MS_OS_20_FEATURE_MODEL_ID = 0x06,
+ MS_OS_20_FEATURE_CCGP_DEVICE = 0x07,
+};
+
+/** \brief Microsoft OS 2.0 Descriptor Set Header (LUFA naming convention).
+ *
+ * \note Regardless of CPU architecture, these values should be stored as little endian.
+ */
+typedef struct {
+ uint16_t Length; /**< The length, in bytes, of this header. Shall be set to 10. */
+ uint16_t DescriptorType; /**< Shall be set to MS_OS_20_SET_HEADER_DESCRIPTOR */
+ uint32_t WindowsVersion;
+ uint16_t TotalLength; /**< The size of entire MS OS 2.0 descriptor set. The value shall match the value in the descriptor set information structure. */
+} ATTR_PACKED MS_OS_20_Descriptor_Set_Header_t;
+
+/** \brief Microsoft OS 2.0 configuration subset header.
+ *
+ */
+typedef struct {
+ uint16_t Length; /**< The length, in bytes, of this subset header. Shall be set to 8. */
+ uint16_t DescriptorType; /**< MS_OS_20_SUBSET_HEADER_CONFIGURATION */
+ uint8_t ConfigurationValue; /**< The configuration value for the USB configuration to which this subset applies. */
+ uint8_t Reserved; /**< Shall be set to 0. */
+ uint16_t TotalLength; /**< The size of entire configuration subset including this header. */
+} ATTR_PACKED MS_OS_20_Configuration_Subset_Header;
+
+/** \brief Microsoft OS 2.0 Function subset header.
+ *
+ */
+typedef struct {
+ uint16_t Length; /**< The length, in bytes, of this subset header. Shall be set to 8. */
+ uint16_t DescriptorType; /**< MS_OS_20_SUBSET_HEADER_FUNCTION */
+ uint8_t FirstInterface; /**< The interface number for the first interface of the function to which this subset applies. */
+ uint8_t Reserved; /**< Shall be set to 0. */
+ uint16_t SubsetLength; /**< The size of entire function subset including this header. */
+} ATTR_PACKED MS_OS_20_Function_Subset_Header;
+
+/** \brief Microsoft OS 2.0 Feature Descriptor for CompatibleID.
+ *
+ * These values are used by Windows to locate the appropriate driver for the device.
+ *
+ * For WebUSB in Chrome, the CompatibleID needs to be WINUSB, and the SubCompatibleID is null.
+ *
+ * \note ID values must be 8 bytes long and contain only the ASCII values for uppercase letters, numbers, underscores, and the NULL character. No other characters are allowed, and the last byte in the ID must be the NULL 0x00.
+ */
+typedef struct {
+ uint16_t Length; /**< The length, bytes, of the compatible ID descriptor including value descriptors. Shall be set to 20. */
+ uint16_t DescriptorType; /**< MS_OS_20_FEATURE_COMPATIBLE_ID */
+ uint8_t CompatibleID[8]; /**< Compatible ID ASCII String */
+ uint8_t SubCompatibleID[8]; /**< Sub-compatible ID ASCII String */
+} ATTR_PACKED MS_OS_20_CompatibleID_Descriptor;
+
+/** \brief Property Data Type values for the Microsoft OS 2.0 Registry Property Descriptor.
+ *
+ */
+enum MS_OS_20_Property_Data_Types {
+ MS_OS_20_REG_SZ = 1, /**< A NULL-terminated Unicode String */
+ MS_OS_20_REG_EXPAND_SZ = 2, /**< A NULL-terminated Unicode String that includes environment variables */
+ MS_OS_20_REG_BINARY = 3, /**< Free-form binary */
+ MS_OS_20_REG_DWORD_LITTLE_ENDIAN = 4, /**< A little-endian 32-bit integer */
+ MS_OS_20_REG_DWORD_BIG_ENDIAN = 5, /**< A big-endian 32-bit integer */
+ MS_OS_20_REG_LINK = 6, /**< A NULL-terminated Unicode string that contains a symbolic link */
+ MS_OS_20_REG_MULTI_SZ = 7 /**< Multiple NULL-terminated Unicode strings */
+};
+
+/** \brief Microsoft OS 2.0 Registry Property Descriptor.
+ *
+ * This descriptor is used to add per-device or per-function registry values that is read by the Windows USB driver stack or the device’s function driver.
+ */
+typedef struct {
+ uint16_t Length; /**< The length in bytes of is descriptor. */
+ uint16_t DescriptorType; /**< MS_OS_20_FEATURE_REG_PROPERTY */
+ uint16_t PropertyDataType; /**< MS_OS_20_Property_Data_types, MS_OS_20_REG_MULTI_SZ even for single interface because libusb. */
+ uint16_t PropertyNameLength; /**< The length of the property name. */
+ uint8_t PropertyName[MS_OS_20_PROPERTY_NAME_LENGTH]; /**< The name of registry property as NULL-terminated UTF-16 LE string. */
+ uint16_t PropertyDataLength; /**< The length of property data. */
+ uint8_t PropertyData[MS_OS_20_PROPERTY_DATA_LENGTH]; /**< Property Data. */
+} ATTR_PACKED MS_OS_20_Registry_Property_Descriptor;
+
+/** \brief Microsoft OS 2.0 Feature Descriptor for CCGP Devices.
+ *
+ * This descriptor indicates that the device should be treated as a composite device by Windows regardless of
+ * the number of interfaces, configuration, or class, subclass, and protocol codes, the device reports.
+ *
+ * \note The CCGP device descriptor must be applied to the entire device.
+ */
+typedef struct {
+ uint16_t Length; /**< The length, bytes, of the compatible ID descriptor including value descriptors. Shall be set to 4. */
+ uint16_t DescriptorType; /**< MS_OS_20_FEATURE_CCGP_DEVICE */
+} ATTR_PACKED MS_OS_20_CCGP_Device_Descriptor;
+
+typedef struct {
+ MS_OS_20_Descriptor_Set_Header_t Header;
+ MS_OS_20_Configuration_Subset_Header ConfigurationSubsetHeader;
+ MS_OS_20_Function_Subset_Header FunctionSubsetHeader;
+ MS_OS_20_CompatibleID_Descriptor CompatibleID;
+ MS_OS_20_Registry_Property_Descriptor RegistryProperty;
+} MS_OS_20_Descriptor_t;
+
+#define MS_OS_20_DESCRIPTOR { \
+ .Header = { \
+ .Length = CPU_TO_LE16(10), \
+ .DescriptorType = CPU_TO_LE16(MS_OS_20_SET_HEADER_DESCRIPTOR), \
+ .WindowsVersion = MS_OS_20_WINDOWS_VERSION_8_1, \
+ .TotalLength = CPU_TO_LE16(MS_OS_20_DESCRIPTOR_SET_TOTAL_LENGTH) \
+ }, \
+ .ConfigurationSubsetHeader = { \
+ .Length = CPU_TO_LE16(8), \
+ .DescriptorType = CPU_TO_LE16(MS_OS_20_SUBSET_HEADER_CONFIGURATION), \
+ .ConfigurationValue = 0, \
+ .Reserved = 0, \
+ .TotalLength = CPU_TO_LE16(MS_OS_20_DESCRIPTOR_CONFIGURATION_HEADER_LENGTH) \
+ }, \
+ .FunctionSubsetHeader = { \
+ .Length = CPU_TO_LE16(8), \
+ .DescriptorType = CPU_TO_LE16(MS_OS_20_SUBSET_HEADER_FUNCTION), \
+ .FirstInterface = INTERFACE_ID_WebUSB, \
+ .Reserved = 0, \
+ .SubsetLength = CPU_TO_LE16(MS_OS_20_DESCRIPTOR_FUNCTION_HEADER_LENGTH) \
+ }, \
+ .CompatibleID = { \
+ .Length = CPU_TO_LE16(20), \
+ .DescriptorType = CPU_TO_LE16(MS_OS_20_FEATURE_COMPATBLE_ID), \
+ .CompatibleID = MS_OS_20_DESCRIPTOR_COMPATIBILITY_ID, \
+ .SubCompatibleID = MS_OS_20_DESCRIPTOR_SUB_COMPATIBILITY_ID \
+ }, \
+ .RegistryProperty = { \
+ .Length = CPU_TO_LE16(132), \
+ .DescriptorType = CPU_TO_LE16(MS_OS_20_FEATURE_REG_PROPERTY), \
+ .PropertyDataType = CPU_TO_LE16(MS_OS_20_REG_MULTI_SZ), \
+ .PropertyNameLength = CPU_TO_LE16(MS_OS_20_PROPERTY_NAME_LENGTH), \
+ .PropertyName = MS_OS_20_PROPERTY_NAME, \
+ .PropertyDataLength = CPU_TO_LE16(MS_OS_20_PROPERTY_DATA_LENGTH), \
+ .PropertyData = MS_OS_20_PROPERTY_DATA \
+ } \
+}
+
diff --git a/util/drivers.txt b/util/drivers.txt
index 4e5439c39b..71d99eaa90 100644
--- a/util/drivers.txt
+++ b/util/drivers.txt
@@ -3,45 +3,12 @@
# Use a comma as a separator without spaces
# Driver can be one of winusb,libusb,libusbk
# Use Windows Powershell and type [guid]::NewGuid() to generate guids
+winusb,STM32 Bootloader,0483,DF11,6d98a87f-4ecf-464d-89ed-8c684d857a75
+libusb,USBaspLoader,16C0,05DC,e69affdc-0ef0-427c-aefb-4e593c9d2724
winusb,Kiibohd DFU Bootloader,1C11,B007,aa5a3f86-b81e-4416-89ad-0c1ea1ed63af
-winusb,STM32 Bootloader,0483,df11,6d98a87f-4ecf-464d-89ed-8c684d857a75
-libusb,ATxmega16C4,03EB,2FD8,23266ee7-5423-4cc4-993b-034571c43a90
-libusb,ATxmega32C4,03EB,2FD9,d4b62886-2ac8-4534-aa24-eae0a2c3ce43
-libusb,ATxmega64C3,03EB,2FD6,08467ca7-9b5a-41d2-8d8a-4a26d0b5285b
-libusb,ATxmega128C3,03EB,2FD7,1ca69799-6d95-46cf-be69-5b3d0eb915e6
-libusb,ATxmega256C3,03EB,2FDA,216ddc8b-6c67-4cc0-b934-482829a483a0
-libusb,ATxmega384C3,03EB,2FDB,0e4e3347-6025-4d49-ba80-2375ea690c28
-libusb,ATxmega64A1U,03EB,2FE8,2553d8fa-7de1-44a6-bdbf-57be8bb37e28
-libusb,ATxmega128A1U,03EB,2FED,6d9fd0ff-755d-4e29-bd29-df0a9a7544b9
-libusb,ATxmega64A4U,03EB,2FDD,bcf5e7c3-44a1-4fd1-971f-9ef9843f6291
-libusb,ATxmega128A4U,03EB,2FDE,3f976bb6-36ca-44cc-a728-844bc1d0d168
-libusb,ATxmega64B3,03EB,2FDF,de280c81-c12a-4ca7-bf34-566151786418
-libusb,ATxmega128B3,03EB,2FE0,2ad1ffeb-eb83-4e78-b34a-d5633771991f
-libusb,ATxmega64B1,03EB,2FE1,002874a6-7fc7-413b-9ac4-2b52c5a230bd
-libusb,ATxmega128B1,03EB,2FEA,60ea9d08-2ae6-4434-b743-ce6f73537136
-libusb,ATxmega256A3BU,03EB,2FE2,5949bd0a-8bd4-417b-b1c5-7d249836bf0d
-libusb,ATxmega16A4U,03EB,2FE3,cc3172b0-e86a-4758-914e-951bca6ca7f5
-libusb,ATxmega32A4U,03EB,2FE4,f44c515f-7d17-4612-a532-ee620afb22b2
-libusb,ATxmega64A4U,03EB,2FE5,c1af4f1c-045f-40c9-893a-3ad4adb2e67d
-libusb,ATxmega128A3U,03EB,2FE6,26f275f0-d6b2-46d8-8334-e4de66996c74
-libusb,ATxmega192A3U,03EB,2FE7,b7b50d98-0429-4235-8f08-5466e4f83ed4
-libusb,UC3,03EB,2FDC,972d9af7-d71b-44c7-a895-9340b362f545
-libusb,ATUC3,03EB,2FE9,d5855d0a-f82e-4df5-9c14-2b0b1dcb65bd
-libusb,AT32UC3C,03EB,2FEB,1eeb52aa-fd24-47fd-8a76-056446d1a54f
-libusb,ATxmega256A3U,03EB,2FEC,198fa8ea-3157-4863-b9a8-a3f6fe027367
-libusb,ATmega8U2,03EB,2FEE,14018055-46f4-4c62-aa03-e8fafeedaf72
libusb,ATmega16U2,03EB,2FEF,007274da-b75f-492e-a288-8fc0aff8339f
libusb,ATmega32U2,03EB,2FF0,ddc2c572-cb6e-4f61-a6cc-1a5de941f063
-libusb,AT32UC3A3,03EB,2FF1,8b614283-36c0-46a2-890d-65f61b5b6201
-libusb,ATmega32U6,03EB,2FF2,a207dd90-2814-4418-b5b7-4b708fdf1bfd
libusb,ATmega16U4,03EB,2FF3,3180d426-bf93-4578-a693-2efbc337da8e
libusb,ATmega32U4,03EB,2FF4,5f9726fd-f9de-487a-9fbd-8b3524a7a56a
-libusb,AT32UC3B,03EB,2FF6,ef90068a-277a-44db-805a-9b83a6beb29a
-libusb,AT90USB82,03EB,2FF7,062fa2ab-f9d8-4a0d-83c1-df0521cfd0f6
-libusb,AT32UC3A,03EB,2FF8,24080a67-3874-4fb8-8808-fb4cc297c466
libusb,AT90USB64,03EB,2FF9,c6a708ad-e97d-43cd-b04a-3180d737a71b
-libusb,AT90USB162,03EB,2FFA,de67bff5-6e39-4e9c-8dfe-de7fce113716
libusb,AT90USB128,03EB,2FFB,fd217df3-59d0-440a-a8f3-4c0c8c84daa3
-libusb,AT89C5130,03EB,2FFD,31b69a56-9ac0-4fab-a3ae-cd7bb7021ec5
-libusb,AT8XC5122,03EB,2FFE,395a6118-8568-41b2-913a-d16912722342
-libusb,AT89C5132,03EB,2FFF,266ca4bc-5e59-4a7b-82dc-6e8732373d40
diff --git a/util/linux_install.sh b/util/linux_install.sh
index d3b79942ab..64dc6d5fa1 100755
--- a/util/linux_install.sh
+++ b/util/linux_install.sh
@@ -76,6 +76,7 @@ elif grep ID /etc/os-release | grep -q 'arch\|manjaro'; then
gcc \
git \
python \
+ python-pip \
unzip \
wget \
zip
@@ -182,6 +183,29 @@ elif grep ID /etc/os-release | grep -q solus; then
unzip
printf "\n$SOLUS_INFO\n"
+elif grep ID /etc/os-release | grep -q void; then
+ # musl Void systems don't have glibc cross compilers avaliable in their repos.
+ # glibc Void systems do have musl cross compilers though, for some reason.
+ # So, default to musl, and switch to glibc if it is installed.
+ CROSS_ARM=cross-arm-linux-musleabi
+ if xbps-query glibc > /dev/null; then # Check is glibc if installed
+ CROSS_ARM=cross-arm-linux-gnueabi
+ fi
+
+ sudo xbps-install \
+ avr-binutils \
+ avr-gcc \
+ avr-libc \
+ $CROSS_ARM \
+ dfu-programmer \
+ dfu-util \
+ gcc \
+ git \
+ make \
+ wget \
+ unzip \
+ zip
+
else
echo "Sorry, we don't recognize your OS. Help us by contributing support!"
echo
@@ -189,4 +213,4 @@ else
fi
# Global install tasks
-pip3 install -r ${util_dir}/../requirements.txt
+pip3 install --user -r ${util_dir}/../requirements.txt