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
This commit is contained in:
achol 2018-10-17 11:23:20 +02:00 committed by a-chol
parent d8f3c28a37
commit 3cf7611139
15 changed files with 675 additions and 2 deletions

View file

@ -47,6 +47,10 @@
extern keymap_config_t keymap_config;
#endif
#ifdef JOYSTICK_ENABLE
# include <quantum/joystick.h>
#endif
/* ---------------------------------------------------------
* Global interface variables and declarations
* ---------------------------------------------------------
@ -246,6 +250,9 @@ typedef struct {
#endif
#ifdef VIRTSER_ENABLE
usb_driver_config_t serial_driver;
#endif
#ifdef JOYSTICK_ENABLE
usb_driver_config_t joystick_driver;
#endif
};
usb_driver_config_t array[0];
@ -283,6 +290,14 @@ static usb_driver_configs_t drivers = {
# define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK
.serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false),
#endif
#ifdef JOYSTICK_ENABLE
#define JOYSTICK_IN_CAPACITY 4
#define JOYSTICK_OUT_CAPACITY 4
#define JOYSTICK_IN_MODE USB_EP_MODE_TYPE_BULK
#define JOYSTICK_OUT_MODE USB_EP_MODE_TYPE_BULK
.joystick_driver = QMK_USB_DRIVER_CONFIG(JOYSTICK, 0, false),
#endif
};
#define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t))
@ -870,3 +885,61 @@ void virtser_task(void) {
}
#endif
#ifdef JOYSTICK_ENABLE
typedef struct {
#if JOYSTICK_AXES_COUNT>0
int8_t axes[JOYSTICK_AXES_COUNT];
#endif
#if JOYSTICK_BUTTON_COUNT>0
uint8_t buttons[(JOYSTICK_BUTTON_COUNT-1)/8+1];
#endif
} __attribute__ ((packed)) joystick_report_t;
void send_joystick_packet(joystick_t* joystick) {
joystick_report_t rep = {
#if JOYSTICK_AXES_COUNT>0
.axes = {
joystick->axes[0]
#if JOYSTICK_AXES_COUNT >= 2
,joystick->axes[1]
#endif
#if JOYSTICK_AXES_COUNT >= 3
,joystick->axes[2]
#endif
#if JOYSTICK_AXES_COUNT >= 4
,joystick->axes[3]
#endif
#if JOYSTICK_AXES_COUNT >= 5
,joystick->axes[4]
#endif
#if JOYSTICK_AXES_COUNT >= 6
,joystick->axes[5]
#endif
},
#endif //JOYSTICK_AXES_COUNT>0
#if JOYSTICK_BUTTON_COUNT>0
.buttons = {
joystick->buttons[0]
#if JOYSTICK_BUTTON_COUNT>8
,joystick->buttons[1]
#endif
#if JOYSTICK_BUTTON_COUNT>16
,joystick->buttons[2]
#endif
#if JOYSTICK_BUTTON_COUNT>24
,joystick->buttons[3]
#endif
}
#endif //JOYSTICK_BUTTON_COUNT>0
};
chnWrite(&drivers.joystick_driver.driver, (uint8_t*)&rep, sizeof(rep));
}
#endif

View file

@ -85,6 +85,10 @@ extern keymap_config_t keymap_config;
# include "raw_hid.h"
#endif
#ifdef JOYSTICK_ENABLE
#include "joystick.h"
#endif
uint8_t keyboard_idle = 0;
/* 0: Boot Protocol, 1: Report Protocol(default) */
uint8_t keyboard_protocol = 1;
@ -263,6 +267,85 @@ static void Console_Task(void) {
}
#endif
/*******************************************************************************
* Joystick
******************************************************************************/
#ifdef JOYSTICK_ENABLE
typedef struct {
#if JOYSTICK_AXES_COUNT>0
int8_t axes[JOYSTICK_AXES_COUNT];
#endif
#if JOYSTICK_BUTTON_COUNT>0
uint8_t buttons[(JOYSTICK_BUTTON_COUNT-1)/8+1];
#endif
} __attribute__ ((packed)) joystick_report_t;
void send_joystick_packet(joystick_t* joystick){
uint8_t timeout = 255;
uint8_t where = where_to_send();
if (where != OUTPUT_USB && where != OUTPUT_USB_AND_BT) {
return;
}
joystick_report_t r = {
#if JOYSTICK_AXES_COUNT>0
.axes = {
joystick->axes[0]
#if JOYSTICK_AXES_COUNT >= 2
,joystick->axes[1]
#endif
#if JOYSTICK_AXES_COUNT >= 3
,joystick->axes[2]
#endif
#if JOYSTICK_AXES_COUNT >= 4
,joystick->axes[3]
#endif
#if JOYSTICK_AXES_COUNT >= 5
,joystick->axes[4]
#endif
#if JOYSTICK_AXES_COUNT >= 6
,joystick->axes[5]
#endif
},
#endif //JOYSTICK_AXES_COUNT>0
#if JOYSTICK_BUTTON_COUNT>0
.buttons = {
joystick->buttons[0]
#if JOYSTICK_BUTTON_COUNT>8
,joystick->buttons[1]
#endif
#if JOYSTICK_BUTTON_COUNT>16
,joystick->buttons[2]
#endif
#if JOYSTICK_BUTTON_COUNT>24
,joystick->buttons[3]
#endif
}
#endif //JOYSTICK_BUTTON_COUNT>0
};
/* Select the Joystick Report Endpoint */
Endpoint_SelectEndpoint(JOYSTICK_IN_EPNUM);
/* Check if write ready for a polling interval around 10ms */
while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
if (!Endpoint_IsReadWriteAllowed()) return;
/* Write Joystick Report Data */
Endpoint_Write_Stream_LE(&r, sizeof(joystick_report_t), NULL);
/* Finalize the stream transfer to send the last packet */
Endpoint_ClearIN();
}
#endif
/*******************************************************************************
* USB Events
******************************************************************************/

View file

@ -278,6 +278,53 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = {
};
#endif
#ifdef JOYSTICK_ENABLE
const USB_Descriptor_HIDReport_Datatype_t PROGMEM JoystickReport[] =
{
HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */
HID_RI_USAGE(8, 0x04), /* Joystick */
HID_RI_COLLECTION(8, 0x01), /* Application */
HID_RI_USAGE(8, 0x01), /* Pointer */
HID_RI_COLLECTION(8, 0x00), /* Physical */
#if JOYSTICK_AXES_COUNT >= 1
HID_RI_USAGE(8, 0x30), // USAGE (X)
#endif
#if JOYSTICK_AXES_COUNT >= 2
HID_RI_USAGE(8, 0x31), // USAGE (Y)
#endif
#if JOYSTICK_AXES_COUNT >= 3
HID_RI_USAGE(8, 0x32), // USAGE (Z)
#endif
#if JOYSTICK_AXES_COUNT >= 4
HID_RI_USAGE(8, 0x33), // USAGE (RX)
#endif
#if JOYSTICK_AXES_COUNT >= 5
HID_RI_USAGE(8, 0x34), // USAGE (RY)
#endif
#if JOYSTICK_AXES_COUNT >= 6
HID_RI_USAGE(8, 0x35), // USAGE (RZ)
#endif
HID_RI_LOGICAL_MINIMUM(8, -127),
HID_RI_LOGICAL_MAXIMUM(8, 127),
HID_RI_REPORT_COUNT(8, JOYSTICK_AXES_COUNT),
HID_RI_REPORT_SIZE(8, 0x08),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),
HID_RI_USAGE_PAGE(8, 0x09), /* Button */
HID_RI_USAGE_MINIMUM(8, 0x01), /* Button 1 */
HID_RI_USAGE_MAXIMUM(8, JOYSTICK_BUTTON_COUNT), /* Button 5 */
HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(8, 0x01),
HID_RI_REPORT_COUNT(8, JOYSTICK_BUTTON_COUNT),
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_END_COLLECTION(0),
HID_RI_END_COLLECTION(0),
};
#endif
/*
* Device descriptor
*/
@ -287,7 +334,6 @@ const USB_Descriptor_Device_t PROGMEM DeviceDescriptor = {
.Type = DTYPE_Device
},
.USBSpecification = VERSION_BCD(1, 1, 0),
#if VIRTSER_ENABLE
.Class = USB_CSCP_IADDeviceClass,
.SubClass = USB_CSCP_IADDeviceSubclass,
@ -812,6 +858,49 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = {
.PollingIntervalMS = 0x05
},
#endif
/*
* Joystick
*/
#ifdef JOYSTICK_ENABLE
.Joystick_Interface =
{
.Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},
.InterfaceNumber = JOYSTICK_INTERFACE,
.AlternateSetting = 0x00,
.TotalEndpoints = 1,
.Class = HID_CSCP_HIDClass,
.SubClass = HID_CSCP_NonBootSubclass,
.Protocol = HID_CSCP_NonBootProtocol,
.InterfaceStrIndex = NO_DESCRIPTOR
},
.Joystick_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(JoystickReport)
},
.Joystick_INEndpoint =
{
.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},
.EndpointAddress = (ENDPOINT_DIR_IN | JOYSTICK_IN_EPNUM),
.Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
.EndpointSize = JOYSTICK_EPSIZE,
.PollingIntervalMS = 0x0A
},
#endif
};
/*
@ -943,6 +1032,12 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
Size = sizeof(USB_HID_Descriptor_HID_t);
break;
#endif
#ifdef JOYSTICK_ENABLE
case JOYSTICK_INTERFACE:
Address = &ConfigurationDescriptor.Joystick_HID;
Size = sizeof(USB_HID_Descriptor_HID_t);
break;
#endif
}
@ -987,6 +1082,12 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
Size = sizeof(ConsoleReport);
break;
#endif
#ifdef JOYSTICK_ENABLE
case JOYSTICK_INTERFACE:
Address = &JoystickReport;
Size = sizeof(JoystickReport);
break;
#endif
}

View file

@ -123,6 +123,13 @@ typedef struct {
USB_Descriptor_Endpoint_t CDC_DataOutEndpoint;
USB_Descriptor_Endpoint_t CDC_DataInEndpoint;
#endif
#ifdef JOYSTICK_ENABLE
// Joystick HID Interface
USB_Descriptor_Interface_t Joystick_Interface;
USB_HID_Descriptor_HID_t Joystick_HID;
USB_Descriptor_Endpoint_t Joystick_INEndpoint;
#endif
} USB_Descriptor_Configuration_t;
/*
@ -164,6 +171,9 @@ enum usb_interfaces {
CDI_INTERFACE,
#endif
#if defined(JOYSTICK_ENABLE)
JOYSTICK_INTERFACE,
#endif
TOTAL_INTERFACES
};
@ -224,6 +234,10 @@ 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 PROTOCOL_LUFA
@ -248,6 +262,7 @@ enum usb_endpoints {
#define MIDI_STREAM_EPSIZE 64
#define CDC_NOTIFICATION_EPSIZE 8
#define CDC_EPSIZE 16
#define JOYSTICK_EPSIZE 8
uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const void** const DescriptorAddress);
#endif

View file

@ -26,6 +26,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "debug.h"
#include "host_driver.h"
#include "vusb.h"
#include "joystick.h"
#include "joystick.h"
#include <util/delay.h>
static uint8_t vusb_keyboard_leds = 0;
@ -79,6 +81,7 @@ static void send_keyboard(report_keyboard_t *report);
static void send_mouse(report_mouse_t *report);
static void send_system(uint16_t data);
static void send_consumer(uint16_t data);
static void send_joystick(void);
static host_driver_t driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
@ -139,6 +142,68 @@ static void send_consumer(uint16_t data) {
#endif
}
typedef struct {
uint8_t report_id;
#if JOYSTICK_AXES_COUNT>0
int8_t axes[JOYSTICK_AXES_COUNT];
#endif
#if JOYSTICK_BUTTON_COUNT>0
uint8_t buttons[(JOYSTICK_BUTTON_COUNT-1)/8+1];
#endif
} __attribute__ ((packed)) vusb_joystick_report_t;
void send_joystick_packet(joystick_t* status)
{
vusb_joystick_report_t r = {
.report_id = 0x4,
#if JOYSTICK_AXES_COUNT>0
.axes = {
status->axes[0]
#if JOYSTICK_AXES_COUNT >= 2
,status->axes[1]
#endif
#if JOYSTICK_AXES_COUNT >= 3
,status->axes[2]
#endif
#if JOYSTICK_AXES_COUNT >= 4
,status->axes[3]
#endif
#if JOYSTICK_AXES_COUNT >= 5
,status->axes[4]
#endif
#if JOYSTICK_AXES_COUNT >= 6
,status->axes[5]
#endif
},
#endif //JOYSTICK_AXES_COUNT>0
#if JOYSTICK_BUTTON_COUNT>0
.buttons = {
status->buttons[0]
#if JOYSTICK_BUTTON_COUNT>8
,status->buttons[1]
#endif
#if JOYSTICK_BUTTON_COUNT>16
,status->buttons[2]
#endif
#if JOYSTICK_BUTTON_COUNT>24
,status->buttons[3]
#endif
}
#endif //JOYSTICK_BUTTON_COUNT>0
};
if (usbInterruptIsReady3()) {
usbSetInterrupt3((void *)&r, sizeof(vusb_joystick_report_t));
}
}
/*------------------------------------------------------------------*
* Request from host *
*------------------------------------------------------------------*/
@ -251,7 +316,7 @@ const PROGMEM uchar keyboard_hid_report[] = {
0xC0 // End Collection
};
#if defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE)
#if defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE) || defined(JOYSTICK_ENABLE)
const PROGMEM uchar mouse_extra_hid_report[] = {
# ifdef MOUSE_ENABLE
// Mouse report descriptor
@ -332,6 +397,47 @@ const PROGMEM uchar mouse_extra_hid_report[] = {
0x81, 0x00, // Input (Data, Array, Absolute)
0xC0 // End Collection
# endif
#if JOYSTICK_ENABLE
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x04, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x4, // REPORT_ID (4)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
#if JOYSTICK_AXES_COUNT >= 1
0x09, 0x30, // USAGE (X)
#endif
#if JOYSTICK_AXES_COUNT >= 2
0x09, 0x31, // USAGE (Y)
#endif
#if JOYSTICK_AXES_COUNT >= 3
0x09, 0x32, // USAGE (Z)
#endif
#if JOYSTICK_AXES_COUNT >= 4
0x09, 0x33, // USAGE (RX)
#endif
#if JOYSTICK_AXES_COUNT >= 5
0x09, 0x34, // USAGE (RY)
#endif
#if JOYSTICK_AXES_COUNT >= 6
0x09, 0x35, // USAGE (RZ)
#endif
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, JOYSTICK_AXES_COUNT, // REPORT_COUNT (JOYSTICK_AXES_COUNT)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, JOYSTICK_BUTTON_COUNT, // USAGE_MAXIMUM
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, JOYSTICK_BUTTON_COUNT, // REPORT_COUNT
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0 // END_COLLECTION
#endif //GAMEPAD_ENABLE
};
#endif