1
0
forked from drath/libratbag

hidpp20: add support for RGB Effects feature (0x8071)

Signed-off-by: Filipe Laíns <lains@archlinux.org>
This commit is contained in:
Filipe Laíns
2019-07-29 13:15:20 +01:00
committed by Peter Hutterer
parent a8b4d9c6f3
commit 02ec2442d3
5 changed files with 393 additions and 25 deletions

View File

@@ -57,6 +57,7 @@
#define HIDPP_CAP_LED_SW_CONTROL_1300 (1 << 7)
#define HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060 (1 << 8)
#define HIDPP_CAP_BATTERY_VOLTAGE_1001 (1 << 9)
#define HIDPP_CAP_RGB_EFFECTS_8071 (1 << 10)
#define HIDPP_HIDDEN_FEATURE (1 << 6)
@@ -338,7 +339,7 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
struct hidpp20_color_led_info info;
int rc;
led_info = &drv_data->led_infos.color_leds[led->index];
led_info = &drv_data->led_infos.color_leds_8070[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
@@ -359,7 +360,7 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
/* pre-filled, only override if unknown */
if (led->type == RATBAG_LED_TYPE_UNKNOWN)
led->type = hidpp20_8070_get_location_mapping(led_info->location);
led->type = hidpp20_led_get_location_mapping(led_info->location);
led->color.red = h_led->color.red;
led->color.green = h_led->color.green;
led->color.blue = h_led->color.blue;
@@ -405,6 +406,81 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
}
}
static void
hidpp20drv_read_led_8071(struct ratbag_led *led, struct hidpp20drv_data* drv_data)
{
struct hidpp20_profile *profile;
struct hidpp20_led *h_led;
struct hidpp20_rgb_device_info device_info;
struct hidpp20_rgb_cluster_info *cluster_info;
int rc;
hidpp20_rgb_effects_get_device_info(drv_data->dev, &device_info);
cluster_info = &drv_data->led_infos.color_leds_8071[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
switch (h_led->mode) {
case HIDPP20_LED_ON:
led->mode = RATBAG_LED_ON;
break;
case HIDPP20_LED_CYCLE:
led->mode = RATBAG_LED_CYCLE;
break;
case HIDPP20_LED_BREATHING:
led->mode = RATBAG_LED_BREATHING;
break;
default:
led->mode = RATBAG_LED_OFF;
break;
}
/* pre-filled, only override if unknown */
if (led->type == RATBAG_LED_TYPE_UNKNOWN)
led->type = hidpp20_led_get_location_mapping(cluster_info->location);
led->color.red = h_led->color.red;
led->color.green = h_led->color.green;
led->color.blue = h_led->color.blue;
led->ms = h_led->period;
led->brightness = h_led->brightness * 255 / 100;
if (device_info.ext_caps & HIDPP20_COLOR_LED_INFO_EXT_CAP_MONOCHROME_ONLY)
led->colordepth = RATBAG_LED_COLORDEPTH_MONOCHROME;
else
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
for (int i = 0; i < cluster_info->num_effects; i++) {
struct hidpp20_rgb_effect_info ei;
rc = hidpp20_rgb_effects_get_effect_info(drv_data->dev,
cluster_info->index,
i, &ei);
if (rc < 0)
break;
switch (ei.effect_id) {
case HIDPP20_COLOR_LED_ZONE_EFFECT_DISABLED:
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_FIXED:
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_CYCLING:
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_WAVE:
case HIDPP20_COLOR_LED_ZONE_EFFECT_BREATHING:
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
break;
default:
log_bug_libratbag(led->profile->device->ratbag,
"%s: Unknown effect id %d\n",
led->profile->device->name,
ei.effect_id);
break;
}
}
}
static void
hidpp20drv_read_led(struct ratbag_led *led)
{
@@ -413,6 +489,8 @@ hidpp20drv_read_led(struct ratbag_led *led)
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
hidpp20drv_read_led_8070(led, drv_data);
else if (drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071)
hidpp20drv_read_led_8071(led, drv_data);
else if (drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300)
hidpp20drv_read_led_1300(led, drv_data);
}
@@ -609,8 +687,8 @@ hidpp20drv_update_led_1300(struct ratbag_led *led, struct hidpp20drv_data *data)
}
static int
hidpp20drv_update_led_8070(struct ratbag_led *led, struct ratbag_profile* profile,
struct hidpp20drv_data *drv_data)
hidpp20drv_update_led_8070_8071(struct ratbag_led *led, struct ratbag_profile* profile,
struct hidpp20drv_data *drv_data)
{
struct hidpp20_profile *h_profile;
struct hidpp20_led *h_led;
@@ -652,8 +730,9 @@ hidpp20drv_update_led(struct ratbag_led *led)
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
return hidpp20drv_update_led_8070(led, profile, drv_data);
if ((drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070) |
(drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071))
return hidpp20drv_update_led_8070_8071(led, profile, drv_data);
if (drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300)
return hidpp20drv_update_led_1300(led, drv_data);
@@ -971,19 +1050,15 @@ hidpp20drv_read_kbd_reprogrammable_key(struct ratbag_device *device)
}
static int
hidpp20drv_read_color_leds(struct ratbag_device *device)
hidpp20drv_read_color_leds_8070(struct hidpp20drv_data *drv_data)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
int rc;
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
return 0;
free(drv_data->led_infos.color_leds);
drv_data->led_infos.color_leds = NULL;
free(drv_data->led_infos.color_leds_8070);
drv_data->led_infos.color_leds_8070 = NULL;
drv_data->num_leds = 0;
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos.color_leds);
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos.color_leds_8070);
if (rc > 0) {
drv_data->num_leds = rc;
@@ -993,6 +1068,38 @@ hidpp20drv_read_color_leds(struct ratbag_device *device)
return rc;
}
static int
hidpp20drv_read_color_leds_8071(struct hidpp20drv_data *drv_data)
{
int rc;
free(drv_data->led_infos.color_leds_8071);
drv_data->led_infos.color_leds_8071 = NULL;
drv_data->num_leds = 0;
rc = hidpp20_rgb_effects_get_cluster_infos(drv_data->dev, &drv_data->led_infos.color_leds_8071);
if (rc > 0) {
drv_data->num_leds = rc;
rc = 0;
}
return rc;
}
static int
hidpp20drv_read_color_leds(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
return hidpp20drv_read_color_leds_8070(drv_data);
else if (drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071)
return hidpp20drv_read_color_leds_8071(drv_data);
return 0;
}
static int
hidpp20drv_read_leds(struct ratbag_device *device)
{
@@ -1206,6 +1313,18 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
device->num_leds = drv_data->num_leds;
break;
}
case HIDPP_PAGE_RGB_EFFECTS: {
log_debug(ratbag, "device has color effects\n");
drv_data->capabilities |= HIDPP_CAP_RGB_EFFECTS_8071;
/* we read the profile once to get the correct number of
* supported leds. */
if (hidpp20drv_read_color_leds(device))
return 0;
device->num_leds = drv_data->num_leds;
break;
}
case HIDPP_PAGE_LED_SW_CONTROL: {
log_debug(ratbag, "device has non-rgb leds\n");
drv_data->capabilities |= HIDPP_CAP_LED_SW_CONTROL_1300;
@@ -1380,7 +1499,8 @@ hidpp20drv_remove(struct ratbag_device *device)
if (drv_data->profiles)
hidpp20_onboard_profiles_destroy(drv_data->profiles);
free(drv_data->led_infos.color_leds);
free(drv_data->led_infos.color_leds_8070);
free(drv_data->led_infos.color_leds_8071);
free(drv_data->controls);
free(drv_data->sensors);
if (drv_data->dev)

View File

@@ -234,7 +234,7 @@ hidpp20_1b04_get_physical_mapping_name(uint16_t value)
}
enum ratbag_led_type
hidpp20_8070_get_location_mapping(uint16_t value)
hidpp20_led_get_location_mapping(uint16_t value)
{
const struct hidpp20_8070_location_mapping *map;
@@ -247,7 +247,7 @@ hidpp20_8070_get_location_mapping(uint16_t value)
}
const char *
hidpp20_8070_get_location_mapping_name(uint16_t value)
hidpp20_led_get_location_mapping_name(uint16_t value)
{
const struct hidpp20_8070_location_mapping *map;

View File

@@ -120,10 +120,10 @@ enum ratbag_button_type
hidpp20_1b04_get_physical_mapping(uint16_t value);
enum ratbag_led_type
hidpp20_8070_get_location_mapping(uint16_t value);
hidpp20_led_get_location_mapping(uint16_t value);
const char *
hidpp20_8070_get_location_mapping_name(uint16_t value);
hidpp20_led_get_location_mapping_name(uint16_t value);
const char *
hidpp20_1b04_get_logical_mapping_name(uint16_t value);

View File

@@ -66,6 +66,7 @@ hidpp20_feature_get_name(uint16_t feature)
CASE_RETURN_STRING(HIDPP_PAGE_ADJUSTABLE_DPI);
CASE_RETURN_STRING(HIDPP_PAGE_ADJUSTABLE_REPORT_RATE);
CASE_RETURN_STRING(HIDPP_PAGE_COLOR_LED_EFFECTS);
CASE_RETURN_STRING(HIDPP_PAGE_RGB_EFFECTS);
CASE_RETURN_STRING(HIDPP_PAGE_ONBOARD_PROFILES);
CASE_RETURN_STRING(HIDPP_PAGE_MOUSE_BUTTON_SPY);
default:
@@ -942,7 +943,7 @@ hidpp20_color_led_effects_get_zone_infos(struct hidpp20_device *device,
"led_info %d: location: %d type %s num_effects: %d persistency_caps: 0x%02x\n",
info->index,
info->location,
hidpp20_8070_get_location_mapping_name(info->location),
hidpp20_led_get_location_mapping_name(info->location),
info->num_effects,
info->persistency_caps);
}
@@ -991,6 +992,176 @@ hidpp20_color_led_effect_get_zone_effect_info(struct hidpp20_device *device,
return 0;
}
/* -------------------------------------------------------------------------- */
/* 0x8071: RGB Effects */
/* -------------------------------------------------------------------------- */
#define CMD_RGB_EFFECTS_GET_INFO 0x00
#define CMD_RGB_EFFECTS_SET_RGB_CLUSTER_EFFECT 0x10
#define CMD_RGB_EFFECTS_SET_MULTI_LED_RGB_CLUSTER_PATTERN 0x20
#define CMD_RGB_EFFECTS_MANAGE_NV_CONFIG 0x30
#define CMD_RGB_EFFECTS_MANAGE_RGB_LED_BIN_INFO 0x40
#define CMD_RGB_EFFECTS_MANAGE_SW_CONTROL 0x50
#define CMD_RGB_EFFECTS_SET_EFFECT_SYNC_CORRECTION 0x60
#define CMD_RGB_EFFECTS_MANAGE_RGB_POWER_MODE_CONFIG 0x70
#define CMD_RGB_EFFECTS_MANAGE_RGB_POWER_MODE 0x80
int
hidpp20_rgb_effects_get_device_info(struct hidpp20_device *device,
struct hidpp20_rgb_device_info *info)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
.msg.parameters[1] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
.msg.parameters[2] = HIDPP20_RGB_EFFECTS_TOI_GENERAL,
};
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_RGB_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info->cluster_index = msg.msg.parameters[0];
info->effect_index = msg.msg.parameters[1];
info->cluster_count = msg.msg.parameters[2];
info->nv_caps = get_unaligned_be_u16(&msg.msg.parameters[3]);
info->ext_caps = get_unaligned_be_u16(&msg.msg.parameters[4]);
return 0;
}
int
hidpp20_rgb_effects_get_cluster_info(struct hidpp20_device *device,
uint8_t cluster_index,
struct hidpp20_rgb_cluster_info *info)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = cluster_index,
.msg.parameters[1] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
.msg.parameters[2] = HIDPP20_RGB_EFFECTS_TOI_GENERAL,
};
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_RGB_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info->index = msg.msg.parameters[0];
info->effect_index = msg.msg.parameters[1];
info->location = get_unaligned_be_u16(&msg.msg.parameters[2]);
info->num_effects = get_unaligned_be_u16(&msg.msg.parameters[4]);
info->persistency_caps = get_unaligned_be_u16(&msg.msg.parameters[6]);
return 0;
}
int
hidpp20_rgb_effects_get_cluster_infos(struct hidpp20_device *device,
struct hidpp20_rgb_cluster_info **infos_list)
{
struct hidpp20_rgb_cluster_info *i_list, *info;
struct hidpp20_rgb_device_info device_info = {0};
uint8_t num_infos;
unsigned i;
int rc;
rc = hidpp20_rgb_effects_get_device_info(device, &device_info);
if (rc < 0)
return rc;
num_infos = device_info.cluster_count;
if (num_infos == 0) {
*infos_list = NULL;
return 0;
}
i_list = zalloc(num_infos * sizeof(struct hidpp20_rgb_cluster_info));
for (i = 0; i < num_infos; i++) {
info = &i_list[i];
info->index = i;
rc = hidpp20_rgb_effects_get_cluster_info(device, i, info);
if (rc)
goto err;
hidpp_log_raw(&device->base,
"cluster_info %d: location: %d type %s num_effects: %d persistency_caps: 0x%02x\n",
info->index,
info->location,
hidpp20_led_get_location_mapping_name(info->location),
info->num_effects,
info->persistency_caps);
}
*infos_list = i_list;
return num_infos;
err:
free(i_list);
return rc;
}
int
hidpp20_rgb_effects_get_effect_info(struct hidpp20_device *device,
uint8_t cluster_index,
uint8_t effect_index,
struct hidpp20_rgb_effect_info *info)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = cluster_index,
.msg.parameters[1] = effect_index,
.msg.parameters[2] = HIDPP20_RGB_EFFECTS_TOI_GENERAL,
};
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_RGB_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info->cluster_index = msg.msg.parameters[0];
info->effect_index = msg.msg.parameters[1];
info->effect_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
info->capabilities = get_unaligned_be_u16(&msg.msg.parameters[4]);
info->effect_period = get_unaligned_be_u16(&msg.msg.parameters[6]);
return 0;
}
/* -------------------------------------------------------------------------- */
/* 0x1b04: Special keys and mouse buttons */
/* -------------------------------------------------------------------------- */

View File

@@ -458,6 +458,31 @@ hidpp20_color_led_effect_get_zone_effect_info(struct hidpp20_device *device,
uint8_t zone_effect_index,
struct hidpp20_color_led_zone_effect_info *info);
struct hidpp20_rgb_device_info;
int
hidpp20_rgb_effects_get_device_info(struct hidpp20_device *device,
struct hidpp20_rgb_device_info *info);
struct hidpp20_rgb_cluster_info;
int
hidpp20_rgb_effects_get_cluster_info(struct hidpp20_device *device,
uint8_t cluster_index,
struct hidpp20_rgb_cluster_info *info);
int
hidpp20_rgb_effects_get_cluster_infos(struct hidpp20_device *device,
struct hidpp20_rgb_cluster_info **infos_list);
struct hidpp20_rgb_effect_info;
int
hidpp20_rgb_effects_get_effect_info(struct hidpp20_device *device,
uint8_t cluster_index,
uint8_t effect_index,
struct hidpp20_rgb_effect_info *info);
enum hidpp20_color_led_location {
HIDPP20_COLOR_LED_LOCATION_UNDEFINED = 0,
HIDPP20_COLOR_LED_LOCATION_PRIMARY,
@@ -491,8 +516,30 @@ enum hidpp20_color_led_zone_effect {
HIDPP20_COLOR_LED_ZONE_EFFECT_BOOT_UP = 8,
HIDPP20_COLOR_LED_ZONE_EFFECT_DEMO_MODE = 8,
HIDPP20_COLOR_LED_ZONE_EFFECT_BREATHING = 10,
HIDPP20_COLOR_LED_ZONE_EFFECT_RIPPLE = 11,
HIDPP20_COLOR_LED_ZONE_EFFECT_CUSTOM = 12,
};
/* -------------------------------------------------------------------------- */
/* 0x8071 - RGB Effects */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_RGB_EFFECTS 0x8071
#define HIDPP20_RGB_EFFECTS_INDEX_ALL 0xFF
/* type of info */
#define HIDPP20_RGB_EFFECTS_TOI_GENERAL 0x00
#define HIDPP20_RGB_EFFECTS_TOI_EFFECT 0x01
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_STATE 0x00
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_DEFAULTS 0x01
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_UUID_0_10 0x02
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_UUID_11_16 0x03
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_EFCT_NAME_0_10 0x04
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_EFCT_NAME_11_21 0x05
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_EFCT_NAME_21_31 0x06
/* -------------------------------------------------------------------------- */
/* 0x8100 - Onboard Profiles */
/* -------------------------------------------------------------------------- */
@@ -578,11 +625,6 @@ 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_led_zone_effect_info {
uint8_t zone_index;
uint8_t zone_effect_index;
@@ -592,6 +634,41 @@ struct hidpp20_color_led_zone_effect_info {
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_zone_effect_info) == 8, "Invalid size");
struct hidpp20_rgb_device_info {
uint8_t cluster_index;
uint8_t effect_index;
uint8_t cluster_count;
/* we don't care about NV capabilities for libratbag, they just
* indicate sale demo effects */
uint16_t nv_caps;
uint16_t ext_caps;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_rgb_device_info) == 7, "Invalid size");
struct hidpp20_rgb_cluster_info {
uint8_t index;
uint8_t effect_index;
uint16_t location;
uint16_t num_effects;
uint16_t persistency_caps;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_rgb_cluster_info) == 8, "Invalid size");
struct hidpp20_rgb_effect_info {
uint8_t cluster_index;
uint8_t effect_index;
uint16_t effect_id;
uint16_t capabilities;
uint16_t effect_period;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_rgb_effect_info) == 8, "Invalid size");
union hidpp20_generic_led_zone_info {
struct hidpp20_led_sw_ctrl_led_info* leds;
struct hidpp20_color_led_zone_info* color_leds_8070;
struct hidpp20_rgb_cluster_info* color_leds_8071;
};
struct hidpp20_color {
uint8_t red;
uint8_t green;