Browse Source

possibly all files needed to make things work

Jack Humbert 5 years ago
parent
commit
09ff4b0c99

+ 10 - 0
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

+ 62 - 0
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 <http://www.gnu.org/licenses/>.
+ */
+
+#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

+ 130 - 0
keyboards/handwired/gc_controller/gc_controller.c

@@ -0,0 +1,130 @@
+#include "gc_controller.h"
+#include "gc_read.h"
+#include "gamepad.h"
+#include <string.h>
+
+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);
+
+}

+ 47 - 0
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

+ 216 - 0
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];
+}

+ 34 - 0
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);

+ 3 - 0
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 } } };

+ 12 - 0
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  |            |

+ 15 - 0
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

+ 4 - 0
quantum/gamepad.h

@@ -0,0 +1,4 @@
+#pragma once
+
+void send_gamepad(report_gamepad_t *report);
+void gamepad_ep_task(void);

+ 25 - 1
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) {

+ 8 - 0
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();

+ 60 - 6
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

+ 125 - 3
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
             }
 

+ 32 - 6
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);