From cef532e51e80900de3e5030477b7d9342a979567 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 12 Oct 2015 08:26:50 +1000 Subject: [PATCH 1/7] hidpp10: add 0x07 Battery Status parsing Untested, my M570 just gives me 0, 0, 0 and all the other devices are wired. Signed-off-by: Peter Hutterer --- src/hidpp10.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/hidpp10.h | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/hidpp10.c b/src/hidpp10.c index 2eaed646..38b3ee4d 100644 --- a/src/hidpp10.c +++ b/src/hidpp10.c @@ -325,6 +325,47 @@ hidpp10_set_individual_feature(struct hidpp10_device *dev, return res; } +/* -------------------------------------------------------------------------- */ +/* 0x07: Battery status */ +/* -------------------------------------------------------------------------- */ +#define __CMD_BATTERY_STATUS 0x07 + +#define CMD_BATTERY_STATUS(idx, sub) { \ + .msg = { \ + .report_id = REPORT_ID_SHORT, \ + .device_idx = idx, \ + .sub_id = sub, \ + .address = __CMD_BATTERY_STATUS, \ + .parameters = {0x00, 0x00, 0x00 }, \ + } \ +} + +int +hidpp10_get_battery_status(struct hidpp10_device *dev, + enum hidpp10_battery_level *level, + enum hidpp10_battery_charge_state *charge_state, + uint8_t *low_threshold_in_percent) +{ + unsigned idx = dev->index; + union hidpp10_message battery = CMD_BATTERY_STATUS(idx, GET_REGISTER_REQ); + int res; + + res = hidpp10_request_command(dev, &battery); + + *level = battery.msg.parameters[0]; + *charge_state = battery.msg.parameters[1]; + *low_threshold_in_percent = battery.msg.parameters[2]; + + if (*low_threshold_in_percent >= 7) { + /* reserved value, we just silently truncate it to 0 */ + *low_threshold_in_percent = 0; + } + + *low_threshold_in_percent *= 5; /* in 5% increments */ + + return res; +} + /* -------------------------------------------------------------------------- */ /* 0x0F: Profile queries */ /* -------------------------------------------------------------------------- */ diff --git a/src/hidpp10.h b/src/hidpp10.h index 494290a8..ebbc5aee 100644 --- a/src/hidpp10.h +++ b/src/hidpp10.h @@ -108,6 +108,41 @@ hidpp10_set_individual_feature(struct hidpp10_device *dev, uint8_t feature_bit_r0, uint8_t feature_bit_r2); +/* -------------------------------------------------------------------------- */ +/* 0x07: Battery Status */ +/* -------------------------------------------------------------------------- */ + +enum hidpp10_battery_level { + HIDPP10_BATTERY_LEVEL_UNKNOWN = 0x00, + HIDPP10_BATTERY_LEVEL_CRITICAL = 0x01, + HIDPP10_BATTERY_LEVEL_CRITICAL_LEGACY = 0x02, + HIDPP10_BATTERY_LEVEL_LOW = 0x03, + HIDPP10_BATTERY_LEVEL_LOW_LEGACY = 0x04, + HIDPP10_BATTERY_LEVEL_GOOD = 0x05, + HIDPP10_BATTERY_LEVEL_GOOD_LEGACY = 0x06, + HIDPP10_BATTERY_LEVEL_FULL_LEGACY = 0x07, + /* 0x08..0xFF ... reserved */ +}; + +enum hidpp10_battery_charge_state { + HIDPP10_BATTERY_CHARGE_STATE_NOT_CHARGING = 0x00, + /* 0x01 ... 0x1F ... reserved (not charging) */ + HIDPP10_BATTERY_CHARGE_STATE_UNKNOWN = 0x20, + HIDPP10_BATTERY_CHARGE_STATE_CHARGING = 0x21, + HIDPP10_BATTERY_CHARGE_STATE_CHARGING_COMPLETE = 0x22, + HIDPP10_BATTERY_CHARGE_STATE_CHARGING_ERROR = 0x23, + HIDPP10_BATTERY_CHARGE_STATE_CHARGING_FAST = 0x24, + HIDPP10_BATTERY_CHARGE_STATE_CHARGING_SLOW = 0x25, + HIDPP10_BATTERY_CHARGE_STATE_TOPPING_CHARGE = 0x26, + /* 0x27 .. 0xff ... reserved */ +}; + +int +hidpp10_get_battery_status(struct hidpp10_device *dev, + enum hidpp10_battery_level *level, + enum hidpp10_battery_charge_state *charge_state, + uint8_t *low_threshold_in_percent); + /* -------------------------------------------------------------------------- */ /* 0x0F: Profile queries */ /* -------------------------------------------------------------------------- */ From 3c5443d20310c61d0f04f06dcb9181b0ded8da77 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 12 Oct 2015 10:33:45 +1000 Subject: [PATCH 2/7] hidpp10: Add a note to the 0x57 LED color command Signed-off-by: Peter Hutterer --- src/hidpp10.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hidpp10.h b/src/hidpp10.h index ebbc5aee..e9172fcc 100644 --- a/src/hidpp10.h +++ b/src/hidpp10.h @@ -218,6 +218,9 @@ hidpp10_set_led_status(struct hidpp10_device *dev, /* -------------------------------------------------------------------------- */ /* 0x57: LED Color */ /* -------------------------------------------------------------------------- */ + +/* Note: this changes the color of the LED only, use 0x51 to turn the LED + * on/off */ int hidpp10_get_led_color(struct hidpp10_device *dev, uint8_t *red, From bc7c48ecc0929aa73ec944da13f94c8b53128680 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 12 Oct 2015 10:34:05 +1000 Subject: [PATCH 3/7] hidpp10: implement 0x54 LED intensity Untested, my devices don't handle this request Signed-off-by: Peter Hutterer --- src/hidpp10.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/hidpp10.h | 14 ++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/hidpp10.c b/src/hidpp10.c index 38b3ee4d..0863ca56 100644 --- a/src/hidpp10.c +++ b/src/hidpp10.c @@ -668,6 +668,69 @@ hidpp10_set_led_status(struct hidpp10_device *dev, return res; } +/* -------------------------------------------------------------------------- */ +/* 0x54: LED Intensity */ +/* -------------------------------------------------------------------------- */ + +#define __CMD_LED_INTENSITY 0x54 + +#define CMD_LED_INTENSITY(idx, sub) { \ + .msg = { \ + .report_id = REPORT_ID_SHORT, \ + .device_idx = idx, \ + .sub_id = sub, \ + .address = __CMD_LED_INTENSITY, \ + .parameters = {0x00, 0x00, 0x00 }, \ + } \ +} + +int +hidpp10_get_led_intensity(struct hidpp10_device *dev, + uint8_t led_intensity_in_percent[6]) +{ + unsigned idx = dev->index; + union hidpp10_message led_intensity = CMD_LED_INTENSITY(idx, GET_REGISTER_REQ); + uint8_t *intensity = led_intensity.msg.parameters; + int res; + + log_raw(dev->ratbag_device->ratbag, "Fetching LED intensity\n"); + + res = hidpp10_request_command(dev, &led_intensity); + if (res) + return res; + + led_intensity_in_percent[0] = 10 * ((intensity[0] ) & 0xF); + led_intensity_in_percent[1] = 10 * ((intensity[0] >> 4) & 0xF); + led_intensity_in_percent[2] = 10 * ((intensity[1] ) & 0xF); + led_intensity_in_percent[3] = 10 * ((intensity[1] >> 4) & 0xF); + led_intensity_in_percent[4] = 10 * ((intensity[2] ) & 0xF); + led_intensity_in_percent[5] = 10 * ((intensity[2] >> 4) & 0xF); + + return 0; +} + +int +hidpp10_set_led_intensity(struct hidpp10_device *dev, + const uint8_t led_intensity_in_percent[6]) +{ + unsigned idx = dev->index; + union hidpp10_message led_intensity = CMD_LED_INTENSITY(idx, SET_REGISTER_REQ); + uint8_t *intensity = led_intensity.msg.parameters; + int res; + + log_raw(dev->ratbag_device->ratbag, "Setting LED intensity\n"); + + intensity[0] = led_intensity_in_percent[0]/10 & 0xF; + intensity[0] |= (led_intensity_in_percent[1]/10 & 0xF) << 4; + intensity[1] = led_intensity_in_percent[2]/10 & 0xF; + intensity[1] |= (led_intensity_in_percent[3]/10 & 0xF) << 4; + intensity[2] = led_intensity_in_percent[4]/10 & 0xF; + intensity[2] |= (led_intensity_in_percent[5]/10 & 0xF) << 4; + + res = hidpp10_request_command(dev, &led_intensity); + return res; +} + /* -------------------------------------------------------------------------- */ /* 0x57: LED Color */ /* -------------------------------------------------------------------------- */ diff --git a/src/hidpp10.h b/src/hidpp10.h index e9172fcc..20a7041d 100644 --- a/src/hidpp10.h +++ b/src/hidpp10.h @@ -215,6 +215,20 @@ int hidpp10_set_led_status(struct hidpp10_device *dev, const bool led[4]); +/* -------------------------------------------------------------------------- */ +/* 0x54: LED Intensity */ +/* -------------------------------------------------------------------------- */ + +int +hidpp10_get_led_intensity(struct hidpp10_device *dev, + uint8_t led_intensity_in_percent[6]); + +/* Granularity for the led intensity is 10% increments. A value of 0 leaves + * the intensity unchanged */ +int +hidpp10_set_led_intensity(struct hidpp10_device *dev, + const uint8_t led_intensity_in_percent[6]); + /* -------------------------------------------------------------------------- */ /* 0x57: LED Color */ /* -------------------------------------------------------------------------- */ From 633c5cb106d8b3c5b7084ccd98e48740487b6269 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 12 Oct 2015 10:47:45 +1000 Subject: [PATCH 4/7] hidpp10: extend handling of 0x51 LEDs First: there are 6 LEDs, not just 4. If the LEDs don't exist, the value is UNCHANGED on the read request. The value is more than a simple on/off, though at least the G500s only looks at the lower two bits, so heartbeat and slow off both translate into "on" and blink and slow on both translate to "off". There doesn't seem to be a detection mechanism for what a device really supports. Signed-off-by: Peter Hutterer --- src/hidpp10.c | 40 +++++++++++++++++++++++++++++----------- src/hidpp10.h | 18 +++++++++++++++--- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/hidpp10.c b/src/hidpp10.c index 0863ca56..02313269 100644 --- a/src/hidpp10.c +++ b/src/hidpp10.c @@ -627,10 +627,11 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr int hidpp10_get_led_status(struct hidpp10_device *dev, - bool led[4]) + enum hidpp10_led_status led[6]) { unsigned idx = dev->index; union hidpp10_message led_status = CMD_LED_STATUS(idx, GET_REGISTER_REQ); + uint8_t *status = led_status.msg.parameters; int res; log_raw(dev->ratbag_device->ratbag, "Fetching LED status\n"); @@ -639,30 +640,47 @@ hidpp10_get_led_status(struct hidpp10_device *dev, if (res) return res; - /* each led is 4-bits, 0x1 == off, 0x2 == on */ - led[0] = !!(led_status.msg.parameters[0] & 0x02); /* running man logo */ - led[1] = !!(led_status.msg.parameters[0] & 0x20); /* lowest */ - led[2] = !!(led_status.msg.parameters[1] & 0x02); /* middle */ - led[3] = !!(led_status.msg.parameters[1] & 0x20); /* highest */ + led[0] = status[0] & 0xF; + led[1] = (status[0] >> 4) & 0xF; + led[2] = status[1] & 0xF; + led[3] = (status[1] >> 4) & 0xF; + led[4] = status[2] & 0xF; + led[5] = (status[2] >> 4) & 0xF; return 0; } int hidpp10_set_led_status(struct hidpp10_device *dev, - const bool led[4]) + const enum hidpp10_led_status led[6]) { unsigned idx = dev->index; union hidpp10_message led_status = CMD_LED_STATUS(idx, SET_REGISTER_REQ); + uint8_t *status = led_status.msg.parameters; int res; + int i; log_raw(dev->ratbag_device->ratbag, "Setting LED status\n"); + for (i = 0; i < 6; i++) { + switch (led[i]) { + case HIDPP10_LED_STATUS_NO_CHANGE: + case HIDPP10_LED_STATUS_OFF: + case HIDPP10_LED_STATUS_ON: + case HIDPP10_LED_STATUS_BLINK: + case HIDPP10_LED_STATUS_HEARTBEAT: + case HIDPP10_LED_STATUS_SLOW_ON: + case HIDPP10_LED_STATUS_SLOW_OFF: + break; + default: + abort(); + } + } + /* each led is 4-bits, 0x1 == off, 0x2 == on */ - led_status.msg.parameters[0] |= led[0] ? 0x02 : 0x01; /* running man logo */ - led_status.msg.parameters[0] |= led[1] ? 0x20 : 0x10; /* lowest */ - led_status.msg.parameters[1] |= led[2] ? 0x02 : 0x01; /* middle */ - led_status.msg.parameters[1] |= led[3] ? 0x20 : 0x10; /* highest */ + status[0] = led[0] | (led[1] << 4); + status[1] = led[2] | (led[3] << 4); + status[2] = led[4] | (led[5] << 4); res = hidpp10_request_command(dev, &led_status); return res; diff --git a/src/hidpp10.h b/src/hidpp10.h index 20a7041d..62ca1455 100644 --- a/src/hidpp10.h +++ b/src/hidpp10.h @@ -208,12 +208,24 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, /* -------------------------------------------------------------------------- */ /* 0x51: LED Status */ /* -------------------------------------------------------------------------- */ + +enum hidpp10_led_status { + HIDPP10_LED_STATUS_NO_CHANGE = 0x0, /**< LED does not exist, or + should not change */ + HIDPP10_LED_STATUS_OFF = 0x1, + HIDPP10_LED_STATUS_ON = 0x2, + HIDPP10_LED_STATUS_BLINK = 0x3, + HIDPP10_LED_STATUS_HEARTBEAT = 0x4, + HIDPP10_LED_STATUS_SLOW_ON = 0x5, + HIDPP10_LED_STATUS_SLOW_OFF = 0x6, +}; + int hidpp10_get_led_status(struct hidpp10_device *dev, - bool led[4]); + enum hidpp10_led_status led[6]); int hidpp10_set_led_status(struct hidpp10_device *dev, - const bool led[4]); + const enum hidpp10_led_status led[6]); /* -------------------------------------------------------------------------- */ /* 0x54: LED Intensity */ @@ -306,7 +318,7 @@ struct hidpp10_device { uint8_t build; uint16_t xres, yres; uint16_t refresh_rate; - bool led[4]; + enum hidpp10_led_status led[6]; int8_t current_profile; struct hidpp10_profile profiles[HIDPP10_NUM_PROFILES]; }; From 9617baf6473d4a779ccf07b7e1c81baf7d1bc2dd Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 12 Oct 2015 11:58:10 +1000 Subject: [PATCH 5/7] hidpp10: add a helper function to dump all pages Signed-off-by: Peter Hutterer --- src/hidpp10.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/hidpp10.c b/src/hidpp10.c index 02313269..92d7dce4 100644 --- a/src/hidpp10.c +++ b/src/hidpp10.c @@ -211,6 +211,9 @@ static int hidpp10_read_memory(struct hidpp10_device *dev, uint8_t page, uint8_t offset, uint8_t bytes[16]); +static inline void +hidpp10_dump_all_pages(struct hidpp10_device *dev); + /* -------------------------------------------------------------------------- */ /* 0x00: Enable HID++ Notifications */ /* -------------------------------------------------------------------------- */ @@ -968,6 +971,30 @@ hidpp10_read_memory(struct hidpp10_device *dev, uint8_t page, uint8_t offset, return 0; } +static inline void +hidpp10_dump_all_pages(struct hidpp10_device *dev) +{ + uint16_t page, offset; + uint8_t bytes[16]; + int res; + + log_info(dev->ratbag_device->ratbag, "::::::: Dumping memory :::::\n"); + + for (page = 0; page < 512; page++) { + log_info(dev->ratbag_device->ratbag, ":: page %d\n", page); + for (offset = 0; offset < 256; offset += 16) { + res = hidpp10_read_memory(dev, page, offset, bytes); + if (res != 0) + goto out; + + log_buffer(dev->ratbag_device->ratbag, RATBAG_LOG_PRIORITY_INFO, " ", bytes, ARRAY_LENGTH(bytes)); + } + } + +out: + log_info(dev->ratbag_device->ratbag, "::::::: Dumping memory complete :::::\n"); +} + /* -------------------------------------------------------------------------- */ /* 0xB2: Device Connection and Disconnection (Pairing) */ /* -------------------------------------------------------------------------- */ From 7003fd567c9b5f5014f3e9572a9076e4baf96998 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 12 Oct 2015 14:22:53 +1000 Subject: [PATCH 6/7] hidpp10: implement 0x0D Battery Mileage Signed-off-by: Peter Hutterer --- src/hidpp10.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/hidpp10.h | 9 +++++++ 2 files changed, 78 insertions(+) diff --git a/src/hidpp10.c b/src/hidpp10.c index 92d7dce4..d04a9236 100644 --- a/src/hidpp10.c +++ b/src/hidpp10.c @@ -369,6 +369,75 @@ hidpp10_get_battery_status(struct hidpp10_device *dev, return res; } +/* -------------------------------------------------------------------------- */ +/* 0x0D: Battery mileage */ +/* -------------------------------------------------------------------------- */ +#define __CMD_BATTERY_MILEAGE 0x0D + +#define CMD_BATTERY_MILEAGE(idx, sub) { \ + .msg = { \ + .report_id = REPORT_ID_SHORT, \ + .device_idx = idx, \ + .sub_id = sub, \ + .address = __CMD_BATTERY_MILEAGE, \ + .parameters = {0x00, 0x00, 0x00 }, \ + } \ +} + +int +hidpp10_get_battery_mileage(struct hidpp10_device *dev, + uint8_t *level_in_percent, + uint32_t *max_seconds, + enum hidpp10_battery_charge_state *state) +{ + unsigned idx = dev->index; + union hidpp10_message battery = CMD_BATTERY_MILEAGE(idx, GET_REGISTER_REQ); + int res; + int max; + + res = hidpp10_request_command(dev, &battery); + + *level_in_percent = battery.msg.parameters[0] & 0x7F; + + max = battery.msg.parameters[1]; + max |= (battery.msg.parameters[2] & 0xF) << 8; + + switch((battery.msg.parameters[2] & 0x30) >> 4) { + case 0x03: /* days */ + max *= 24; + + /* fallthrough */ + case 0x02: /* hours */ + max *= 60; + + /* fallthrough */ + case 0x01: /* min */ + max *= 60; + break; + case 0x00: /* seconds */ + break; + } + + *max_seconds = max; + + switch(battery.msg.parameters[2] >> 6) { + case 0x00: + *state = HIDPP10_BATTERY_CHARGE_STATE_NOT_CHARGING; + break; + case 0x01: + *state = HIDPP10_BATTERY_CHARGE_STATE_CHARGING; + break; + case 0x02: + *state = HIDPP10_BATTERY_CHARGE_STATE_CHARGING_COMPLETE; + break; + case 0x03: + *state = HIDPP10_BATTERY_CHARGE_STATE_CHARGING_ERROR; + break; + } + + return res; +} + /* -------------------------------------------------------------------------- */ /* 0x0F: Profile queries */ /* -------------------------------------------------------------------------- */ diff --git a/src/hidpp10.h b/src/hidpp10.h index 62ca1455..6466c1c4 100644 --- a/src/hidpp10.h +++ b/src/hidpp10.h @@ -142,6 +142,15 @@ hidpp10_get_battery_status(struct hidpp10_device *dev, enum hidpp10_battery_level *level, enum hidpp10_battery_charge_state *charge_state, uint8_t *low_threshold_in_percent); +/* -------------------------------------------------------------------------- */ +/* 0x0D: Battery Mileage */ +/* -------------------------------------------------------------------------- */ + +int +hidpp10_get_battery_mileage(struct hidpp10_device *dev, + uint8_t *level_in_percent, + uint32_t *max_seconds, + enum hidpp10_battery_charge_state *state); /* -------------------------------------------------------------------------- */ /* 0x0F: Profile queries */ From 1b6e65bf8882bc133eb2ba48e3d373d3fc0286dd Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 13 Oct 2015 11:42:15 +1000 Subject: [PATCH 7/7] hidpp10: implement 0x64 set usb refresh rate call Signed-off-by: Peter Hutterer --- src/hidpp10.c | 14 ++++++++++++++ src/hidpp10.h | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/hidpp10.c b/src/hidpp10.c index d04a9236..2ec908d4 100644 --- a/src/hidpp10.c +++ b/src/hidpp10.c @@ -1001,6 +1001,20 @@ hidpp10_get_usb_refresh_rate(struct hidpp10_device *dev, return 0; } +int +hidpp10_set_usb_refresh_rate(struct hidpp10_device *dev, + uint16_t rate) +{ + unsigned idx = dev->index; + union hidpp10_message refresh = CMD_USB_REFRESH_RATE(idx, GET_REGISTER_REQ); + + log_raw(dev->ratbag_device->ratbag, "Setting USB refresh rate\n"); + + refresh.msg.parameters[0] = 1000/rate; + + return hidpp10_request_command(dev, &refresh); +} + /* -------------------------------------------------------------------------- */ /* 0xA2: Reading memory */ /* -------------------------------------------------------------------------- */ diff --git a/src/hidpp10.h b/src/hidpp10.h index 6466c1c4..239c1cd0 100644 --- a/src/hidpp10.h +++ b/src/hidpp10.h @@ -290,6 +290,10 @@ int hidpp10_get_usb_refresh_rate(struct hidpp10_device *dev, uint16_t *rate); +int +hidpp10_set_usb_refresh_rate(struct hidpp10_device *dev, + uint16_t rate); + /* -------------------------------------------------------------------------- */ /* 0xF1: Device Firmware Information */ /* -------------------------------------------------------------------------- */