Compare commits

...

25 commits

Author SHA1 Message Date
Jack Humbert
d2123f3c07 remove some outdated changes 2021-02-08 14:48:56 -05:00
Jack Humbert
3996250d81 merge from master 2021-02-07 21:01:30 -05:00
Jack Humbert
09ff4b0c99 possibly all files needed to make things work 2021-02-07 19:53:50 -05:00
Jack Humbert
708bb4f55d Merge branch 'hid_joystick' of git://github.com/a-chol/qmk_firmware into a-chol-hid_joystick 2020-05-04 13:19:48 -04:00
a-chol
a80ea8b7cc
Merge branch 'master' into hid_joystick 2020-03-30 14:41:56 +02:00
a-chol
a03d1eb66b Add PR corrections 2020-03-30 14:38:57 +02:00
a-chol
92c6466089
Update docs/feature_joystick.md
Co-Authored-By: Ryan <fauxpark@gmail.com>
2020-03-30 14:25:49 +02:00
a-chol
16fe12e6b6
Update docs/feature_joystick.md
Co-Authored-By: Ryan <fauxpark@gmail.com>
2020-03-30 14:24:48 +02:00
a-chol
1d8102183f Switch saveState and restoreState signature to use pin_t type.
onekey:joystick : add a second axis, virtual and programmatically animated.
2020-03-30 14:11:44 +02:00
a-chol
476fce8a32 Add missing mcuconf.h and halconf.h to onekey:joystick keymap.
Add suggested fixes from PR.
2020-03-30 11:19:23 +02:00
a-chol
0ce015d0c9
Update tmk_core/protocol/lufa/lufa.c
Co-Authored-By: Ryan <fauxpark@gmail.com>
2020-03-30 11:17:26 +02:00
a-chol
c447c4ffd5
Update tmk_core/protocol/chibios/usb_main.c
Co-Authored-By: Ryan <fauxpark@gmail.com>
2020-03-30 11:17:14 +02:00
a-chol
887e96cbb2 Fix HID joystick report sending for ChibiOS.
Add one analog axis to the onekey:joystick keymap.
Fix pin state save and restore during joystick analog read for STM32
MCUs.
2020-03-30 00:51:43 +02:00
a-chol
def06b4976 Add support for joystick adc reading for stm32 MCUs. Fix joystick hid report sending for chibios 2020-03-29 15:43:19 +02:00
a-chol
34bedc3787 Update tmk_core/protocol/usb_descriptor.c
Co-Authored-By: Ryan <fauxpark@gmail.com>
2020-03-22 11:26:01 +01:00
a-chol
c6c01f9eff Update tmk_core/protocol/usb_descriptor.c
Co-Authored-By: Ryan <fauxpark@gmail.com>
2020-03-22 11:26:01 +01:00
a-chol
86853a4110 Update tmk_core/protocol/usb_descriptor.c
Co-Authored-By: Ryan <fauxpark@gmail.com>
2020-03-22 11:26:01 +01:00
a-chol
a8a2825bd4 Update tmk_core/protocol/lufa/lufa.c
Co-Authored-By: Ryan <fauxpark@gmail.com>
2020-03-22 11:26:01 +01:00
Nick Brassel
38cd264dd8 Remove V-USB support for now. Updated docs accordingly. 2020-03-22 11:25:55 +01:00
a-chol
6fd7f9d12f avoid float functions to compute range mapping for axis adc reading 2020-03-22 11:24:43 +01:00
Nick Brassel
801be60473 The other required set of changes
As per the PR, the changes still holding it up.
Add onekey for testing.
Fix ARM builds.
Fix device descriptor when either axes or buttons is zero.
Add compile-time check for at least one axis or button.
Move definition to try to fix conflict.
PR review comments.
qmk cformat
2020-03-22 11:24:43 +01:00
a-chol
d88bdc6a1b Fix port addressing for joystick analog read 2020-03-22 11:24:42 +01:00
achol
b030c45705 Add save and restore of each pin used in reading joystick (AVR).
Allow output pin to be JS_VIRTUAL_AXIS if the axis is connected to Vcc
instead of an output pin from the MCU.

Fix joystick report id

Fix broken v-usb hid joystick interface. Make it more resilient to unusual settings (none multiple of eight button count, 0 buttons or 0 axes)

Correct adc reading for multiple axes. Piecewise range conversion for uncentered raw value range. Input, output and ground pin configuration per axis.

Documentation fixes
2020-03-22 11:24:36 +01:00
José Júnior
ee43b338ea Incorporates patches and changes to HID reporting
There are some patches provided by @a-chol incorporated on this commit,
and also some changes I made to the HID Report structure.

The most interesting is the one dealing with number of buttons: Linux
doesn't seem to care, but Windows requires the HID structure to be byte
aligned (that's in the spec). So if one declares 8/16/32... buttons they
should not have any issues, but this is what happens when you have 9
buttons:

```
 bits |0|1|2|3|4|5|6|7|
      |*|*|*|*|*|*|*|*| axis 0 (report size 8)
      |*|*|*|*|*|*|*|*| ...
      |*|*|*|*|*|*|*|*|
      |*|*|*|*|*|*|*|*|
      |*|*|*|*|*|*|*|*|
      |*|*|*|*|*|*|*|*|
      |*|*|*|*|*|*|*|*| axis 6
      |*|*|*|*|*|*|*|*| first 8 buttons (report size 1)
      |*| | | | | | | | last of 9 buttons, not aligned
```

So for that I added a conditonal that will add a number of reports with
size 1 to make sure it aligns to the next multiple of 8. Those reports
send dummy inputs that don't do anything aside from aligning the data.

Tested on Linux, Windows 10 and Street Fighter (where the joystick is
recognized as direct-input)
2020-03-22 11:23:32 +01:00
achol
3cf7611139 add support for hid gamepad interface
add documentation for HID joystick
Add joystick_task to read analog axes values even when no key is pressed or release. update doc
Update docs/feature_joystick.md
Manage pin setup and read to maintain matrix scan after analog read
2020-03-22 11:23:25 +01:00
17 changed files with 788 additions and 16 deletions

View file

@ -22,6 +22,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 $(DEBUG_MATRIX_SCAN_RATE_ENABLE)), yes)
OPT_DEFS += -DDEBUG_MATRIX_SCAN_RATE
CONSOLE_ENABLE = yes
@ -600,3 +605,8 @@ endif
ifeq ($(strip $(JOYSTICK_ENABLE)), digital)
OPT_DEFS += -DDIGITAL_JOYSTICK_ENABLE
endif
ifeq ($(strip $(SWITCH_CONTROLLER_ENABLE)), yes)
OPT_DEFS += -DSWITCH_CONTROLLER_ENABLE
OPT_DEFS += -DGAMEPAD_ENABLE
endif

View file

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

View file

@ -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);
}

View file

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

View file

@ -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];
}

View file

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

View file

@ -0,0 +1,3 @@
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { { { 0 } } };

View file

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

View file

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

View file

@ -29,4 +29,4 @@
#define RGB_DI_PIN A0
#define ADC_PIN A0
#define ADC_PIN A1

4
quantum/gamepad.h Normal file
View file

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

View file

@ -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 */
@ -198,6 +199,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) {

View file

@ -86,10 +86,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.
*/
@ -259,6 +264,9 @@ int main(void) {
#ifdef MIDI_ENABLE
midi_ep_task();
#endif
#ifdef GAMEPAD_ENABLE
gamepad_ep_task();
#endif
#ifdef VIRTSER_ENABLE
virtser_task();
#endif

View file

@ -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 */
@ -314,6 +318,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];
@ -359,6 +366,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))
@ -378,7 +393,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)
@ -491,11 +506,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);
@ -511,7 +527,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;
}
@ -528,7 +544,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
@ -539,7 +557,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;
@ -660,7 +678,7 @@ void restart_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;
@ -693,9 +711,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);
}
@ -711,6 +731,7 @@ uint8_t keyboard_leds(void) { return keyboard_led_state; }
/* 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;
@ -767,6 +788,7 @@ void send_keyboard(report_keyboard_t *report) {
unlock:
osalSysUnlock();
#endif
}
/* ---------------------------------------------------------
@ -1021,3 +1043,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

View file

@ -524,6 +524,9 @@ void EVENT_USB_Device_ConfigurationChanged(void) {
/* Setup joystick endpoint */
ConfigSuccess &= Endpoint_ConfigureEndpoint((JOYSTICK_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, JOYSTICK_EPSIZE, 1);
#endif
#ifdef JOYSTICK_ENABLE
ConfigSuccess &= ENDPOINT_CONFIG(JOYSTICK_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN, JOYSTICK_EPSIZE, ENDPOINT_BANK_SINGLE);
#endif
}
/* FIXME: Expose this table in the docs somehow

View file

@ -50,6 +50,7 @@
/*
* HID report descriptors
*/
#ifdef KEYBOARD_ENABLE
#ifdef KEYBOARD_SHARED_EP
const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {
# define SHARED_REPORT_STARTED
@ -100,6 +101,7 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM KeyboardReport[] = {
#ifndef KEYBOARD_SHARED_EP
};
#endif
#endif
#ifdef MOUSE_ENABLE
# ifndef MOUSE_SHARED_EP
@ -347,6 +349,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
*/
@ -406,7 +466,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
*/
@ -925,6 +985,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
};
/*
@ -1019,7 +1129,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);
@ -1062,13 +1172,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);
@ -1112,6 +1228,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
}

View file

@ -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;
@ -134,14 +134,26 @@ 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
SHARED_INTERFACE,
@ -177,9 +189,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
};
@ -191,9 +207,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
@ -259,6 +276,16 @@ enum usb_endpoints {
JOYSTICK_OUT_EPNUM = NEXT_EPNUM,
# endif
#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
@ -283,6 +310,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);