From patchwork Thu Feb 2 14:12:22 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 9551947 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id B006260236 for ; Thu, 2 Feb 2017 14:15:19 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 94D6F26E49 for ; Thu, 2 Feb 2017 14:15:19 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8995D28113; Thu, 2 Feb 2017 14:15:19 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2BDBA26E49 for ; Thu, 2 Feb 2017 14:15:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751352AbdBBOM4 (ORCPT ); Thu, 2 Feb 2017 09:12:56 -0500 Received: from mx1.redhat.com ([209.132.183.28]:48578 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751189AbdBBOMy (ORCPT ); Thu, 2 Feb 2017 09:12:54 -0500 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C0B133D967; Thu, 2 Feb 2017 14:12:54 +0000 (UTC) Received: from plouf.banquise.eu.com (ovpn-116-126.ams2.redhat.com [10.36.116.126]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id v12ECXt6011601; Thu, 2 Feb 2017 09:12:52 -0500 From: Benjamin Tissoires To: Jiri Kosina , Bastien Nocera , Peter Hutterer , Nestor Lopez Casado , Olivier Gay , Simon Wood Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 08/15] HID: logitech-hidpp: add support for battery status for the K750 Date: Thu, 2 Feb 2017 15:12:22 +0100 Message-Id: <20170202141229.17322-9-benjamin.tissoires@redhat.com> In-Reply-To: <20170202141229.17322-1-benjamin.tissoires@redhat.com> References: <20170202141229.17322-1-benjamin.tissoires@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.30]); Thu, 02 Feb 2017 14:12:54 +0000 (UTC) Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The Solar Keyboard uses a different feature to report the battery level. Signed-off-by: Benjamin Tissoires --- changes in v2: * update state according to lux information * do not update Lux if the event does not contain lux information --- drivers/hid/hid-logitech-hidpp.c | 97 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index f7b2589..ec3f47c 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -56,6 +56,7 @@ MODULE_PARM_DESC(disable_tap_to_click, #define HIDPP_QUIRK_CLASS_M560 BIT(1) #define HIDPP_QUIRK_CLASS_K400 BIT(2) #define HIDPP_QUIRK_CLASS_G920 BIT(3) +#define HIDPP_QUIRK_CLASS_K750 BIT(4) /* bits 2..20 are reserved for classes */ /* #define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) disabled */ @@ -113,6 +114,7 @@ struct hidpp_report { struct hidpp_battery { u8 feature_index; + u8 solar_feature_index; struct power_supply_desc desc; struct power_supply *ps; char name[64]; @@ -704,7 +706,7 @@ static int hidpp20_query_battery_info(struct hidpp_device *hidpp) int ret; int status, level, next_level; - if (hidpp->battery.feature_index == 0) { + if (hidpp->battery.feature_index == 0xff) { ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_BATTERY_LEVEL_STATUS, &hidpp->battery.feature_index, @@ -810,6 +812,83 @@ static int hidpp_battery_get_property(struct power_supply *psy, } /* -------------------------------------------------------------------------- */ +/* 0x4301: Solar Keyboard */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_SOLAR_KEYBOARD 0x4301 + +#define CMD_SOLAR_SET_LIGHT_MEASURE 0x00 + +#define EVENT_SOLAR_BATTERY_BROADCAST 0x00 +#define EVENT_SOLAR_BATTERY_LIGHT_MEASURE 0x10 +#define EVENT_SOLAR_CHECK_LIGHT_BUTTON 0x20 + +static int hidpp_solar_request_battery_event(struct hidpp_device *hidpp) +{ + struct hidpp_report response; + u8 params[2] = { 1, 1 }; + u8 feature_type; + int ret; + + if (hidpp->battery.feature_index == 0xff) { + ret = hidpp_root_get_feature(hidpp, + HIDPP_PAGE_SOLAR_KEYBOARD, + &hidpp->battery.solar_feature_index, + &feature_type); + if (ret) + return ret; + } + + ret = hidpp_send_fap_command_sync(hidpp, + hidpp->battery.solar_feature_index, + CMD_SOLAR_SET_LIGHT_MEASURE, + params, 2, &response); + if (ret > 0) { + hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", + __func__, ret); + return -EPROTO; + } + if (ret) + return ret; + + return 0; +} + +static int hidpp_solar_battery_event(struct hidpp_device *hidpp, + u8 *data, int size) +{ + struct hidpp_report *report = (struct hidpp_report *)data; + int level, lux, status; + + if (report->fap.feature_index != hidpp->battery.solar_feature_index || + !(report->fap.funcindex_clientid == EVENT_SOLAR_BATTERY_BROADCAST || + report->fap.funcindex_clientid == EVENT_SOLAR_BATTERY_LIGHT_MEASURE || + report->fap.funcindex_clientid == EVENT_SOLAR_CHECK_LIGHT_BUTTON)) + return 0; + + level = report->fap.params[0]; + + if (report->fap.funcindex_clientid == EVENT_SOLAR_BATTERY_LIGHT_MEASURE) { + lux = (report->fap.params[1] << 8) | report->fap.params[2]; + if (lux > 200) + status = POWER_SUPPLY_STATUS_CHARGING; + else + status = POWER_SUPPLY_STATUS_DISCHARGING; + } else { + status = hidpp->battery.status; + } + + if (level != hidpp->battery.level || status != hidpp->battery.status) { + hidpp->battery.level = level; + hidpp->battery.status = status; + if (hidpp->battery.ps) + power_supply_changed(hidpp->battery.ps); + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ /* 0x6010: Touchpad FW items */ /* -------------------------------------------------------------------------- */ @@ -2256,6 +2335,9 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report, ret = hidpp20_battery_event(hidpp, data, size); if (ret != 0) return ret; + ret = hidpp_solar_battery_event(hidpp, data, size); + if (ret != 0) + return ret; } if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) @@ -2278,8 +2360,15 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp) if (hidpp->battery.ps) return 0; + hidpp->battery.feature_index = 0xff; + hidpp->battery.solar_feature_index = 0xff; + if (hidpp->protocol_major >= 2) { - ret = hidpp20_query_battery_info(hidpp); + if (hidpp->quirks & HIDPP_QUIRK_CLASS_K750) + ret = hidpp_solar_request_battery_event(hidpp); + else + ret = hidpp20_query_battery_info(hidpp); + if (ret) return ret; hidpp->quirks |= HIDPP_QUIRK_HIDPP20_BATTERY; @@ -2608,6 +2697,10 @@ static const struct hid_device_id hidpp_devices[] = { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, USB_VENDOR_ID_LOGITECH, 0x4024), .driver_data = HIDPP_QUIRK_CLASS_K400 }, + { /* Solar Keyboard Logitech K750 */ + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, 0x4002), + .driver_data = HIDPP_QUIRK_CLASS_K750 }, { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},