hidpp20: add support for non-RGB LEDs, feature 0x1300

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>
This commit is contained in:
Ogier Bouvier
2017-09-15 16:10:20 +02:00
committed by Peter Hutterer
parent 186a1deda8
commit a47492e62c
3 changed files with 440 additions and 9 deletions

View File

@@ -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)

View File

@@ -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 */
/* -------------------------------------------------------------------------- */

View File

@@ -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