From patchwork Sun May 2 23:28:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Emmanuel Gil Peyrot X-Patchwork-Id: 430523 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 34D9FC43461 for ; Sun, 2 May 2021 23:38:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 12DE361288 for ; Sun, 2 May 2021 23:38:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232341AbhEBXjF (ORCPT ); Sun, 2 May 2021 19:39:05 -0400 Received: from 82-65-109-163.subs.proxad.net ([82.65.109.163]:55116 "EHLO luna.linkmauve.fr" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232358AbhEBXjD (ORCPT ); Sun, 2 May 2021 19:39:03 -0400 Received: by luna.linkmauve.fr (Postfix, from userid 1000) id DF23FF405D8; Mon, 3 May 2021 01:28:58 +0200 (CEST) From: Emmanuel Gil Peyrot To: linux-input@vger.kernel.org Cc: Emmanuel Gil Peyrot , Ash Logan , =?utf-8?q?Jonathan_Neusch=C3=A4fer?= , Jiri Kosina , Benjamin Tissoires , linux-kernel@vger.kernel.org Subject: [PATCH 2/4] HID: wiiu-drc: Implement touch reports Date: Mon, 3 May 2021 01:28:33 +0200 Message-Id: <20210502232836.26134-3-linkmauve@linkmauve.fr> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210502232836.26134-1-linkmauve@linkmauve.fr> References: <20210502232836.26134-1-linkmauve@linkmauve.fr> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org There is a 100×200 inaccessible border on each side, and the Y axis is inverted, these are the two main quirks of this touch panel. I’ve been testing with weston-simple-touch mostly, but it also with the rest of Weston. Signed-off-by: Ash Logan Signed-off-by: Emmanuel Gil Peyrot --- drivers/hid/hid-wiiu-drc.c | 83 +++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c index 018cbdb53a2c..77e70827c37d 100644 --- a/drivers/hid/hid-wiiu-drc.c +++ b/drivers/hid/hid-wiiu-drc.c @@ -49,13 +49,27 @@ #define BUTTON_POWER BIT(25) +/* Touch constants */ +/* Resolution in pixels */ +#define RES_X 854 +#define RES_Y 480 +/* Display/touch size in mm */ +#define WIDTH 138 +#define HEIGHT 79 +#define NUM_TOUCH_POINTS 10 +#define MAX_TOUCH_RES (1 << 12) +#define TOUCH_BORDER_X 100 +#define TOUCH_BORDER_Y 200 + /* * The device is setup with multiple input devices: * - A joypad with the buttons and sticks. + * - The touch area which works as a touchscreen. */ struct drc { struct input_dev *joy_input_dev; + struct input_dev *touch_input_dev; struct hid_device *hdev; }; @@ -63,7 +77,7 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int len) { struct drc *drc = hid_get_drvdata(hdev); - int i; + int i, x, y, pressure, base; u32 buttons; if (len != 128) @@ -126,6 +140,37 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report, input_sync(drc->joy_input_dev); + /* touch */ + /* Average touch points for improved accuracy. */ + x = y = 0; + for (i = 0; i < NUM_TOUCH_POINTS; i++) { + base = 36 + 4 * i; + + x += ((data[base + 1] & 0xF) << 8) | data[base]; + y += ((data[base + 3] & 0xF) << 8) | data[base + 2]; + } + x /= NUM_TOUCH_POINTS; + y /= NUM_TOUCH_POINTS; + + /* Pressure reporting isn’t properly understood, so we don’t report it yet. */ + pressure = 0; + pressure |= ((data[37] >> 4) & 7) << 0; + pressure |= ((data[39] >> 4) & 7) << 3; + pressure |= ((data[41] >> 4) & 7) << 6; + pressure |= ((data[43] >> 4) & 7) << 9; + + if (pressure != 0) { + input_report_key(drc->touch_input_dev, BTN_TOUCH, 1); + input_report_key(drc->touch_input_dev, BTN_TOOL_FINGER, 1); + + input_report_abs(drc->touch_input_dev, ABS_X, x); + input_report_abs(drc->touch_input_dev, ABS_Y, MAX_TOUCH_RES - y); + } else { + input_report_key(drc->touch_input_dev, BTN_TOUCH, 0); + input_report_key(drc->touch_input_dev, BTN_TOOL_FINGER, 0); + } + input_sync(drc->touch_input_dev); + /* let hidraw and hiddev handle the report */ return 0; } @@ -168,6 +213,32 @@ static struct input_dev *allocate_and_setup(struct hid_device *hdev, return input_dev; } +static bool drc_setup_touch(struct drc *drc, + struct hid_device *hdev) +{ + struct input_dev *input_dev; + + input_dev = allocate_and_setup(hdev, DEVICE_NAME " Touch"); + if (!input_dev) + return false; + + input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); + + input_set_abs_params(input_dev, ABS_X, TOUCH_BORDER_X, MAX_TOUCH_RES - TOUCH_BORDER_X, 20, 0); + input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH); + input_set_abs_params(input_dev, ABS_Y, TOUCH_BORDER_Y, MAX_TOUCH_RES - TOUCH_BORDER_Y, 20, 0); + input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT); + + set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(BTN_TOOL_FINGER, input_dev->keybit); + + set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + drc->touch_input_dev = input_dev; + + return true; +} + static bool drc_setup_joypad(struct drc *drc, struct hid_device *hdev) { @@ -231,14 +302,16 @@ static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } - if (!drc_setup_joypad(drc, hdev)) { - hid_err(hdev, "could not allocate interface\n"); + if (!drc_setup_joypad(drc, hdev) || + !drc_setup_touch(drc, hdev)) { + hid_err(hdev, "could not allocate interfaces\n"); return -ENOMEM; } - ret = input_register_device(drc->joy_input_dev); + ret = input_register_device(drc->joy_input_dev) || + input_register_device(drc->touch_input_dev); if (ret) { - hid_err(hdev, "failed to register interface\n"); + hid_err(hdev, "failed to register interfaces\n"); return ret; } From patchwork Sun May 2 23:28:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Emmanuel Gil Peyrot X-Patchwork-Id: 430524 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4BDA2C43462 for ; Sun, 2 May 2021 23:38:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 214DF61376 for ; Sun, 2 May 2021 23:38:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232358AbhEBXjF (ORCPT ); Sun, 2 May 2021 19:39:05 -0400 Received: from 82-65-109-163.subs.proxad.net ([82.65.109.163]:55102 "EHLO luna.linkmauve.fr" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232374AbhEBXjD (ORCPT ); Sun, 2 May 2021 19:39:03 -0400 Received: by luna.linkmauve.fr (Postfix, from userid 1000) id AB16DF405DA; Mon, 3 May 2021 01:29:01 +0200 (CEST) From: Emmanuel Gil Peyrot To: linux-input@vger.kernel.org Cc: Emmanuel Gil Peyrot , Ash Logan , =?utf-8?q?Jonathan_Neusch=C3=A4fer?= , Jiri Kosina , Benjamin Tissoires , linux-kernel@vger.kernel.org Subject: [PATCH 4/4] HID: wiiu-drc: Add battery reporting Date: Mon, 3 May 2021 01:28:35 +0200 Message-Id: <20210502232836.26134-5-linkmauve@linkmauve.fr> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210502232836.26134-1-linkmauve@linkmauve.fr> References: <20210502232836.26134-1-linkmauve@linkmauve.fr> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@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. A spinlock is used to avoid the battery level and status from being reported unsynchronised. Signed-off-by: Emmanuel Gil Peyrot --- drivers/hid/hid-wiiu-drc.c | 107 +++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c index 80faaaad2bb5..119d55542e31 100644 --- a/drivers/hid/hid-wiiu-drc.c +++ b/drivers/hid/hid-wiiu-drc.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "hid-ids.h" #define DEVICE_NAME "Nintendo Wii U gamepad" @@ -69,6 +71,11 @@ #define MAGNET_MIN ACCEL_MIN #define MAGNET_MAX ACCEL_MAX +/* Battery constants */ +#define BATTERY_MIN 142 +#define BATTERY_MAX 178 +#define BATTERY_CAPACITY(val) ((val - BATTERY_MIN) * 100 / (BATTERY_MAX - BATTERY_MIN)) + /* * The device is setup with multiple input devices: * - A joypad with the buttons and sticks. @@ -77,10 +84,17 @@ */ struct drc { + spinlock_t lock; + struct input_dev *joy_input_dev; struct input_dev *touch_input_dev; struct input_dev *accel_input_dev; struct hid_device *hdev; + struct power_supply *battery; + struct power_supply_desc battery_desc; + + u8 battery_energy; + int battery_status; }; static int drc_raw_event(struct hid_device *hdev, struct hid_report *report, @@ -89,6 +103,7 @@ static int drc_raw_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; + unsigned long flags; if (len != 128) return 0; @@ -206,6 +221,17 @@ static int drc_raw_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); + /* battery */ + spin_lock_irqsave(&drc->lock, flags); + drc->battery_energy = data[5]; + if (drc->battery_energy == BATTERY_MAX) + drc->battery_status = POWER_SUPPLY_STATUS_FULL; + else if ((data[4] & 0x40) != 0) + drc->battery_status = POWER_SUPPLY_STATUS_CHARGING; + else + drc->battery_status = POWER_SUPPLY_STATUS_DISCHARGING; + spin_unlock_irqrestore(&drc->lock, flags); + /* let hidraw and hiddev handle the report */ return 0; } @@ -309,10 +335,67 @@ static bool drc_setup_accel(struct drc *drc, return true; } +static enum power_supply_property drc_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_ENERGY_EMPTY, + POWER_SUPPLY_PROP_ENERGY_FULL, +}; + +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->lock, flags); + battery_energy = drc->battery_energy; + battery_status = drc->battery_status; + spin_unlock_irqrestore(&drc->lock, flags); + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = BATTERY_CAPACITY(battery_energy); + break; + case POWER_SUPPLY_PROP_STATUS: + val->intval = battery_status; + break; + case POWER_SUPPLY_PROP_ENERGY_NOW: + val->intval = battery_energy; + break; + case POWER_SUPPLY_PROP_ENERGY_EMPTY: + val->intval = BATTERY_MIN; + break; + case POWER_SUPPLY_PROP_ENERGY_FULL: + val->intval = BATTERY_MAX; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + static bool drc_setup_joypad(struct drc *drc, struct hid_device *hdev) { struct input_dev *input_dev; + struct power_supply_config psy_cfg = { .drv_data = drc, }; + int ret; + static uint8_t drc_num = 0; input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad"); if (!input_dev) @@ -350,6 +433,30 @@ static bool drc_setup_joypad(struct drc *drc, drc->joy_input_dev = input_dev; + 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; + + /* + * TODO: It might be better to use the interface number as the drc_num, + * but I don’t know how to fetch it from here. In userland it is + * /sys/devices/platform/latte/d140000.usb/usb3/3-1/3-1:1.?/bInterfaceNumber + */ + drc->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "wiiu-drc-%i-battery", 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 true; }