From fdd47609cf80830792eb120719f2b1f47c083452 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 20 Aug 2015 15:32:21 -0400 Subject: [PATCH] hidpp20: implement 0x2201, adjustable dpi only the getter right now. Signed-off-by: Benjamin Tissoires --- src/driver-logitech-unifying.c | 23 ++++- src/hidpp20.c | 163 +++++++++++++++++++++++++++++++++ src/hidpp20.h | 26 ++++++ 3 files changed, 210 insertions(+), 2 deletions(-) diff --git a/src/driver-logitech-unifying.c b/src/driver-logitech-unifying.c index d5be4860..76483348 100644 --- a/src/driver-logitech-unifying.c +++ b/src/driver-logitech-unifying.c @@ -49,6 +49,7 @@ #include "libratbag-hidraw.h" #define HIDPP_CAP_RESOLUTION_2200 (1 << 0) +#define HIDPP_CAP_SWITCHABLE_RESOLUTION_2201 (1 << 1) struct unifying_data { unsigned proto_major; @@ -100,6 +101,7 @@ static int unifying_init_feature(struct ratbag_device *device, uint16_t feature) { struct unifying_data *drv_data = ratbag_get_drv_data(device); + struct ratbag *ratbag = device->ratbag; int rc; switch (feature) { @@ -115,11 +117,28 @@ unifying_init_feature(struct ratbag_device *device, uint16_t feature) rc = hidpp20_mousepointer_get_mousepointer_info(device, &resolution, &flags); if (rc) return rc; - log_info(device->ratbag, "device is at %d dpi\n", resolution); + log_info(ratbag, "device is at %d dpi\n", resolution); + break; + } + case 0x2201: { + struct hidpp20_sensor *sensors; + + log_info(ratbag, "device has adjustable dpi\n"); + rc = hidpp20_adjustable_dpi_get_sensors(device, &sensors); + if (rc < 0) + return rc; + if (rc > 0) + log_info(ratbag, + "device is at %d dpi (variable between %d and %d).\n", + sensors[0].dpi, + sensors[0].dpi_min, + sensors[0].dpi_max); + free(sensors); + drv_data->capabilities |= HIDPP_CAP_SWITCHABLE_RESOLUTION_2201; break; } default: - log_info(device->ratbag, "unknown feature 0x%04x\n", feature); + log_debug(device->ratbag, "unknown feature 0x%04x\n", feature); } return 0; } diff --git a/src/hidpp20.c b/src/hidpp20.c index a074adbf..35bf7871 100644 --- a/src/hidpp20.c +++ b/src/hidpp20.c @@ -29,6 +29,7 @@ * https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing */ +#include #include #include #include @@ -119,6 +120,12 @@ out_err: return ret; } +static inline uint16_t +hidpp20_get_unaligned_u16(uint8_t *buf) +{ + return (buf[0] << 8) | buf[1]; +} + /* -------------------------------------------------------------------------- */ /* 0x0000: Root */ /* -------------------------------------------------------------------------- */ @@ -330,3 +337,159 @@ hidpp20_mousepointer_get_mousepointer_info(struct ratbag_device *device, return 0; } + +/* -------------------------------------------------------------------------- */ +/* 0x2201: Adjustable DPI */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_ADJUSTABLE_DPI 0x2201 + +#define CMD_ADJUSTABLE_DPI_GET_SENSOR_COUNT 0x08 +#define CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI_LIST 0x18 +#define CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI 0x28 +#define CMD_ADJUSTABLE_DPI_SET_SENSOR_DPI 0x38 + +static int +hidpp20_adjustable_dpi_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_ADJUSTABLE_DPI_GET_SENSOR_COUNT, + }; + + rc = hidpp20_request_command(device, &msg); + if (rc) + return rc; + + return msg.msg.parameters[0]; +} + +static int +hidpp20_adjustable_dpi_get_dpi_list(struct ratbag_device *device, + uint8_t reg, + struct hidpp20_sensor *sensor) +{ + int rc; + unsigned i = 1, dpi_index = 0; + union hidpp20_message msg = { + .msg.report_id = REPORT_ID_LONG, + .msg.device_idx = 0xff, + .msg.sub_id = reg, + .msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI_LIST, + .msg.parameters[0] = sensor->index, + }; + + rc = hidpp20_request_command(device, &msg); + if (rc) + return rc; + + sensor->dpi_min = 0xffff; + + sensor->index = msg.msg.parameters[0]; + while (i < LONG_MESSAGE_LENGTH - 1 && + hidpp20_get_unaligned_u16(&msg.msg.parameters[i]) != 0) { + uint16_t value = hidpp20_get_unaligned_u16(&msg.msg.parameters[i]); + + if (value > 0xe000) { + sensor->dpi_steps = value - 0xe000; + } else { + sensor->dpi_min = value < sensor->dpi_min ? value : sensor->dpi_min; + sensor->dpi_max = value > sensor->dpi_max ? value : sensor->dpi_max; + sensor->dpi_list[dpi_index++] = value; + } + assert(sensor->dpi_list[dpi_index] == 0x0000); + i += 2; + } + + return 0; +} + + +static int +hidpp20_adjustable_dpi_get_dpi(struct ratbag_device *device, + uint8_t reg, + struct hidpp20_sensor *sensor) +{ + int rc; + union hidpp20_message msg = { + .msg.report_id = REPORT_ID_LONG, + .msg.device_idx = 0xff, + .msg.sub_id = reg, + .msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI, + .msg.parameters[0] = sensor->index, + }; + + rc = hidpp20_request_command(device, &msg); + if (rc) + return rc; + + sensor->dpi = hidpp20_get_unaligned_u16(&msg.msg.parameters[1]); + sensor->default_dpi = hidpp20_get_unaligned_u16(&msg.msg.parameters[3]); + + return 0; +} + +int hidpp20_adjustable_dpi_get_sensors(struct ratbag_device *device, + struct hidpp20_sensor **sensors_list) +{ + uint8_t feature_index, feature_type, feature_version; + struct hidpp20_sensor *s_list, *sensor; + uint8_t num_sensors; + unsigned i; + int rc; + + + rc = hidpp_root_get_feature(device, + HIDPP_PAGE_ADJUSTABLE_DPI, + &feature_index, + &feature_type, + &feature_version); + if (rc) + return rc; + + rc = hidpp20_adjustable_dpi_get_count(device, feature_index); + if (rc < 0) + return rc; + + num_sensors = rc; + if (num_sensors == 0) { + *sensors_list = NULL; + return 0; + } + + s_list = zalloc(num_sensors * sizeof(struct hidpp20_sensor)); + if (!s_list) + return -ENOMEM; + + for (i = 0; i < num_sensors; i++) { + sensor = &s_list[i]; + sensor->index = i; + rc = hidpp20_adjustable_dpi_get_dpi_list(device, + feature_index, + sensor); + if (rc) + goto err; + + rc = hidpp20_adjustable_dpi_get_dpi(device, feature_index, sensor); + if (rc) + goto err; + + log_debug(device->ratbag, + "sensor %d: current dpi: %d (default: %d) min: %d max: %d steps: %d\n", + sensor->index, + sensor->dpi, + sensor->default_dpi, + sensor->dpi_min, + sensor->dpi_max, + sensor->dpi_steps); + } + + *sensors_list = s_list; + return num_sensors; +err: + free(s_list); + return rc; +} diff --git a/src/hidpp20.h b/src/hidpp20.h index 54917341..ae0b7906 100644 --- a/src/hidpp20.h +++ b/src/hidpp20.h @@ -94,4 +94,30 @@ int hidpp20_mousepointer_get_mousepointer_info(struct ratbag_device *device, uint16_t *resolution, uint8_t *flags); +/* -------------------------------------------------------------------------- */ +/* 0x2201: Adjustable DPI */ +/* -------------------------------------------------------------------------- */ + +/** + * either dpi_steps is not null or the values are stored in the null terminated + * array dpi_list. + */ +struct hidpp20_sensor { + uint8_t index; + uint16_t dpi; + uint16_t dpi_min; + uint16_t dpi_max; + uint16_t dpi_steps; + uint16_t default_dpi; + uint16_t dpi_list[LONG_MESSAGE_LENGTH / 2 + 1]; +}; + +/** + * allocates a list of sensors that has to be freed by the caller. + * + * returns the elements in the list or a negative error + */ +int hidpp20_adjustable_dpi_get_sensors(struct ratbag_device *device, + struct hidpp20_sensor **sensors_list); + #endif /* HIDPP_20_H */