diff --git a/firmware/diva_pico.uf2 b/firmware/diva_pico.uf2 new file mode 100644 index 0000000..0918b9b Binary files /dev/null and b/firmware/diva_pico.uf2 differ diff --git a/firmware/src/CMakeLists.txt b/firmware/src/CMakeLists.txt index 30f3670..69b9914 100644 --- a/firmware/src/CMakeLists.txt +++ b/firmware/src/CMakeLists.txt @@ -5,7 +5,7 @@ function(make_firmware board board_def) pico_sdk_init() add_executable(${board} main.c slider.c rgb.c button.c save.c config.c commands.c - cli.c mpr121.c hebtn.c usb_descriptors.c) + cli.c mpr121.c hebtn.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) diff --git a/firmware/src/lzfx.c b/firmware/src/lzfx.c new file mode 100644 index 0000000..63dddeb --- /dev/null +++ b/firmware/src/lzfx.c @@ -0,0 +1,169 @@ +/* + * Lzfx decompressor + * WHowe + * This is actually taken from CrazyRedMachine's repo + * + */ + + +#include +#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; +} diff --git a/firmware/src/lzfx.h b/firmware/src/lzfx.h new file mode 100644 index 0000000..761f624 --- /dev/null +++ b/firmware/src/lzfx.h @@ -0,0 +1,18 @@ +/* + * Lzfx decompressor + * WHowe + * This is actually taken from CrazyRedMachine's repo + * + */ + +#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); \ No newline at end of file diff --git a/firmware/src/main.c b/firmware/src/main.c index 36763a2..9a29d7f 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -33,6 +33,7 @@ #include "rgb.h" #include "button.h" #include "hebtn.h" +#include "lzfx.h" struct __attribute__((packed)) { uint16_t buttons; // 16 buttons; see JoystickButtons_t for bit mapping @@ -101,12 +102,8 @@ static void gen_joy_report() } -static uint64_t last_hid_time = 0; -static bool motor_running = false; - static void run_lights() { - uint64_t now = time_us_64(); uint32_t button_colors[] = { rgb32(0, 0xff, 0, false), rgb32(0xe0, 0x10, 0xe0, false), @@ -119,19 +116,27 @@ static void run_lights() for (int i = 0; i < 5; i++) { bool pressed = buttons & (1 << i); uint32_t color = pressed ? button_colors[i] : 0x505050; - if (motor_running && (now / 50000 % 2 == 0)) { - color = 0; - } rgb_button_color(i, color); } + uint64_t now = time_us_64(); + + static int rainbow_level = 0; + int zone_num = slider_zone_num(); uint32_t phase = ((now >> 10) / zone_num) & 0xff; - if (now - last_hid_time >= 1000000) { - for (int i = 0; i < zone_num; i++) { - uint32_t color = rgb32_from_hsv(i * 256 / zone_num + phase, 255, 96); - rgb_slider_color(i, slider_touched(i) ? 0xffffff: color); - } + uint32_t touch_bits = slider_touch_bits(); + + if ((touch_bits) && (rainbow_level > 80)) { + rainbow_level -= (rainbow_level > 168) ? 2 : 1; + } + if ((!touch_bits) && (rainbow_level < 256)) { + rainbow_level += (rainbow_level < 168) ? 2 : 1; + } + + for (int i = 0; i < zone_num; i++) { + uint32_t color = rgb32_from_hsv(i * 256 / zone_num + phase, 255, rainbow_level / 2); + rgb_slider_color(i, touch_bits & (1 << i) ? 0xffffff: color); } } @@ -271,12 +276,30 @@ 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 (bufsize > 1) { - motor_running = !motor_running; - } if (report_type == HID_REPORT_TYPE_OUTPUT) { - last_hid_time = time_us_64(); - return; - } + static uint8_t obuf[48]; + memcpy(obuf, buffer, bufsize); + if (report_id == REPORT_ID_LED_SLIDER_1) { + rgb_set_hid_slider(0, 16, obuf); + } else if (report_id == REPORT_ID_LED_SLIDER_2) { + rgb_set_hid_slider(16, 16, obuf); + } else if (report_id == REPORT_ID_LED_BUTTON) { + rgb_set_hid_button(obuf); + } + } else if (report_type == HID_REPORT_TYPE_FEATURE) { + if (report_id == REPORT_ID_LED_COMPRESSED) { + static uint8_t fbuf[64]; + memcpy(fbuf, buffer, bufsize); + static uint8_t decomp[100]; + unsigned int olen = sizeof(decomp); + if (lzfx_decompress(fbuf + 1, fbuf[0], decomp, &olen) != 0) { + return; + } + if (olen < 96) { + return; + } + rgb_set_hid_slider(0, 32, decomp); + } + } } diff --git a/firmware/src/rgb.c b/firmware/src/rgb.c index e72d65f..bca7fce 100644 --- a/firmware/src/rgb.c +++ b/firmware/src/rgb.c @@ -20,9 +20,13 @@ #include "board_defs.h" #include "config.h" -static uint32_t rgb_buf[37]; // 4(Main buttons) + 1(Start button) + 32(Max Slider) +#define HID_LIGHT_TIMEOUT_MS 1000 -static int8_t button_rgb[] = RGB_BUTTON_MAP; +static uint32_t main_buf[37]; // 4(Main buttons) + 1(Start button) + 32(Max Slider) +static uint32_t hid_buf[37]; +static uint64_t hid_timeout = 0; + +static int8_t button_rgb_map[] = RGB_BUTTON_MAP; #define _MAP_LED(x) _MAKE_MAPPER(x) #define _MAKE_MAPPER(x) MAP_LED_##x @@ -92,20 +96,23 @@ static void drive_led() } last = now; - for (int i = 0; i < count_of(rgb_buf); i++) { - pio_sm_put_blocking(pio0, 0, rgb_buf[i] << 8u); + bool use_hid = (hid_timeout > 0) && (now < hid_timeout); + const uint32_t *buf = use_hid ? hid_buf : main_buf; + + for (int i = 0; i < count_of(main_buf); i++) { + pio_sm_put_blocking(pio0, 0, buf[i] << 8u); } } void rgb_set_colors(const uint32_t *colors, unsigned index, size_t num) { - if (index >= count_of(rgb_buf)) { + if (index >= count_of(main_buf)) { return; } - if (index + num > count_of(rgb_buf)) { - num = count_of(rgb_buf) - index; + if (index + num > count_of(main_buf)) { + num = count_of(main_buf) - index; } - memcpy(&rgb_buf[index], colors, num * sizeof(*colors)); + memcpy(&main_buf[index], colors, num * sizeof(*colors)); } static inline uint32_t apply_level(uint32_t color, uint8_t level) @@ -123,8 +130,8 @@ static inline uint32_t apply_level(uint32_t color, uint8_t level) void rgb_button_color(unsigned index, uint32_t color) { - if (index < count_of(button_rgb)) { - rgb_buf[button_rgb[index]] = apply_level(color, diva_cfg->light.level.button); + if (index < count_of(button_rgb_map)) { + main_buf[button_rgb_map[index]] = apply_level(color, diva_cfg->light.level.button); } } @@ -133,7 +140,51 @@ void rgb_slider_color(unsigned index, uint32_t color) if (index > 32) { return; } - rgb_buf[5 + index] = apply_level(color, diva_cfg->light.level.slider); + main_buf[5 + index] = apply_level(color, diva_cfg->light.level.slider); +} + +static inline void update_hid_timeout() +{ + hid_timeout = time_us_64() + HID_LIGHT_TIMEOUT_MS * 1000; +} + +static inline uint32_t led_rgb(const uint8_t *grb, int index) +{ + const uint8_t *src = grb + index * 3; + return rgb32(src[1], src[0], src[2], false); +} + +void rgb_set_hid_slider(unsigned index, unsigned num, const uint8_t *grb) +{ + for (int i = 0; i < num; i++) { + if (index + i >= 32) { + return; + } + uint32_t *dest = &hid_buf[5 + index + i]; + *dest = apply_level(led_rgb(grb, i), diva_cfg->light.level.slider); + } + + update_hid_timeout(); +} + +void rgb_set_hid_button(const uint8_t *scale) +{ + uint32_t button_colors[] = { + rgb32(0, 0xff, 0, false), + rgb32(0xe0, 0x10, 0xe0, false), + rgb32(0, 0, 0xff, false), + rgb32(0xff, 0, 0, false), + }; + + for (int i = 0; i < 4; i++) { + int button_led = button_rgb_map[i]; + uint32_t color = apply_level(button_colors[i], scale[i]); + hid_buf[button_led] = apply_level(color, diva_cfg->light.level.button); + } + + hid_buf[4] = main_buf[4]; // HID doesn't have start button light + + update_hid_timeout(); } void rgb_init() @@ -144,6 +195,7 @@ void rgb_init() ws2812_program_init(pio0, 0, pio0_offset, RGB_PIN, 800000, false); } + void rgb_update() { drive_led(); diff --git a/firmware/src/rgb.h b/firmware/src/rgb.h index 178fb95..2056448 100644 --- a/firmware/src/rgb.h +++ b/firmware/src/rgb.h @@ -22,4 +22,7 @@ void rgb_set_colors(const uint32_t *colors, unsigned index, size_t num); void rgb_button_color(unsigned index, uint32_t color); void rgb_slider_color(unsigned index, uint32_t color); +void rgb_set_hid_slider(unsigned index, unsigned num, const uint8_t *grb); +void rgb_set_hid_button(const uint8_t *scale); + #endif diff --git a/firmware/src/slider.c b/firmware/src/slider.c index 94c37dc..d974e71 100644 --- a/firmware/src/slider.c +++ b/firmware/src/slider.c @@ -102,6 +102,11 @@ bool slider_touched(unsigned zone) return touch_bits & (1 << zone); } +uint32_t slider_touch_bits() +{ + return touch_bits; +} + unsigned slider_count(unsigned zone) { if (zone >= zone_num) { diff --git a/firmware/src/slider.h b/firmware/src/slider.h index 1447270..49560f9 100644 --- a/firmware/src/slider.h +++ b/firmware/src/slider.h @@ -13,6 +13,8 @@ void slider_init(); void slider_update(); uint16_t slider_zone_num(); bool slider_touched(unsigned zone); +uint32_t slider_touch_bits(); + const uint16_t *slider_raw(); void slider_update_config(); unsigned slider_count(unsigned zone); diff --git a/firmware/src/tusb_config.h b/firmware/src/tusb_config.h index 610a6c4..bc48905 100644 --- a/firmware/src/tusb_config.h +++ b/firmware/src/tusb_config.h @@ -102,9 +102,6 @@ extern "C" { #define CFG_TUD_MIDI 0 #define CFG_TUD_VENDOR 0 -// HID buffer size Should be sufficient to hold ID (if any) + Data -#define CFG_TUD_HID_EP_BUFSIZE 64 - #define CFG_TUD_VENDOR 0 // HID buffer size Should be sufficient to hold ID (if any) + Data diff --git a/firmware/src/usb_descriptors.c b/firmware/src/usb_descriptors.c index f47b86f..c8b18f2 100644 --- a/firmware/src/usb_descriptors.c +++ b/firmware/src/usb_descriptors.c @@ -78,8 +78,13 @@ uint8_t const desc_hid_report_joy[] = { DIVAPICO_REPORT_DESC_JOYSTICK, }; -uint8_t const desc_hid_report_nkro[] = { - DIVAPICO_REPORT_DESC_NKRO, +uint8_t const desc_hid_report_led[] = { + DIVAPICO_LED_HEADER, + DIVAPICO_REPORT_DESC_LED_SLIDER_1, + DIVAPICO_REPORT_DESC_LED_SLIDER_2, + DIVAPICO_REPORT_DESC_LED_BUTTON, + DIVAPICO_REPORT_DESC_LED_COMPRESSED, + DIVAPICO_LED_FOOTER }; // Invoked when received GET HID REPORT DESCRIPTOR @@ -91,7 +96,7 @@ uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf) case 0: return desc_hid_report_joy; case 1: - return desc_hid_report_nkro; + return desc_hid_report_led; default: return NULL; } @@ -100,7 +105,7 @@ uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf) // Configuration Descriptor //--------------------------------------------------------------------+ -enum { ITF_NUM_JOY, ITF_NUM_NKRO, +enum { ITF_NUM_JOY, ITF_NUM_LED, ITF_NUM_CLI, ITF_NUM_CLI_DATA, ITF_NUM_TOTAL }; @@ -112,8 +117,7 @@ enum { ITF_NUM_JOY, ITF_NUM_NKRO, #define EPNUM_JOY_OUT 0x01 #define EPNUM_JOY_IN 0x81 -//#define EPNUM_LED 0x86 -#define EPNUM_KEY 0x87 +#define EPNUM_LED 0x86 #define EPNUM_CLI_NOTIF 0x89 #define EPNUM_CLI_OUT 0x0a @@ -131,9 +135,9 @@ 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_NKRO, 5, HID_ITF_PROTOCOL_NONE, - sizeof(desc_hid_report_nkro), EPNUM_KEY, - 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_CDC_DESCRIPTOR(ITF_NUM_CLI, 6, EPNUM_CLI_NOTIF, 8, EPNUM_CLI_OUT, EPNUM_CLI_IN, 64), @@ -157,7 +161,7 @@ const char *string_desc_arr[] = { "Diva Pico Controller", // 2: Product "123456", // 3: Serials, should use chip ID "Diva Pico Joystick", - "Diva Pico NKRO", + "Diva Pico LED", "Diva Pico CLI Port", }; @@ -175,25 +179,12 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) } 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); + sprintf(str, "B%02d", index - base_num); } uint8_t chr_count = strlen(str); diff --git a/firmware/src/usb_descriptors.h b/firmware/src/usb_descriptors.h index 01a583c..07f71ac 100644 --- a/firmware/src/usb_descriptors.h +++ b/firmware/src/usb_descriptors.h @@ -6,6 +6,10 @@ enum { REPORT_ID_JOYSTICK = 1, + REPORT_ID_LED_SLIDER_1 = 4, + REPORT_ID_LED_SLIDER_2 = 5, + REPORT_ID_LED_BUTTON = 6, + REPORT_ID_LED_COMPRESSED = 11, }; // because they are missing from tusb_hid.h @@ -58,40 +62,52 @@ enum { HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ HID_COLLECTION_END -#define DIVAPICO_REPORT_DESC_NKRO \ - HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \ - HID_USAGE(HID_USAGE_DESKTOP_KEYBOARD), \ +#define DIVAPICO_LED_HEADER \ + HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(0x00), \ HID_COLLECTION(HID_COLLECTION_APPLICATION), \ - /* Modifier */ \ - HID_REPORT_SIZE(1), \ - HID_REPORT_COUNT(8), \ - HID_USAGE_PAGE(HID_USAGE_PAGE_KEYBOARD), \ - HID_USAGE_MIN(224), \ - HID_USAGE_MAX(231), \ - HID_LOGICAL_MIN(0), \ - HID_LOGICAL_MAX(1), \ - HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ - /* LED output that we don't care */ \ - HID_REPORT_COUNT(5), \ - HID_REPORT_SIZE(1), \ - HID_USAGE_PAGE(HID_USAGE_PAGE_LED), \ - HID_USAGE_MIN(1), \ - HID_USAGE_MAX(5), \ - HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ - HID_REPORT_COUNT(1), \ - HID_REPORT_SIZE(3), \ - HID_OUTPUT(HID_CONSTANT), \ - /* Full Keyboard Bitmap */ \ - HID_REPORT_SIZE(1), \ - HID_REPORT_COUNT(120), \ - HID_LOGICAL_MIN(0), \ - HID_LOGICAL_MAX(1), \ - HID_USAGE_PAGE(HID_USAGE_PAGE_KEYBOARD), \ - HID_USAGE_MIN(0), \ - HID_USAGE_MAX(119), \ - HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ + HID_REPORT_COUNT(1), HID_REPORT_SIZE(8), \ + HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE) + +#define DIVAPICO_LED_FOOTER \ HID_COLLECTION_END -// HID_REPORT_ID(REPORT_ID_NKRO) +// Slider First 16 LEDs (48 rgb zones, BRG order) +#define DIVAPICO_REPORT_DESC_LED_SLIDER_1 \ + HID_REPORT_ID(REPORT_ID_LED_SLIDER_1) \ + 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(8), HID_STRING_MAXIMUM(55), \ + HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE) + +// Slider Remaining 16 LEDs (48 rgb zones, BRG order) +#define DIVAPICO_REPORT_DESC_LED_SLIDER_2 \ + HID_REPORT_ID(REPORT_ID_LED_SLIDER_2) \ + 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(49), HID_USAGE_MAX(96), \ + HID_STRING_MINIMUM(56), HID_STRING_MAXIMUM(103), /* Delta to previous */ \ + HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE) + +// Button LEDs (18 rgb zones, BRG order) +#define DIVAPICO_REPORT_DESC_LED_BUTTON \ + HID_REPORT_ID(REPORT_ID_LED_BUTTON) \ + HID_REPORT_COUNT(4), HID_REPORT_SIZE(8), \ + HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \ + HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \ + HID_USAGE_MIN(97), HID_USAGE_MAX(100), \ + HID_STRING_MINIMUM(104), HID_STRING_MAXIMUM(107), /* Delta to previous */ \ + HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE) + +// LEDs Compressed +#define DIVAPICO_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_ */