Received: by 2002:a05:6a10:5bc5:0:0:0:0 with SMTP id os5csp883315pxb; Wed, 27 Oct 2021 14:25:19 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwc92BF10O4lQpHewOgeBPyTBjT9Z69OfR4Bx1U5m2eEeGSOfLHEhICxee5RjiAqEndQKD2 X-Received: by 2002:a05:6402:1711:: with SMTP id y17mr515119edu.96.1635369919504; Wed, 27 Oct 2021 14:25:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1635369919; cv=none; d=google.com; s=arc-20160816; b=PTMZqoWmBBcOH4mDc83GNm4PuLzkr8bjRGBqLr2NBPOcFb1AB8rsvleTP07Y5QtCOG suHWXAQoE8LxAYlUj26hQkTrI+gALml4wlc5m9cgx7j+0S8Lk7u8334vcre5MSvpOkCo ZmpcshvNVBRGFuvxdOg+S56yvNsdICW2zLaZWAk/lXgrJrtdCEMZ94d66D3Bc+jgZxHG 7fH/wDOljXevv5c21TNKIBf31KBKYtVXabyE2QTD47YEx6OWrQTuAK6HzG2T+CVHBUM7 jJPWpV+ipVOQ+j5Sx3b0DRQUphj7XbI/fWJD7I2KQgZ5MNjWT9nQpdGO1h3dETGOSXTN D55Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=3w+WzDUEpv+cVN24b0C8mckcLoQj3x7mxsO05G0GWmo=; b=0JevWnEv8MDDKB+CI7t0mCEK9ZI758jPrBlQFT+FDJtmYPd7qUxF0KhxZn8Ocz0CHq imcP8xv0tIfq468WQeolOUfq2wliMYo4SI0ncxIyk2V4WFRzu5FFNfuU0Jgr+GRuDTof 2qdWYGooZtVFraZrwn6J+4zUoMj/r3MLHmUl1oPDz5MmoBKon9vEbBLzhMw3dplTu+5J BulBC4ODtJtNNENaURM6Qcg3WNabhrpXjQkfwtJsQsSWQuU4Ik+qr7m1XMbKz6DRkobU i/Q5XZR6yGKd+jev+QonTwjxTMROo1oHMQxDQppi4sysMfD8mJexaUg8aj2t34j4u2n2 05aA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id hd20si1434605ejc.747.2021.10.27.14.24.54; Wed, 27 Oct 2021 14:25:19 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241337AbhJ0KNj (ORCPT + 97 others); Wed, 27 Oct 2021 06:13:39 -0400 Received: from 82-65-109-163.subs.proxad.net ([82.65.109.163]:50224 "EHLO luna.linkmauve.fr" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241326AbhJ0KNS (ORCPT ); Wed, 27 Oct 2021 06:13:18 -0400 Received: by luna.linkmauve.fr (Postfix, from userid 1000) id 55C5EF40CA7; Wed, 27 Oct 2021 12:10:49 +0200 (CEST) From: Emmanuel Gil Peyrot To: Jiri Kosina Cc: Emmanuel Gil Peyrot , linux-input@vger.kernel.org, Ash Logan , =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= , =?UTF-8?q?Barnab=C3=A1s=20P=C5=91cze?= , Benjamin Tissoires , linux-kernel@vger.kernel.org, "Daniel J . Ogorchock" Subject: [PATCH v5 5/5] HID: nintendo: drc: add battery reporting Date: Wed, 27 Oct 2021 12:10:43 +0200 Message-Id: <20211027101043.31609-6-linkmauve@linkmauve.fr> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211027101043.31609-1-linkmauve@linkmauve.fr> References: <20211019110418.26874-1-linkmauve@linkmauve.fr> <20211027101043.31609-1-linkmauve@linkmauve.fr> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On my DRC the values only go between 142 (battery LED blinking red before shutdown) and 178 (charge LED stopping), it seems to be the same on other units according to other testers. This might be the raw voltage value as reported by an ADC, so adding a linear interpolation between two common battery voltage values. A spinlock is used to avoid the battery level and status from being reported unsynchronised. Signed-off-by: Emmanuel Gil Peyrot --- drivers/hid/hid-nintendo-wiiu.c | 136 ++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/drivers/hid/hid-nintendo-wiiu.c b/drivers/hid/hid-nintendo-wiiu.c index 813abb104275..b18fa403eb42 100644 --- a/drivers/hid/hid-nintendo-wiiu.c +++ b/drivers/hid/hid-nintendo-wiiu.c @@ -17,6 +17,11 @@ #include #include #include +#ifdef CONFIG_HID_BATTERY_STRENGTH +#include +#include +#include +#endif #include "hid-ids.h" #include "hid-nintendo.h" @@ -72,6 +77,13 @@ #define MAGNET_MIN -(1 << 15) #define MAGNET_MAX ((1 << 15) - 1) +/* ADC constants for the battery */ +#define BATTERY_CHARGING_BIT BIT(6) +#define BATTERY_MIN 142 +#define BATTERY_MAX 178 +#define VOLTAGE_MIN 3270000 +#define VOLTAGE_MAX 4100000 + /* * The device is setup with multiple input devices: * - A joypad with the buttons and sticks. @@ -85,6 +97,14 @@ struct drc { struct input_dev *joy_input_dev; struct input_dev *touch_input_dev; struct input_dev *accel_input_dev; + +#ifdef CONFIG_HID_BATTERY_STRENGTH + struct power_supply *battery; + struct power_supply_desc battery_desc; + spinlock_t battery_lock; + u8 battery_energy; + int battery_status; +#endif }; /* @@ -101,6 +121,9 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report, struct drc *drc = hid_get_drvdata(hdev); int i, x, y, z, pressure, base; u32 buttons; +#ifdef CONFIG_HID_BATTERY_STRENGTH + unsigned long flags; +#endif if (len != 128) return -EINVAL; @@ -219,6 +242,19 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report, input_report_abs(drc->accel_input_dev, ABS_WHEEL, (int16_t)z); input_sync(drc->accel_input_dev); +#ifdef CONFIG_HID_BATTERY_STRENGTH + /* battery */ + spin_lock_irqsave(&drc->battery_lock, flags); + drc->battery_energy = data[5]; + if (drc->battery_energy == BATTERY_MAX) + drc->battery_status = POWER_SUPPLY_STATUS_FULL; + else if (data[4] & BATTERY_CHARGING_BIT) + drc->battery_status = POWER_SUPPLY_STATUS_CHARGING; + else + drc->battery_status = POWER_SUPPLY_STATUS_DISCHARGING; + spin_unlock_irqrestore(&drc->battery_lock, flags); +#endif + /* let hidraw and hiddev handle the report */ return 0; } @@ -368,6 +404,98 @@ static bool drc_setup_accel(struct drc *drc, return true; } +#ifdef CONFIG_HID_BATTERY_STRENGTH +static enum power_supply_property drc_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_SCOPE, +}; + +static int drc_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct drc *drc = power_supply_get_drvdata(psy); + unsigned long flags; + int ret = 0; + u8 battery_energy; + int battery_status; + + spin_lock_irqsave(&drc->battery_lock, flags); + battery_energy = drc->battery_energy; + battery_status = drc->battery_status; + spin_unlock_irqrestore(&drc->battery_lock, flags); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = battery_status; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = VOLTAGE_MAX; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + val->intval = VOLTAGE_MIN; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = fixp_linear_interpolate(BATTERY_MIN, VOLTAGE_MIN, + BATTERY_MAX, VOLTAGE_MAX, + battery_energy); + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = fixp_linear_interpolate(BATTERY_MIN, 0, + BATTERY_MAX, 100, + battery_energy); + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int drc_setup_battery(struct drc *drc, + struct hid_device *hdev) +{ + struct power_supply_config psy_cfg = { .drv_data = drc, }; + static atomic_t drc_num = ATOMIC_INIT(0); + int ret; + + spin_lock_init(&drc->battery_lock); + + drc->battery_desc.properties = drc_battery_props; + drc->battery_desc.num_properties = ARRAY_SIZE(drc_battery_props); + drc->battery_desc.get_property = drc_battery_get_property; + drc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; + drc->battery_desc.use_for_apm = 0; + + drc->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "wiiu-drc-%i-battery", atomic_fetch_inc(&drc_num)); + if (!drc->battery_desc.name) + return -ENOMEM; + + drc->battery = devm_power_supply_register(&hdev->dev, &drc->battery_desc, &psy_cfg); + if (IS_ERR(drc->battery)) { + ret = PTR_ERR(drc->battery); + hid_err(hdev, "Unable to register battery device\n"); + return ret; + } + + power_supply_powers(drc->battery, &hdev->dev); + + return 0; +} +#endif + int wiiu_hid_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -396,6 +524,14 @@ int wiiu_hid_probe(struct hid_device *hdev, return -ENOMEM; } +#ifdef CONFIG_HID_BATTERY_STRENGTH + ret = drc_setup_battery(drc, hdev); + if (ret) { + hid_err(hdev, "could not allocate battery interface\n"); + return ret; + } +#endif + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER); if (ret) { hid_err(hdev, "hw start failed\n"); -- 2.33.1