This commit is contained in:
whowechina 2024-09-13 23:18:43 +08:00
parent edb04529f1
commit 1ef3c14dfa
9 changed files with 279 additions and 212 deletions

View File

@ -4,8 +4,8 @@ set(LWIP_ROOT ${PICO_SDK_PATH}/lib/lwip)
function(make_firmware board board_def)
pico_sdk_init()
add_executable(${board}
main.c light.c button.c gimbal.c sound.c wad.c vl53l0x.c save.c config.c commands.c
cli.c usb_descriptors.c)
main.c light.c button.c gimbal.c sound.c wad.c vl53l0x.c save.c config.c
commands.c cli.c hid.c usb_descriptors.c)
target_compile_definitions(${board} PUBLIC ${board_def})
pico_enable_stdio_usb(${board} 1)
pico_enable_stdio_uart(${board} 0)

View File

@ -53,7 +53,7 @@ static geki_cfg_t default_cfg = {
},
};
geki_runtime_t *geki_runtime;
geki_runtime_t geki_runtime;
static void config_loaded()
{

View File

@ -44,10 +44,11 @@ typedef struct __attribute__((packed)) {
typedef struct {
uint16_t fps[2];
bool key_stuck;
} geki_runtime_t;
extern geki_cfg_t *geki_cfg;
extern geki_runtime_t *geki_runtime;
extern geki_runtime_t geki_runtime;
void config_init();
void config_changed(); // Notify the config has changed

135
firmware/src/hid.c Normal file
View File

@ -0,0 +1,135 @@
#include <stdint.h>
#include <stdbool.h>
#include "board_defs.h"
#include "tusb.h"
#include "usb_descriptors.h"
#include "button.h"
#include "gimbal.h"
#include "wad.h"
#include "config.h"
#include "hid.h"
struct __attribute__((packed)) {
uint16_t adcs[8];
uint16_t spinners[4];
uint16_t chutes[2];
uint16_t buttons[2];
uint8_t system_status;
uint8_t usb_status;
uint8_t padding[29];
} hid_joy;
struct __attribute__((packed)) {
uint8_t modifier;
uint8_t keymap[15];
} hid_nkro;
const static struct {
uint8_t group;
uint8_t bit;
} button_to_io4_map[] = {
{ 0, 0 }, { 0, 5 }, { 0, 4 }, // Left ABC
{ 0, 1 }, { 1, 0 }, { 0, 15 }, // Right ABC
{ 1, 14 }, { 0, 13 }, // AUX 12
}, wad_left = { 1, 15 }, wad_right = { 0, 14 };
static void report_usb_hid()
{
if (tud_hid_ready()) {
if (geki_cfg->hid.joy || geki_runtime.key_stuck) {
hid_joy.adcs[0] = (gimbal_read() - 128) << 8;
static uint16_t last_buttons = 0;
uint16_t buttons = button_read();
hid_joy.buttons[0] = 0;
hid_joy.buttons[1] = 0;
for (int i = 0; i < button_num(); i++) {
uint8_t group = button_to_io4_map[i].group;
uint8_t bit = button_to_io4_map[i].bit;
if (buttons & (1 << i)) {
hid_joy.buttons[group] |= (1 << bit);
}
}
if (wad_read_left()) {
hid_joy.buttons[wad_left.group] |= (1 << wad_left.bit);
}
if (wad_read_right()) {
hid_joy.buttons[wad_right.group] |= (1 << wad_right.bit);
}
if ((last_buttons ^ buttons) & (1 << 11)) {
if (buttons & (1 << 11)) {
// just pressed coin button
hid_joy.chutes[0] += 0x100;
}
}
tud_hid_n_report(0, REPORT_ID_JOYSTICK, &hid_joy, sizeof(hid_joy));
last_buttons = buttons;
}
if (geki_cfg->hid.nkro && !geki_runtime.key_stuck) {
tud_hid_n_report(1, 0, &hid_nkro, sizeof(hid_nkro));
}
}
}
static void gen_nkro_report()
{
if (!geki_cfg->hid.nkro) {
return;
}
return;
const char keymap[] = "\x1a\x08\x07\x06\x1b\x1d\x04\x14\x20\x3a\x3b\x3c";
uint16_t buttons = button_read();
for (int i = 0; i < button_num(); i++) {
uint8_t code = keymap[i];
uint8_t byte = code / 8;
uint8_t bit = code % 8;
if (buttons & (1 << i)) {
hid_nkro.keymap[byte] |= (1 << bit);
} else {
hid_nkro.keymap[byte] &= ~(1 << bit);
}
}
}
void hid_update()
{
gen_nkro_report();
report_usb_hid();
}
typedef struct __attribute__((packed)) {
uint8_t report_id;
uint8_t cmd;
uint8_t payload[62];
} hid_output_t;
void hid_proc(const uint8_t *data, uint8_t len)
{
hid_output_t *output = (hid_output_t *)data;
if (output->report_id == REPORT_ID_OUTPUT) {
switch (output->cmd) {
case 0x01: // Set Timeout
case 0x02: // Set Sampling Count
hid_joy.system_status = 0x30;
break;
case 0x03: // Clear Board Status
hid_joy.chutes[0] = 0;
hid_joy.chutes[1] = 0;
hid_joy.system_status = 0x00;
break;
case 0x04: // Set General Output
// LED
break;
case 0x41: // I don't know what this is
break;
default:
printf("USB unknown cmd: %d\n", output->cmd);
break;
}
}
}

12
firmware/src/hid.h Normal file
View File

@ -0,0 +1,12 @@
/*
* HID Report Functions
* WHowe <github.com/whowechina>
*/
#ifndef HID_H_
#define HID_H_
void hid_update();
void hid_proc(const uint8_t *data, uint8_t len);
#endif

View File

@ -28,92 +28,14 @@
#include "cli.h"
#include "commands.h"
#include "hid.h"
#include "light.h"
#include "button.h"
#include "gimbal.h"
#include "wad.h"
#include "sound.h"
struct __attribute__((packed)) {
uint16_t buttons;
uint8_t HAT;
uint8_t lx;
uint8_t ly;
uint8_t rx;
uint8_t ry;
uint8_t vendor;
} hid_joy;
struct __attribute__((packed)) {
uint8_t modifier;
uint8_t keymap[15];
} hid_nkro, sent_hid_nkro;
void report_usb_hid()
{
if (tud_hid_ready()) {
hid_joy.HAT = 0x08;
hid_joy.vendor = 0;
if (geki_cfg->hid.joy) {
tud_hid_n_report(0x00, 0, &hid_joy, sizeof(hid_joy));
}
if (geki_cfg->hid.nkro &&
(memcmp(&hid_nkro, &sent_hid_nkro, sizeof(hid_nkro)) != 0)) {
sent_hid_nkro = hid_nkro;
tud_hid_n_report(0x02, 0, &sent_hid_nkro, sizeof(sent_hid_nkro));
}
}
}
#define SWITCH_BIT_Y (1U << 0)
#define SWITCH_BIT_B (1U << 1)
#define SWITCH_BIT_A (1U << 2)
#define SWITCH_BIT_X (1U << 3)
#define SWITCH_BIT_L (1U << 4)
#define SWITCH_BIT_R (1U << 5)
#define SWITCH_BIT_ZL (1U << 6)
#define SWITCH_BIT_ZR (1U << 7)
#define SWITCH_BIT_MINUS (1U << 8)
#define SWITCH_BIT_PLUS (1U << 9)
#define SWITCH_BIT_L3 (1U << 10)
#define SWITCH_BIT_R3 (1U << 11)
#define SWITCH_BIT_HOME (1U << 12)
static void gen_joy_report()
{
hid_joy.lx = gimbal_read();
uint16_t button = button_read();
hid_joy.buttons = 0;
hid_joy.buttons |= (button & 0x01) ? SWITCH_BIT_L : 0;
hid_joy.buttons |= (button & 0x02) ? SWITCH_BIT_R : 0;
if (button & 0x08) {
hid_joy.buttons |= (button & 0x04) ? SWITCH_BIT_MINUS : 0;
hid_joy.buttons |= (button & 0x10) ? SWITCH_BIT_PLUS : 0;
} else {
hid_joy.buttons |= (button & 0x04) ? SWITCH_BIT_B : 0;
hid_joy.buttons |= (button & 0x10) ? SWITCH_BIT_A : 0;
}
}
const uint8_t keycode_table[128][2] = { HID_ASCII_TO_KEYCODE };
const uint8_t keymap[38 + 1] = NKRO_KEYMAP; // 32 keys, 6 air keys, 1 terminator
static void gen_nkro_report()
{
for (int i = 0; i < 6; i++) {
uint8_t code = keycode_table[keymap[32 + i]][1];
uint8_t byte = code / 8;
uint8_t bit = code % 8;
if (hid_joy.buttons & (1 << i)) {
hid_nkro.keymap[byte] |= (1 << bit);
} else {
hid_nkro.keymap[byte] &= ~(1 << bit);
}
}
}
static uint64_t last_hid_time = 0;
static void run_lights()
{
int gimbal = gimbal_read();
@ -173,8 +95,8 @@ static void run_lights()
static void run_sound()
{
sound_set(0, wad_read_left());
sound_set(1, wad_read_right());
//sound_set(0, wad_read_left());
//sound_set(1, wad_read_right());
}
static mutex_t core1_io_lock;
@ -204,10 +126,9 @@ static void core0_loop()
button_update();
wad_update();
gen_joy_report();
gen_nkro_report();
report_usb_hid();
hid_update();
sleep_us(900);
}
}
@ -295,8 +216,5 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id,
hid_report_type_t report_type, uint8_t const *buffer,
uint16_t bufsize)
{
if (report_type == HID_REPORT_TYPE_OUTPUT) {
last_hid_time = time_us_64();
return;
}
hid_proc(buffer, bufsize);
}

View File

@ -96,8 +96,8 @@ extern "C" {
#endif
//------------- CLASS -------------//
#define CFG_TUD_HID 3
#define CFG_TUD_CDC 1
#define CFG_TUD_HID 2
#define CFG_TUD_CDC 2
#define CFG_TUD_MSC 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 0

View File

@ -24,24 +24,9 @@
*/
#include "usb_descriptors.h"
#include "pico/unique_id.h"
#include "tusb.h"
/* A combination of interfaces must have a unique product id, since PC will save
* device driver after the first plug. Same VID/PID with different interface e.g
* MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
#define USB_PID \
(0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4))
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t desc_device_joy = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@ -51,8 +36,8 @@ tusb_desc_device_t desc_device_joy = {
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x0f0d,
.idProduct = 0x0092,
.idVendor = 0x0ca3,
.idProduct = 0x0021,
.bcdDevice = 0x0100,
.iManufacturer = 1,
@ -76,11 +61,6 @@ uint8_t const desc_hid_report_joy[] = {
GEKI_PICO_REPORT_DESC_JOYSTICK,
};
uint8_t const desc_hid_report_led[] = {
GEKI_PICO_LED_HEADER,
GEKI_PICO_LED_FOOTER
};
uint8_t const desc_hid_report_nkro[] = {
GEKI_PICO_REPORT_DESC_NKRO,
};
@ -94,8 +74,6 @@ uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf)
case 0:
return desc_hid_report_joy;
case 1:
return desc_hid_report_led;
case 2:
return desc_hid_report_nkro;
default:
return NULL;
@ -105,20 +83,19 @@ uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf)
// Configuration Descriptor
//--------------------------------------------------------------------+
enum { ITF_NUM_JOY, ITF_NUM_LED, ITF_NUM_NKRO,
ITF_NUM_CLI, ITF_NUM_CLI_DATA,
enum { ITF_NUM_JOY, ITF_NUM_NKRO,
ITF_NUM_CLI, ITF_NUM_CLI_DATA, ITF_NUM_AIME, ITF_NUM_AIME_DATA,
ITF_NUM_TOTAL };
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + \
TUD_HID_INOUT_DESC_LEN * 1 + \
TUD_HID_DESC_LEN * 2 + \
TUD_CDC_DESC_LEN * 1)
TUD_HID_DESC_LEN * 1 + \
TUD_CDC_DESC_LEN * 2)
#define EPNUM_JOY_OUT 0x01
#define EPNUM_JOY_IN 0x81
#define EPNUM_LED 0x86
#define EPNUM_KEY 0x87
#define EPNUM_NKRO 0x87
#define EPNUM_CLI_NOTIF 0x89
#define EPNUM_CLI_OUT 0x0a
@ -140,16 +117,16 @@ uint8_t const desc_configuration_joy[] = {
sizeof(desc_hid_report_joy), EPNUM_JOY_OUT, EPNUM_JOY_IN,
CFG_TUD_HID_EP_BUFSIZE, 1),
TUD_HID_DESCRIPTOR(ITF_NUM_LED, 5, HID_ITF_PROTOCOL_NONE,
sizeof(desc_hid_report_led), EPNUM_LED,
CFG_TUD_HID_EP_BUFSIZE, 4),
TUD_HID_DESCRIPTOR(ITF_NUM_NKRO, 6, HID_ITF_PROTOCOL_NONE,
sizeof(desc_hid_report_nkro), EPNUM_KEY,
TUD_HID_DESCRIPTOR(ITF_NUM_NKRO, 5, HID_ITF_PROTOCOL_NONE,
sizeof(desc_hid_report_nkro), EPNUM_NKRO,
CFG_TUD_HID_EP_BUFSIZE, 1),
TUD_CDC_DESCRIPTOR(ITF_NUM_CLI, 7, EPNUM_CLI_NOTIF,
TUD_CDC_DESCRIPTOR(ITF_NUM_CLI, 6, EPNUM_CLI_NOTIF,
8, EPNUM_CLI_OUT, EPNUM_CLI_IN, 64),
TUD_CDC_DESCRIPTOR(ITF_NUM_AIME, 7, EPNUM_AIME_NOTIF,
8, EPNUM_AIME_OUT, EPNUM_AIME_IN, 64),
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
@ -162,17 +139,17 @@ uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
static char serial_number_str[24] = "123456\0";
// array of pointer to string descriptors
const char *string_desc_arr[] = {
static const char *string_desc_arr[] = {
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"WHowe", // 1: Manufacturer
"Geki Pico Controller", // 2: Product
"123456", // 3: Serial
"Geki Pico Joystick",
"Geki Pico LED",
"SEGA", // 1: Manufacturer
"Geki Pico", // 2: Product
serial_number_str, // 3: Serials, use chip ID
"I/O CONTROL BD;15257;01;90;1831;6679A;00;GOUT=14_ADIN=8,E_ROTIN=4_COININ=2_SWIN=2,E_UQ1=41,6;",
"Geki Pico NKRO",
"Geki Pico CLI Port",
"Geki Pico CLI",
"Mai Pico AIME Port",
};
// Invoked when received GET STRING DESCRIPTOR request
@ -180,39 +157,24 @@ const char *string_desc_arr[] = {
// enough for transfer to complete
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
static uint16_t _desc_str[64];
static uint16_t _desc_str[128];
if (index == 0) {
memcpy(&_desc_str[1], string_desc_arr[0], 2);
_desc_str[0] = (TUSB_DESC_STRING << 8) | (2 + 2);
return _desc_str;
}
const size_t base_num = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]);
const char *colors[] = {"Blue", "Red", "Green"};
char str[64];
if (index < base_num) {
strcpy(str, string_desc_arr[index]);
} else if (index < base_num + 48 + 45) {
const char *names[] = {"Key ", "Splitter "};
int led = index - base_num;
int id = led / 6 + 1;
int type = led / 3 % 2;
int brg = led % 3;
sprintf(str, "%s%02d %s", names[type], id, colors[brg]);
} else if (index < base_num + 48 + 45 + 18) {
int led = index - base_num - 48 - 45;
int id = led / 3 + 1;
int brg = led % 3;
sprintf(str, "Tower %02d %s", id, colors[brg]);
} else {
sprintf(str, "Unknown %d", index);
if (index == 3) {
pico_unique_board_id_t board_id;
pico_get_unique_board_id(&board_id);
sprintf(serial_number_str, "%016llx", *(uint64_t *)&board_id);
}
const char *str = string_desc_arr[index];
uint8_t chr_count = strlen(str);
if (chr_count > 63) {
chr_count = 63;
if (chr_count > count_of(_desc_str)) {
chr_count = count_of(_desc_str);
}
// Convert ASCII string into UTF-16

View File

@ -6,10 +6,7 @@
enum {
REPORT_ID_JOYSTICK = 1,
REPORT_ID_LED_SLIDER_16 = 4,
REPORT_ID_LED_SLIDER_15 = 5,
REPORT_ID_LED_TOWER_6 = 6,
REPORT_ID_LED_COMPRESSED = 11,
REPORT_ID_OUTPUT = 16,
};
// because they are missing from tusb_hid.h
@ -20,57 +17,99 @@ enum {
#define HID_STRING_MAXIMUM(x) HID_REPORT_ITEM(x, 9, RI_TYPE_LOCAL, 1)
#define HID_STRING_MAXIMUM_N(x, n) HID_REPORT_ITEM(x, 9, RI_TYPE_LOCAL, n)
// Joystick Report Descriptor Template - Based off Drewol/rp2040-gamecon
// Button Map | X | Y
//HID_REPORT_ID(REPORT_ID_JOYSTICK)
#define GEKI_PICO_REPORT_DESC_JOYSTICK \
// Joystick Report Descriptor to Emulate IO4
#define GEKI_PICO_REPORT_DESC_JOYSTICK \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_GAMEPAD), \
HID_USAGE(HID_USAGE_DESKTOP_JOYSTICK), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \
HID_PHYSICAL_MIN(0), HID_PHYSICAL_MAX(1), \
HID_REPORT_SIZE(1), HID_REPORT_COUNT(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_BUTTON), \
HID_USAGE_MIN(1), HID_USAGE_MAX(16), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_REPORT_ID(REPORT_ID_JOYSTICK) \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_LOGICAL_MAX(7), \
HID_PHYSICAL_MAX_N(315, 2), \
HID_REPORT_SIZE(4), HID_REPORT_COUNT(1), \
0x65, 0x14, /* Unit */ \
HID_USAGE(HID_USAGE_DESKTOP_HAT_SWITCH), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE | HID_NO_NULL_POSITION),\
0x65, 0x00, /* Unit None */ \
HID_REPORT_COUNT(1), \
HID_INPUT(HID_CONSTANT | HID_ARRAY | HID_ABSOLUTE), \
\
HID_LOGICAL_MAX_N(0xff, 2), HID_PHYSICAL_MAX_N(0xff, 2), /* Analog */ \
HID_USAGE(HID_USAGE_DESKTOP_X), HID_USAGE(HID_USAGE_DESKTOP_Y), \
HID_USAGE(HID_USAGE_DESKTOP_Z), HID_USAGE(HID_USAGE_DESKTOP_RZ), \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(4), \
HID_USAGE(HID_USAGE_DESKTOP_X), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_USAGE_PAGE_N(HID_USAGE_PAGE_VENDOR, 2), \
HID_USAGE(0x20), \
HID_REPORT_COUNT(1), \
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_Y), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_USAGE_N(0x2621, 2), \
HID_REPORT_COUNT(8), \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END
//HID_USAGE_PAGE_N(9761, 2), HID_REPORT_COUNT(8), HID_OUTPUT(2),
#define GEKI_PICO_LED_HEADER \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(0x00), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_REPORT_COUNT(1), HID_REPORT_SIZE(8), \
HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE)
#define GEKI_PICO_LED_FOOTER \
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_X), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_Y), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_X), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_Y), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_X), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_Y), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_RX), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_RY), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_RX), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_RY), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_SLIDER), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(16), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_SLIDER), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(48), HID_REPORT_SIZE(1), \
HID_USAGE_MIN_N(1, 2), HID_USAGE_MAX_N(48, 2), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_REPORT_COUNT(1), HID_REPORT_SIZE(232), \
HID_INPUT(HID_CONSTANT | HID_ABSOLUTE), \
\
HID_USAGE_PAGE_N(0xffa0, 2), \
HID_USAGE(0x00), \
HID_REPORT_ID(REPORT_ID_OUTPUT) \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_USAGE(0x00), \
HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(255), \
HID_REPORT_COUNT(63), HID_REPORT_SIZE(8), \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END, \
HID_COLLECTION_END
#define GEKI_PICO_REPORT_DESC_NKRO \