All of lore.kernel.org
 help / color / mirror / Atom feed
From: Benjamin Tissoires <benjamin.tissoires@redhat.com>
To: Jiri Kosina <jikos@kernel.org>, Bastien Nocera <hadess@hadess.net>
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v3 18/19] HID: logitech-hidpp: enable HID++ 1.0 battery reporting
Date: Mon, 27 Mar 2017 16:59:38 +0200	[thread overview]
Message-ID: <20170327145939.29824-19-benjamin.tissoires@redhat.com> (raw)
In-Reply-To: <20170327145939.29824-1-benjamin.tissoires@redhat.com>

Also enable battery reporting for HID++ 1.0 devices through 2 registers:
0x07: battery status -> reports only 4 levels (critical, low, good, full)
0x0D: battery mileage -> reports true pourcentage

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v3:
- differentiate between level and mileage
- better online reporting

no changes in v2
---
 drivers/hid/hid-logitech-hidpp.c | 234 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 230 insertions(+), 4 deletions(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index bb40e5d..eb4339e 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -400,6 +400,211 @@ static void hidpp_prefix_name(char **name, int name_length)
 #define HIDPP_SET_LONG_REGISTER				0x82
 #define HIDPP_GET_LONG_REGISTER				0x83
 
+#define HIDPP_REG_GENERAL				0x00
+
+static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
+{
+	struct hidpp_report response;
+	int ret;
+	u8 params[3] = { 0 };
+
+	ret = hidpp_send_rap_command_sync(hidpp_dev,
+					REPORT_ID_HIDPP_SHORT,
+					HIDPP_GET_REGISTER,
+					HIDPP_REG_GENERAL,
+					NULL, 0, &response);
+	if (ret)
+		return ret;
+
+	memcpy(params, response.rap.params, 3);
+
+	/* Set the battery bit */
+	params[0] |= BIT(4);
+
+	return hidpp_send_rap_command_sync(hidpp_dev,
+					REPORT_ID_HIDPP_SHORT,
+					HIDPP_SET_REGISTER,
+					HIDPP_REG_GENERAL,
+					params, 3, &response);
+}
+
+#define HIDPP_REG_BATTERY_STATUS			0x07
+
+static int hidpp10_battery_status_map_level(u8 param)
+{
+	int level;
+
+	switch (param) {
+	case 1 ... 2:
+		level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+		break;
+	case 3 ... 4:
+		level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+		break;
+	case 5 ... 6:
+		level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+		break;
+	case 7:
+		level = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
+		break;
+	default:
+		level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+	}
+
+	return level;
+}
+
+static int hidpp10_battery_status_map_status(u8 param)
+{
+	int status;
+
+	switch (param) {
+	case 0x00:
+		/* discharging (in use) */
+		status = POWER_SUPPLY_STATUS_DISCHARGING;
+		break;
+	case 0x21: /* (standard) charging */
+	case 0x24: /* fast charging */
+	case 0x25: /* slow charging */
+		status = POWER_SUPPLY_STATUS_CHARGING;
+		break;
+	case 0x26: /* topping charge */
+	case 0x22: /* charge complete */
+		status = POWER_SUPPLY_STATUS_FULL;
+		break;
+	case 0x20: /* unknown */
+		status = POWER_SUPPLY_STATUS_UNKNOWN;
+		break;
+	/*
+	 * 0x01...0x1F = reserved (not charging)
+	 * 0x23 = charging error
+	 * 0x27..0xff = reserved
+	 */
+	default:
+		status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+		break;
+	}
+
+	return status;
+}
+
+static int hidpp10_query_battery_status(struct hidpp_device *hidpp)
+{
+	struct hidpp_report response;
+	int ret, status;
+
+	ret = hidpp_send_rap_command_sync(hidpp,
+					REPORT_ID_HIDPP_SHORT,
+					HIDPP_GET_REGISTER,
+					HIDPP_REG_BATTERY_STATUS,
+					NULL, 0, &response);
+	if (ret)
+		return ret;
+
+	hidpp->battery.level =
+		hidpp10_battery_status_map_level(response.rap.params[0]);
+	status = hidpp10_battery_status_map_status(response.rap.params[1]);
+	hidpp->battery.status = status;
+	/* the capacity is only available when discharging or full */
+	hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
+				status == POWER_SUPPLY_STATUS_FULL;
+
+	return 0;
+}
+
+#define HIDPP_REG_BATTERY_MILEAGE			0x0D
+
+static int hidpp10_battery_mileage_map_status(u8 param)
+{
+	int status;
+
+	switch (param >> 6) {
+	case 0x00:
+		/* discharging (in use) */
+		status = POWER_SUPPLY_STATUS_DISCHARGING;
+		break;
+	case 0x01: /* charging */
+		status = POWER_SUPPLY_STATUS_CHARGING;
+		break;
+	case 0x02: /* charge complete */
+		status = POWER_SUPPLY_STATUS_FULL;
+		break;
+	/*
+	 * 0x03 = charging error
+	 */
+	default:
+		status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+		break;
+	}
+
+	return status;
+}
+
+static int hidpp10_query_battery_mileage(struct hidpp_device *hidpp)
+{
+	struct hidpp_report response;
+	int ret, status;
+
+	ret = hidpp_send_rap_command_sync(hidpp,
+					REPORT_ID_HIDPP_SHORT,
+					HIDPP_GET_REGISTER,
+					HIDPP_REG_BATTERY_MILEAGE,
+					NULL, 0, &response);
+	if (ret)
+		return ret;
+
+	hidpp->battery.capacity = response.rap.params[0];
+	status = hidpp10_battery_mileage_map_status(response.rap.params[2]);
+	hidpp->battery.status = status;
+	/* the capacity is only available when discharging or full */
+	hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
+				status == POWER_SUPPLY_STATUS_FULL;
+
+	return 0;
+}
+
+static int hidpp10_battery_event(struct hidpp_device *hidpp, u8 *data, int size)
+{
+	struct hidpp_report *report = (struct hidpp_report *)data;
+	int status, capacity, level;
+	bool changed;
+
+	if (report->report_id != REPORT_ID_HIDPP_SHORT)
+		return 0;
+
+	switch (report->rap.sub_id) {
+	case HIDPP_REG_BATTERY_STATUS:
+		capacity = hidpp->battery.capacity;
+		level = hidpp10_battery_status_map_level(report->rawbytes[1]);
+		status = hidpp10_battery_status_map_status(report->rawbytes[2]);
+		break;
+	case HIDPP_REG_BATTERY_MILEAGE:
+		capacity = report->rap.params[0];
+		level = hidpp->battery.level;
+		status = hidpp10_battery_mileage_map_status(report->rawbytes[3]);
+		break;
+	default:
+		return 0;
+	}
+
+	changed = capacity != hidpp->battery.capacity ||
+		  level != hidpp->battery.level ||
+		  status != hidpp->battery.status;
+
+	/* the capacity is only available when discharging or full */
+	hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
+				status == POWER_SUPPLY_STATUS_FULL;
+
+	if (changed) {
+		hidpp->battery.level = level;
+		hidpp->battery.status = status;
+		if (hidpp->battery.ps)
+			power_supply_changed(hidpp->battery.ps);
+	}
+
+	return 0;
+}
+
 #define HIDPP_REG_PAIRING_INFORMATION			0xB5
 #define HIDPP_EXTENDED_PAIRING				0x30
 #define HIDPP_DEVICE_NAME				0x40
@@ -2428,6 +2633,12 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
 			return ret;
 	}
 
+	if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
+		ret = hidpp10_battery_event(hidpp, data, size);
+		if (ret != 0)
+			return ret;
+	}
+
 	return 0;
 }
 
@@ -2505,7 +2716,16 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
 			return ret;
 		hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP20_BATTERY;
 	} else {
-		return -ENOENT;
+		ret = hidpp10_query_battery_status(hidpp);
+		if (ret) {
+			ret = hidpp10_query_battery_mileage(hidpp);
+			if (ret)
+				return -ENOENT;
+			hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_MILEAGE;
+		} else {
+			hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS;
+		}
+		hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP10_BATTERY;
 	}
 
 	battery_props = devm_kmemdup(&hidpp->hid_dev->dev,
@@ -2665,11 +2885,17 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
 	hidpp_initialize_battery(hidpp);
 
 	/* forward current battery state */
-	if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) {
+	if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
+		hidpp10_enable_battery_reporting(hidpp);
+		if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE)
+			hidpp10_query_battery_mileage(hidpp);
+		else
+			hidpp10_query_battery_status(hidpp);
+	} else if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) {
 		hidpp20_query_battery_info(hidpp);
-		if (hidpp->battery.ps)
-			power_supply_changed(hidpp->battery.ps);
 	}
+	if (hidpp->battery.ps)
+		power_supply_changed(hidpp->battery.ps);
 
 	if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)
 		/* if the input nodes are already created, we can stop now */
-- 
2.9.3

  parent reply	other threads:[~2017-03-27 15:02 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-03-27 14:59 [PATCH v3 00/19] Report power supply from hid-logitech-hidpp Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 01/19] HID: logitech-dj: allow devices to request full pairing information Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 02/19] HID: logitech-hidpp: Add scope to battery Benjamin Tissoires
2017-04-06  9:48   ` Jiri Kosina
2017-04-06  9:57     ` Benjamin Tissoires
2017-04-06 18:34       ` Jiri Kosina
2017-03-27 14:59 ` [PATCH v3 03/19] HID: logitech-hidpp: make sure we only register one battery per device Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 04/19] HID: logitech-hidpp: do not query the name through HID++ for 1.0 devices Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 05/19] HID: logitech-hidpp: create a capabilities bits field Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 06/19] HID: logitech-hidpp: rework probe path for unifying devices Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 07/19] HID: logitech-hidpp: retrieve the HID++ device name when available Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 08/19] HID: logitech-hidpp: rework hidpp_connect_event() Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 09/19] HID: logitech-hidpp: handle battery events in hidpp_raw_hidpp_event() Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 10/19] HID: logitech-hidpp: forward device info in power_supply Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 11/19] HID: logitech-hidpp: create the battery for all types of HID++ devices Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 12/19] HID: logitech-hidpp: return an error if the queried feature is not present Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 13/19] HID: logitech-hidpp: notify battery on connect Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 14/19] HID: logitech-hidpp: battery: provide ONLINE property Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 15/19] HID: logitech-hidpp: rename battery level into capacity Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 16/19] HID: logitech-hidpp: battery: provide CAPACITY_LEVEL Benjamin Tissoires
2017-03-27 14:59 ` [PATCH v3 17/19] HID: logitech-hidpp: add support for battery status for the K750 Benjamin Tissoires
2017-03-27 14:59 ` Benjamin Tissoires [this message]
2017-03-27 14:59 ` [PATCH v3 19/19] HID: logitech-hidpp: add a sysfs file to tell we support power_supply Benjamin Tissoires
2017-03-27 16:23 ` [PATCH v3 00/19] Report power supply from hid-logitech-hidpp Bastien Nocera
2017-06-01 18:06 ` Dave Hansen
2017-06-01 19:26   ` Bastien Nocera
2017-06-02  7:29     ` Benjamin Tissoires
2017-06-02 12:54       ` Bastien Nocera
2017-06-02 13:47         ` Benjamin Tissoires
2017-06-02 14:10       ` Dave Hansen
2017-06-05  8:01         ` Jiri Kosina
2017-06-05 13:09           ` Bastien Nocera
2017-06-05 14:53             ` Dave Hansen
2017-06-05 16:22               ` Bastien Nocera
2017-06-06  7:25           ` Benjamin Tissoires
2017-06-06  7:46             ` Bastien Nocera

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170327145939.29824-19-benjamin.tissoires@redhat.com \
    --to=benjamin.tissoires@redhat.com \
    --cc=hadess@hadess.net \
    --cc=jikos@kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.