1
0
mirror of https://github.com/whowechina/chu_pico.git synced 2024-11-11 22:47:09 +01:00

Firmware done, I call it beta

This commit is contained in:
whowechina 2023-09-09 22:23:55 +08:00
parent 94273bff74
commit 67d415db50
12 changed files with 492 additions and 197 deletions

Binary file not shown.

View File

@ -4,7 +4,7 @@ set(LWIP_ROOT ${PICO_SDK_PATH}/lib/lwip)
function(make_firmware board board_def)
pico_sdk_init()
add_executable(${board}
main.c slider.c air.c rgb.c save.c usb_descriptors.c)
main.c slider.c air.c rgb.c save.c lzfx.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

@ -21,6 +21,10 @@
static const uint8_t TOF_LIST[] = TOF_MUX_LIST;
static uint16_t distances[sizeof(TOF_LIST)];
const int offset_a = 800;
const int offset_b = 1000;
const int pitch = 200;
void air_init()
{
i2c_init(TOF_I2C, 400 * 1000);
@ -46,14 +50,45 @@ size_t air_num()
return sizeof(TOF_LIST);
}
uint16_t air_value(uint8_t index)
static inline uint8_t air_bits(int dist, int offset)
{
if (dist < offset) {
return 0;
}
int index = (dist - offset) / pitch;
if (index >= 6) {
return 0;
}
return 1 << index;
}
uint8_t air_bitmap()
{
uint8_t bitmap = 0;
for (int i = 0; i < sizeof(TOF_LIST); i++) {
bitmap |= air_bits(distances[i], offset_a);
bitmap |= air_bits(distances[i], offset_b);
}
return bitmap;
}
unsigned air_value(uint8_t index)
{
if (index >= sizeof(TOF_LIST)) {
return 0;
}
uint16_t dist = distances[index] >> 6;
return dist < 63 ? dist : 0;
uint8_t bitmap = air_bits(distances[index], offset_a) |
air_bits(distances[index], offset_b);
for (int i = 0; i < 6; i++) {
if (bitmap & (1 << i)) {
return i + 1;
}
}
return 0;
}
void air_update()

View File

@ -11,7 +11,8 @@
void air_init();
size_t air_num();
uint16_t air_value(uint8_t index);
unsigned air_value(uint8_t index);
uint8_t air_bitmap();
void air_update();
#endif

169
firmware/src/lzfx.c Normal file
View File

@ -0,0 +1,169 @@
/*
* Lzfx decompressor
* WHowe <github.com/whowechina>
* This is actually taken from CrazyRedMachine's repo
* <https://github.com/CrazyRedMachine/RedBoard/blob/main/io_dll/src/utils/hid_impl.c>
*/
#include <stdlib.h>
#include "lzfx.h"
typedef unsigned char u8;
typedef const u8 *LZSTATE[LZFX_HSIZE];
/* Guess len. No parameters may be NULL; this is not checked. */
static int lzfx_getsize(const void *ibuf, unsigned int ilen, unsigned int *olen)
{
u8 const *ip = (const u8 *)ibuf;
u8 const *const in_end = ip + ilen;
int tot_len = 0;
while (ip < in_end)
{
unsigned int ctrl = *ip++;
if (ctrl < (1 << 5))
{
ctrl++;
if (ip + ctrl > in_end)
return LZFX_ECORRUPT;
tot_len += ctrl;
ip += ctrl;
}
else
{
unsigned int len = (ctrl >> 5);
if (len == 7)
{ /* i.e. format #2 */
len += *ip++;
}
len += 2; /* len is now #octets */
if (ip >= in_end)
return LZFX_ECORRUPT;
ip++; /* skip the ref byte */
tot_len += len;
}
}
*olen = tot_len;
return 0;
}
/* Decompressor */
int lzfx_decompress(const void *ibuf, unsigned int ilen,
void *obuf, unsigned int *olen)
{
u8 const *ip = (const u8 *)ibuf;
u8 const *const in_end = ip + ilen;
u8 *op = (u8 *)obuf;
u8 const *const out_end = (olen == NULL ? NULL : op + *olen);
unsigned int remain_len = 0;
int rc;
if (olen == NULL)
return LZFX_EARGS;
if (ibuf == NULL)
{
if (ilen != 0)
return LZFX_EARGS;
*olen = 0;
return 0;
}
if (obuf == NULL)
{
if (olen != 0)
return LZFX_EARGS;
return lzfx_getsize(ibuf, ilen, olen);
}
do
{
unsigned int ctrl = *ip++;
/* Format 000LLLLL: a literal byte string follows, of length L+1 */
if (ctrl < (1 << 5))
{
ctrl++;
if (fx_expect_false(op + ctrl > out_end))
{
--ip; /* Rewind to control byte */
goto guess;
}
if (fx_expect_false(ip + ctrl > in_end))
return LZFX_ECORRUPT;
do
*op++ = *ip++;
while (--ctrl);
/* Format #1 [LLLooooo oooooooo]: backref of length L+1+2
^^^^^ ^^^^^^^^
A B
#2 [111ooooo LLLLLLLL oooooooo] backref of length L+7+2
^^^^^ ^^^^^^^^
A B
In both cases the location of the backref is computed from the
remaining part of the data as follows:
location = op - A*256 - B - 1
*/
}
else
{
unsigned int len = (ctrl >> 5);
u8 *ref = op - ((ctrl & 0x1f) << 8) - 1;
if (len == 7)
len += *ip++; /* i.e. format #2 */
len += 2; /* len is now #octets */
if (fx_expect_false(op + len > out_end))
{
ip -= (len >= 9) ? 2 : 1; /* Rewind to control byte */
goto guess;
}
if (fx_expect_false(ip >= in_end))
return LZFX_ECORRUPT;
ref -= *ip++;
if (fx_expect_false(ref < (u8 *)obuf))
return LZFX_ECORRUPT;
do
*op++ = *ref++;
while (--len);
}
} while (ip < in_end);
*olen = op - (u8 *)obuf;
return 0;
guess:
rc = lzfx_getsize(ip, ilen - (ip - (u8 *)ibuf), &remain_len);
if (rc >= 0)
*olen = remain_len + (op - (u8 *)obuf);
return rc;
}

18
firmware/src/lzfx.h Normal file
View File

@ -0,0 +1,18 @@
/*
* Lzfx decompressor
* WHowe <github.com/whowechina>
* This is actually taken from CrazyRedMachine's repo
* <https://github.com/CrazyRedMachine/RedBoard/blob/main/io_dll/src/utils/hid_impl.c>
*/
#define LZFX_ESIZE -1 /* Output buffer too small */
#define LZFX_ECORRUPT -2 /* Invalid data for decompression */
#define LZFX_EARGS -3 /* Arguments invalid (NULL) */
#define LZFX_HLOG 16
#define LZFX_HSIZE (1 << (LZFX_HLOG))
#define fx_expect_false(expr) (expr)
#define fx_expect_true(expr) (expr)
int lzfx_decompress(const void* ibuf, unsigned int ilen,
void* obuf, unsigned int *olen);

View File

@ -7,10 +7,11 @@
#include <stdint.h>
#include <stdbool.h>
#include "pico/stdio.h"
#include "pico/stdlib.h"
#include "bsp/board.h"
#include "pico/multicore.h"
#include "pico/bootrom.h"
#include "pico/stdio.h"
#include "hardware/gpio.h"
#include "hardware/sync.h"
@ -23,6 +24,7 @@
#include "slider.h"
#include "air.h"
#include "rgb.h"
#include "lzfx.h"
/* Measure the time of a function call */
#define RUN_TIME(func) \
@ -39,7 +41,6 @@ struct __attribute__((packed)) {
void report_usb_hid()
{
if (tud_hid_ready()) {
hid_joy.buttons = 0;
hid_joy.HAT = 0;
hid_joy.VendorSpec = 0;
tud_hid_n_report(0x00, REPORT_ID_JOYSTICK, &hid_joy, sizeof(hid_joy));
@ -56,32 +57,72 @@ static void pause_core1(bool pause)
}
}
static void fps_count()
static int fps[2] = {0};
static int get_fps(int core)
{
static uint64_t last = 0;
static int fps_counter = 0;
return fps[core];
}
fps_counter++;
static void fps_count(int core)
{
static uint32_t last[2] = {0};
static int counter[2] = {0};
uint64_t now = time_us_64();
if (now - last < 1000000) {
counter[core]++;
uint32_t now = time_us_32();
if (now - last[core] < 1000000) {
return;
}
last[core] = now;
fps[core] = counter[core];
counter[core] = 0;
}
static void print_fps()
{
static uint32_t last = 0;
uint32_t now = time_us_32();
if (now - last < 5000000) {
return;
}
last = now;
printf("FPS: %d\n", fps_counter);
fps_counter = 0;
printf("FPS: %d %d\n", get_fps(0), get_fps(1));
}
static void core1_loop()
static void gen_hid_report()
{
while (1) {
rgb_update();
sleep_ms(1);
hid_joy.axis = 0;
for (int i = 0; i < 16; i++) {
if (slider_touched(i * 2)) {
hid_joy.axis |= 1 << (30 - i * 2);
}
if (slider_touched(i * 2 + 1)) {
hid_joy.axis |= 1 << (31 - i * 2);
}
}
hid_joy.axis ^= 0x80808080; // some magic number from CrazyRedMachine
hid_joy.buttons = air_bitmap();
}
static void core0_loop()
static uint64_t last_hid_time = 0;
static void run_lights()
{
uint64_t now = time_us_64();
if (now - last_hid_time < 1000000) {
return;
}
const uint32_t colors[] = {0x000000, 0x0000ff, 0xff0000, 0xffff00,
0x00ff00, 0x00ffff, 0xffffff};
for (int i = 0; i < air_num(); i++) {
int d = air_value(i);
rgb_set_color(31 + i, colors[d]);
}
for (int i = 0; i < 15; i++) {
int x = 15 - i;
uint8_t r = (x & 0x01) ? 10 : 0;
@ -90,50 +131,44 @@ static void core0_loop()
rgb_gap_color(i, rgb32(r, g, b, false));
}
for (int i = 0; i < 16; i++) {
bool r = slider_touched(i * 2);
bool g = slider_touched(i * 2 + 1);
rgb_set_color(30 - i * 2, rgb32(r ? 80 : 0, g ? 80 : 0, 0, false));
}
}
static void core1_loop()
{
while (1) {
rgb_update();
fps_count(1);
sleep_ms(1);
}
}
static void core0_loop()
{
while(1) {
tud_task();
report_usb_hid();
slider_update();
air_update();
#if 0
static uint16_t old_touch = 0;
uint16_t touch = slider_hw_touch(0);
if (touch != old_touch) {
printf("Touch: %04x\n", touch);
old_touch = touch;
}
#endif
hid_joy.axis = 0;
for (int i = 0; i < 16; i++) {
bool k1 = slider_touched(i * 2);
bool k2 = slider_touched(i * 2 + 1);
gen_hid_report();
report_usb_hid();
if (k1) {
hid_joy.axis |= 1 << (30 - i * 2);
}
if (k2) {
hid_joy.axis |= 1 << (31 - i * 2);
}
uint8_t r = k1 ? 255 : 0;
uint8_t g = k2 ? 255 : 0;
rgb_key_color(i, rgb32(r, g, g, false));
}
hid_joy.axis ^= 0x80808080; // some magic number from CrazyRedMachine
for (int i = 0; i < air_num(); i++) {
uint8_t v = air_value(i) << 2;
rgb_set_color(31 + i, rgb32(v, v, v, false));
}
run_lights();
slider_update_baseline();
fps_count();
fps_count(0);
print_fps();
}
}
void init()
{
sleep_ms(200);
// set_sys_clock_khz(166000, true);
board_init();
tusb_init();
stdio_init_all();
@ -168,14 +203,26 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id,
uint16_t bufsize)
{
if (report_type == HID_REPORT_TYPE_OUTPUT) {
if (report_id == REPORT_ID_LED_SLIDER_15) {
printf("Slider 16: %d\n", bufsize);
} else if (report_id == REPORT_ID_LED_SLIDER_16) {
printf("Slider 15: %d\n", bufsize);
if (report_id == REPORT_ID_LED_SLIDER_16) {
rgb_set_brg(0, buffer, bufsize / 3);
} else if (report_id == REPORT_ID_LED_SLIDER_15) {
rgb_set_brg(16, buffer, bufsize / 3);
} else if (report_id == REPORT_ID_LED_TOWER_6) {
printf("Tower 6: %d\n", bufsize);
} else {
printf("Report: %d - %d\n", report_id, bufsize);
rgb_set_brg(31, buffer, bufsize / 3);
}
last_hid_time = time_us_64();
return;
}
if (report_type == HID_REPORT_TYPE_FEATURE) {
if (report_id == REPORT_ID_LED_COMPRESSED) {
uint8_t buf[(48 + 45 + 6) * 3];
unsigned int olen = sizeof(buf);
if (lzfx_decompress(buffer + 1, buffer[0], buf, &olen) == 0) {
rgb_set_brg(0, buf, olen / 3);
}
}
last_hid_time = time_us_64();
return;
}
}

View File

@ -22,7 +22,7 @@
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static uint32_t rgb_buf[48]; // 32 + 16(maximum) ToF indicators
static uint32_t rgb_buf[47]; // 16(Keys) + 15(Gaps) + 16(maximum ToF indicators)
#define _MAP_LED(x) _MAKE_MAPPER(x)
#define _MAKE_MAPPER(x) MAP_LED_##x
@ -61,7 +61,10 @@ static void drive_led()
}
last = now;
for (int i = 0; i < ARRAY_SIZE(rgb_buf); i++) {
for (int i = 30; i >= 0; i--) {
pio_sm_put_blocking(pio0, 0, rgb_buf[i] << 8u);
}
for (int i = 31; i < ARRAY_SIZE(rgb_buf); i++) {
pio_sm_put_blocking(pio0, 0, rgb_buf[i] << 8u);
}
}
@ -101,6 +104,22 @@ void rgb_gap_color(unsigned index, uint32_t color)
rgb_buf[index * 2 + 1] = color;
}
void rgb_set_brg(unsigned index, const uint8_t *brg_array, size_t num)
{
if (index >= ARRAY_SIZE(rgb_buf)) {
return;
}
if (index + num > ARRAY_SIZE(rgb_buf)) {
num = ARRAY_SIZE(rgb_buf) - index;
}
for (int i = 0; i < num; i++) {
uint8_t b = brg_array[i * 3 + 0];
uint8_t r = brg_array[i * 3 + 1];
uint8_t g = brg_array[i * 3 + 2];
rgb_buf[index + i] = rgb32(r, g, b, false);
}
}
void rgb_init()
{

View File

@ -22,4 +22,7 @@ void rgb_set_color(unsigned index, uint32_t color);
void rgb_key_color(unsigned index, uint32_t color);
void rgb_gap_color(unsigned index, uint32_t color);
/* num of the rgb leds, num*3 bytes in the array */
void rgb_set_brg(unsigned index, const uint8_t *brg_array, size_t num);
#endif

View File

@ -90,7 +90,6 @@ void slider_update()
void slider_update_baseline()
{
static int iteration = 0;
iteration++;
for (int i = 0; i < 32; i++) {
int16_t delta = readout[i] - baseline[i];
@ -100,21 +99,18 @@ void slider_update_baseline()
error[i] += delta;
}
iteration++;
if (iteration > 100) {
iteration = 0;
for (int i = 0; i < 32; i++) {
if (error[i] > 100) {
baseline[i] ++;
printf("+");
} else if (error[i] < -100) {
baseline[i] --;
printf("-");
} else {
printf(" ");
}
error[i] = 0;
}
printf("\n");
}
}

View File

@ -36,8 +36,8 @@
*/
#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))
(0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4))
//--------------------------------------------------------------------+
// Device Descriptors
@ -67,7 +67,7 @@ tusb_desc_device_t desc_device_joy = {
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const* tud_descriptor_device_cb(void) {
return (uint8_t const*)&desc_device_joy;
return (uint8_t const*)&desc_device_joy;
}
//--------------------------------------------------------------------+
@ -75,15 +75,17 @@ uint8_t const* tud_descriptor_device_cb(void) {
//--------------------------------------------------------------------+
uint8_t const desc_hid_report_joy[] = {
GAMECON_REPORT_DESC_JOYSTICK(HID_REPORT_ID(REPORT_ID_JOYSTICK)),
GAMECON_REPORT_DESC_JOYSTICK,
};
uint8_t const desc_hid_report_led[] = {
GAMECON_REPORT_DESC_LED_SLIDER_16(HID_REPORT_ID(REPORT_ID_LED_SLIDER_16)),
GAMECON_REPORT_DESC_LED_SLIDER_15(HID_REPORT_ID(REPORT_ID_LED_SLIDER_15)),
GAMECON_REPORT_DESC_LED_TOWER_6(HID_REPORT_ID(REPORT_ID_LED_TOWER_6)),
GAMECON_REPORT_DESC_LED_COMPRESSED(HID_REPORT_ID(REPORT_ID_LED_COMPRESSED)),
GAMECON_LED_HEADER,
GAMECON_REPORT_DESC_LED_SLIDER_16,
GAMECON_REPORT_DESC_LED_SLIDER_15,
GAMECON_REPORT_DESC_LED_TOWER_6,
GAMECON_REPORT_DESC_LED_COMPRESSED,
GAMECON_LED_FOOTER
};
// Invoked when received GET HID REPORT DESCRIPTOR
@ -128,19 +130,18 @@ uint8_t const desc_configuration_joy[] = {
TUD_HID_DESCRIPTOR(ITF_NUM_LED, 5, HID_ITF_PROTOCOL_NONE,
sizeof(desc_hid_report_led), EPNUM_LED,
CFG_TUD_HID_EP_BUFSIZE, 1),
CFG_TUD_HID_EP_BUFSIZE, 4),
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 6, EPNUM_CDC_NOTIF,
8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64)
};
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
(void)index; // for multiple configurations
return desc_configuration_joy;
return desc_configuration_joy;
}
//--------------------------------------------------------------------+
@ -153,47 +154,58 @@ const char *string_desc_arr[] = {
"WHowe" , // 1: Manufacturer
"Chu Pico Controller", // 2: Product
"123456", // 3: Serials, should use chip ID
"Joystick Interface",
"LED Interface",
"Serial Port",
"Chu Pico Joystick",
"Chu Pico LED",
"Chu Pico Serial Port",
};
static uint16_t _desc_str[64];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long
// enough for transfer to complete
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
(void)langid;
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
static uint16_t _desc_str[64];
uint8_t chr_count;
if (index == 0) {
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
} else {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if (index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) {
const char* str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
if (chr_count > 63) chr_count = 63;
// Convert ASCII string into UTF-16
for (uint8_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
} else {
_desc_str[1] = 'X';
chr_count = 1;
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];
// first byte is length (including header), second byte is string type
_desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2);
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);
}
return _desc_str;
uint8_t chr_count = strlen(str);
if (chr_count > 63) {
chr_count = 63;
}
// Convert ASCII string into UTF-16
for (uint8_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2);
return _desc_str;
}

View File

@ -5,11 +5,11 @@
#include "device/usbd.h"
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_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,
};
// because they are missing from tusb_hid.h
@ -22,94 +22,89 @@ enum {
// Joystick Report Descriptor Template - Based off Drewol/rp2040-gamecon
// Button Map | X | Y
#define GAMECON_REPORT_DESC_JOYSTICK(...) \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_JOYSTICK), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
__VA_ARGS__ HID_USAGE_PAGE(HID_USAGE_PAGE_BUTTON), \
HID_USAGE_MIN(1), HID_USAGE_MAX(16), \
HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \
HID_REPORT_COUNT(16), HID_REPORT_SIZE(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
#define GAMECON_REPORT_DESC_JOYSTICK \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_JOYSTICK), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_REPORT_ID(REPORT_ID_JOYSTICK) \
HID_USAGE_PAGE(HID_USAGE_PAGE_BUTTON), \
HID_USAGE_MIN(1), HID_USAGE_MAX(16), \
HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \
HID_REPORT_COUNT(16), HID_REPORT_SIZE(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_HAT_SWITCH), \
HID_LOGICAL_MIN(1), HID_LOGICAL_MAX(8), \
HID_PHYSICAL_MIN(0), HID_PHYSICAL_MAX_N(315, 2), \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_HAT_SWITCH), \
HID_LOGICAL_MIN(1), HID_LOGICAL_MAX(8), \
HID_PHYSICAL_MIN(0), HID_PHYSICAL_MAX_N(315, 2), \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_X), HID_USAGE(HID_USAGE_DESKTOP_Y), \
HID_USAGE(HID_USAGE_DESKTOP_Z), HID_USAGE(HID_USAGE_DESKTOP_RX), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX(0xff), /* Analog */ \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(4), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_X), HID_USAGE(HID_USAGE_DESKTOP_Y), \
HID_USAGE(HID_USAGE_DESKTOP_Z), HID_USAGE(HID_USAGE_DESKTOP_RX), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX(0xff), /* Analog */ \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(4), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_USAGE_PAGE_N(HID_USAGE_PAGE_VENDOR, 2), \
HID_USAGE(0), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX(0xff), \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_COLLECTION_END
HID_USAGE_PAGE_N(HID_USAGE_PAGE_VENDOR, 2), \
HID_USAGE(0), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX(0xff), \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END
#define GAMECON_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 GAMECON_LED_FOOTER \
HID_COLLECTION_END
// Slider First 16 LEDs (48 rgb zones, BRG order)
#define GAMECON_REPORT_DESC_LED_SLIDER_16 \
HID_REPORT_ID(REPORT_ID_LED_SLIDER_16) \
HID_REPORT_COUNT(48), HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(1), HID_USAGE_MAX(48), \
HID_STRING_MINIMUM(7), HID_STRING_MAXIMUM(54), \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE)
// Slider Remaining 15 LEDs (45 rgb zones, BRG order)
#define GAMECON_REPORT_DESC_LED_SLIDER_15 \
HID_REPORT_ID(REPORT_ID_LED_SLIDER_15) \
HID_REPORT_COUNT(45), HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(49), HID_USAGE_MAX(93), \
HID_STRING_MINIMUM(7), HID_STRING_MAXIMUM(51), /* Delta to previous */ \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE)
// Tower LEDs (18 rgb zones, BRG order)
#define GAMECON_REPORT_DESC_LED_TOWER_6 \
HID_REPORT_ID(REPORT_ID_LED_TOWER_6) \
HID_REPORT_COUNT(18), HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(94), HID_USAGE_MAX(111), \
HID_STRING_MINIMUM(7), HID_STRING_MAXIMUM(24), /* Delta to previous */ \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE)
/*
report id 4 : slider first 48 leds (16 rgb zones, brg order)
report id 5 : slider remaining 45 leds (15 rgb zones, brg order)
report id 6 : tower 18 leds (6 rgb, brg order)
HID_STRING_MINIMUM(56), HID_STRING_MAXIMUM(100), \
HID_STRING_MINIMUM(101), HID_STRING_MAXIMUM(117), \
*/
// Slider First 16 LEDs
#define GAMECON_REPORT_DESC_LED_SLIDER_16(...) \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(0x00), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
__VA_ARGS__ HID_REPORT_COUNT(48), HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(1), HID_USAGE_MAX(48), \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_REPORT_COUNT(1), HID_REPORT_SIZE(8), \
HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END
// Slider Remaining 15 LEDs
#define GAMECON_REPORT_DESC_LED_SLIDER_15(...) \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(0x00), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
__VA_ARGS__ HID_REPORT_COUNT(45), HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(1), HID_USAGE_MAX(45), \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_REPORT_COUNT(1), HID_REPORT_SIZE(8), \
HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END
// Tower LEDs
#define GAMECON_REPORT_DESC_LED_TOWER_6(...) \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(0x00), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
__VA_ARGS__ HID_REPORT_COUNT(18), HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(1), HID_USAGE_MAX(18), \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_REPORT_COUNT(1), HID_REPORT_SIZE(8), \
HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END
// LEDs Compressed
#define GAMECON_REPORT_DESC_LED_COMPRESSED(...) \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(0x00), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
__VA_ARGS__ HID_REPORT_COUNT(63), HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(1), HID_USAGE_MAX(63), \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_REPORT_COUNT(1), HID_REPORT_SIZE(8), \
HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END
#define GAMECON_REPORT_DESC_LED_COMPRESSED \
HID_REPORT_ID(REPORT_ID_LED_COMPRESSED) \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE(0x00), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(63), \
HID_FEATURE(HID_DATA | HID_VARIABLE | HID_ABSOLUTE)
#endif /* USB_DESCRIPTORS_H_ */
#endif /* USB_DESCRIPTORS_H_ */