diff --git a/builddefs/generic_features.mk b/builddefs/generic_features.mk
index 9c869586255..38f9f91ccb0 100644
--- a/builddefs/generic_features.mk
+++ b/builddefs/generic_features.mk
@@ -35,6 +35,7 @@ GENERIC_FEATURES = \
HAPTIC \
KEY_LOCK \
KEY_OVERRIDE \
+ LAMPARRAY \
LEADER \
MAGIC \
MOUSEKEY \
diff --git a/lib/python/qmk/cli/generate/config_h.py b/lib/python/qmk/cli/generate/config_h.py
index 00fb1d9585f..776e68a8d28 100755
--- a/lib/python/qmk/cli/generate/config_h.py
+++ b/lib/python/qmk/cli/generate/config_h.py
@@ -85,6 +85,27 @@ def generate_matrix_masked(kb_info_json, config_h_lines):
config_h_lines.append(generate_define('MATRIX_MASKED'))
+def generate_estimated_dimensions(kb_info_json, config_h_lines):
+ """Try and guess physical keyboard dimensions from the declared layouts
+ """
+ if 'layouts' in kb_info_json:
+ width = 0
+ height = 0
+ for layout_data in kb_info_json['layouts'].values():
+ for key in layout_data['layout']:
+ x = key.get('x', 0)
+ y = key.get('y', 0)
+ w = key.get('w', 1)
+ h = key.get('h', 1)
+
+ width = max(width, x + w)
+ height = max(height, y + h)
+
+ # sizes are in micrometers - assume 1u = 19.05mm
+ config_h_lines.append(generate_define('ESTIMATED_KEYBOARD_WIDTH', width * 19050))
+ config_h_lines.append(generate_define('ESTIMATED_KEYBOARD_HEIGHT', height * 19050))
+
+
def generate_config_items(kb_info_json, config_h_lines):
"""Iterate through the info_config map to generate basic config values.
"""
@@ -202,6 +223,8 @@ def generate_config_h(cli):
generate_matrix_masked(kb_info_json, config_h_lines)
+ generate_estimated_dimensions(kb_info_json, config_h_lines)
+
if 'matrix_pins' in kb_info_json:
config_h_lines.append(matrix_pins(kb_info_json['matrix_pins']))
diff --git a/quantum/keyboard.c b/quantum/keyboard.c
index b5fa1a6e2e9..6dfafe43371 100644
--- a/quantum/keyboard.c
+++ b/quantum/keyboard.c
@@ -135,6 +135,9 @@ along with this program. If not, see .
#ifdef WPM_ENABLE
# include "wpm.h"
#endif
+#ifdef LAMPARRAY_ENABLE
+# include "lamparray.h"
+#endif
static uint32_t last_input_modification_time = 0;
uint32_t last_input_activity_time(void) {
@@ -407,6 +410,9 @@ void keyboard_init(void) {
#ifdef SPLIT_KEYBOARD
split_pre_init();
#endif
+#ifdef LAMPARRAY_ENABLE
+ lamparray_init();
+#endif
#ifdef ENCODER_ENABLE
encoder_init();
#endif
@@ -629,6 +635,10 @@ void quantum_task(void) {
#ifdef SECURE_ENABLE
secure_task();
#endif
+
+#ifdef LAMPARRAY_ENABLE
+ lamparray_task();
+#endif
}
/** \brief Main task that is repeatedly called as fast as possible. */
diff --git a/quantum/lamparray/lamparray.c b/quantum/lamparray/lamparray.c
new file mode 100644
index 00000000000..09f412d8ca7
--- /dev/null
+++ b/quantum/lamparray/lamparray.c
@@ -0,0 +1,219 @@
+// Copyright 2024 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include // for memcpy
+#include "lamparray.h"
+#include "lamparray_surface.h"
+#include "keycodes.h"
+#include "keymap_introspection.h"
+#include "action_layer.h"
+
+// Defaults are generated from info.json layout content
+#ifndef LAMPARRAY_WIDTH
+# define LAMPARRAY_WIDTH ESTIMATED_KEYBOARD_WIDTH
+#endif
+#ifndef LAMPARRAY_HEIGHT
+# define LAMPARRAY_HEIGHT ESTIMATED_KEYBOARD_HEIGHT
+#endif
+#ifndef LAMPARRAY_DEPTH
+# define LAMPARRAY_DEPTH 30000
+#endif
+#ifndef LAMPARRAY_KIND
+# define LAMPARRAY_KIND LAMPARRAY_KIND_KEYBOARD
+#endif
+
+#ifdef RGB_MATRIX_ENABLE
+# include "rgb_matrix.h"
+# define LAMPARRAY_RED_LEVELS 255
+# define LAMPARRAY_GREEN_LEVELS 255
+# define LAMPARRAY_BLUE_LEVELS 255
+# define LAMPARRAY_INTENSITY_LEVELS 1
+# define LAMPARRAY_LAMP_COUNT RGB_MATRIX_LED_COUNT
+# define LAMPARRAY_UPDATE_INTERVAL (RGB_MATRIX_LED_FLUSH_LIMIT * 1000)
+#endif
+
+//****************************************************************************
+// utils
+
+/**
+ * \brief Query a HID usage for a given location
+ *
+ * This can be requested while the user is changing layers. This is mitigated somewhat by assuming the default layer changes infrequently.
+ * This is currently accepted as a limitation as there is no method to invalidate the hosts view of the data.
+ */
+uint8_t lamparray_binding_at_keymap_location(uint8_t row, uint8_t col) {
+ uint16_t keycode = keycode_at_keymap_location(get_highest_layer(default_layer_state), row, col);
+ (void)keycode;
+#if LAMPARRAY_KIND == LAMPARRAY_KIND_KEYBOARD
+ // Basic QMK keycodes currently map directly to Keyboard UsagePage so safe to return without added indirection
+ // Mousekeys are ignored due to values overlap Keyboard UsagePage
+ if (IS_BASIC_KEYCODE(keycode) || IS_MODIFIER_KEYCODE(keycode)) {
+ return keycode;
+ }
+#elif LAMPARRAY_KIND == LAMPARRAY_KIND_MOUSE
+ // Usages from the Button UsagePage (0x09) in the range of Button1 (0x01) to Button5 (0x05) inclusive
+ if ((code) >= KC_MS_BTN1 && (code) <= KC_MS_BTN5) {
+ return keycode - KC_MS_BTN1 + 1;
+ }
+#endif
+ return 0;
+}
+
+//****************************************************************************
+// cache
+
+static uint8_t input_binding_cache[LAMPARRAY_LAMP_COUNT];
+
+void lamparray_update_cache(void) {
+ for (uint8_t lamp_id = 0; lamp_id < LAMPARRAY_LAMP_COUNT; lamp_id++) {
+ input_binding_cache[lamp_id] = lamparray_get_lamp_binding_impl(lamp_id);
+ }
+}
+
+uint8_t lamparray_get_lamp_binding(uint16_t lamp_id) {
+ return input_binding_cache[lamp_id];
+}
+
+//****************************************************************************
+// queue
+
+#ifndef LAMPARRAY_REQUEST_QUEUE_SIZE
+# define LAMPARRAY_REQUEST_QUEUE_SIZE 5
+#endif
+
+universal_lamparray_response_t request_queue[LAMPARRAY_REQUEST_QUEUE_SIZE] = {0};
+uint8_t queue_size = 0;
+
+void lamparray_queue_request(universal_lamparray_response_t* report) {
+ // get next slot
+ universal_lamparray_response_t* target = &request_queue[queue_size++];
+
+ // copy data
+ memcpy(target, report, sizeof(universal_lamparray_response_t));
+}
+
+void lamparray_handle_queue(void) {
+ for (uint8_t id = 0; id < queue_size; id++) {
+ universal_lamparray_response_t* report = &request_queue[id];
+ switch (report->report_id) {
+ case LAMPARRAY_REPORT_ID_RANGE_UPDATE:
+ lamparray_set_range(&report->range_update);
+ break;
+ case LAMPARRAY_REPORT_ID_MULTI_UPDATE:
+ lamparray_set_items(&report->multi_update);
+ break;
+ case LAMPARRAY_REPORT_ID_CONTROL:
+ lamparray_set_control_response(report->autonomous);
+ break;
+ }
+ }
+ queue_size = 0;
+}
+
+//****************************************************************************
+// impl
+
+static uint16_t cur_lamp_id = 0;
+static bool is_autonomous = true;
+
+void lamparray_get_attributes(lamparray_attributes_t* data) {
+ data->lamp_count = LAMPARRAY_LAMP_COUNT;
+ data->update_interval = LAMPARRAY_UPDATE_INTERVAL;
+ data->kind = LAMPARRAY_KIND;
+ data->bounds.width = LAMPARRAY_WIDTH;
+ data->bounds.height = LAMPARRAY_HEIGHT;
+ data->bounds.depth = LAMPARRAY_DEPTH;
+}
+
+void lamparray_get_attributes_response(lamparray_attributes_response_t* data) {
+ data->lamp_id = cur_lamp_id;
+ data->update_latency = 1000;
+ data->is_programmable = 1;
+ data->input_binding = lamparray_get_lamp_binding(cur_lamp_id);
+
+ data->levels.red = LAMPARRAY_RED_LEVELS;
+ data->levels.green = LAMPARRAY_GREEN_LEVELS;
+ data->levels.blue = LAMPARRAY_BLUE_LEVELS;
+ data->levels.intensity = LAMPARRAY_INTENSITY_LEVELS;
+
+ lamparray_get_lamp_impl(cur_lamp_id, data);
+
+ // Automatic address pointer incrementing - 26.8.1 LampAttributesRequestReport
+ cur_lamp_id++;
+ if (cur_lamp_id >= LAMPARRAY_LAMP_COUNT) cur_lamp_id = 0;
+}
+
+void lamparray_set_attributes_response(uint16_t lamp_id) {
+ cur_lamp_id = lamp_id;
+}
+
+void lamparray_set_control_response(uint8_t autonomous) {
+ is_autonomous = !!autonomous;
+
+ lamparray_surface_enable(!autonomous);
+}
+
+void lamparray_set_range(lamparray_range_update_t* data) {
+ // Any Lamp*UpdateReports can be ignored - 26.10.1 AutonomousMode
+ if (is_autonomous) {
+ return;
+ }
+
+ // Ensure IDs are within bounds
+ if ((data->start >= LAMPARRAY_LAMP_COUNT) || (data->end >= LAMPARRAY_LAMP_COUNT)) {
+ return;
+ }
+
+ for (uint16_t index = data->start; index <= data->end; index++) {
+ lamparray_surface_set_item(index, data->color);
+ }
+
+ // Batch update complete - 26.11 Updating Lamp State
+ if (data->flags & LAMP_UPDATE_FLAG_COMPLETE) {
+ lamparray_surface_update_finished();
+ }
+}
+
+void lamparray_set_items(lamparray_multi_update_t* data) {
+ // Any Lamp*UpdateReports can be ignored - 26.10.1 AutonomousMode
+ if (is_autonomous) {
+ return;
+ }
+
+ // Ensure data is within bounds
+ if (data->count > LAMP_MULTI_UPDATE_LAMP_COUNT) {
+ return;
+ }
+
+ for (uint8_t i = 0; i < data->count; i++) {
+ // Ensure IDs are within bounds
+ if (data->ids[i] >= LAMPARRAY_LAMP_COUNT) {
+ continue;
+ }
+ lamparray_surface_set_item(data->ids[i], data->colors[i]);
+ }
+
+ // Batch update complete - 26.11 Updating Lamp State
+ if (data->flags & LAMP_UPDATE_FLAG_COMPLETE) {
+ lamparray_surface_update_finished();
+ }
+}
+
+//****************************************************************************
+// feature hooks
+
+void lamparray_init(void) {
+ lamparray_update_cache();
+}
+
+void lamparray_task(void) {
+ lamparray_handle_queue();
+
+ // TODO: regen cache if dynamic keymap updated?
+ uint16_t temp = 0;
+ if (!++temp) lamparray_update_cache();
+}
+
+// TODO: SRC += ...
+#ifdef RGB_MATRIX_ENABLE
+# include "lamparray_rgb_matrix.c"
+#endif
diff --git a/quantum/lamparray/lamparray.h b/quantum/lamparray/lamparray.h
new file mode 100644
index 00000000000..a9c7c5a1f28
--- /dev/null
+++ b/quantum/lamparray/lamparray.h
@@ -0,0 +1,159 @@
+// Copyright 2024 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#include
+#include
+#include "util.h" // PACKED
+
+#define LAMPARRAY_REPORT_ID_ATTRIBUTES 0x01
+#define LAMPARRAY_REPORT_ID_ATTRIBUTES_REQUEST 0x02
+#define LAMPARRAY_REPORT_ID_ATTRIBUTES_RESPONSE 0x03
+#define LAMPARRAY_REPORT_ID_MULTI_UPDATE 0x04
+#define LAMPARRAY_REPORT_ID_RANGE_UPDATE 0x05
+#define LAMPARRAY_REPORT_ID_CONTROL 0x06
+
+// 26.2.1 LampArrayKind Values
+#define LAMPARRAY_KIND_UNDEFINED 0x00
+#define LAMPARRAY_KIND_KEYBOARD 0x01
+#define LAMPARRAY_KIND_MOUSE 0x02
+#define LAMPARRAY_KIND_GAMECONTROLLER 0x03
+#define LAMPARRAY_KIND_PERIPHERAL 0x04
+#define LAMPARRAY_KIND_SCENE 0x05
+#define LAMPARRAY_KIND_NOTIFICATION 0x06
+#define LAMPARRAY_KIND_CHASSIS 0x07
+#define LAMPARRAY_KIND_WEARABLE 0x08
+#define LAMPARRAY_KIND_FURNITURE 0x09
+#define LAMPARRAY_KIND_ART 0x0A
+
+// 26.3.1 LampPurposes Flags
+#define LAMP_PURPOSE_CONTROL 0x01
+#define LAMP_PURPOSE_ACCENT 0x02
+#define LAMP_PURPOSE_BRANDING 0x04
+#define LAMP_PURPOSE_STATUS 0x08
+#define LAMP_PURPOSE_ILLUMINATION 0x10
+#define LAMP_PURPOSE_PRESENTATION 0x20
+
+// 26.4.1 LampUpdate Flags
+#define LAMP_UPDATE_FLAG_COMPLETE 0x01
+
+typedef struct PACKED {
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+ uint8_t intensity;
+} lamp_state_t;
+
+typedef struct PACKED {
+ uint16_t lamp_count;
+ struct {
+ uint32_t width;
+ uint32_t height;
+ uint32_t depth;
+ } bounds;
+ uint32_t kind;
+ uint32_t update_interval;
+} lamparray_attributes_t;
+
+typedef struct PACKED {
+ uint16_t lamp_id;
+ struct {
+ int32_t x;
+ int32_t y;
+ int32_t z;
+ } position;
+ int32_t update_latency;
+ int32_t purposes;
+ lamp_state_t levels;
+ uint8_t is_programmable;
+ uint8_t input_binding;
+} lamparray_attributes_response_t;
+
+typedef struct PACKED {
+ uint8_t flags;
+ uint16_t start;
+ uint16_t end;
+ lamp_state_t color;
+} lamparray_range_update_t;
+
+#define LAMP_MULTI_UPDATE_LAMP_COUNT 8
+typedef struct PACKED {
+ uint8_t count;
+ uint8_t flags;
+ uint16_t ids[LAMP_MULTI_UPDATE_LAMP_COUNT];
+ lamp_state_t colors[LAMP_MULTI_UPDATE_LAMP_COUNT];
+} lamparray_multi_update_t;
+
+typedef struct PACKED universal_lamparray_response_t {
+ uint8_t report_id;
+ union {
+ struct {
+ uint16_t lamp_id;
+ };
+ struct {
+ uint8_t autonomous;
+ };
+ lamparray_range_update_t range_update;
+ lamparray_multi_update_t multi_update;
+ };
+} universal_lamparray_response_t;
+
+typedef struct PACKED lamparray_attributes_report_t {
+ uint8_t report_id;
+ lamparray_attributes_t attributes;
+} lamparray_attributes_report_t;
+
+typedef struct PACKED lamparray_attributes_response_report_t {
+ uint8_t report_id;
+ lamparray_attributes_response_t attributes_response;
+} lamparray_attributes_response_report_t;
+
+/**
+ * \brief Gets LampArrayAttributesReport data
+ */
+void lamparray_get_attributes(lamparray_attributes_t* data);
+
+/**
+ * \brief Sets LampAttributesRequestReport data
+ */
+void lamparray_set_attributes_response(uint16_t lamp_id);
+
+/**
+ * \brief Gets LampAttributesResponseReport data
+ */
+void lamparray_get_attributes_response(lamparray_attributes_response_t* data);
+
+/**
+ * \brief Sets LampRangeUpdateReport data
+ */
+void lamparray_set_range(lamparray_range_update_t* data);
+
+/**
+ * \brief Sets LampMultiUpdateReport data
+ */
+void lamparray_set_items(lamparray_multi_update_t* data);
+
+/**
+ * \brief Sets LampArrayControlReport data
+ */
+void lamparray_set_control_response(uint8_t autonomous);
+
+//****************************************************************************
+// utils
+
+uint8_t lamparray_binding_at_keymap_location(uint8_t row, uint8_t col);
+
+void lamparray_queue_request(universal_lamparray_response_t* report);
+
+//****************************************************************************
+// feature hooks
+
+void lamparray_init(void);
+
+void lamparray_task(void);
+
+//****************************************************************************
+// lighting framework bindings
+
+void lamparray_get_lamp_impl(uint16_t lamp_id, lamparray_attributes_response_t* data);
+uint8_t lamparray_get_lamp_binding_impl(uint16_t lamp_id);
diff --git a/quantum/lamparray/lamparray_rgb_matrix.c b/quantum/lamparray/lamparray_rgb_matrix.c
new file mode 100644
index 00000000000..ce8265d78d6
--- /dev/null
+++ b/quantum/lamparray/lamparray_rgb_matrix.c
@@ -0,0 +1,56 @@
+// Copyright 2024 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "lamparray.h"
+#include "lamparray_surface.h"
+#include "rgb_matrix.h"
+
+/**
+ * \brief Get feature specific lamp info.
+ *
+ * Scales the LED config with the assumed RGB Matrix dimensions (224x64), for simplicity, as a completely flat device.
+ * Assumes all keys are either on the top or bottom of the resulting rectangle.
+ */
+__attribute__((weak)) void lamparray_get_lamp_impl(uint16_t lamp_id, lamparray_attributes_response_t* data) {
+ data->position.x = (LAMPARRAY_WIDTH / 224) * g_led_config.point[lamp_id].x;
+ data->position.y = (LAMPARRAY_HEIGHT / 64) * g_led_config.point[lamp_id].y;
+
+ if (g_led_config.flags[lamp_id] & LED_FLAG_UNDERGLOW) {
+ data->position.z = LAMPARRAY_DEPTH;
+ data->purposes = LAMP_PURPOSE_ACCENT;
+ } else if (g_led_config.flags[lamp_id] & LED_FLAG_INDICATOR) {
+ data->position.z = 0;
+ data->purposes = LAMP_PURPOSE_STATUS;
+ } else {
+ data->position.z = 0;
+ data->purposes = LAMP_PURPOSE_CONTROL;
+ }
+}
+
+/**
+ * \brief Query a HID usage for a given lamp
+ */
+__attribute__((weak)) uint8_t lamparray_get_lamp_binding_impl(uint16_t lamp_id) {
+ for (uint8_t i_row = 0; i_row < MATRIX_ROWS; i_row++) {
+ for (uint8_t i_col = 0; i_col < MATRIX_COLS; i_col++) {
+ if (g_led_config.matrix_co[i_row][i_col] == lamp_id) {
+ return lamparray_binding_at_keymap_location(i_row, i_col);
+ }
+ }
+ }
+ return 0;
+}
+
+// TODO: temporay binding of storage and render
+#include "rgb_matrix/overlay.c"
+
+void lamparray_surface_enable(bool enable) {
+ rgb_matrix_overlay_enable(enable);
+}
+
+void lamparray_surface_set_item(uint16_t index, lamp_state_t color) {
+ rgb_matrix_overlay_set(index, (rgba_t){color.red, color.green, color.blue, color.intensity ? 0xFF : 0});
+}
+
+void lamparray_surface_update_finished(void) {
+ rgb_matrix_overlay_flush();
+}
diff --git a/quantum/lamparray/lamparray_surface.h b/quantum/lamparray/lamparray_surface.h
new file mode 100644
index 00000000000..a558c7875c2
--- /dev/null
+++ b/quantum/lamparray/lamparray_surface.h
@@ -0,0 +1,18 @@
+// Copyright 2023 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+/**
+ * \brief lamparray_surface_enable
+ */
+void lamparray_surface_enable(bool enable);
+
+/**
+ * \brief lamparray_surface_set_item
+ */
+void lamparray_surface_set_item(uint16_t index, lamp_state_t color);
+
+/**
+ * \brief lamparray_surface_update_finished
+ */
+void lamparray_surface_update_finished(void);
diff --git a/quantum/quantum.h b/quantum/quantum.h
index 996e93a12fc..2b82e572566 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -233,6 +233,10 @@ extern layer_state_t layer_state;
# include "process_repeat_key.h"
#endif
+#ifdef LAMPARRAY_ENABLE
+# include "lamparray.h"
+#endif
+
void set_single_persistent_default_layer(uint8_t default_layer);
#define IS_LAYER_ON(layer) layer_state_is(layer)
diff --git a/quantum/rgb_matrix/overlay.c b/quantum/rgb_matrix/overlay.c
new file mode 100644
index 00000000000..ed5771c79eb
--- /dev/null
+++ b/quantum/rgb_matrix/overlay.c
@@ -0,0 +1,59 @@
+// Copyright 2023 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include
+#include
+
+// TODO: Pack "enabled" of single LED more efficiently than as alpha channels
+typedef struct PACKED rgba_t {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t a;
+} rgba_t;
+
+#define OVERLAY_DOUBLE_BUFFER
+#ifdef OVERLAY_DOUBLE_BUFFER
+rgba_t overlay_buffer_a[RGB_MATRIX_LED_COUNT] = {0};
+rgba_t overlay_buffer_b[RGB_MATRIX_LED_COUNT] = {0};
+
+rgba_t* overlay_buffer_render = overlay_buffer_a;
+rgba_t* overlay_buffer_write = overlay_buffer_b;
+#else
+rgba_t overlay_buffer[RGB_MATRIX_LED_COUNT] = {0};
+
+rgba_t* overlay_buffer_render = overlay_buffer;
+rgba_t* overlay_buffer_write = overlay_buffer;
+#endif
+
+static bool is_enabled = false;
+
+void rgb_matrix_overlay_enable(bool enable) {
+ is_enabled = enable;
+}
+
+void rgb_matrix_overlay_set(uint8_t index, rgba_t color) {
+ overlay_buffer_write[index] = color;
+}
+
+void rgb_matrix_overlay_flush(void) {
+#ifdef OVERLAY_DOUBLE_BUFFER
+ memcpy(overlay_buffer_render, overlay_buffer_write, sizeof(rgba_t) * RGB_MATRIX_LED_COUNT);
+
+ rgba_t* buffer_tmp = overlay_buffer_render;
+ overlay_buffer_render = overlay_buffer_write;
+ overlay_buffer_write = buffer_tmp;
+#endif
+}
+
+bool rgb_matrix_indicators_user(void) {
+ if (!is_enabled) return true;
+
+ for (uint8_t i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
+ rgba_t* led = &overlay_buffer_render[i];
+
+ // Allow "transparent" to running effect?
+ if (led->a) rgb_matrix_set_color(i, led->r, led->g, led->b);
+ }
+
+ return false;
+}
diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c
index 66f9ad03186..517bb70f851 100644
--- a/tmk_core/protocol/chibios/usb_main.c
+++ b/tmk_core/protocol/chibios/usb_main.c
@@ -51,6 +51,10 @@
extern keymap_config_t keymap_config;
#endif
+#ifdef LAMPARRAY_ENABLE
+# include "lamparray.h"
+#endif
+
/* ---------------------------------------------------------
* Global interface variables and declarations
* ---------------------------------------------------------
@@ -217,6 +221,24 @@ static const USBEndpointConfig digitizer_ep_config = {
};
#endif
+#ifdef LAMPARRAY_ENABLE
+/* LampArray endpoint state structure */
+static USBInEndpointState lamparray_ep_state;
+
+/* LampArray endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
+static const USBEndpointConfig lamparray_ep_config = {
+ USB_EP_MODE_TYPE_INTR, /* Interrupt EP */
+ NULL, /* SETUP packet notification callback */
+ dummy_usb_cb, /* IN notification callback */
+ NULL, /* OUT notification callback */
+ LAMPARRAY_EPSIZE, /* IN maximum packet size */
+ 0, /* OUT maximum packet size */
+ &lamparray_ep_state, /* IN Endpoint state */
+ NULL, /* OUT endpoint state */
+ usb_lld_endpoint_fields /* USB driver specific endpoint fields */
+};
+#endif
+
#ifdef USB_ENDPOINTS_ARE_REORDERABLE
typedef struct {
size_t queue_capacity_in;
@@ -509,6 +531,9 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
#endif
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
usbInitEndpointI(usbp, DIGITIZER_IN_EPNUM, &digitizer_ep_config);
+#endif
+#ifdef LAMPARRAY_ENABLE
+ usbInitEndpointI(usbp, LAMPARRAY_IN_EPNUM, &lamparray_ep_config);
#endif
for (int i = 0; i < NUM_USB_DRIVERS; i++) {
#ifdef USB_ENDPOINTS_ARE_REORDERABLE
@@ -586,6 +611,19 @@ static void set_led_transfer_cb(USBDriver *usbp) {
}
}
+#ifdef LAMPARRAY_ENABLE
+static universal_lamparray_response_t universal_lamparray_report_buf;
+
+static void set_lamparray_transfer_cb(USBDriver *usbp) {
+ // handle directly to avoid sync issues with get
+ if (universal_lamparray_report_buf.report_id == LAMPARRAY_REPORT_ID_ATTRIBUTES_REQUEST) {
+ lamparray_set_attributes_response(universal_lamparray_report_buf.lamp_id);
+ } else {
+ lamparray_queue_request(&universal_lamparray_report_buf);
+ }
+}
+#endif
+
static bool usb_requests_hook_cb(USBDriver *usbp) {
usb_control_request_t *setup = (usb_control_request_t *)usbp->setup;
@@ -623,6 +661,24 @@ static bool usb_requests_hook_cb(USBDriver *usbp) {
}
# endif
#endif /* SHARED_EP_ENABLE */
+
+#ifdef LAMPARRAY_ENABLE
+ case LAMPARRAY_INTERFACE:
+ switch (setup->wValue.lbyte) {
+ case LAMPARRAY_REPORT_ID_ATTRIBUTES:
+ static lamparray_attributes_report_t ret = {.report_id = LAMPARRAY_REPORT_ID_ATTRIBUTES};
+ lamparray_get_attributes(&ret.attributes);
+
+ usbSetupTransfer(usbp, (uint8_t *)&ret, sizeof(ret), NULL);
+ return TRUE;
+ case LAMPARRAY_REPORT_ID_ATTRIBUTES_RESPONSE:
+ static lamparray_attributes_response_report_t res = {.report_id = LAMPARRAY_REPORT_ID_ATTRIBUTES_RESPONSE};
+ lamparray_get_attributes_response(&res.attributes_response);
+
+ usbSetupTransfer(usbp, (uint8_t *)&res, sizeof(res), NULL);
+ return TRUE;
+ }
+#endif
default:
universal_report_blank.report_id = setup->wValue.lbyte;
usbSetupTransfer(usbp, (uint8_t *)&universal_report_blank, setup->wLength, NULL);
@@ -653,6 +709,18 @@ static bool usb_requests_hook_cb(USBDriver *usbp) {
#endif
usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb);
return true;
+#ifdef LAMPARRAY_ENABLE
+ case LAMPARRAY_INTERFACE:
+ switch (setup->wValue.lbyte) {
+ case LAMPARRAY_REPORT_ID_ATTRIBUTES_REQUEST:
+ case LAMPARRAY_REPORT_ID_RANGE_UPDATE:
+ case LAMPARRAY_REPORT_ID_MULTI_UPDATE:
+ case LAMPARRAY_REPORT_ID_CONTROL:
+ usbSetupTransfer(usbp, (uint8_t *)&universal_lamparray_report_buf, sizeof(universal_lamparray_report_buf), set_lamparray_transfer_cb);
+ return true;
+ }
+ break;
+#endif
}
break;
diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c
index 553f69b1e46..538ed1172da 100644
--- a/tmk_core/protocol/lufa/lufa.c
+++ b/tmk_core/protocol/lufa/lufa.c
@@ -67,6 +67,10 @@
# include "raw_hid.h"
#endif
+#ifdef LAMPARRAY_ENABLE
+# include "lamparray.h"
+#endif
+
uint8_t keyboard_idle = 0;
/* 0: Boot Protocol, 1: Report Protocol(default) */
uint8_t keyboard_protocol = 1;
@@ -409,6 +413,11 @@ void EVENT_USB_Device_ConfigurationChanged(void) {
ConfigSuccess &= Endpoint_ConfigureEndpoint((DIGITIZER_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, DIGITIZER_EPSIZE, 1);
#endif
+#ifdef LAMPARRAY_ENABLE
+ /* Setup LampArray endpoint */
+ ConfigSuccess &= Endpoint_ConfigureEndpoint((LAMPARRAY_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, LAMPARRAY_EPSIZE, 1);
+#endif
+
usb_device_state_set_configuration(USB_DeviceState == DEVICE_STATE_Configured, USB_Device_ConfigurationNumber);
}
@@ -446,6 +455,26 @@ void EVENT_USB_Device_ControlRequest(void) {
ReportData = (uint8_t *)&keyboard_report_sent;
ReportSize = sizeof(keyboard_report_sent);
break;
+#ifdef LAMPARRAY_ENABLE
+ case LAMPARRAY_INTERFACE:
+ switch ((USB_ControlRequest.wValue & 0xFF)) {
+ case LAMPARRAY_REPORT_ID_ATTRIBUTES:
+ static lamparray_attributes_report_t ret = {.report_id = LAMPARRAY_REPORT_ID_ATTRIBUTES};
+ lamparray_get_attributes(&ret.attributes);
+
+ ReportData = (uint8_t *)&ret;
+ ReportSize = sizeof(ret);
+ break;
+ case LAMPARRAY_REPORT_ID_ATTRIBUTES_RESPONSE:
+ static lamparray_attributes_response_report_t res = {.report_id = LAMPARRAY_REPORT_ID_ATTRIBUTES_RESPONSE};
+ lamparray_get_attributes_response(&res.attributes_response);
+
+ ReportData = (uint8_t *)&res;
+ ReportSize = sizeof(res);
+ break;
+ }
+ break;
+#endif
}
/* Write the report data to the control endpoint */
@@ -481,6 +510,26 @@ void EVENT_USB_Device_ControlRequest(void) {
Endpoint_ClearOUT();
Endpoint_ClearStatusStage();
break;
+#ifdef LAMPARRAY_ENABLE
+ case LAMPARRAY_INTERFACE:
+ Endpoint_ClearSETUP();
+
+ while (!(Endpoint_IsINReady()))
+ ;
+
+ static universal_lamparray_response_t universal_lamparray_report_buf;
+
+ Endpoint_Read_Control_Stream_LE(&universal_lamparray_report_buf, USB_ControlRequest.wLength);
+ Endpoint_ClearIN();
+
+ // handle directly to avoid sync issues with get
+ if (universal_lamparray_report_buf.report_id == LAMPARRAY_REPORT_ID_ATTRIBUTES_REQUEST) {
+ lamparray_set_attributes_response(universal_lamparray_report_buf.lamp_id);
+ } else {
+ lamparray_queue_request(&universal_lamparray_report_buf);
+ }
+ break;
+#endif
}
}
diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c
index eb214c0492d..0f08435e603 100644
--- a/tmk_core/protocol/usb_descriptor.c
+++ b/tmk_core/protocol/usb_descriptor.c
@@ -432,6 +432,173 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = {
};
#endif
+#ifdef LAMPARRAY_ENABLE
+const USB_Descriptor_HIDReport_Datatype_t PROGMEM LampArrayReport[] = {
+ HID_RI_USAGE_PAGE(8, 0x59), // Usage Page (Lighting and Illumination)
+ HID_RI_USAGE(8, 0x01), // Usage (Lamp Array)
+ HID_RI_COLLECTION(8, 0x01), // Collection (Application)
+ HID_RI_REPORT_ID(8, 1), // Report ID (1)
+ HID_RI_USAGE(8, 0x02), // Usage (Lamp Array Attributes Report)
+ HID_RI_COLLECTION(8, 0x02), // Collection (Logical)
+ HID_RI_USAGE(8, 0x03), // Usage (Lamp Count)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(32, (uint32_t)0xFFFF), // Logical Maximum (65535)
+ HID_RI_REPORT_SIZE(8, 0x10), // Report Size (16)
+ HID_RI_REPORT_COUNT(8, 0x01), // Report Count (1)
+ HID_RI_FEATURE(8, 0x03), // Feature (Cnst,Var,Abs)
+ HID_RI_USAGE(8, 0x04), // Usage (Bounding Box Width In Micrometers)
+ HID_RI_USAGE(8, 0x05), // Usage (Bounding Box Height In Micrometers)
+ HID_RI_USAGE(8, 0x06), // Usage (Bounding Box Depth In Micrometers)
+ HID_RI_USAGE(8, 0x07), // Usage (Lamp Array Kind)
+ HID_RI_USAGE(8, 0x08), // Usage (Min Update Interval In Microseconds)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(32, 0x7FFFFFFF), // Logical Maximum (2147483647)
+ HID_RI_REPORT_SIZE(8, 0x20), // Report Size (32)
+ HID_RI_REPORT_COUNT(8, 0x05), // Report Count (5)
+ HID_RI_FEATURE(8, 0x03), // Feature (Cnst,Var,Abs)
+ HID_RI_END_COLLECTION(0), // End Collection
+ HID_RI_REPORT_ID(8, 2), // Report ID (2)
+ HID_RI_USAGE(8, 0x20), // Usage (Lamp Attributes Request Report)
+ HID_RI_COLLECTION(8, 0x02), // Collection (Logical)
+ HID_RI_USAGE(8, 0x21), // Usage (Lamp Id)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(32, (uint32_t)0xFFFF), // Logical Maximum (65535)
+ HID_RI_REPORT_SIZE(8, 0x10), // Report Size (16)
+ HID_RI_REPORT_COUNT(8, 0x01), // Report Count (1)
+ HID_RI_FEATURE(8, 0x02), // Feature (Data,Var,Abs)
+ HID_RI_END_COLLECTION(0), // End Collection
+ HID_RI_REPORT_ID(8, 3), // Report ID (3)
+ HID_RI_USAGE(8, 0x22), // Usage (Lamp Attributes Response Report)
+ HID_RI_COLLECTION(8, 0x02), // Collection (Logical)
+ HID_RI_USAGE(8, 0x21), // Usage (Lamp Id)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(32, (uint32_t)0xFFFF), // Logical Maximum (65535)
+ HID_RI_REPORT_SIZE(8, 0x10), // Report Size (16)
+ HID_RI_REPORT_COUNT(8, 0x01), // Report Count (1)
+ HID_RI_FEATURE(8, 0x02), // Feature (Data,Var,Abs)
+ HID_RI_USAGE(8, 0x23), // Usage (Position X In Micrometers)
+ HID_RI_USAGE(8, 0x24), // Usage (Position Y In Micrometers)
+ HID_RI_USAGE(8, 0x25), // Usage (Position Z In Micrometers)
+ HID_RI_USAGE(8, 0x27), // Usage (Update Latency In Microseconds)
+ HID_RI_USAGE(8, 0x26), // Usage (Lamp Purposes)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(32, 0x7FFFFFFF), // Logical Maximum (2147483647)
+ HID_RI_REPORT_SIZE(8, 0x20), // Report Size (32)
+ HID_RI_REPORT_COUNT(8, 0x05), // Report Count (5)
+ HID_RI_FEATURE(8, 0x02), // Feature (Data,Var,Abs)
+ HID_RI_USAGE(8, 0x28), // Usage (Red Level Count)
+ HID_RI_USAGE(8, 0x29), // Usage (Green Level Count)
+ HID_RI_USAGE(8, 0x2a), // Usage (Blue Level Count)
+ HID_RI_USAGE(8, 0x2b), // Usage (Intensity Level Count)
+ HID_RI_USAGE(8, 0x2c), // Usage (Is Programmable)
+ HID_RI_USAGE(8, 0x2d), // Usage (Input Binding)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(16, 0x00FF), // Logical Maximum (255)
+ HID_RI_REPORT_SIZE(8, 0x08), // Report Size (8)
+ HID_RI_REPORT_COUNT(8, 0x06), // Report Count (6)
+ HID_RI_FEATURE(8, 0x02), // Feature (Data,Var,Abs)
+ HID_RI_END_COLLECTION(0), // End Collection
+ HID_RI_REPORT_ID(8, 4), // Report ID (4)
+ HID_RI_USAGE(8, 0x50), // Usage (Lamp Multi Update Report)
+ HID_RI_COLLECTION(8, 0x02), // Collection (Logical)
+ HID_RI_USAGE(8, 0x03), // Usage (Lamp Count)
+ HID_RI_USAGE(8, 0x55), // Usage (Lamp Update Flags)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(8, 0x08), // Logical Maximum (8)
+ HID_RI_REPORT_SIZE(8, 0x08), // Report Size (8)
+ HID_RI_REPORT_COUNT(8, 0x02), // Report Count (2)
+ HID_RI_FEATURE(8, 0x02), // Feature (Data,Var,Abs)
+ HID_RI_USAGE(8, 0x21), // Usage (Lamp Id)
+ HID_RI_USAGE(8, 0x21), // Usage (Lamp Id)
+ HID_RI_USAGE(8, 0x21), // Usage (Lamp Id)
+ HID_RI_USAGE(8, 0x21), // Usage (Lamp Id)
+ HID_RI_USAGE(8, 0x21), // Usage (Lamp Id)
+ HID_RI_USAGE(8, 0x21), // Usage (Lamp Id)
+ HID_RI_USAGE(8, 0x21), // Usage (Lamp Id)
+ HID_RI_USAGE(8, 0x21), // Usage (Lamp Id)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(32, (uint32_t)0xFFFF), // Logical Maximum (65535)
+ HID_RI_REPORT_SIZE(8, 0x10), // Report Size (16)
+ HID_RI_REPORT_COUNT(8, 0x08), // Report Count (8)
+ HID_RI_FEATURE(8, 0x02), // Feature (Data,Var,Abs)
+ HID_RI_USAGE(8, 0x51), // Usage (Red Update Channel)
+ HID_RI_USAGE(8, 0x52), // Usage (Green Update Channel)
+ HID_RI_USAGE(8, 0x53), // Usage (Blue Update Channel)
+ HID_RI_USAGE(8, 0x54), // Usage (Intensity Update Channel)
+ HID_RI_USAGE(8, 0x51), // Usage (Red Update Channel)
+ HID_RI_USAGE(8, 0x52), // Usage (Green Update Channel)
+ HID_RI_USAGE(8, 0x53), // Usage (Blue Update Channel)
+ HID_RI_USAGE(8, 0x54), // Usage (Intensity Update Channel)
+ HID_RI_USAGE(8, 0x51), // Usage (Red Update Channel)
+ HID_RI_USAGE(8, 0x52), // Usage (Green Update Channel)
+ HID_RI_USAGE(8, 0x53), // Usage (Blue Update Channel)
+ HID_RI_USAGE(8, 0x54), // Usage (Intensity Update Channel)
+ HID_RI_USAGE(8, 0x51), // Usage (Red Update Channel)
+ HID_RI_USAGE(8, 0x52), // Usage (Green Update Channel)
+ HID_RI_USAGE(8, 0x53), // Usage (Blue Update Channel)
+ HID_RI_USAGE(8, 0x54), // Usage (Intensity Update Channel)
+ HID_RI_USAGE(8, 0x51), // Usage (Red Update Channel)
+ HID_RI_USAGE(8, 0x52), // Usage (Green Update Channel)
+ HID_RI_USAGE(8, 0x53), // Usage (Blue Update Channel)
+ HID_RI_USAGE(8, 0x54), // Usage (Intensity Update Channel)
+ HID_RI_USAGE(8, 0x51), // Usage (Red Update Channel)
+ HID_RI_USAGE(8, 0x52), // Usage (Green Update Channel)
+ HID_RI_USAGE(8, 0x53), // Usage (Blue Update Channel)
+ HID_RI_USAGE(8, 0x54), // Usage (Intensity Update Channel)
+ HID_RI_USAGE(8, 0x51), // Usage (Red Update Channel)
+ HID_RI_USAGE(8, 0x52), // Usage (Green Update Channel)
+ HID_RI_USAGE(8, 0x53), // Usage (Blue Update Channel)
+ HID_RI_USAGE(8, 0x54), // Usage (Intensity Update Channel)
+ HID_RI_USAGE(8, 0x51), // Usage (Red Update Channel)
+ HID_RI_USAGE(8, 0x52), // Usage (Green Update Channel)
+ HID_RI_USAGE(8, 0x53), // Usage (Blue Update Channel)
+ HID_RI_USAGE(8, 0x54), // Usage (Intensity Update Channel)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(16, 0x00FF), // Logical Maximum (255)
+ HID_RI_REPORT_SIZE(8, 0x08), // Report Size (8)
+ HID_RI_REPORT_COUNT(8, 0x20), // Report Count (32)
+ HID_RI_FEATURE(8, 0x02), // Feature (Data,Var,Abs)
+ HID_RI_END_COLLECTION(0), // End Collection
+ HID_RI_REPORT_ID(8, 5), // Report ID (5)
+ HID_RI_USAGE(8, 0x60), // Usage (Lamp Range Update Report)
+ HID_RI_COLLECTION(8, 0x02), // Collection (Logical)
+ HID_RI_USAGE(8, 0x55), // Usage (Lamp Update Flags)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(8, 0x08), // Logical Maximum (8)
+ HID_RI_REPORT_SIZE(8, 0x08), // Report Size (8)
+ HID_RI_REPORT_COUNT(8, 0x01), // Report Count (1)
+ HID_RI_FEATURE(8, 0x02), // Feature (Data,Var,Abs)
+ HID_RI_USAGE(8, 0x61), // Usage (Lamp Id Start)
+ HID_RI_USAGE(8, 0x62), // Usage (Lamp Id End)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(32, (uint32_t)0xFFFF), // Logical Maximum (65535)
+ HID_RI_REPORT_SIZE(8, 0x10), // Report Size (16)
+ HID_RI_REPORT_COUNT(8, 0x02), // Report Count (2)
+ HID_RI_FEATURE(8, 0x02), // Feature (Data,Var,Abs)
+ HID_RI_USAGE(8, 0x51), // Usage (Red Update Channel)
+ HID_RI_USAGE(8, 0x52), // Usage (Green Update Channel)
+ HID_RI_USAGE(8, 0x53), // Usage (Blue Update Channel)
+ HID_RI_USAGE(8, 0x54), // Usage (Intensity Update Channel)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(16, 0x00FF), // Logical Maximum (255)
+ HID_RI_REPORT_SIZE(8, 0x08), // Report Size (8)
+ HID_RI_REPORT_COUNT(8, 0x04), // Report Count (4)
+ HID_RI_FEATURE(8, 0x02), // Feature (Data,Var,Abs)
+ HID_RI_END_COLLECTION(0), // End Collection
+ HID_RI_REPORT_ID(8, 6), // Report ID (6)
+ HID_RI_USAGE(8, 0x70), // Usage (Lamp Array Control Report)
+ HID_RI_COLLECTION(8, 0x02), // Collection (Logical)
+ HID_RI_USAGE(8, 0x71), // Usage (Autonomous Mode)
+ HID_RI_LOGICAL_MINIMUM(8, 0x00), // Logical Minimum (0)
+ HID_RI_LOGICAL_MAXIMUM(8, 0x01), // Logical Maximum (1)
+ HID_RI_REPORT_SIZE(8, 0x08), // Report Size (8)
+ HID_RI_REPORT_COUNT(8, 0x01), // Report Count (1)
+ HID_RI_FEATURE(8, 0x02), // Feature (Data,Var,Abs)
+ HID_RI_END_COLLECTION(0), // End Collection
+ HID_RI_END_COLLECTION(0) // End Collection
+};
+#endif
+
/*
* Device descriptor
*/
@@ -1050,6 +1217,46 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = {
.PollingIntervalMS = USB_POLLING_INTERVAL_MS
},
#endif
+
+#ifdef LAMPARRAY_ENABLE
+ /*
+ * LampArray
+ */
+ .LampArray_Interface = {
+ .Header = {
+ .Size = sizeof(USB_Descriptor_Interface_t),
+ .Type = DTYPE_Interface
+ },
+ .InterfaceNumber = LAMPARRAY_INTERFACE,
+ .AlternateSetting = 0x00,
+ .TotalEndpoints = 1,
+ .Class = HID_CSCP_HIDClass,
+ .SubClass = HID_CSCP_NonBootSubclass,
+ .Protocol = HID_CSCP_NonBootProtocol,
+ .InterfaceStrIndex = NO_DESCRIPTOR
+ },
+ .LampArray_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(LampArrayReport)
+ },
+ .LampArray_INEndpoint = {
+ .Header = {
+ .Size = sizeof(USB_Descriptor_Endpoint_t),
+ .Type = DTYPE_Endpoint
+ },
+ .EndpointAddress = (ENDPOINT_DIR_IN | LAMPARRAY_IN_EPNUM),
+ .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
+ .EndpointSize = LAMPARRAY_EPSIZE,
+ .PollingIntervalMS = 0x01
+ },
+#endif
};
/*
@@ -1198,6 +1405,13 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
break;
#endif
+#ifdef LAMPARRAY_ENABLE
+ case LAMPARRAY_INTERFACE:
+ Address = &ConfigurationDescriptor.LampArray_HID;
+ Size = sizeof(USB_HID_Descriptor_HID_t);
+
+ break;
+#endif
}
break;
@@ -1254,6 +1468,13 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
Size = sizeof(DigitizerReport);
break;
#endif
+#ifdef LAMPARRAY_ENABLE
+ case LAMPARRAY_INTERFACE:
+ Address = &LampArrayReport;
+ Size = sizeof(LampArrayReport);
+
+ break;
+#endif
}
break;
diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h
index 1268bdae733..69354d54b79 100644
--- a/tmk_core/protocol/usb_descriptor.h
+++ b/tmk_core/protocol/usb_descriptor.h
@@ -145,6 +145,13 @@ typedef struct {
USB_HID_Descriptor_HID_t Digitizer_HID;
USB_Descriptor_Endpoint_t Digitizer_INEndpoint;
#endif
+
+#ifdef LAMPARRAY_ENABLE
+ // HID LampArray Interface
+ USB_Descriptor_Interface_t LampArray_Interface;
+ USB_HID_Descriptor_HID_t LampArray_HID;
+ USB_Descriptor_Endpoint_t LampArray_INEndpoint;
+#endif
} USB_Descriptor_Configuration_t;
/*
@@ -194,6 +201,11 @@ enum usb_interfaces {
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
DIGITIZER_INTERFACE,
#endif
+
+#ifdef LAMPARRAY_ENABLE
+ LAMPARRAY_INTERFACE,
+#endif
+
TOTAL_INTERFACES
};
@@ -281,6 +293,10 @@ enum usb_endpoints {
# define DIGITIZER_IN_EPNUM SHARED_IN_EPNUM
# endif
#endif
+
+#ifdef LAMPARRAY_ENABLE
+ LAMPARRAY_IN_EPNUM = NEXT_EPNUM,
+#endif
};
#ifdef PROTOCOL_LUFA
@@ -307,5 +323,6 @@ enum usb_endpoints {
#define CDC_EPSIZE 16
#define JOYSTICK_EPSIZE 8
#define DIGITIZER_EPSIZE 8
+#define LAMPARRAY_EPSIZE 8
uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const uint16_t wLength, const void** const DescriptorAddress);