diff --git a/common_features.mk b/common_features.mk index 7dad1ee8ed9..8266366711f 100644 --- a/common_features.mk +++ b/common_features.mk @@ -20,6 +20,11 @@ QUANTUM_SRC += \ $(QUANTUM_DIR)/keymap_common.c \ $(QUANTUM_DIR)/keycode_config.c +KEYBOARD_ENABLE ?= yes +ifeq ($(strip $(KEYBOARD_ENABLE)), yes) + OPT_DEFS += -DKEYBOARD_ENABLE +endif + ifeq ($(strip $(API_SYSEX_ENABLE)), yes) OPT_DEFS += -DAPI_SYSEX_ENABLE OPT_DEFS += -DAPI_ENABLE @@ -523,3 +528,8 @@ ifeq ($(strip $(JOYSTICK_ENABLE)), yes) SRC += $(QUANTUM_DIR)/joystick.c SRC += analog.c endif + +ifeq ($(strip $(SWITCH_CONTROLLER_ENABLE)), yes) + OPT_DEFS += -DSWITCH_CONTROLLER_ENABLE + OPT_DEFS += -DGAMEPAD_ENABLE +endif diff --git a/keyboards/handwired/gc_controller/config.h b/keyboards/handwired/gc_controller/config.h new file mode 100644 index 00000000000..84361b49b3c --- /dev/null +++ b/keyboards/handwired/gc_controller/config.h @@ -0,0 +1,62 @@ +/* Copyright 2019 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 . + */ + +#pragma once + +#include "config_common.h" + +/* USB Device descriptor parameter */ +#define VENDOR_ID 0x0e6f +#define PRODUCT_ID 0x0185 +#define DEVICE_VER 0x0001 +#define MANUFACTURER QMK +#define PRODUCT Proton C +#define DESCRIPTION Handwired Gamepad + +/* key matrix size */ +#define MATRIX_ROWS 1 +#define MATRIX_COLS 1 + +#define DIODE_DIRECTION COL2ROW + +#define MATRIX_COL_PINS { A3 } +#define MATRIX_ROW_PINS { A1 } + +/* define if matrix has ghost */ +//#define MATRIX_HAS_GHOST + +/* Set 0 if debouncing isn't needed */ +#define DEBOUNCE 5 + +/* + * Feature disable options + * These options are also useful to firmware size reduction. + */ + +/* disable debug print */ +//#define NO_DEBUG + +/* disable print */ +//#define NO_PRINT + +/* disable action features */ +//#define NO_ACTION_LAYER +//#define NO_ACTION_TAPPING +//#define NO_ACTION_ONESHOT +//#define NO_ACTION_MACRO +//#define NO_ACTION_FUNCTION + +#define GAMECUBE_DATA_PIN A2 \ No newline at end of file diff --git a/keyboards/handwired/gc_controller/gc_controller.c b/keyboards/handwired/gc_controller/gc_controller.c new file mode 100644 index 00000000000..4b4ea993d2a --- /dev/null +++ b/keyboards/handwired/gc_controller/gc_controller.c @@ -0,0 +1,130 @@ +#include "gc_controller.h" +#include "gc_read.h" +#include "gamepad.h" +#include + +uint16_t gamecube_buttons = 0; +uint8_t gamecube_joysticks[6] = {0}; +bool z_button = false; + +report_gamepad_t report = { + .Button = 0, + .LX = STICK_CENTER, + .LY = STICK_CENTER, + .RX = STICK_CENTER, + .RY = STICK_CENTER, + .HAT = HAT_CENTER +}; + +void board_init(void) { + setPinOutput(C13); + writePinLow(C13); + gamecube_init(); +} + +void matrix_init_user(void) { + +} + +void matrix_scan_user(void) { + + gamecube_scan(&gamecube_buttons, gamecube_joysticks); + + z_button = gamecube_buttons & GAMECUBE_Z; + + // Home & Capture + if (gamecube_buttons & GAMECUBE_START) { + if (z_button && !(report.Button & SWITCH_HOME)) + report.Button |= SWITCH_CAPTURE; + else + report.Button |= SWITCH_HOME; + } else { + report.Button &= ~SWITCH_HOME; + report.Button &= ~SWITCH_CAPTURE; + } + // Y and L + if (gamecube_buttons & GAMECUBE_Y) { + if (z_button && !(report.Button & SWITCH_Y)) + report.Button |= SWITCH_L; + else + report.Button |= SWITCH_Y; + } else { + report.Button &= ~SWITCH_Y; + report.Button &= ~SWITCH_L; + } + // X and R + if (gamecube_buttons & GAMECUBE_X) { + if (z_button && !(report.Button & SWITCH_X)) + report.Button |= SWITCH_R; + else + report.Button |= SWITCH_X; + } else { + report.Button &= ~SWITCH_X; + report.Button &= ~SWITCH_R; + } + // B and - + if (gamecube_buttons & GAMECUBE_B) { + if (z_button && !(report.Button & SWITCH_B)) + report.Button |= SWITCH_MINUS; + else + report.Button |= SWITCH_B; + } else { + report.Button &= ~SWITCH_B; + report.Button &= ~SWITCH_MINUS; + } + // A and + + if (gamecube_buttons & GAMECUBE_A) { + if (z_button && !(report.Button & SWITCH_A)) + report.Button |= SWITCH_PLUS; + else + report.Button |= SWITCH_A; + } else { + report.Button &= ~SWITCH_A; + report.Button &= ~SWITCH_PLUS; + } + + if (gamecube_buttons & GAMECUBE_L) { + report.Button |= SWITCH_ZL; + } else { + report.Button &= ~SWITCH_ZL; + } + if (gamecube_buttons & GAMECUBE_R) { + report.Button |= SWITCH_ZR; + } else { + report.Button &= ~SWITCH_ZR; + } + + if ((gamecube_buttons & GAMECUBE_UP) && (gamecube_buttons & GAMECUBE_RIGHT)) + report.HAT = HAT_TOP_RIGHT; + else if ((gamecube_buttons & GAMECUBE_UP) && (gamecube_buttons & GAMECUBE_LEFT)) + report.HAT = HAT_TOP_LEFT; + else if ((gamecube_buttons & GAMECUBE_DOWN) && (gamecube_buttons & GAMECUBE_RIGHT)) + report.HAT = HAT_BOTTOM_RIGHT; + else if ((gamecube_buttons & GAMECUBE_DOWN) && (gamecube_buttons & GAMECUBE_LEFT)) + report.HAT = HAT_BOTTOM_LEFT; + else if (gamecube_buttons & GAMECUBE_UP) + report.HAT = HAT_TOP; + else if (gamecube_buttons & GAMECUBE_DOWN) + report.HAT = HAT_BOTTOM; + else if (gamecube_buttons & GAMECUBE_RIGHT) + report.HAT = HAT_RIGHT; + else if (gamecube_buttons & GAMECUBE_LEFT) + report.HAT = HAT_LEFT; + else + report.HAT = HAT_CENTER; + + if (report.Button || report.HAT != HAT_CENTER) + writePinHigh(C13); + else + writePinLow(C13); + + // joystick calculations + + report.LX = gamecube_joysticks[0]; + report.LY = 255 - gamecube_joysticks[1]; + report.RX = gamecube_joysticks[2]; + report.RY = 255 - gamecube_joysticks[3]; + + send_gamepad(&report); + +} \ No newline at end of file diff --git a/keyboards/handwired/gc_controller/gc_controller.h b/keyboards/handwired/gc_controller/gc_controller.h new file mode 100644 index 00000000000..9e1468fcf01 --- /dev/null +++ b/keyboards/handwired/gc_controller/gc_controller.h @@ -0,0 +1,47 @@ +#include "quantum.h" + +typedef enum { + SWITCH_Y = 0x01, + SWITCH_B = 0x02, + SWITCH_A = 0x04, + SWITCH_X = 0x08, + SWITCH_L = 0x10, + SWITCH_R = 0x20, + SWITCH_ZL = 0x40, + SWITCH_ZR = 0x80, + SWITCH_MINUS = 0x100, + SWITCH_PLUS = 0x200, + SWITCH_LCLICK = 0x400, + SWITCH_RCLICK = 0x800, + SWITCH_HOME = 0x1000, + SWITCH_CAPTURE = 0x2000, +} SwitchButtons_t; + +typedef enum { + GAMECUBE_A = 0b0000000000000001, + GAMECUBE_B = 0b0000000000000010, + GAMECUBE_X = 0b0000000000000100, + GAMECUBE_Y = 0b0000000000001000, + GAMECUBE_START = 0b0000000000010000, + GAMECUBE_LEFT = 0b0000000100000000, + GAMECUBE_RIGHT = 0b0000001000000000, + GAMECUBE_DOWN = 0b0000010000000000, + GAMECUBE_UP = 0b0000100000000000, + GAMECUBE_Z = 0b0001000000000000, + GAMECUBE_R = 0b0010000000000000, + GAMECUBE_L = 0b0100000000000000, +} GamecubeButtons_t; + +#define HAT_TOP 0x00 +#define HAT_TOP_RIGHT 0x01 +#define HAT_RIGHT 0x02 +#define HAT_BOTTOM_RIGHT 0x03 +#define HAT_BOTTOM 0x04 +#define HAT_BOTTOM_LEFT 0x05 +#define HAT_LEFT 0x06 +#define HAT_TOP_LEFT 0x07 +#define HAT_CENTER 0x08 + +#define STICK_MIN 0 +#define STICK_CENTER 128 +#define STICK_MAX 255 diff --git a/keyboards/handwired/gc_controller/gc_read.c b/keyboards/handwired/gc_controller/gc_read.c new file mode 100644 index 00000000000..d73e986c884 --- /dev/null +++ b/keyboards/handwired/gc_controller/gc_read.c @@ -0,0 +1,216 @@ +#include "gc_read.h" +#include "gc_controller.h" + +#define CONTROLLER_TIMEOUT 60 + +bool init_message[9] = {0, 0, 0, 0, 0, 0, 0, 0, 1}; +bool request_message[25] = { + 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 1}; + +void start_message(void) { + setPinOutput(GAMECUBE_DATA_PIN); + writePinLow(GAMECUBE_DATA_PIN); +} + +void send_bit(bool b) { + if (b) { + writePinLow(GAMECUBE_DATA_PIN); + wait_ns(900); + writePinHigh(GAMECUBE_DATA_PIN); + wait_ns(2900); + } else { + writePinLow(GAMECUBE_DATA_PIN); + wait_ns(2900); + writePinHigh(GAMECUBE_DATA_PIN); + wait_ns(900); + } +} + +void wait_for_ready(void) { + setPinInputHigh(GAMECUBE_DATA_PIN); + + // wait for long high + uint8_t ready = 0; + while (ready < 5) { + if (readPin(GAMECUBE_DATA_PIN)) + ready++; + else + ready = 0; + wait_us(1); + } +} + +void end_message(void) { + setPinInputHigh(GAMECUBE_DATA_PIN); +} + +uint8_t buffer[9] = {0}; +uint16_t buttons_debounce = 0; +bool initialised = false; +bool calibrated = false; +uint8_t mid_values[4] = {128, 128, 128, 128}; +uint8_t max_values[4] = {210, 210, 210, 210}; +uint8_t min_values[4] = {35, 35, 35, 35}; + +void gamecube_init(void) { + setPinInputHigh(GAMECUBE_DATA_PIN); +} + +void gamecube_scan(uint16_t * buttons, uint8_t * joysticks) { + bool exiting = false; + uint16_t timeout_counter = 0; + + // somehow we're missing the first bit, which can safely be ignored + // i'm not sure if it's something with the timing or what + uint8_t buffer_bit = 1; + + chSysLock(); + + if (!initialised) { + wait_for_ready(); + + start_message(); + for (uint8_t i = 0; i < 9; i++) + send_bit(init_message[i]); + end_message(); + + initialised = true; + } + + wait_for_ready(); + + start_message(); + for (uint8_t i = 0; i < 25; i++) + send_bit(request_message[i]); + end_message(); + + while (!exiting) { + timeout_counter = 0; + // wait for low or timeout + while (readPin(GAMECUBE_DATA_PIN)) { + wait_ns(100); + timeout_counter++; + if (timeout_counter > CONTROLLER_TIMEOUT) { + exiting = true; + break; + } + } + + if (!exiting) { + // wait for the data part + wait_ns(1950); + bool b = readPin(GAMECUBE_DATA_PIN); + if (b) + buffer[buffer_bit / 8] |= (1 << (7 - (buffer_bit % 8))); + else + buffer[buffer_bit / 8] &= ~(1 << (7 - (buffer_bit % 8))); + buffer_bit++; + + // wait for high + while (!readPin(GAMECUBE_DATA_PIN)); + } + } + + chSysUnlock(); + + // basic debouncing for buttons + uint16_t combined_buttons = buffer[0] | (buffer[1] << 8); + *buttons |= buttons_debounce & combined_buttons; + *buttons &= buttons_debounce | combined_buttons; + buttons_debounce = combined_buttons; + + if (!calibrated && mid_values[0] > 0) { + mid_values[0] = buffer[2]; + mid_values[1] = buffer[3]; + mid_values[2] = buffer[4]; + mid_values[3] = buffer[5]; + calibrated = true; + } + + if (max_values[0] < buffer[2]) + max_values[0] = buffer[2]; + if (max_values[1] < buffer[3]) + max_values[1] = buffer[3]; + if (max_values[2] < buffer[4]) + max_values[2] = buffer[4]; + if (max_values[3] < buffer[5]) + max_values[3] = buffer[5]; + + if (min_values[0] > buffer[2]) + min_values[0] = buffer[2]; + if (min_values[1] > buffer[3]) + min_values[1] = buffer[3]; + if (min_values[2] > buffer[4]) + min_values[2] = buffer[4]; + if (min_values[3] > buffer[5]) + min_values[3] = buffer[5]; + + // values from my GC controller in Windows + // 30 - 138 - 236 + // 20 - 124 - 225 + // 37 - 135 - 222 + // 34 - 126 - 216 + + // this should be 127? i don't understand what i'm doing wrong yet + #define JOYSTICK_SCALER 180.0 + + int32_t lx_temp = (int16_t)buffer[2] - mid_values[0]; + if (lx_temp > 0) + lx_temp *= JOYSTICK_SCALER / (max_values[0]-mid_values[0]); + else + lx_temp *= JOYSTICK_SCALER / (mid_values[0]-min_values[0]); + lx_temp += STICK_CENTER; + if (lx_temp > 255) + lx_temp = 255; + if (lx_temp < 0) + lx_temp = 0; + + int32_t ly_temp = (int16_t)buffer[3] - mid_values[1]; + if (ly_temp > 0) + ly_temp *= JOYSTICK_SCALER / (max_values[1]-mid_values[1]); + else + ly_temp *= JOYSTICK_SCALER / (mid_values[1]-min_values[1]); + ly_temp += STICK_CENTER; + if (ly_temp > 255) + ly_temp = 255; + if (ly_temp < 0) + ly_temp = 0; + + int32_t rx_temp = (int16_t)buffer[4] - mid_values[2]; + if (rx_temp > 0) + rx_temp *= JOYSTICK_SCALER / (max_values[2]-mid_values[2]); + else + rx_temp *= JOYSTICK_SCALER / (mid_values[2]-min_values[2]); + rx_temp += STICK_CENTER; + if (rx_temp > 255) + rx_temp = 255; + if (rx_temp < 0) + rx_temp = 0; + + int32_t ry_temp = (int16_t)buffer[5] - mid_values[3]; + if (ry_temp > 0) + ry_temp *= JOYSTICK_SCALER / (max_values[3]-mid_values[3]); + else + ry_temp *= JOYSTICK_SCALER / (mid_values[3]-min_values[3]); + ry_temp += STICK_CENTER; + if (ry_temp > 255) + ry_temp = 255; + if (ry_temp < 0) + ry_temp = 0; + + // checks to avoid a data skip (0 values on input, which aren't possible, i think) + if (buffer[2]) + joysticks[0] = lx_temp; + if (buffer[3]) + joysticks[1] = ly_temp; + if (buffer[4]) + joysticks[2] = rx_temp; + if (buffer[5]) + joysticks[3] = ry_temp; + if (buffer[6]) + joysticks[4] = buffer[6]; + if (buffer[7]) + joysticks[5] = buffer[7]; +} diff --git a/keyboards/handwired/gc_controller/gc_read.h b/keyboards/handwired/gc_controller/gc_read.h new file mode 100644 index 00000000000..0153414f2ad --- /dev/null +++ b/keyboards/handwired/gc_controller/gc_read.h @@ -0,0 +1,34 @@ +#include "quantum.h" + +/* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */ + +#ifndef NOP_FUDGE +# if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) +# define NOP_FUDGE 0.4 +# else +# error("NOP_FUDGE configuration required") +# define NOP_FUDGE 1 // this just pleases the compile so the above error is easier to spot +# endif +#endif + +#define NUMBER_NOPS 6 +#define CYCLES_PER_SEC (STM32_SYSCLK / NUMBER_NOPS * NOP_FUDGE) +#define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives +#define NS_PER_CYCLE (NS_PER_SEC / CYCLES_PER_SEC) +#define NS_TO_CYCLES(n) ((n) / NS_PER_CYCLE) + +#define wait_ns(x) \ + do { \ + for (int i = 0; i < NS_TO_CYCLES(x); i++) { \ + __asm__ volatile("nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t"); \ + } \ + } while (0) + + +void gamecube_init(void); +void gamecube_scan(uint16_t * buttons, uint8_t * joysticks); diff --git a/keyboards/handwired/gc_controller/keymaps/default/keymap.c b/keyboards/handwired/gc_controller/keymaps/default/keymap.c new file mode 100644 index 00000000000..bfba0fbb68e --- /dev/null +++ b/keyboards/handwired/gc_controller/keymaps/default/keymap.c @@ -0,0 +1,3 @@ +#include QMK_KEYBOARD_H + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { { { 0 } } }; diff --git a/keyboards/handwired/gc_controller/readme.md b/keyboards/handwired/gc_controller/readme.md new file mode 100644 index 00000000000..455f13f7ffe --- /dev/null +++ b/keyboards/handwired/gc_controller/readme.md @@ -0,0 +1,12 @@ + +J1 header +--------- + +|#|Color |Proton C|Description| +|-|-------|----|------------| +|1|blue |3.3V| | +|2|yellow |5V | | +|3|red |A2 |Data (3.3V) | +|4|green |GND | | +|5|white |GND | | +|6|black |NC | | \ No newline at end of file diff --git a/keyboards/handwired/gc_controller/rules.mk b/keyboards/handwired/gc_controller/rules.mk new file mode 100644 index 00000000000..758422b0177 --- /dev/null +++ b/keyboards/handwired/gc_controller/rules.mk @@ -0,0 +1,15 @@ +# MCU name +MCU = STM32F303 + +BACKLIGHT_ENABLE = no +MOUSEKEY_ENABLE = no +EXTRAKEY_ENABLE = no +CONSOLE_ENABLE = no +NKRO_ENABLE = no +RAW_ENABLE = no +MIDI_ENABLE = no +VIRTSER_ENABLE = no +KEYBOARD_ENABLE = no +SWITCH_CONTROLLER_ENABLE = yes + +SRC += gc_read.c \ No newline at end of file diff --git a/quantum/gamepad.h b/quantum/gamepad.h new file mode 100644 index 00000000000..69082de939f --- /dev/null +++ b/quantum/gamepad.h @@ -0,0 +1,4 @@ +#pragma once + +void send_gamepad(report_gamepad_t *report); +void gamepad_ep_task(void); \ No newline at end of file diff --git a/tmk_core/common/report.h b/tmk_core/common/report.h index 58baa2bcd34..eebd6442363 100644 --- a/tmk_core/common/report.h +++ b/tmk_core/common/report.h @@ -30,7 +30,8 @@ enum hid_report_ids { REPORT_ID_SYSTEM, REPORT_ID_CONSUMER, REPORT_ID_NKRO, - REPORT_ID_JOYSTICK + REPORT_ID_JOYSTICK, + REPORT_ID_GAMEPAD }; /* Mouse buttons */ @@ -197,6 +198,29 @@ typedef struct { #endif } __attribute__((packed)) joystick_report_t; +#ifdef GAMEPAD_ENABLE +typedef struct { +#ifdef SWITCH_CONTROLLER_ENABLE + uint16_t Button; // 16 buttons; see JoystickButtons_t for bit mapping + uint8_t HAT; // HAT switch; one nibble w/ unused nibble + uint8_t LX; // Left Stick X + uint8_t LY; // Left Stick Y + uint8_t RX; // Right Stick X + uint8_t RY; // Right Stick Y + uint8_t VendorSpec; +#else + // TODO add generic gamepad report + uint16_t Button; // 16 buttons; see JoystickButtons_t for bit mapping + uint8_t HAT; // HAT switch; one nibble w/ unused nibble + uint8_t LX; // Left Stick X + uint8_t LY; // Left Stick Y + uint8_t RX; // Right Stick X + uint8_t RY; // Right Stick Y + uint8_t VendorSpec; +#endif +} report_gamepad_t; +#endif + /* keycode to system usage */ static inline uint16_t KEYCODE2SYSTEM(uint8_t key) { switch (key) { diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index 61665eb6f4b..3f302c9590b 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -82,10 +82,15 @@ void raw_hid_task(void); #ifdef CONSOLE_ENABLE void console_task(void); #endif + #ifdef MIDI_ENABLE void midi_ep_task(void); #endif +#ifdef GAMEPAD_ENABLE +void gamepad_ep_task(void); +#endif + /* TESTING * Amber LED blinker thread, times are in milliseconds. */ @@ -253,6 +258,9 @@ int main(void) { #endif #ifdef MIDI_ENABLE midi_ep_task(); +#endif +#ifdef GAMEPAD_ENABLE + gamepad_ep_task(); #endif #ifdef VIRTSER_ENABLE virtser_task(); diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index cd34fd28910..922a18e9e84 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -51,6 +51,10 @@ extern keymap_config_t keymap_config; # include "joystick.h" #endif +#ifdef GAMEPAD_ENABLE +# include "gamepad.h" +#endif + /* --------------------------------------------------------- * Global interface variables and declarations * --------------------------------------------------------- @@ -109,7 +113,7 @@ static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype return &desc; } -#ifndef KEYBOARD_SHARED_EP +#if !defined(KEYBOARD_SHARED_EP) && defined(KEYBOARD_ENABLE) /* keyboard endpoint state structure */ static USBInEndpointState kbd_ep_state; /* keyboard endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */ @@ -253,6 +257,9 @@ typedef struct { #endif #ifdef JOYSTICK_ENABLE usb_driver_config_t joystick_driver; +#endif +#ifdef GAMEPAD_ENABLE + usb_driver_config_t gamepad_driver; #endif }; usb_driver_config_t array[0]; @@ -298,6 +305,14 @@ static usb_driver_configs_t drivers = { # define JOYSTICK_OUT_MODE USB_EP_MODE_TYPE_BULK .joystick_driver = QMK_USB_DRIVER_CONFIG(JOYSTICK, 0, false), #endif + +#ifdef GAMEPAD_ENABLE +# define GAMEPAD_IN_CAPACITY 4 +# define GAMEPAD_OUT_CAPACITY 4 +# define GAMEPAD_IN_MODE USB_EP_MODE_TYPE_BULK +# define GAMEPAD_OUT_MODE USB_EP_MODE_TYPE_BULK + .gamepad_driver = QMK_USB_DRIVER_CONFIG(GAMEPAD, 0, false), +#endif }; #define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t)) @@ -317,7 +332,7 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { case USB_EVENT_CONFIGURED: osalSysLockFromISR(); /* Enable the endpoints specified into the configuration. */ -#ifndef KEYBOARD_SHARED_EP +#if !defined(KEYBOARD_SHARED_EP) && defined(KEYBOARD_ENABLE) usbInitEndpointI(usbp, KEYBOARD_IN_EPNUM, &kbd_ep_config); #endif #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) @@ -426,11 +441,12 @@ static bool usb_request_hook_cb(USBDriver *usbp) { switch (usbp->setup[1]) { /* bRequest */ case HID_GET_REPORT: switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ +#ifdef KEYBOARD_ENABLE case KEYBOARD_INTERFACE: usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent), NULL); return TRUE; break; - +#endif #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) case MOUSE_INTERFACE: usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL); @@ -446,7 +462,7 @@ static bool usb_request_hook_cb(USBDriver *usbp) { break; case HID_GET_PROTOCOL: - if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ + if ((usbp->setup[4] == 0) && (usbp->setup[5] == 0)) { /* wIndex */ usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL); return TRUE; } @@ -463,7 +479,9 @@ static bool usb_request_hook_cb(USBDriver *usbp) { switch (usbp->setup[1]) { /* bRequest */ case HID_SET_REPORT: switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ +#ifdef KEYBOARD_ENABLE case KEYBOARD_INTERFACE: +#endif #if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) case SHARED_INTERFACE: #endif @@ -474,7 +492,7 @@ static bool usb_request_hook_cb(USBDriver *usbp) { break; case HID_SET_PROTOCOL: - if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ + if ((usbp->setup[4] == 0) && (usbp->setup[5] == 0)) { /* wIndex */ keyboard_protocol = ((usbp->setup[2]) != 0x00); /* LSB(wValue) */ #ifdef NKRO_ENABLE keymap_config.nkro = !!keyboard_protocol; @@ -579,7 +597,7 @@ void init_usb_driver(USBDriver *usbp) { * --------------------------------------------------------- */ /* keyboard IN callback hander (a kbd report has made it IN) */ -#ifndef KEYBOARD_SHARED_EP +#if !defined(KEYBOARD_SHARED_EP) && defined(KEYBOARD_ENABLE) void kbd_in_cb(USBDriver *usbp, usbep_t ep) { /* STUB */ (void)usbp; @@ -612,9 +630,11 @@ static void keyboard_idle_timer_cb(void *arg) { if (keyboard_idle && keyboard_protocol) { #endif /* NKRO_ENABLE */ /* TODO: are we sure we want the KBD_ENDPOINT? */ +#ifdef KEYBOARD_ENABLE if (!usbGetTransmitStatusI(usbp, KEYBOARD_IN_EPNUM)) { usbStartTransmitI(usbp, KEYBOARD_IN_EPNUM, (uint8_t *)&keyboard_report_sent, KEYBOARD_EPSIZE); } +#endif /* rearm the timer */ chVTSetI(&keyboard_idle_timer, 4 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); } @@ -630,6 +650,7 @@ uint8_t keyboard_leds(void) { return keyboard_led_stats; } /* prepare and start sending a report IN * not callable from ISR or locked state */ void send_keyboard(report_keyboard_t *report) { +#ifdef KEYBOARD_ENABLE osalSysLock(); if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { goto unlock; @@ -686,6 +707,7 @@ void send_keyboard(report_keyboard_t *report) { unlock: osalSysUnlock(); +#endif } /* --------------------------------------------------------- @@ -939,3 +961,35 @@ void send_joystick_packet(joystick_t *joystick) { } #endif + +#ifdef GAMEPAD_ENABLE + +void send_gamepad(report_gamepad_t *report) { + osalSysLock(); + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + + usbStartTransmitI(&USB_DRIVER, GAMEPAD_IN_EPNUM, (uint8_t *)report, sizeof(report_gamepad_t)); + osalSysUnlock(); +} + +bool recv_gamepad_packet(report_gamepad_t *const report) { + size_t size = chnReadTimeout(&drivers.gamepad_driver.driver, (uint8_t *)report, sizeof(report_gamepad_t), TIME_IMMEDIATE); + return size == sizeof(report_gamepad_t); +} + +void gamepad_ep_task(void) { + uint8_t buffer[GAMEPAD_EPSIZE]; + size_t size = 0; + do { + size_t size = chnReadTimeout(&drivers.gamepad_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); + if (size > 0) { + report_gamepad_t report; + recv_gamepad_packet(&report); + } + } while (size > 0); +} + +#endif diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c index e8842d27a17..7fea8244feb 100644 --- a/tmk_core/protocol/usb_descriptor.c +++ b/tmk_core/protocol/usb_descriptor.c @@ -45,6 +45,7 @@ /* * HID report descriptors */ +#ifdef KEYBOARD_ENABLE #ifdef KEYBOARD_SHARED_EP const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = { # define SHARED_REPORT_STARTED @@ -95,6 +96,7 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM KeyboardReport[] = { #ifndef KEYBOARD_SHARED_EP }; #endif +#endif #ifdef MOUSE_ENABLE # ifndef MOUSE_SHARED_EP @@ -335,6 +337,64 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM JoystickReport[] = { }; #endif +#ifdef GAMEPAD_ENABLE +const USB_Descriptor_HIDReport_Datatype_t PROGMEM GamepadReport[] = { + HID_RI_USAGE_PAGE(8,1), /* Generic Desktop */ + HID_RI_USAGE(8,5), /* Joystick */ + HID_RI_COLLECTION(8,1), /* Application */ + // Buttons (2 bytes) + HID_RI_LOGICAL_MINIMUM(8,0), + HID_RI_LOGICAL_MAXIMUM(8,1), + HID_RI_PHYSICAL_MINIMUM(8,0), + HID_RI_PHYSICAL_MAXIMUM(8,1), + // The Switch will allow us to expand the original HORI descriptors to a full 16 buttons. + // The Switch will make use of 14 of those buttons. + HID_RI_REPORT_SIZE(8,1), + HID_RI_REPORT_COUNT(8,16), + HID_RI_USAGE_PAGE(8,9), + HID_RI_USAGE_MINIMUM(8,1), + HID_RI_USAGE_MAXIMUM(8,16), + HID_RI_INPUT(8,2), + // HAT Switch (1 nibble) + HID_RI_USAGE_PAGE(8,1), + HID_RI_LOGICAL_MAXIMUM(8,7), + HID_RI_PHYSICAL_MAXIMUM(16,315), + HID_RI_REPORT_SIZE(8,4), + HID_RI_REPORT_COUNT(8,1), + HID_RI_UNIT(8,20), + HID_RI_USAGE(8,57), + HID_RI_INPUT(8,66), + // There's an additional nibble here that's utilized as part of the Switch Pro Controller. + // I believe this -might- be separate U/D/L/R bits on the Switch Pro Controller, as they're utilized as four button descriptors on the Switch Pro Controller. + HID_RI_UNIT(8,0), + HID_RI_REPORT_COUNT(8,1), + HID_RI_INPUT(8,1), + // Joystick (4 bytes) + HID_RI_LOGICAL_MAXIMUM(16,255), + HID_RI_PHYSICAL_MAXIMUM(16,255), + HID_RI_USAGE(8,48), + HID_RI_USAGE(8,49), + HID_RI_USAGE(8,50), + HID_RI_USAGE(8,53), + HID_RI_REPORT_SIZE(8,8), + HID_RI_REPORT_COUNT(8,4), + HID_RI_INPUT(8,2), + // ??? Vendor Specific (1 byte) + // This byte requires additional investigation. + HID_RI_USAGE_PAGE(16,65280), + HID_RI_USAGE(8,32), + HID_RI_REPORT_COUNT(8,1), + HID_RI_INPUT(8,2), + // Output (8 bytes) + // Original observation of this suggests it to be a mirror of the inputs that we sent. + // The Switch requires us to have these descriptors available. + HID_RI_USAGE(16,9761), + HID_RI_REPORT_COUNT(8,8), + HID_RI_OUTPUT(8,2), + HID_RI_END_COLLECTION(0), +}; +#endif + /* * Device descriptor */ @@ -390,7 +450,7 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = { .ConfigAttributes = (USB_CONFIG_ATTR_RESERVED | USB_CONFIG_ATTR_REMOTEWAKEUP), .MaxPowerConsumption = USB_CONFIG_POWER_MA(USB_MAX_POWER_CONSUMPTION) }, -#ifndef KEYBOARD_SHARED_EP +#if !defined(KEYBOARD_SHARED_EP) && defined(KEYBOARD_ENABLE) /* * Keyboard */ @@ -909,6 +969,56 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = { .PollingIntervalMS = USB_POLLING_INTERVAL_MS } #endif + +#ifdef GAMEPAD_ENABLE + /* + * Gamepad + */ + .Gamepad_Interface = { + .Header = { + .Size = sizeof(USB_Descriptor_Interface_t), + .Type = DTYPE_Interface + }, + .InterfaceNumber = GAMEPAD_INTERFACE, + .AlternateSetting = 0x00, + .TotalEndpoints = 2, + .Class = HID_CSCP_HIDClass, + .SubClass = HID_CSCP_NonBootSubclass, + .Protocol = HID_CSCP_NonBootProtocol, + .InterfaceStrIndex = NO_DESCRIPTOR + }, + .Gamepad_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(GamepadReport) + }, + .Gamepad_INEndpoint = { + .Header = { + .Size = sizeof(USB_Descriptor_Endpoint_t), + .Type = DTYPE_Endpoint + }, + .EndpointAddress = (ENDPOINT_DIR_IN | GAMEPAD_IN_EPNUM), + .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), + .EndpointSize = GAMEPAD_EPSIZE, + .PollingIntervalMS = USB_POLLING_INTERVAL_MS + }, + .Gamepad_OUTEndpoint = { + .Header = { + .Size = sizeof(USB_Descriptor_Endpoint_t), + .Type = DTYPE_Endpoint + }, + .EndpointAddress = (ENDPOINT_DIR_OUT | GAMEPAD_OUT_EPNUM), + .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), + .EndpointSize = GAMEPAD_EPSIZE, + .PollingIntervalMS = USB_POLLING_INTERVAL_MS + } +#endif }; /* @@ -1003,7 +1113,7 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const break; case HID_DTYPE_HID: switch (wIndex) { -#ifndef KEYBOARD_SHARED_EP +#if !defined(KEYBOARD_SHARED_EP) && defined(KEYBOARD_ENABLE) case KEYBOARD_INTERFACE: Address = &ConfigurationDescriptor.Keyboard_HID; Size = sizeof(USB_HID_Descriptor_HID_t); @@ -1046,13 +1156,19 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const Address = &ConfigurationDescriptor.Joystick_HID; Size = sizeof(USB_HID_Descriptor_HID_t); break; +#endif +#ifdef GAMEPAD_ENABLE + case GAMEPAD_INTERFACE: + Address = &ConfigurationDescriptor.Gamepad_HID; + Size = sizeof(USB_HID_Descriptor_HID_t); + break; #endif } break; case HID_DTYPE_Report: switch (wIndex) { -#ifndef KEYBOARD_SHARED_EP +#if !defined(KEYBOARD_SHARED_EP) && defined(KEYBOARD_ENABLE) case KEYBOARD_INTERFACE: Address = &KeyboardReport; Size = sizeof(KeyboardReport); @@ -1096,6 +1212,12 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const Address = &JoystickReport; Size = sizeof(JoystickReport); break; +#endif +#ifdef GAMEPAD_ENABLE + case GAMEPAD_INTERFACE: + Address = &GamepadReport; + Size = sizeof(GamepadReport); + break; #endif } diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index 17ed7517c1c..758007d49c1 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -55,7 +55,7 @@ typedef struct { USB_Descriptor_Configuration_Header_t Config; -#ifndef KEYBOARD_SHARED_EP +#if !defined(KEYBOARD_SHARED_EP) && defined(KEYBOARD_ENABLE) // Keyboard HID Interface USB_Descriptor_Interface_t Keyboard_Interface; USB_HID_Descriptor_HID_t Keyboard_HID; @@ -129,16 +129,30 @@ typedef struct { USB_Descriptor_Interface_t Joystick_Interface; USB_HID_Descriptor_HID_t Joystick_HID; USB_Descriptor_Endpoint_t Joystick_INEndpoint; + #ifdef SWITCH_CONTROLLER_ENABLE + USB_Descriptor_Endpoint_t Joystick_OUTEndpoint; + #endif #endif + +#ifdef GAMEPAD_ENABLE + // Gamepad HID Interface + USB_Descriptor_Interface_t Gamepad_Interface; + USB_HID_Descriptor_HID_t Gamepad_HID; + USB_Descriptor_Endpoint_t Gamepad_INEndpoint; + USB_Descriptor_Endpoint_t Gamepad_OUTEndpoint; +#endif + } USB_Descriptor_Configuration_t; /* * Interface indexes */ enum usb_interfaces { -#ifndef KEYBOARD_SHARED_EP +#if !defined(KEYBOARD_SHARED_EP) && defined(KEYBOARD_ENABLE) KEYBOARD_INTERFACE, -#else +#endif + +#ifdef KEYBOARD_SHARED_EP # define KEYBOARD_INTERFACE SHARED_INTERFACE #endif @@ -171,9 +185,13 @@ enum usb_interfaces { CDI_INTERFACE, #endif -#if defined(JOYSTICK_ENABLE) +#ifdef JOYSTICK_ENABLE JOYSTICK_INTERFACE, #endif + +#ifdef GAMEPAD_ENABLE + GAMEPAD_INTERFACE, +#endif TOTAL_INTERFACES }; @@ -185,9 +203,10 @@ enum usb_interfaces { enum usb_endpoints { __unused_epnum__ = NEXT_EPNUM, // Endpoint numbering starts at 1 -#ifndef KEYBOARD_SHARED_EP +#if !defined(KEYBOARD_SHARED_EP) && defined(KEYBOARD_ENABLE) KEYBOARD_IN_EPNUM = NEXT_EPNUM, -#else +#endif +#ifdef KEYBOARD_SHARED_EP # define KEYBOARD_IN_EPNUM SHARED_IN_EPNUM #endif @@ -234,10 +253,16 @@ 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 JOYSTICK_ENABLE JOYSTICK_IN_EPNUM = NEXT_EPNUM, JOYSTICK_OUT_EPNUM = NEXT_EPNUM, #endif + +#ifdef GAMEPAD_ENABLE + GAMEPAD_IN_EPNUM = NEXT_EPNUM, + GAMEPAD_OUT_EPNUM = NEXT_EPNUM, +#endif }; #ifdef PROTOCOL_LUFA @@ -262,6 +287,7 @@ enum usb_endpoints { #define MIDI_STREAM_EPSIZE 64 #define CDC_NOTIFICATION_EPSIZE 8 #define CDC_EPSIZE 16 +#define GAMEPAD_EPSIZE 64 #define JOYSTICK_EPSIZE 8 uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const void** const DescriptorAddress);