From 9754810c38b4b903a965a291a1fb0ca5009e32e9 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 20 Aug 2015 14:12:56 -0400 Subject: [PATCH] unifying: retrieve the list of features available on HID++ 2.0 devices Signed-off-by: Benjamin Tissoires --- src/driver-logitech-unifying.c | 31 +++++++- src/hidpp20.c | 130 +++++++++++++++++++++++++++++++++ src/hidpp20.h | 20 +++++ 3 files changed, 180 insertions(+), 1 deletion(-) diff --git a/src/driver-logitech-unifying.c b/src/driver-logitech-unifying.c index 6d284e33..3cdbaee9 100644 --- a/src/driver-logitech-unifying.c +++ b/src/driver-logitech-unifying.c @@ -93,6 +93,29 @@ unifying_write_profile(struct ratbag_profile *profile) return -1; } +static int +unifying_20_probe(struct ratbag_device *device, const struct ratbag_id id) +{ + struct hidpp20_feature *feature_list; + int rc, i; + + rc = hidpp20_feature_set_get(device, &feature_list); + if (rc < 0) + return rc; + + if (rc > 0) { + log_info(device->ratbag, "'%s' has %d features:\n", ratbag_device_get_name(device), rc); + for (i = 0; i < rc; i++) { + log_info(device->ratbag, "0x%04x\n", feature_list[i].feature); + } + } + + free(feature_list); + + return 0; + +} + static int unifying_probe(struct ratbag_device *device, const struct ratbag_id id) { @@ -122,10 +145,16 @@ unifying_probe(struct ratbag_device *device, const struct ratbag_id id) goto err; } - log_info(device->ratbag, "'%s' is using protocol v%d.%d\n", ratbag_device_get_name(device), drv_data->proto_major, drv_data->proto_minor); + log_debug(device->ratbag, "'%s' is using protocol v%d.%d\n", ratbag_device_get_name(device), drv_data->proto_major, drv_data->proto_minor); ratbag_set_drv_data(device, drv_data); + if (drv_data->proto_major >= 2) { + rc = unifying_20_probe(device, id); + if (rc) + goto err; + } + device->num_profiles = 1; device->num_buttons = 8; diff --git a/src/hidpp20.c b/src/hidpp20.c index 6d23e5de..fa1111fa 100644 --- a/src/hidpp20.c +++ b/src/hidpp20.c @@ -129,6 +129,35 @@ out_err: #define CMD_ROOT_GET_FEATURE 0x08 #define CMD_ROOT_GET_PROTOCOL_VERSION 0x18 +int +hidpp_root_get_feature(struct ratbag_device *device, + uint16_t feature, + uint8_t *feature_index, + uint8_t *feature_type, + uint8_t *feature_version) +{ + int rc; + union hidpp20_message msg = { + .msg.report_id = REPORT_ID_LONG, + .msg.device_idx = 0xff, + .msg.sub_id = HIDPP_PAGE_ROOT_IDX, + .msg.address = CMD_ROOT_GET_FEATURE, + .msg.parameters[0] = feature >> 8, + .msg.parameters[1] = feature & 0xff, + }; + + rc = hidpp20_request_command(device, &msg); + if (rc) + return rc; + + *feature_index = msg.msg.parameters[0]; + *feature_type = msg.msg.parameters[1]; + *feature_version = msg.msg.parameters[2]; + + log_debug(device->ratbag, "feature 0x%04x is at 0x%02x\n", feature, *feature_index); + return 0; +} + int hidpp20_root_get_protocol_version(struct ratbag_device *device, unsigned *major, @@ -158,3 +187,104 @@ hidpp20_root_get_protocol_version(struct ratbag_device *device, return rc; } +/* -------------------------------------------------------------------------- */ +/* 0x0001: Feature Set */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_FEATURE_SET 0x0001 + +#define CMD_FEATURE_SET_GET_COUNT 0x08 +#define CMD_FEATURE_SET_GET_FEATURE_ID 0x18 + +static int +hidpp20_feature_set_get_count(struct ratbag_device *device, uint8_t reg) +{ + int rc; + union hidpp20_message msg = { + .msg.report_id = REPORT_ID_LONG, + .msg.device_idx = 0xff, + .msg.sub_id = reg, + .msg.address = CMD_FEATURE_SET_GET_COUNT, + }; + + rc = hidpp20_request_command(device, &msg); + if (rc) + return rc; + + return msg.msg.parameters[0]; +} + +static int +hidpp20_feature_set_get_feature_id(struct ratbag_device *device, + uint8_t reg, + uint8_t feature_index, + uint16_t *feature, + uint8_t *type) +{ + int rc; + union hidpp20_message msg = { + .msg.report_id = REPORT_ID_LONG, + .msg.device_idx = 0xff, + .msg.sub_id = reg, + .msg.address = CMD_FEATURE_SET_GET_FEATURE_ID, + .msg.parameters[0] = feature_index, + }; + + rc = hidpp20_request_command(device, &msg); + if (rc) + return rc; + + *feature = (msg.msg.parameters[0] << 8) | msg.msg.parameters[1]; + *type = msg.msg.parameters[2]; + + return 0; +} + +int hidpp20_feature_set_get(struct ratbag_device *device, + struct hidpp20_feature **feature_list) +{ + uint8_t feature_index, feature_type, feature_version; + struct hidpp20_feature *flist; + int rc; + uint8_t feature_count; + unsigned int i; + + rc = hidpp_root_get_feature(device, + HIDPP_PAGE_FEATURE_SET, + &feature_index, + &feature_type, + &feature_version); + if (rc) + return rc; + + rc = hidpp20_feature_set_get_count(device, feature_index); + if (rc < 0) + return rc; + + feature_count = (uint8_t)rc; + + if (!feature_count) { + *feature_list = NULL; + return 0; + } + + flist = zalloc(feature_count * sizeof(struct hidpp20_feature)); + if (!flist) + return -ENOMEM; + + for (i = 0; i < feature_count; i++) { + rc = hidpp20_feature_set_get_feature_id(device, + feature_index, + i, + &flist[i].feature, + &flist[i].type); + if (rc) + goto err; + } + + *feature_list = flist; + return feature_count; +err: + free(flist); + return rc; +} diff --git a/src/hidpp20.h b/src/hidpp20.h index c4e834ef..40be3bcd 100644 --- a/src/hidpp20.h +++ b/src/hidpp20.h @@ -52,9 +52,29 @@ int hidpp20_request_command(struct ratbag_device *device, union hidpp20_message /* 0x0000: Root */ /* -------------------------------------------------------------------------- */ +int hidpp_root_get_feature(struct ratbag_device *device, + uint16_t feature, + uint8_t *feature_index, + uint8_t *feature_type, + uint8_t *feature_version); int hidpp20_root_get_protocol_version(struct ratbag_device *device, unsigned *major, unsigned *minor); +/* -------------------------------------------------------------------------- */ +/* 0x0001: Feature Set */ +/* -------------------------------------------------------------------------- */ +struct hidpp20_feature { + uint16_t feature; + uint8_t type; +}; + +/** + * allocates a list of features that has to be freed by the caller. + * + * returns the elements in the list or a negative error + */ +int hidpp20_feature_set_get(struct ratbag_device *device, + struct hidpp20_feature **feature_list); #endif /* HIDPP_20_H */