Compare commits

...

13 Commits

Author SHA1 Message Date
Florian Didron
04113a76ac chore: code cleanup and refactors 2019-11-04 16:30:23 +09:00
Florian Didron
8e142defed fix: wait for usb init before starting the webusb task 2019-11-01 18:39:29 +09:00
Florian Didron
7a928b5c3c feat: add MS OS 2.0 descriptors 2019-11-01 07:41:03 +09:00
Florian Didron
bc620d40ec feat: planck webusb keymap example 2019-10-31 19:03:36 +09:00
Florian Didron
f4a7822c24 feat: make the landing page url a parameter 2019-10-31 18:33:50 +09:00
Florian Didron
9c66847843 feat: set bcdUSb to 0x210 to trigger BOS descriptors 2019-10-31 18:07:04 +09:00
Florian Didron
580e7e6454 feat: trigger the webusb popup 2019-10-31 17:56:06 +09:00
Florian Didron
a2155a9498 chore: set product field to Planck EZ 2019-10-31 17:55:25 +09:00
Florian Didron
6af7a696cc adds webusb to chibios 2019-10-23 09:11:03 +09:00
Drashna Jaelre
207f5894c0 Point lib/lufa to personal fork 2019-09-19 20:40:25 -07:00
Drashna Jael're
da7daf1f23 Additional endpoint config 2019-09-19 19:57:17 -07:00
Drashna Jael're
1efec820b8 Add USB Descriptors 2019-09-19 19:31:01 -07:00
Drashna Jael're
710886d476 Update LUFA Library 2019-09-19 17:54:09 -07:00
16 changed files with 1377 additions and 449 deletions

2
.gitmodules vendored
View File

@@ -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/drashna/lufa

View File

@@ -28,6 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MANUFACTURER ErgoDox EZ
#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

View File

@@ -18,6 +18,10 @@
#pragma once
/* USB Device descriptor parameter */
#undef MANUFACTURER
#define MANUFACTURER ErgoDox EZ
#undef PRODUCT
#define PRODUCT Planck EZ
#define DEVICE_VER 0x0000
#undef MATRIX_ROWS
@@ -25,6 +29,7 @@
/* key matrix size */
#define MATRIX_ROWS 8
#define MATRIX_COLS 6
#define WEBUSB_LANDING_PAGE_URL u8"configure.ergodox-ez.com"
/*
* Keyboard Matrix Assignments

View File

@@ -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

View File

@@ -0,0 +1,351 @@
/* Copyright 2015-2017 Jack Humbert
*
* 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 QMK_KEYBOARD_H
#include "muse.h"
// Ping - Pong
void webusb_receive(uint8_t *data, uint8_t length) {
webusb_send(data, length);
}
extern keymap_config_t keymap_config;
enum planck_layers {
_QWERTY,
_COLEMAK,
_DVORAK,
_LOWER,
_RAISE,
_PLOVER,
_ADJUST
};
enum planck_keycodes {
QWERTY = SAFE_RANGE,
COLEMAK,
DVORAK,
PLOVER,
BACKLIT,
EXT_PLV
};
#define LOWER MO(_LOWER)
#define RAISE MO(_RAISE)
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
/* Qwerty
* ,-----------------------------------------------------------------------------------.
* | Tab | Q | W | E | R | T | Y | U | I | O | P | Bksp |
* |------+------+------+------+------+-------------+------+------+------+------+------|
* | Esc | A | S | D | F | G | H | J | K | L | ; | " |
* |------+------+------+------+------+------|------+------+------+------+------+------|
* | Shift| Z | X | C | V | B | N | M | , | . | / |Enter |
* |------+------+------+------+------+------+------+------+------+------+------+------|
* | Brite| Ctrl | Alt | GUI |Lower | Space |Raise | Left | Down | Up |Right |
* `-----------------------------------------------------------------------------------'
*/
[_QWERTY] = 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, RESET,
KC_ESC, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT ,
BACKLIT, KC_LCTL, KC_LALT, KC_LGUI, LOWER, KC_SPC, KC_SPC, RAISE, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT
),
/* Colemak
* ,-----------------------------------------------------------------------------------.
* | Tab | Q | W | F | P | G | J | L | U | Y | ; | Bksp |
* |------+------+------+------+------+-------------+------+------+------+------+------|
* | Esc | A | R | S | T | D | H | N | E | I | O | " |
* |------+------+------+------+------+------|------+------+------+------+------+------|
* | Shift| Z | X | C | V | B | K | M | , | . | / |Enter |
* |------+------+------+------+------+------+------+------+------+------+------+------|
* | Brite| Ctrl | Alt | GUI |Lower | Space |Raise | Left | Down | Up |Right |
* `-----------------------------------------------------------------------------------'
*/
[_COLEMAK] = LAYOUT_planck_grid(
KC_TAB, KC_Q, KC_W, KC_F, KC_P, KC_G, KC_J, KC_L, KC_U, KC_Y, KC_SCLN, KC_BSPC,
KC_ESC, KC_A, KC_R, KC_S, KC_T, KC_D, KC_H, KC_N, KC_E, KC_I, KC_O, KC_QUOT,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_K, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT ,
BACKLIT, KC_LCTL, KC_LALT, KC_LGUI, LOWER, KC_SPC, KC_SPC, RAISE, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT
),
/* Dvorak
* ,-----------------------------------------------------------------------------------.
* | Tab | " | , | . | P | Y | F | G | C | R | L | Bksp |
* |------+------+------+------+------+-------------+------+------+------+------+------|
* | Esc | A | O | E | U | I | D | H | T | N | S | / |
* |------+------+------+------+------+------|------+------+------+------+------+------|
* | Shift| ; | Q | J | K | X | B | M | W | V | Z |Enter |
* |------+------+------+------+------+------+------+------+------+------+------+------|
* | Brite| Ctrl | Alt | GUI |Lower | Space |Raise | Left | Down | Up |Right |
* `-----------------------------------------------------------------------------------'
*/
[_DVORAK] = LAYOUT_planck_grid(
KC_TAB, KC_QUOT, KC_COMM, KC_DOT, KC_P, KC_Y, KC_F, KC_G, KC_C, KC_R, KC_L, KC_BSPC,
KC_ESC, KC_A, KC_O, KC_E, KC_U, KC_I, KC_D, KC_H, KC_T, KC_N, KC_S, KC_SLSH,
KC_LSFT, KC_SCLN, KC_Q, KC_J, KC_K, KC_X, KC_B, KC_M, KC_W, KC_V, KC_Z, KC_ENT ,
BACKLIT, KC_LCTL, KC_LALT, KC_LGUI, LOWER, KC_SPC, KC_SPC, RAISE, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT
),
/* Lower
* ,-----------------------------------------------------------------------------------.
* | ~ | ! | @ | # | $ | % | ^ | & | * | ( | ) | Bksp |
* |------+------+------+------+------+-------------+------+------+------+------+------|
* | Del | F1 | F2 | F3 | F4 | F5 | F6 | _ | + | { | } | | |
* |------+------+------+------+------+------|------+------+------+------+------+------|
* | | F7 | F8 | F9 | F10 | F11 | F12 |ISO ~ |ISO | | Home | End | |
* |------+------+------+------+------+------+------+------+------+------+------+------|
* | | | | | | | | Next | Vol- | Vol+ | Play |
* `-----------------------------------------------------------------------------------'
*/
[_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_BSPC,
KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_UNDS, KC_PLUS, KC_LCBR, KC_RCBR, KC_PIPE,
_______, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, S(KC_NUHS), S(KC_NUBS), KC_HOME, KC_END, _______,
_______, _______, _______, _______, _______, _______, _______, _______, KC_MNXT, KC_VOLD, KC_VOLU, KC_MPLY
),
/* Raise
* ,-----------------------------------------------------------------------------------.
* | ` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | Bksp |
* |------+------+------+------+------+-------------+------+------+------+------+------|
* | Del | F1 | F2 | F3 | F4 | F5 | F6 | - | = | [ | ] | \ |
* |------+------+------+------+------+------|------+------+------+------+------+------|
* | | F7 | F8 | F9 | F10 | F11 | F12 |ISO # |ISO / |Pg Up |Pg Dn | |
* |------+------+------+------+------+------+------+------+------+------+------+------|
* | | | | | | | | Next | Vol- | Vol+ | Play |
* `-----------------------------------------------------------------------------------'
*/
[_RAISE] = LAYOUT_planck_grid(
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSPC,
KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_MINS, KC_EQL, KC_LBRC, KC_RBRC, KC_BSLS,
_______, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_NUHS, KC_NUBS, KC_PGUP, KC_PGDN, _______,
_______, _______, _______, _______, _______, _______, _______, _______, KC_MNXT, KC_VOLD, KC_VOLU, KC_MPLY
),
/* Plover layer (http://opensteno.org)
* ,-----------------------------------------------------------------------------------.
* | # | # | # | # | # | # | # | # | # | # | # | # |
* |------+------+------+------+------+-------------+------+------+------+------+------|
* | | S | T | P | H | * | * | F | P | L | T | D |
* |------+------+------+------+------+------|------+------+------+------+------+------|
* | | S | K | W | R | * | * | R | B | G | S | Z |
* |------+------+------+------+------+------+------+------+------+------+------+------|
* | Exit | | | A | O | | E | U | | | |
* `-----------------------------------------------------------------------------------'
*/
[_PLOVER] = LAYOUT_planck_grid(
KC_1, KC_1, KC_1, KC_1, KC_1, KC_1, KC_1, KC_1, KC_1, KC_1, KC_1, KC_1 ,
XXXXXXX, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC,
XXXXXXX, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT,
EXT_PLV, XXXXXXX, XXXXXXX, KC_C, KC_V, XXXXXXX, XXXXXXX, KC_N, KC_M, XXXXXXX, XXXXXXX, XXXXXXX
),
/* Adjust (Lower + Raise)
* ,-----------------------------------------------------------------------------------.
* | | Reset| | | | | | | | | | Del |
* |------+------+------+------+------+-------------+------+------+------+------+------|
* | | | |Aud on|Audoff|AGnorm|AGswap|Qwerty|Colemk|Dvorak|Plover| |
* |------+------+------+------+------+------|------+------+------+------+------+------|
* | |Voice-|Voice+|Mus on|Musoff|MIDIon|MIDIof| | | | | |
* |------+------+------+------+------+------+------+------+------+------+------+------|
* | | | | | | | | | | | |
* `-----------------------------------------------------------------------------------'
*/
[_ADJUST] = LAYOUT_planck_grid(
_______, RESET, DEBUG, RGB_TOG, RGB_MOD, RGB_HUI, RGB_HUD, RGB_SAI, RGB_SAD, RGB_VAI, RGB_VAD, KC_DEL ,
_______, _______, MU_MOD, AU_ON, AU_OFF, AG_NORM, AG_SWAP, QWERTY, COLEMAK, DVORAK, PLOVER, _______,
_______, MUV_DE, MUV_IN, MU_ON, MU_OFF, MI_ON, MI_OFF, TERM_ON, TERM_OFF, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______
)
};
#ifdef AUDIO_ENABLE
float plover_song[][2] = SONG(PLOVER_SOUND);
float plover_gb_song[][2] = SONG(PLOVER_GOODBYE_SOUND);
#endif
layer_state_t layer_state_set_user(layer_state_t state) {
return update_tri_layer_state(state, _LOWER, _RAISE, _ADJUST);
}
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case QWERTY:
if (record->event.pressed) {
print("mode just switched to qwerty and this is a huge string\n");
set_single_persistent_default_layer(_QWERTY);
}
return false;
break;
case COLEMAK:
if (record->event.pressed) {
set_single_persistent_default_layer(_COLEMAK);
}
return false;
break;
case DVORAK:
if (record->event.pressed) {
set_single_persistent_default_layer(_DVORAK);
}
return false;
break;
case BACKLIT:
if (record->event.pressed) {
register_code(KC_RSFT);
#ifdef BACKLIGHT_ENABLE
backlight_step();
#endif
#ifdef KEYBOARD_planck_rev5
PORTE &= ~(1<<6);
#endif
} else {
unregister_code(KC_RSFT);
#ifdef KEYBOARD_planck_rev5
PORTE |= (1<<6);
#endif
}
return false;
break;
case PLOVER:
if (record->event.pressed) {
#ifdef AUDIO_ENABLE
stop_all_notes();
PLAY_SONG(plover_song);
#endif
layer_off(_RAISE);
layer_off(_LOWER);
layer_off(_ADJUST);
layer_on(_PLOVER);
if (!eeconfig_is_enabled()) {
eeconfig_init();
}
keymap_config.raw = eeconfig_read_keymap();
keymap_config.nkro = 1;
eeconfig_update_keymap(keymap_config.raw);
}
return false;
break;
case EXT_PLV:
if (record->event.pressed) {
#ifdef AUDIO_ENABLE
PLAY_SONG(plover_gb_song);
#endif
layer_off(_PLOVER);
}
return false;
break;
}
return true;
}
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 dip_update(uint8_t index, bool active) {
switch (index) {
case 0:
if (active) {
#ifdef AUDIO_ENABLE
PLAY_SONG(plover_song);
#endif
layer_on(_ADJUST);
} else {
#ifdef AUDIO_ENABLE
PLAY_SONG(plover_gb_song);
#endif
layer_off(_ADJUST);
}
break;
case 1:
if (active) {
muse_mode = true;
} else {
muse_mode = false;
#ifdef AUDIO_ENABLE
stop_all_notes();
#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;
}
}

View File

@@ -0,0 +1,2 @@
# The Default Planck Layout

View File

@@ -0,0 +1,4 @@
SRC += muse.c
RGB_MATRIX_ENABLE = no
WEBUSB_ENABLE = yes
CONSOLE_ENABLE = true

View File

@@ -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

View File

@@ -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.
*/
@@ -214,6 +218,9 @@ int main(void) {
#endif
#ifdef RAW_ENABLE
raw_hid_task();
#endif
#ifdef WEBUSB_ENABLE
webusb_task();
#endif
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -268,6 +268,66 @@ 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_IsINReady()) {
Endpoint_Write_Stream_LE(data, length, NULL);
Endpoint_ClearIN();
}
}
__attribute__((weak)) void webusb_receive(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 +465,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 +602,47 @@ 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
@@ -954,6 +1061,8 @@ int main(void) {
setup_usb();
sei();
#if defined(MODULE_ADAFRUIT_EZKEY) || defined(MODULE_RN42)
serial_init();
#endif
@@ -1018,6 +1127,10 @@ int main(void) {
raw_hid_task();
#endif
#ifdef WEBUSB_ENABLE
webusb_task();
#endif
#if !defined(INTERRUPT_CONTROL_ENDPOINT)
USB_USBTask();
#endif

View File

@@ -86,3 +86,7 @@ typedef struct {
// #endif
#endif
void webusb_receive(uint8_t *data, uint8_t length);
void webusb_send(uint8_t *data, uint8_t length);

View File

@@ -39,7 +39,10 @@
#include "util.h"
#include "report.h"
#include "usb_descriptor.h"
#include "print.h"
#ifdef WEBUSB_ENABLE
# include "webusb.h"
#endif
/*
* HID report descriptors
*/
@@ -274,11 +277,19 @@ 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},
.USBSpecification = VERSION_BCD(1, 1, 0),
.USBSpecification = VERSION_BCD(2, 1, 0),
#if VIRTSER_ENABLE
.Class = USB_CSCP_IADDeviceClass,
.SubClass = USB_CSCP_IADDeviceSubclass,
@@ -377,6 +388,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
@@ -516,6 +559,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);

View File

@@ -44,7 +44,9 @@
#define _DESCRIPTORS_H_
#include <LUFA/Drivers/USB/USB.h>
#ifdef WEBUSB_ENABLE
#include "webusb.h"
#endif
#ifdef PROTOCOL_CHIBIOS
# include "hal.h"
#endif
@@ -91,6 +93,9 @@ typedef struct {
USB_Descriptor_Endpoint_t Console_INEndpoint;
USB_Descriptor_Endpoint_t Console_OUTEndpoint;
#endif
USB_Descriptor_Interface_t WebUSB_Interface;
USB_Descriptor_Endpoint_t WebUSB_DataInEndpoint;
USB_Descriptor_Endpoint_t WebUSB_DataOutEndpoint;
#ifdef MIDI_ENABLE
USB_Descriptor_Interface_Association_t Audio_Interface_Association;
@@ -164,6 +169,10 @@ enum usb_interfaces {
CDI_INTERFACE,
#endif
#ifdef WEBUSB_ENABLE
INTERFACE_ID_WebUSB,
#endif
TOTAL_INTERFACES
};
@@ -216,6 +225,13 @@ enum usb_endpoints {
# define MIDI_STREAM_OUT_EPADDR (ENDPOINT_DIR_OUT | MIDI_STREAM_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 VIRTSER_ENABLE
CDC_NOTIFICATION_EPNUM = NEXT_EPNUM,
CDC_IN_EPNUM = NEXT_EPNUM,
@@ -248,6 +264,9 @@ 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

255
tmk_core/protocol/webusb.h Normal file
View File

@@ -0,0 +1,255 @@
#pragma once
void webusb_receive(uint8_t *data, uint8_t length);
void webusb_send(uint8_t *data, uint8_t length);
#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 u8"WINUSB\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 u8"DeviceInterfaceGUIDs\0"
#define MS_OS_20_PROPERTY_DATA_LENGTH 80
#define MS_OS_20_PROPERTY_DATA u8"{9D32F82C-1FB2-4486-8501-B6145B5BA336}\0\0"
#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 devices 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 \
} \
}