summaryrefslogtreecommitdiff
path: root/tmk_core
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 /tmk_core
parent1d600f09a8b813f77d8b6ff4cc55484b7154ae25 (diff)
parent038cef3054a816021a797c217d147057c8edd926 (diff)
Merge pull request #222 from zsa/staging
Firmware 15
Diffstat (limited to 'tmk_core')
-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/backlight.c193
-rw-r--r--tmk_core/common/backlight.h58
-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
21 files changed, 778 insertions, 286 deletions
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/backlight.c b/tmk_core/common/backlight.c
deleted file mode 100644
index 708022f68f..0000000000
--- a/tmk_core/common/backlight.c
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
-Copyright 2013 Mathias Andersson <wraul@dbox.se>
-
-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/>.
-*/
-
-#include "backlight.h"
-#include "eeconfig.h"
-#include "debug.h"
-
-backlight_config_t backlight_config;
-
-/** \brief Backlight initialization
- *
- * FIXME: needs doc
- */
-void backlight_init(void) {
- /* check signature */
- if (!eeconfig_is_enabled()) {
- eeconfig_init();
- }
- backlight_config.raw = eeconfig_read_backlight();
- if (backlight_config.level > BACKLIGHT_LEVELS) {
- backlight_config.level = BACKLIGHT_LEVELS;
- }
- backlight_set(backlight_config.enable ? backlight_config.level : 0);
-}
-
-/** \brief Backlight increase
- *
- * FIXME: needs doc
- */
-void backlight_increase(void) {
- if (backlight_config.level < BACKLIGHT_LEVELS) {
- backlight_config.level++;
- }
- backlight_config.enable = 1;
- eeconfig_update_backlight(backlight_config.raw);
- dprintf("backlight increase: %u\n", backlight_config.level);
- backlight_set(backlight_config.level);
-}
-
-/** \brief Backlight decrease
- *
- * FIXME: needs doc
- */
-void backlight_decrease(void) {
- if (backlight_config.level > 0) {
- backlight_config.level--;
- backlight_config.enable = !!backlight_config.level;
- eeconfig_update_backlight(backlight_config.raw);
- }
- dprintf("backlight decrease: %u\n", backlight_config.level);
- backlight_set(backlight_config.level);
-}
-
-/** \brief Backlight toggle
- *
- * FIXME: needs doc
- */
-void backlight_toggle(void) {
- bool enabled = backlight_config.enable;
- dprintf("backlight toggle: %u\n", enabled);
- if (enabled)
- backlight_disable();
- else
- backlight_enable();
-}
-
-/** \brief Enable backlight
- *
- * FIXME: needs doc
- */
-void backlight_enable(void) {
- if (backlight_config.enable) return; // do nothing if backlight is already on
-
- backlight_config.enable = true;
- if (backlight_config.raw == 1) // enabled but level == 0
- backlight_config.level = 1;
- eeconfig_update_backlight(backlight_config.raw);
- dprintf("backlight enable\n");
- backlight_set(backlight_config.level);
-}
-
-/** \brief Disable backlight
- *
- * FIXME: needs doc
- */
-void backlight_disable(void) {
- if (!backlight_config.enable) return; // do nothing if backlight is already off
-
- backlight_config.enable = false;
- eeconfig_update_backlight(backlight_config.raw);
- dprintf("backlight disable\n");
- backlight_set(0);
-}
-
-/** /brief Get the backlight status
- *
- * FIXME: needs doc
- */
-bool is_backlight_enabled(void) { return backlight_config.enable; }
-
-/** \brief Backlight step through levels
- *
- * FIXME: needs doc
- */
-void backlight_step(void) {
- backlight_config.level++;
- if (backlight_config.level > BACKLIGHT_LEVELS) {
- backlight_config.level = 0;
- }
- backlight_config.enable = !!backlight_config.level;
- eeconfig_update_backlight(backlight_config.raw);
- dprintf("backlight step: %u\n", backlight_config.level);
- backlight_set(backlight_config.level);
-}
-
-/** \brief Backlight set level
- *
- * FIXME: needs doc
- */
-void backlight_level(uint8_t level) {
- if (level > BACKLIGHT_LEVELS) level = BACKLIGHT_LEVELS;
- backlight_config.level = level;
- backlight_config.enable = !!backlight_config.level;
- eeconfig_update_backlight(backlight_config.raw);
- backlight_set(backlight_config.level);
-}
-
-/** \brief Get backlight level
- *
- * FIXME: needs doc
- */
-uint8_t get_backlight_level(void) { return backlight_config.level; }
-
-#ifdef BACKLIGHT_BREATHING
-/** \brief Backlight breathing toggle
- *
- * FIXME: needs doc
- */
-void backlight_toggle_breathing(void) {
- bool breathing = backlight_config.breathing;
- dprintf("backlight breathing toggle: %u\n", breathing);
- if (breathing)
- backlight_disable_breathing();
- else
- backlight_enable_breathing();
-}
-
-/** \brief Enable backlight breathing
- *
- * FIXME: needs doc
- */
-void backlight_enable_breathing(void) {
- if (backlight_config.breathing) return; // do nothing if breathing is already on
-
- backlight_config.breathing = true;
- eeconfig_update_backlight(backlight_config.raw);
- dprintf("backlight breathing enable\n");
- breathing_enable();
-}
-
-/** \brief Disable backlight breathing
- *
- * FIXME: needs doc
- */
-void backlight_disable_breathing(void) {
- if (!backlight_config.breathing) return; // do nothing if breathing is already off
-
- backlight_config.breathing = false;
- eeconfig_update_backlight(backlight_config.raw);
- dprintf("backlight breathing disable\n");
- breathing_disable();
-}
-
-/** \brief Get the backlight breathing status
- *
- * FIXME: needs doc
- */
-bool is_backlight_breathing(void) { return backlight_config.breathing; }
-#endif
diff --git a/tmk_core/common/backlight.h b/tmk_core/common/backlight.h
deleted file mode 100644
index bb1f897ee8..0000000000
--- a/tmk_core/common/backlight.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-Copyright 2013 Mathias Andersson <wraul@dbox.se>
-
-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/>.
-*/
-
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-
-#ifndef BACKLIGHT_LEVELS
-# define BACKLIGHT_LEVELS 3
-#elif BACKLIGHT_LEVELS > 31
-# error "Maximum value of BACKLIGHT_LEVELS is 31"
-#endif
-
-typedef union {
- uint8_t raw;
- struct {
- bool enable : 1;
- bool breathing : 1;
- uint8_t reserved : 1; // Reserved for possible future backlight modes
- uint8_t level : 5;
- };
-} backlight_config_t;
-
-void backlight_init(void);
-void backlight_increase(void);
-void backlight_decrease(void);
-void backlight_toggle(void);
-void backlight_enable(void);
-void backlight_disable(void);
-bool is_backlight_enabled(void);
-void backlight_step(void);
-void backlight_set(uint8_t level);
-void backlight_level(uint8_t level);
-uint8_t get_backlight_level(void);
-
-#ifdef BACKLIGHT_BREATHING
-void backlight_toggle_breathing(void);
-void backlight_enable_breathing(void);
-void backlight_disable_breathing(void);
-bool is_backlight_breathing(void);
-void breathing_enable(void);
-void breathing_disable(void);
-#endif
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 \
+ } \
+}
+