mirror of
https://github.com/libratbag/libratbag.git
synced 2026-11-08 07:33:53 +01:00
hidpp20: add support for non-RGB LEDs, feature 0x1300
Signed-off-by: Ogier Bouvier <obouvier@logitech.com>
This commit is contained in:
committed by
Peter Hutterer
parent
186a1deda8
commit
a47492e62c
@@ -54,6 +54,7 @@
|
||||
#define HIDPP_CAP_KBD_REPROGRAMMABLE_KEYS_1b00 (1 << 4)
|
||||
#define HIDPP_CAP_COLOR_LED_EFFECTS_8070 (1 << 5)
|
||||
#define HIDPP_CAP_ONBOARD_PROFILES_8100 (1 << 6)
|
||||
#define HIDPP_CAP_LED_SW_CONTROL (1 << 7)
|
||||
|
||||
struct hidpp20drv_data {
|
||||
struct hidpp20_device *dev;
|
||||
@@ -63,7 +64,7 @@ struct hidpp20drv_data {
|
||||
unsigned num_controls;
|
||||
struct hidpp20_control_id *controls;
|
||||
struct hidpp20_profiles *profiles;
|
||||
struct hidpp20_color_led_zone_info *led_infos;
|
||||
union hidpp20_generic_led_zone_info led_infos;
|
||||
|
||||
unsigned int num_profiles;
|
||||
unsigned int num_resolutions;
|
||||
@@ -257,7 +258,7 @@ hidpp20drv_read_led(struct ratbag_led *led)
|
||||
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
|
||||
return;
|
||||
|
||||
led_info = &drv_data->led_infos[led->index];
|
||||
led_info = &drv_data->led_infos.color_leds[led->index];
|
||||
profile = &drv_data->profiles->profiles[led->profile->index];
|
||||
h_led = &profile->leds[led->index];
|
||||
|
||||
@@ -677,11 +678,11 @@ hidpp20drv_read_color_leds(struct ratbag_device *device)
|
||||
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
|
||||
return 0;
|
||||
|
||||
free(drv_data->led_infos);
|
||||
drv_data->led_infos = NULL;
|
||||
free(drv_data->led_infos.color_leds);
|
||||
drv_data->led_infos.color_leds = NULL;
|
||||
drv_data->num_leds = 0;
|
||||
|
||||
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos);
|
||||
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos.color_leds);
|
||||
|
||||
if (rc > 0) {
|
||||
drv_data->num_leds = rc;
|
||||
@@ -999,7 +1000,7 @@ hidpp20drv_remove(struct ratbag_device *device)
|
||||
|
||||
if (drv_data->profiles)
|
||||
hidpp20_onboard_profiles_destroy(drv_data->profiles);
|
||||
free(drv_data->led_infos);
|
||||
free(drv_data->led_infos.color_leds);
|
||||
free(drv_data->controls);
|
||||
free(drv_data->sensors);
|
||||
if (drv_data->dev)
|
||||
|
||||
296
src/hidpp20.c
296
src/hidpp20.c
@@ -70,7 +70,32 @@ hidpp20_feature_get_name(uint16_t feature)
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
const char*
|
||||
hidpp20_sw_led_control_get_mode_string(const enum hidpp20_led_sw_ctrl_led_mode mode)
|
||||
{
|
||||
static char numeric[8];
|
||||
const char* str;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
CASE_RETURN_STRING(HIDPP20_LED_MODE_OFF);
|
||||
CASE_RETURN_STRING(HIDPP20_LED_MODE_ON);
|
||||
CASE_RETURN_STRING(HIDPP20_LED_MODE_BLINK);
|
||||
CASE_RETURN_STRING(HIDPP20_LED_MODE_RAMP_UP);
|
||||
CASE_RETURN_STRING(HIDPP20_LED_MODE_RAMP_DOWN);
|
||||
CASE_RETURN_STRING(HIDPP20_LED_MODE_BREATHING);
|
||||
CASE_RETURN_STRING(HIDPP20_LED_MODE_HEARTBEAT);
|
||||
CASE_RETURN_STRING(HIDPP20_LED_MODE_TRAVEL);
|
||||
default:
|
||||
sprintf_safe(numeric, "%#4x", mode);
|
||||
str = numeric;
|
||||
break;
|
||||
}
|
||||
#undef CASE_RETURN_STRING
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -400,6 +425,277 @@ hidpp20_batterylevel_get_battery_level(struct hidpp20_device *device,
|
||||
return msg.msg.parameters[2];
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x1300: Non-RGB led support */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define CMD_LED_SW_CONTROL_GET_LED_COUNT 0x00
|
||||
#define CMD_LED_SW_CONTROL_GET_LED_INFO 0x10
|
||||
#define CMD_LED_SW_CONTROL_GET_SW_CTRL 0x20
|
||||
#define CMD_LED_SW_CONTROL_SET_SW_CTRL 0x30
|
||||
#define CMD_LED_SW_CONTROL_GET_LED_STATE 0x40
|
||||
#define CMD_LED_SW_CONTROL_SET_LED_STATE 0x50
|
||||
#define CMD_LED_SW_CONTROL_GET_NV_CONFIG 0x60
|
||||
|
||||
static bool hidpp20_led_sw_control_check_state(uint16_t state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case HIDPP20_LED_MODE_ON:
|
||||
case HIDPP20_LED_MODE_OFF:
|
||||
case HIDPP20_LED_MODE_BLINK:
|
||||
case HIDPP20_LED_MODE_TRAVEL:
|
||||
case HIDPP20_LED_MODE_RAMP_UP:
|
||||
case HIDPP20_LED_MODE_RAMP_DOWN:
|
||||
case HIDPP20_LED_MODE_HEARTBEAT:
|
||||
case HIDPP20_LED_MODE_BREATHING:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int hidpp20_led_sw_control_read_leds(struct hidpp20_device* device,
|
||||
struct hidpp20_led_sw_ctrl_led_info** info_list)
|
||||
{
|
||||
int rc;
|
||||
struct hidpp20_led_sw_ctrl_led_info *i_list, *info;
|
||||
unsigned i;
|
||||
uint8_t num_infos;
|
||||
|
||||
rc = hidpp20_led_sw_control_get_led_count(device);
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
num_infos = rc;
|
||||
|
||||
if(rc == 0) {
|
||||
*info_list = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
i_list = zalloc(rc * sizeof(struct hidpp20_led_sw_ctrl_led_info));
|
||||
|
||||
for (i = 0; i < num_infos; i++) {
|
||||
info = &i_list[i];
|
||||
info->index = i;
|
||||
|
||||
rc = hidpp20_led_sw_control_get_led_info(device, i, info);
|
||||
|
||||
if (rc != 0)
|
||||
goto err;
|
||||
|
||||
hidpp_log_raw(&device->base, "non-color led %d: type: %d supports: %d\n",
|
||||
info->index,
|
||||
info->type,
|
||||
info->caps);
|
||||
}
|
||||
|
||||
*info_list = i_list;
|
||||
return num_infos;
|
||||
|
||||
err:
|
||||
free(i_list);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hidpp20_led_sw_control_get_led_count(struct hidpp20_device* device)
|
||||
{
|
||||
union hidpp20_message msg = {
|
||||
.msg = {
|
||||
.report_id = REPORT_ID_LONG,
|
||||
.address = CMD_LED_SW_CONTROL_GET_LED_COUNT,
|
||||
.device_idx = device->index,
|
||||
},
|
||||
};
|
||||
|
||||
uint8_t feature_idx;
|
||||
|
||||
feature_idx = hidpp_root_get_feature_idx(device, HIDPP_PAGE_LED_SW_CONTROL);
|
||||
if (feature_idx == 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
msg.msg.sub_id = feature_idx;
|
||||
|
||||
if (hidpp20_request_command(device, &msg)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return msg.msg.parameters[0];
|
||||
}
|
||||
|
||||
int hidpp20_led_sw_control_get_led_info(struct hidpp20_device* device,
|
||||
uint8_t led_idx,
|
||||
struct hidpp20_led_sw_ctrl_led_info *info)
|
||||
{
|
||||
union hidpp20_message msg = {
|
||||
.msg = {
|
||||
.report_id = REPORT_ID_LONG,
|
||||
.address = CMD_LED_SW_CONTROL_GET_LED_INFO,
|
||||
.device_idx = device->index,
|
||||
},
|
||||
};
|
||||
struct hidpp20_led_sw_ctrl_led_info *params;
|
||||
uint8_t feature_idx;
|
||||
|
||||
feature_idx = hidpp_root_get_feature_idx(device, HIDPP_PAGE_LED_SW_CONTROL);
|
||||
if (feature_idx == 0)
|
||||
return -ENOTSUP;
|
||||
|
||||
msg.msg.sub_id = feature_idx;
|
||||
msg.msg.parameters[0] = led_idx;
|
||||
|
||||
if (hidpp20_request_command(device, &msg))
|
||||
// Only error possible is an invalid index, which means the led doesn't exist
|
||||
return -ENOENT;
|
||||
|
||||
params = (struct hidpp20_led_sw_ctrl_led_info*) msg.msg.parameters;
|
||||
params->type = hidpp_be_u16_to_cpu(params->type);
|
||||
params->caps = hidpp_be_u16_to_cpu(params->caps);
|
||||
|
||||
*info = *params;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool hidpp20_led_sw_control_get_sw_ctrl(struct hidpp20_device* device)
|
||||
{
|
||||
uint8_t feature_idx;
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg = {
|
||||
.report_id = REPORT_ID_SHORT,
|
||||
.address = CMD_LED_SW_CONTROL_GET_SW_CTRL,
|
||||
.device_idx = device->index,
|
||||
},
|
||||
};
|
||||
|
||||
feature_idx = hidpp_root_get_feature_idx(device, HIDPP_PAGE_LED_SW_CONTROL);
|
||||
|
||||
if (feature_idx == 0)
|
||||
return -ENOTSUP;
|
||||
|
||||
msg.msg.sub_id = feature_idx;
|
||||
|
||||
rc = hidpp20_request_command(device, &msg);
|
||||
|
||||
if (rc) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return msg.msg.parameters[0];
|
||||
}
|
||||
|
||||
int hidpp20_led_sw_control_set_sw_ctrl(struct hidpp20_device* device, bool ctrl)
|
||||
{
|
||||
uint8_t feature_idx;
|
||||
union hidpp20_message msg = {
|
||||
.msg = {
|
||||
.report_id = REPORT_ID_SHORT,
|
||||
.address = CMD_LED_SW_CONTROL_SET_SW_CTRL,
|
||||
.device_idx = device->index,
|
||||
},
|
||||
};
|
||||
|
||||
feature_idx = hidpp_root_get_feature_idx(device, HIDPP_PAGE_LED_SW_CONTROL);
|
||||
|
||||
if (feature_idx == 0)
|
||||
return -ENOTSUP;
|
||||
|
||||
msg.msg.sub_id = feature_idx;
|
||||
msg.msg.parameters[0] = ctrl;
|
||||
|
||||
if (hidpp20_request_command(device, &msg))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hidpp20_led_sw_control_get_led_state(struct hidpp20_device* device,
|
||||
uint8_t led_idx,
|
||||
struct hidpp20_led_sw_ctrl_led_state *out)
|
||||
{
|
||||
int rc;
|
||||
uint8_t feature_idx;
|
||||
union hidpp20_message msg = {
|
||||
.msg = {
|
||||
.report_id = REPORT_ID_LONG,
|
||||
.address = CMD_LED_SW_CONTROL_GET_LED_STATE,
|
||||
.device_idx = device->index,
|
||||
}
|
||||
};
|
||||
struct hidpp20_led_sw_ctrl_led_state *state;
|
||||
|
||||
feature_idx = hidpp_root_get_feature_idx(device, HIDPP_PAGE_LED_SW_CONTROL);
|
||||
|
||||
if (feature_idx == 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
msg.msg.sub_id = feature_idx;
|
||||
|
||||
msg.msg.parameters[0] = led_idx;
|
||||
|
||||
rc = hidpp20_request_command(device, &msg);
|
||||
|
||||
if (rc)
|
||||
return -ENOENT;
|
||||
|
||||
state = (struct hidpp20_led_sw_ctrl_led_state*) msg.msg.parameters;
|
||||
|
||||
// This field has to be stored in little-endian
|
||||
state->mode = state->mode;
|
||||
|
||||
if (state->mode == HIDPP20_LED_MODE_BREATHING) {
|
||||
// Only parameters that is reported by these LEDs is brightness when breathing
|
||||
state->breathing.brightness = hidpp_be_u16_to_cpu(state->breathing.brightness);
|
||||
}
|
||||
|
||||
*out = *state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hidpp20_led_sw_control_set_led_state(struct hidpp20_device* device,
|
||||
const struct hidpp20_led_sw_ctrl_led_state *state)
|
||||
{
|
||||
int rc;
|
||||
uint8_t feature_idx;
|
||||
union hidpp20_message msg = {
|
||||
.msg = {
|
||||
.report_id = REPORT_ID_LONG,
|
||||
.address = CMD_LED_SW_CONTROL_SET_LED_STATE,
|
||||
.device_idx = device->index,
|
||||
}
|
||||
};
|
||||
|
||||
feature_idx = hidpp_root_get_feature_idx(device, HIDPP_PAGE_LED_SW_CONTROL);
|
||||
|
||||
if (feature_idx == 0)
|
||||
return -ENOTSUP;
|
||||
|
||||
msg.msg.sub_id = feature_idx;
|
||||
|
||||
if (!hidpp20_led_sw_control_check_state(state->mode))
|
||||
return -EINVAL;
|
||||
|
||||
msg.msg.parameters[0] = state->index;
|
||||
hidpp_set_unaligned_be_u16(&msg.msg.parameters[1], state->mode);
|
||||
hidpp_set_unaligned_be_u16(&msg.msg.parameters[3], state->blink.index);
|
||||
hidpp_set_unaligned_be_u16(&msg.msg.parameters[5], state->blink.on_time);
|
||||
hidpp_set_unaligned_be_u16(&msg.msg.parameters[7], state->blink.off_time);
|
||||
|
||||
rc = hidpp20_request_command(device, &msg);
|
||||
|
||||
if (rc)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x1b00: KBD reprogrammable keys and mouse buttons */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
140
src/hidpp20.h
140
src/hidpp20.h
@@ -126,6 +126,130 @@ int hidpp20_batterylevel_get_battery_level(struct hidpp20_device *device,
|
||||
uint16_t *level,
|
||||
uint16_t *next_level);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x1300: LED software control */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define HIDPP_PAGE_LED_SW_CONTROL 0x1300
|
||||
|
||||
enum hidpp20_led_sw_ctrl_led_type {
|
||||
HIDPP20_LED_TYPE_BATTERY = 0x01,
|
||||
HIDPP20_LED_TYPE_DPI,
|
||||
HIDPP20_LED_TYPE_PROFILE,
|
||||
HIDPP20_LED_TYPE_LOGO,
|
||||
HIDPP20_LED_TYPE_COSMETIC
|
||||
};
|
||||
|
||||
enum hidpp20_led_sw_ctrl_led_mode {
|
||||
HIDPP20_LED_MODE_OFF = 0x01,
|
||||
HIDPP20_LED_MODE_ON = 0x02,
|
||||
HIDPP20_LED_MODE_BLINK = 0x04,
|
||||
HIDPP20_LED_MODE_TRAVEL = 0x08,
|
||||
HIDPP20_LED_MODE_RAMP_UP = 0x10,
|
||||
HIDPP20_LED_MODE_RAMP_DOWN = 0x20,
|
||||
HIDPP20_LED_MODE_HEARTBEAT = 0x40,
|
||||
HIDPP20_LED_MODE_BREATHING = 0x80
|
||||
};
|
||||
|
||||
#define HIDPP20_LED_SW_CONTROL_LED_INDEX_ALL 0xff
|
||||
|
||||
struct hidpp20_led_sw_ctrl_led_info {
|
||||
uint8_t index;
|
||||
uint8_t type;
|
||||
uint8_t physical_count;
|
||||
uint16_t caps;
|
||||
uint8_t nvconfig_caps;
|
||||
} __attribute__((packed));
|
||||
_Static_assert(sizeof(struct hidpp20_led_sw_ctrl_led_info) == 6, "Invalid size");
|
||||
|
||||
struct hidpp20_led_sw_ctrl_led_state {
|
||||
uint8_t index;
|
||||
uint16_t mode;
|
||||
union {
|
||||
struct {
|
||||
uint16_t brightness;
|
||||
uint16_t period;
|
||||
uint16_t timeout;
|
||||
} breathing;
|
||||
struct {
|
||||
uint16_t unused;
|
||||
uint16_t delay;
|
||||
} traveling;
|
||||
struct {
|
||||
uint16_t index;
|
||||
uint16_t on_time;
|
||||
uint16_t off_time;
|
||||
} blink;
|
||||
struct {
|
||||
/* Logical information to display on the LED
|
||||
* Meaning and value range depend on the LED
|
||||
*/
|
||||
uint16_t index;
|
||||
} on;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
_Static_assert(sizeof(struct hidpp20_led_sw_ctrl_led_state) == 9, "Invalid size");
|
||||
|
||||
int hidpp20_led_sw_control_read_leds(struct hidpp20_device* device,
|
||||
struct hidpp20_led_sw_ctrl_led_info** info_list);
|
||||
|
||||
const char*
|
||||
hidpp20_sw_led_control_get_mode_string(const enum hidpp20_led_sw_ctrl_led_mode mode);
|
||||
|
||||
int hidpp20_led_sw_control_read_leds(struct hidpp20_device* device,
|
||||
struct hidpp20_led_sw_ctrl_led_info** info_list);
|
||||
|
||||
/**
|
||||
* Retrieves the number of non-RGB logical leds from the mouse
|
||||
* @return the led count or a negative errno
|
||||
*/
|
||||
int hidpp20_led_sw_control_get_led_count(struct hidpp20_device* device);
|
||||
|
||||
/**
|
||||
* Retrieves the info for a given logical led
|
||||
* @return 0 or a negative errno in case of error
|
||||
*/
|
||||
int hidpp20_led_sw_control_get_led_info(struct hidpp20_device* device,
|
||||
uint8_t led_idx,
|
||||
struct hidpp20_led_sw_ctrl_led_info* info);
|
||||
|
||||
/**
|
||||
* Reads all LEDs on the device for their current state
|
||||
* @return 0 on success or a negative errno
|
||||
*/
|
||||
int hidpp20_led_sw_control_read_leds(struct hidpp20_device* device,
|
||||
struct hidpp20_led_sw_ctrl_led_info** info_list);
|
||||
|
||||
/**
|
||||
* Get the current sw control value
|
||||
* @return 0 if the leds are sofware-controlled 1 if they are firmware controlled
|
||||
*/
|
||||
bool hidpp20_led_sw_control_get_sw_ctrl(struct hidpp20_device* device);
|
||||
|
||||
/**
|
||||
* Sets who controls the LEDs
|
||||
* ctrl = 0, the software controls the LEDs
|
||||
* ctrl = 1, the firmware controls the LEDs
|
||||
* @return 0 on success or a negative errno
|
||||
*/
|
||||
int hidpp20_led_sw_control_set_sw_ctrl(struct hidpp20_device* device, bool ctrl);
|
||||
|
||||
/**
|
||||
* Gets the state of a LED
|
||||
* @return 0 on success or a negative errno
|
||||
*/
|
||||
int hidpp20_led_sw_control_get_led_state(struct hidpp20_device* device,
|
||||
uint8_t led_idx,
|
||||
struct hidpp20_led_sw_ctrl_led_state* state);
|
||||
|
||||
/**
|
||||
* Sets the state of a LED
|
||||
* The LED index should be passed in the state structure
|
||||
* @return 0 on success or a negative errno
|
||||
*/
|
||||
int hidpp20_led_sw_control_set_led_state(struct hidpp20_device* device,
|
||||
const struct hidpp20_led_sw_ctrl_led_state* state);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x1b00: KBD reprogrammable keys and mouse buttons */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@@ -258,7 +382,6 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
|
||||
|
||||
#define HIDPP_PAGE_COLOR_LED_EFFECTS 0x8070
|
||||
|
||||
|
||||
struct hidpp20_color_led_info;
|
||||
|
||||
struct hidpp20_color_led_zone_info;
|
||||
@@ -362,6 +485,14 @@ struct hidpp20_color_led_info {
|
||||
} __attribute__((packed));
|
||||
_Static_assert(sizeof(struct hidpp20_color_led_info) == 5, "Invalid size");
|
||||
|
||||
struct hidpp20_non_color_led_zone_info {
|
||||
uint8_t index;
|
||||
uint8_t type;
|
||||
uint16_t capabilities;
|
||||
uint8_t nv_config;
|
||||
} __attribute__((packed));
|
||||
_Static_assert(sizeof(struct hidpp20_non_color_led_zone_info) == 5, "Invalid size");
|
||||
|
||||
struct hidpp20_color_led_zone_info {
|
||||
uint8_t index;
|
||||
uint16_t location;
|
||||
@@ -370,6 +501,11 @@ struct hidpp20_color_led_zone_info {
|
||||
} __attribute__((packed));
|
||||
_Static_assert(sizeof(struct hidpp20_color_led_zone_info) == 5, "Invalid size");
|
||||
|
||||
union hidpp20_generic_led_zone_info {
|
||||
struct hidpp20_led_sw_ctrl_led_info* leds;
|
||||
struct hidpp20_color_led_zone_info* color_leds;
|
||||
};
|
||||
|
||||
struct hidpp20_color {
|
||||
uint8_t red;
|
||||
uint8_t green;
|
||||
@@ -610,5 +746,3 @@ hidpp20_onboard_profiles_allocate_sector(struct hidpp20_profiles *profiles)
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define HIDPP_PAGE_MOUSE_BUTTON_SPY 0x8110
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user