2019-06-01 16:08:55 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2014-10-01 01:18:27 +08:00
|
|
|
/*
|
2020-01-13 07:50:09 +08:00
|
|
|
* HIDPP protocol for Logitech receivers
|
2014-10-01 01:18:27 +08:00
|
|
|
*
|
|
|
|
* Copyright (c) 2011 Logitech (c)
|
|
|
|
* Copyright (c) 2012-2013 Google (c)
|
|
|
|
* Copyright (c) 2013-2014 Red Hat Inc.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
|
|
|
#include <linux/device.h>
|
2016-01-11 07:25:15 +08:00
|
|
|
#include <linux/input.h>
|
|
|
|
#include <linux/usb.h>
|
2014-10-01 01:18:27 +08:00
|
|
|
#include <linux/hid.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/sched.h>
|
2018-12-05 08:42:27 +08:00
|
|
|
#include <linux/sched/clock.h>
|
2014-10-01 01:18:27 +08:00
|
|
|
#include <linux/kfifo.h>
|
|
|
|
#include <linux/input/mt.h>
|
2016-01-11 07:25:15 +08:00
|
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <linux/atomic.h>
|
|
|
|
#include <linux/fixp-arith.h>
|
2014-10-01 01:18:27 +08:00
|
|
|
#include <asm/unaligned.h>
|
2016-01-11 07:25:15 +08:00
|
|
|
#include "usbhid/usbhid.h"
|
2014-10-01 01:18:27 +08:00
|
|
|
#include "hid-ids.h"
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
|
|
|
|
MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>");
|
|
|
|
|
2015-03-27 00:41:57 +08:00
|
|
|
static bool disable_raw_mode;
|
|
|
|
module_param(disable_raw_mode, bool, 0644);
|
|
|
|
MODULE_PARM_DESC(disable_raw_mode,
|
|
|
|
"Disable Raw mode reporting for touchpads and keep firmware gestures.");
|
|
|
|
|
2015-09-03 21:08:30 +08:00
|
|
|
static bool disable_tap_to_click;
|
|
|
|
module_param(disable_tap_to_click, bool, 0644);
|
|
|
|
MODULE_PARM_DESC(disable_tap_to_click,
|
|
|
|
"Disable Tap-To-Click mode reporting for touchpads (only on the K400 currently).");
|
|
|
|
|
2022-08-30 21:25:48 +08:00
|
|
|
/* Define a non-zero software ID to identify our own requests */
|
|
|
|
#define LINUX_KERNEL_SW_ID 0x01
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
#define REPORT_ID_HIDPP_SHORT 0x10
|
|
|
|
#define REPORT_ID_HIDPP_LONG 0x11
|
2015-11-20 07:42:11 +08:00
|
|
|
#define REPORT_ID_HIDPP_VERY_LONG 0x12
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
#define HIDPP_REPORT_SHORT_LENGTH 7
|
|
|
|
#define HIDPP_REPORT_LONG_LENGTH 20
|
2019-04-20 19:22:12 +08:00
|
|
|
#define HIDPP_REPORT_VERY_LONG_MAX_LENGTH 64
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2019-10-28 01:44:06 +08:00
|
|
|
#define HIDPP_REPORT_SHORT_SUPPORTED BIT(0)
|
|
|
|
#define HIDPP_REPORT_LONG_SUPPORTED BIT(1)
|
|
|
|
#define HIDPP_REPORT_VERY_LONG_SUPPORTED BIT(2)
|
|
|
|
|
2019-04-20 19:22:17 +08:00
|
|
|
#define HIDPP_SUB_ID_CONSUMER_VENDOR_KEYS 0x03
|
2019-04-20 19:22:15 +08:00
|
|
|
#define HIDPP_SUB_ID_ROLLER 0x05
|
|
|
|
#define HIDPP_SUB_ID_MOUSE_EXTRA_BTNS 0x06
|
2021-04-05 02:56:09 +08:00
|
|
|
#define HIDPP_SUB_ID_USER_IFACE_EVENT 0x08
|
|
|
|
#define HIDPP_USER_IFACE_EVENT_ENCRYPTION_KEY_LOST BIT(5)
|
2019-04-20 19:22:15 +08:00
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
#define HIDPP_QUIRK_CLASS_WTP BIT(0)
|
2015-05-30 17:00:27 +08:00
|
|
|
#define HIDPP_QUIRK_CLASS_M560 BIT(1)
|
2015-09-03 21:08:30 +08:00
|
|
|
#define HIDPP_QUIRK_CLASS_K400 BIT(2)
|
2015-11-20 07:42:12 +08:00
|
|
|
#define HIDPP_QUIRK_CLASS_G920 BIT(3)
|
2017-03-27 22:59:37 +08:00
|
|
|
#define HIDPP_QUIRK_CLASS_K750 BIT(4)
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2015-05-30 17:00:27 +08:00
|
|
|
/* bits 2..20 are reserved for classes */
|
2016-06-29 17:28:02 +08:00
|
|
|
/* #define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) disabled */
|
2014-10-01 01:18:34 +08:00
|
|
|
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
|
2015-09-03 21:08:29 +08:00
|
|
|
#define HIDPP_QUIRK_NO_HIDINPUT BIT(23)
|
2015-11-20 07:42:12 +08:00
|
|
|
#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
|
2017-03-27 22:59:26 +08:00
|
|
|
#define HIDPP_QUIRK_UNIFYING BIT(25)
|
2022-09-14 21:21:46 +08:00
|
|
|
#define HIDPP_QUIRK_HIDPP_WHEELS BIT(26)
|
|
|
|
#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(27)
|
|
|
|
#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(28)
|
2019-04-20 19:22:15 +08:00
|
|
|
|
|
|
|
/* These are just aliases for now */
|
|
|
|
#define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
|
|
|
|
#define HIDPP_QUIRK_KBD_ZOOM_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
|
2018-12-05 08:42:27 +08:00
|
|
|
|
|
|
|
/* Convenience constant to check for any high-res support. */
|
2022-09-14 21:21:46 +08:00
|
|
|
#define HIDPP_CAPABILITY_HI_RES_SCROLL (HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL | \
|
|
|
|
HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL | \
|
|
|
|
HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL)
|
2015-09-03 21:08:29 +08:00
|
|
|
|
2016-06-29 17:28:02 +08:00
|
|
|
#define HIDPP_QUIRK_DELAYED_INIT HIDPP_QUIRK_NO_HIDINPUT
|
2014-10-01 01:18:32 +08:00
|
|
|
|
2017-03-27 22:59:25 +08:00
|
|
|
#define HIDPP_CAPABILITY_HIDPP10_BATTERY BIT(0)
|
|
|
|
#define HIDPP_CAPABILITY_HIDPP20_BATTERY BIT(1)
|
2017-03-27 22:59:36 +08:00
|
|
|
#define HIDPP_CAPABILITY_BATTERY_MILEAGE BIT(2)
|
|
|
|
#define HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS BIT(3)
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
#define HIDPP_CAPABILITY_BATTERY_VOLTAGE BIT(4)
|
HID: logitech-hidpp: add support for Unified Battery (1004) feature
This new feature present in new devices replaces the old Battery Level Status
(0x1000) feature. It keeps essentially the same information for levels
(reporting critical, low, good and full) but makes these levels optional, the
device exports a capability setting which describes which levels it supports.
In addition to this, there is an optional state_of_charge paramenter that
exports the battery percentage.
This patch adds support for this new feature. There were some implementation
choices, as described below and in the code.
If the device supports the state_of_charge parameter, we will just export the
battery percentage and not the levels, which the device might still support.
Since this feature can co-exist with the Battery Voltage (0x1001) feature and
we currently only support one battery feature, I changed the battery feature
discovery to try to use 0x1000 and 0x1004 first and only then 0x1001, the
battery voltage feature.
In the future we could uncouple this and make the battery feature co-exists
with 0x1000 and 0x1004, allowing the device to export voltage information in
addition to the battery percentage or level.
I tested this patch with a MX Anywhere 3, which supports the new feature. Since
I don't have any device that doesn't support the state_of_charge parameter of
this feature, I forced the MX Anywhere 3 to use the level information, instead
of battery percentage, to test that part of the implementation.
I also tested with a MX Master 3, which supports the Battery Level Status
(0x1000) feature, and a G703 Hero, which supports the Battery Voltage (0x1001)
feature, to make sure nothing broke there.
[jkosina@suse.cz: fix comment]
Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-01-05 02:29:37 +08:00
|
|
|
#define HIDPP_CAPABILITY_BATTERY_PERCENTAGE BIT(5)
|
|
|
|
#define HIDPP_CAPABILITY_UNIFIED_BATTERY BIT(6)
|
2022-09-14 21:21:46 +08:00
|
|
|
#define HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL BIT(7)
|
|
|
|
#define HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL BIT(8)
|
|
|
|
#define HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL BIT(9)
|
2017-03-27 22:59:25 +08:00
|
|
|
|
2020-11-15 05:20:56 +08:00
|
|
|
#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
/*
|
|
|
|
* There are two hidpp protocols in use, the first version hidpp10 is known
|
|
|
|
* as register access protocol or RAP, the second version hidpp20 is known as
|
|
|
|
* feature access protocol or FAP
|
|
|
|
*
|
|
|
|
* Most older devices (including the Unifying usb receiver) use the RAP protocol
|
|
|
|
* where as most newer devices use the FAP protocol. Both protocols are
|
|
|
|
* compatible with the underlying transport, which could be usb, Unifiying, or
|
|
|
|
* bluetooth. The message lengths are defined by the hid vendor specific report
|
|
|
|
* descriptor for the HIDPP_SHORT report type (total message lenth 7 bytes) and
|
|
|
|
* the HIDPP_LONG report type (total message length 20 bytes)
|
|
|
|
*
|
|
|
|
* The RAP protocol uses both report types, whereas the FAP only uses HIDPP_LONG
|
|
|
|
* messages. The Unifying receiver itself responds to RAP messages (device index
|
|
|
|
* is 0xFF for the receiver), and all messages (short or long) with a device
|
|
|
|
* index between 1 and 6 are passed untouched to the corresponding paired
|
|
|
|
* Unifying device.
|
|
|
|
*
|
|
|
|
* The paired device can be RAP or FAP, it will receive the message untouched
|
|
|
|
* from the Unifiying receiver.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct fap {
|
|
|
|
u8 feature_index;
|
|
|
|
u8 funcindex_clientid;
|
2019-04-20 19:22:12 +08:00
|
|
|
u8 params[HIDPP_REPORT_VERY_LONG_MAX_LENGTH - 4U];
|
2014-10-01 01:18:27 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct rap {
|
|
|
|
u8 sub_id;
|
|
|
|
u8 reg_address;
|
2019-04-20 19:22:12 +08:00
|
|
|
u8 params[HIDPP_REPORT_VERY_LONG_MAX_LENGTH - 4U];
|
2014-10-01 01:18:27 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct hidpp_report {
|
|
|
|
u8 report_id;
|
|
|
|
u8 device_index;
|
|
|
|
union {
|
|
|
|
struct fap fap;
|
|
|
|
struct rap rap;
|
|
|
|
u8 rawbytes[sizeof(struct fap)];
|
|
|
|
};
|
|
|
|
} __packed;
|
|
|
|
|
2016-06-29 17:28:01 +08:00
|
|
|
struct hidpp_battery {
|
|
|
|
u8 feature_index;
|
2017-03-27 22:59:37 +08:00
|
|
|
u8 solar_feature_index;
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
u8 voltage_feature_index;
|
2016-06-29 17:28:01 +08:00
|
|
|
struct power_supply_desc desc;
|
|
|
|
struct power_supply *ps;
|
|
|
|
char name[64];
|
|
|
|
int status;
|
2017-03-27 22:59:35 +08:00
|
|
|
int capacity;
|
2017-03-27 22:59:36 +08:00
|
|
|
int level;
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
int voltage;
|
|
|
|
int charge_type;
|
2017-03-27 22:59:34 +08:00
|
|
|
bool online;
|
HID: logitech-hidpp: add support for Unified Battery (1004) feature
This new feature present in new devices replaces the old Battery Level Status
(0x1000) feature. It keeps essentially the same information for levels
(reporting critical, low, good and full) but makes these levels optional, the
device exports a capability setting which describes which levels it supports.
In addition to this, there is an optional state_of_charge paramenter that
exports the battery percentage.
This patch adds support for this new feature. There were some implementation
choices, as described below and in the code.
If the device supports the state_of_charge parameter, we will just export the
battery percentage and not the levels, which the device might still support.
Since this feature can co-exist with the Battery Voltage (0x1001) feature and
we currently only support one battery feature, I changed the battery feature
discovery to try to use 0x1000 and 0x1004 first and only then 0x1001, the
battery voltage feature.
In the future we could uncouple this and make the battery feature co-exists
with 0x1000 and 0x1004, allowing the device to export voltage information in
addition to the battery percentage or level.
I tested this patch with a MX Anywhere 3, which supports the new feature. Since
I don't have any device that doesn't support the state_of_charge parameter of
this feature, I forced the MX Anywhere 3 to use the level information, instead
of battery percentage, to test that part of the implementation.
I also tested with a MX Master 3, which supports the Battery Level Status
(0x1000) feature, and a G703 Hero, which supports the Battery Voltage (0x1001)
feature, to make sure nothing broke there.
[jkosina@suse.cz: fix comment]
Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-01-05 02:29:37 +08:00
|
|
|
u8 supported_levels_1004;
|
2016-06-29 17:28:01 +08:00
|
|
|
};
|
|
|
|
|
2018-12-05 08:42:27 +08:00
|
|
|
/**
|
|
|
|
* struct hidpp_scroll_counter - Utility class for processing high-resolution
|
|
|
|
* scroll events.
|
|
|
|
* @dev: the input device for which events should be reported.
|
|
|
|
* @wheel_multiplier: the scalar multiplier to be applied to each wheel event
|
|
|
|
* @remainder: counts the number of high-resolution units moved since the last
|
|
|
|
* low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should
|
|
|
|
* only be used by class methods.
|
|
|
|
* @direction: direction of last movement (1 or -1)
|
|
|
|
* @last_time: last event time, used to reset remainder after inactivity
|
|
|
|
*/
|
|
|
|
struct hidpp_scroll_counter {
|
|
|
|
int wheel_multiplier;
|
|
|
|
int remainder;
|
|
|
|
int direction;
|
|
|
|
unsigned long long last_time;
|
|
|
|
};
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
struct hidpp_device {
|
|
|
|
struct hid_device *hid_dev;
|
2019-04-20 19:22:13 +08:00
|
|
|
struct input_dev *input;
|
2014-10-01 01:18:27 +08:00
|
|
|
struct mutex send_mutex;
|
|
|
|
void *send_receive_buf;
|
2015-01-09 03:37:12 +08:00
|
|
|
char *name; /* will never be NULL and should not be freed */
|
2014-10-01 01:18:27 +08:00
|
|
|
wait_queue_head_t wait;
|
2019-04-20 19:22:12 +08:00
|
|
|
int very_long_report_length;
|
2014-10-01 01:18:27 +08:00
|
|
|
bool answer_available;
|
|
|
|
u8 protocol_major;
|
|
|
|
u8 protocol_minor;
|
|
|
|
|
|
|
|
void *private_data;
|
|
|
|
|
2014-10-01 01:18:32 +08:00
|
|
|
struct work_struct work;
|
|
|
|
struct kfifo delayed_work_fifo;
|
|
|
|
atomic_t connected;
|
|
|
|
struct input_dev *delayed_input;
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
unsigned long quirks;
|
2017-03-27 22:59:25 +08:00
|
|
|
unsigned long capabilities;
|
2019-10-28 01:44:06 +08:00
|
|
|
u8 supported_reports;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2016-06-29 17:28:01 +08:00
|
|
|
struct hidpp_battery battery;
|
2018-12-05 08:42:27 +08:00
|
|
|
struct hidpp_scroll_counter vertical_wheel_counter;
|
2019-10-28 01:44:13 +08:00
|
|
|
|
|
|
|
u8 wireless_feature_index;
|
2016-06-29 17:28:01 +08:00
|
|
|
};
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2014-12-16 08:50:14 +08:00
|
|
|
/* HID++ 1.0 error codes */
|
2014-10-01 01:18:27 +08:00
|
|
|
#define HIDPP_ERROR 0x8f
|
|
|
|
#define HIDPP_ERROR_SUCCESS 0x00
|
|
|
|
#define HIDPP_ERROR_INVALID_SUBID 0x01
|
|
|
|
#define HIDPP_ERROR_INVALID_ADRESS 0x02
|
|
|
|
#define HIDPP_ERROR_INVALID_VALUE 0x03
|
|
|
|
#define HIDPP_ERROR_CONNECT_FAIL 0x04
|
|
|
|
#define HIDPP_ERROR_TOO_MANY_DEVICES 0x05
|
|
|
|
#define HIDPP_ERROR_ALREADY_EXISTS 0x06
|
|
|
|
#define HIDPP_ERROR_BUSY 0x07
|
|
|
|
#define HIDPP_ERROR_UNKNOWN_DEVICE 0x08
|
|
|
|
#define HIDPP_ERROR_RESOURCE_ERROR 0x09
|
|
|
|
#define HIDPP_ERROR_REQUEST_UNAVAILABLE 0x0a
|
|
|
|
#define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b
|
|
|
|
#define HIDPP_ERROR_WRONG_PIN_CODE 0x0c
|
2014-12-16 08:50:14 +08:00
|
|
|
/* HID++ 2.0 error codes */
|
|
|
|
#define HIDPP20_ERROR 0xff
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2014-10-01 01:18:32 +08:00
|
|
|
static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
static int __hidpp_send_report(struct hid_device *hdev,
|
|
|
|
struct hidpp_report *hidpp_report)
|
|
|
|
{
|
2015-11-20 07:42:12 +08:00
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
2014-10-01 01:18:27 +08:00
|
|
|
int fields_count, ret;
|
|
|
|
|
|
|
|
switch (hidpp_report->report_id) {
|
|
|
|
case REPORT_ID_HIDPP_SHORT:
|
|
|
|
fields_count = HIDPP_REPORT_SHORT_LENGTH;
|
|
|
|
break;
|
|
|
|
case REPORT_ID_HIDPP_LONG:
|
|
|
|
fields_count = HIDPP_REPORT_LONG_LENGTH;
|
|
|
|
break;
|
2015-11-20 07:42:11 +08:00
|
|
|
case REPORT_ID_HIDPP_VERY_LONG:
|
2019-04-20 19:22:12 +08:00
|
|
|
fields_count = hidpp->very_long_report_length;
|
2015-11-20 07:42:11 +08:00
|
|
|
break;
|
2014-10-01 01:18:27 +08:00
|
|
|
default:
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set the device_index as the receiver, it will be overwritten by
|
|
|
|
* hid_hw_request if needed
|
|
|
|
*/
|
|
|
|
hidpp_report->device_index = 0xff;
|
|
|
|
|
2015-11-20 07:42:12 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_FORCE_OUTPUT_REPORTS) {
|
|
|
|
ret = hid_hw_output_report(hdev, (u8 *)hidpp_report, fields_count);
|
|
|
|
} else {
|
|
|
|
ret = hid_hw_raw_request(hdev, hidpp_report->report_id,
|
|
|
|
(u8 *)hidpp_report, fields_count, HID_OUTPUT_REPORT,
|
|
|
|
HID_REQ_SET_REPORT);
|
|
|
|
}
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
return ret == fields_count ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
2021-03-26 22:34:49 +08:00
|
|
|
/*
|
2014-11-04 05:09:58 +08:00
|
|
|
* hidpp_send_message_sync() returns 0 in case of success, and something else
|
|
|
|
* in case of a failure.
|
|
|
|
* - If ' something else' is positive, that means that an error has been raised
|
|
|
|
* by the protocol itself.
|
|
|
|
* - If ' something else' is negative, that means that we had a classic error
|
|
|
|
* (-ENOMEM, -EPIPE, etc...)
|
|
|
|
*/
|
2014-10-01 01:18:27 +08:00
|
|
|
static int hidpp_send_message_sync(struct hidpp_device *hidpp,
|
|
|
|
struct hidpp_report *message,
|
|
|
|
struct hidpp_report *response)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_lock(&hidpp->send_mutex);
|
|
|
|
|
|
|
|
hidpp->send_receive_buf = response;
|
|
|
|
hidpp->answer_available = false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* So that we can later validate the answer when it arrives
|
|
|
|
* in hidpp_raw_event
|
|
|
|
*/
|
|
|
|
*response = *message;
|
|
|
|
|
|
|
|
ret = __hidpp_send_report(hidpp->hid_dev, message);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
dbg_hid("__hidpp_send_report returned err: %d\n", ret);
|
|
|
|
memset(response, 0, sizeof(struct hidpp_report));
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!wait_event_timeout(hidpp->wait, hidpp->answer_available,
|
|
|
|
5*HZ)) {
|
|
|
|
dbg_hid("%s:timeout waiting for response\n", __func__);
|
|
|
|
memset(response, 0, sizeof(struct hidpp_report));
|
|
|
|
ret = -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (response->report_id == REPORT_ID_HIDPP_SHORT &&
|
2014-12-16 08:50:14 +08:00
|
|
|
response->rap.sub_id == HIDPP_ERROR) {
|
|
|
|
ret = response->rap.params[1];
|
|
|
|
dbg_hid("%s:got hidpp error %02X\n", __func__, ret);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2015-11-20 07:42:11 +08:00
|
|
|
if ((response->report_id == REPORT_ID_HIDPP_LONG ||
|
|
|
|
response->report_id == REPORT_ID_HIDPP_VERY_LONG) &&
|
|
|
|
response->fap.feature_index == HIDPP20_ERROR) {
|
2014-10-01 01:18:27 +08:00
|
|
|
ret = response->fap.params[1];
|
2014-12-16 08:50:14 +08:00
|
|
|
dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret);
|
2014-10-01 01:18:27 +08:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
exit:
|
|
|
|
mutex_unlock(&hidpp->send_mutex);
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
|
|
|
|
u8 feat_index, u8 funcindex_clientid, u8 *params, int param_count,
|
|
|
|
struct hidpp_report *response)
|
|
|
|
{
|
2014-10-31 17:14:39 +08:00
|
|
|
struct hidpp_report *message;
|
2014-10-01 01:18:27 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (param_count > sizeof(message->fap.params))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-10-31 17:14:39 +08:00
|
|
|
message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
|
|
|
|
if (!message)
|
|
|
|
return -ENOMEM;
|
2015-11-20 07:42:11 +08:00
|
|
|
|
|
|
|
if (param_count > (HIDPP_REPORT_LONG_LENGTH - 4))
|
|
|
|
message->report_id = REPORT_ID_HIDPP_VERY_LONG;
|
|
|
|
else
|
|
|
|
message->report_id = REPORT_ID_HIDPP_LONG;
|
2014-10-01 01:18:27 +08:00
|
|
|
message->fap.feature_index = feat_index;
|
2022-08-30 21:25:48 +08:00
|
|
|
message->fap.funcindex_clientid = funcindex_clientid | LINUX_KERNEL_SW_ID;
|
2014-10-01 01:18:27 +08:00
|
|
|
memcpy(&message->fap.params, params, param_count);
|
|
|
|
|
|
|
|
ret = hidpp_send_message_sync(hidpp, message, response);
|
|
|
|
kfree(message);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:30 +08:00
|
|
|
static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
|
|
|
|
u8 report_id, u8 sub_id, u8 reg_address, u8 *params, int param_count,
|
|
|
|
struct hidpp_report *response)
|
|
|
|
{
|
2014-10-31 17:14:39 +08:00
|
|
|
struct hidpp_report *message;
|
2015-11-20 07:42:11 +08:00
|
|
|
int ret, max_count;
|
2014-10-01 01:18:30 +08:00
|
|
|
|
2019-10-28 01:44:06 +08:00
|
|
|
/* Send as long report if short reports are not supported. */
|
|
|
|
if (report_id == REPORT_ID_HIDPP_SHORT &&
|
|
|
|
!(hidpp_dev->supported_reports & HIDPP_REPORT_SHORT_SUPPORTED))
|
|
|
|
report_id = REPORT_ID_HIDPP_LONG;
|
|
|
|
|
2015-11-20 07:42:11 +08:00
|
|
|
switch (report_id) {
|
|
|
|
case REPORT_ID_HIDPP_SHORT:
|
|
|
|
max_count = HIDPP_REPORT_SHORT_LENGTH - 4;
|
|
|
|
break;
|
|
|
|
case REPORT_ID_HIDPP_LONG:
|
|
|
|
max_count = HIDPP_REPORT_LONG_LENGTH - 4;
|
|
|
|
break;
|
|
|
|
case REPORT_ID_HIDPP_VERY_LONG:
|
2019-04-20 19:22:12 +08:00
|
|
|
max_count = hidpp_dev->very_long_report_length - 4;
|
2015-11-20 07:42:11 +08:00
|
|
|
break;
|
|
|
|
default:
|
2014-10-01 01:18:30 +08:00
|
|
|
return -EINVAL;
|
2015-11-20 07:42:11 +08:00
|
|
|
}
|
2014-10-01 01:18:30 +08:00
|
|
|
|
2015-11-20 07:42:11 +08:00
|
|
|
if (param_count > max_count)
|
2014-10-01 01:18:30 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2014-10-31 17:14:39 +08:00
|
|
|
message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
|
|
|
|
if (!message)
|
|
|
|
return -ENOMEM;
|
2014-10-01 01:18:30 +08:00
|
|
|
message->report_id = report_id;
|
|
|
|
message->rap.sub_id = sub_id;
|
|
|
|
message->rap.reg_address = reg_address;
|
|
|
|
memcpy(&message->rap.params, params, param_count);
|
|
|
|
|
|
|
|
ret = hidpp_send_message_sync(hidpp_dev, message, response);
|
|
|
|
kfree(message);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:32 +08:00
|
|
|
static void delayed_work_cb(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = container_of(work, struct hidpp_device,
|
|
|
|
work);
|
|
|
|
hidpp_connect_event(hidpp);
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
static inline bool hidpp_match_answer(struct hidpp_report *question,
|
|
|
|
struct hidpp_report *answer)
|
|
|
|
{
|
|
|
|
return (answer->fap.feature_index == question->fap.feature_index) &&
|
|
|
|
(answer->fap.funcindex_clientid == question->fap.funcindex_clientid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool hidpp_match_error(struct hidpp_report *question,
|
|
|
|
struct hidpp_report *answer)
|
|
|
|
{
|
2014-12-16 08:50:14 +08:00
|
|
|
return ((answer->rap.sub_id == HIDPP_ERROR) ||
|
|
|
|
(answer->fap.feature_index == HIDPP20_ERROR)) &&
|
2014-10-01 01:18:27 +08:00
|
|
|
(answer->fap.funcindex_clientid == question->fap.feature_index) &&
|
|
|
|
(answer->fap.params[0] == question->fap.funcindex_clientid);
|
|
|
|
}
|
|
|
|
|
2019-10-28 01:44:13 +08:00
|
|
|
static inline bool hidpp_report_is_connect_event(struct hidpp_device *hidpp,
|
|
|
|
struct hidpp_report *report)
|
2014-10-01 01:18:32 +08:00
|
|
|
{
|
2019-10-28 01:44:13 +08:00
|
|
|
return (hidpp->wireless_feature_index &&
|
|
|
|
(report->fap.feature_index == hidpp->wireless_feature_index)) ||
|
|
|
|
((report->report_id == REPORT_ID_HIDPP_SHORT) &&
|
|
|
|
(report->rap.sub_id == 0x41));
|
2014-10-01 01:18:32 +08:00
|
|
|
}
|
|
|
|
|
2021-03-26 22:34:49 +08:00
|
|
|
/*
|
2014-12-12 06:39:59 +08:00
|
|
|
* hidpp_prefix_name() prefixes the current given name with "Logitech ".
|
|
|
|
*/
|
|
|
|
static void hidpp_prefix_name(char **name, int name_length)
|
|
|
|
{
|
|
|
|
#define PREFIX_LENGTH 9 /* "Logitech " */
|
|
|
|
|
|
|
|
int new_length;
|
|
|
|
char *new_name;
|
|
|
|
|
|
|
|
if (name_length > PREFIX_LENGTH &&
|
|
|
|
strncmp(*name, "Logitech ", PREFIX_LENGTH) == 0)
|
|
|
|
/* The prefix has is already in the name */
|
|
|
|
return;
|
|
|
|
|
|
|
|
new_length = PREFIX_LENGTH + name_length;
|
|
|
|
new_name = kzalloc(new_length, GFP_KERNEL);
|
|
|
|
if (!new_name)
|
|
|
|
return;
|
|
|
|
|
|
|
|
snprintf(new_name, new_length, "Logitech %s", *name);
|
|
|
|
|
|
|
|
kfree(*name);
|
|
|
|
|
|
|
|
*name = new_name;
|
|
|
|
}
|
|
|
|
|
2018-12-05 08:42:27 +08:00
|
|
|
/**
|
|
|
|
* hidpp_scroll_counter_handle_scroll() - Send high- and low-resolution scroll
|
|
|
|
* events given a high-resolution wheel
|
|
|
|
* movement.
|
2021-03-26 22:34:49 +08:00
|
|
|
* @input_dev: Pointer to the input device
|
2018-12-05 08:42:27 +08:00
|
|
|
* @counter: a hid_scroll_counter struct describing the wheel.
|
|
|
|
* @hi_res_value: the movement of the wheel, in the mouse's high-resolution
|
|
|
|
* units.
|
|
|
|
*
|
|
|
|
* Given a high-resolution movement, this function converts the movement into
|
|
|
|
* fractions of 120 and emits high-resolution scroll events for the input
|
|
|
|
* device. It also uses the multiplier from &struct hid_scroll_counter to
|
|
|
|
* emit low-resolution scroll events when appropriate for
|
|
|
|
* backwards-compatibility with userspace input libraries.
|
|
|
|
*/
|
2019-04-20 19:22:13 +08:00
|
|
|
static void hidpp_scroll_counter_handle_scroll(struct input_dev *input_dev,
|
|
|
|
struct hidpp_scroll_counter *counter,
|
2018-12-05 08:42:27 +08:00
|
|
|
int hi_res_value)
|
|
|
|
{
|
|
|
|
int low_res_value, remainder, direction;
|
|
|
|
unsigned long long now, previous;
|
|
|
|
|
|
|
|
hi_res_value = hi_res_value * 120/counter->wheel_multiplier;
|
2019-04-20 19:22:13 +08:00
|
|
|
input_report_rel(input_dev, REL_WHEEL_HI_RES, hi_res_value);
|
2018-12-05 08:42:27 +08:00
|
|
|
|
|
|
|
remainder = counter->remainder;
|
|
|
|
direction = hi_res_value > 0 ? 1 : -1;
|
|
|
|
|
|
|
|
now = sched_clock();
|
|
|
|
previous = counter->last_time;
|
|
|
|
counter->last_time = now;
|
|
|
|
/*
|
|
|
|
* Reset the remainder after a period of inactivity or when the
|
|
|
|
* direction changes. This prevents the REL_WHEEL emulation point
|
|
|
|
* from sliding for devices that don't always provide the same
|
|
|
|
* number of movements per detent.
|
|
|
|
*/
|
|
|
|
if (now - previous > 1000000000 || direction != counter->direction)
|
|
|
|
remainder = 0;
|
|
|
|
|
|
|
|
counter->direction = direction;
|
|
|
|
remainder += hi_res_value;
|
|
|
|
|
|
|
|
/* Some wheels will rest 7/8ths of a detent from the previous detent
|
|
|
|
* after slow movement, so we want the threshold for low-res events to
|
|
|
|
* be in the middle between two detents (e.g. after 4/8ths) as
|
|
|
|
* opposed to on the detents themselves (8/8ths).
|
|
|
|
*/
|
|
|
|
if (abs(remainder) >= 60) {
|
|
|
|
/* Add (or subtract) 1 because we want to trigger when the wheel
|
|
|
|
* is half-way to the next detent (i.e. scroll 1 detent after a
|
|
|
|
* 1/2 detent movement, 2 detents after a 1 1/2 detent movement,
|
|
|
|
* etc.).
|
|
|
|
*/
|
|
|
|
low_res_value = remainder / 120;
|
|
|
|
if (low_res_value == 0)
|
|
|
|
low_res_value = (hi_res_value > 0 ? 1 : -1);
|
2019-04-20 19:22:13 +08:00
|
|
|
input_report_rel(input_dev, REL_WHEEL, low_res_value);
|
2018-12-05 08:42:27 +08:00
|
|
|
remainder -= low_res_value * 120;
|
|
|
|
}
|
|
|
|
counter->remainder = remainder;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:30 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* HIDP++ 1.0 commands */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#define HIDPP_SET_REGISTER 0x80
|
|
|
|
#define HIDPP_GET_REGISTER 0x81
|
|
|
|
#define HIDPP_SET_LONG_REGISTER 0x82
|
|
|
|
#define HIDPP_GET_LONG_REGISTER 0x83
|
|
|
|
|
2018-12-05 08:42:26 +08:00
|
|
|
/**
|
2019-04-20 19:22:14 +08:00
|
|
|
* hidpp10_set_register - Modify a HID++ 1.0 register.
|
2018-12-05 08:42:26 +08:00
|
|
|
* @hidpp_dev: the device to set the register on.
|
|
|
|
* @register_address: the address of the register to modify.
|
|
|
|
* @byte: the byte of the register to modify. Should be less than 3.
|
2019-04-20 19:22:14 +08:00
|
|
|
* @mask: mask of the bits to modify
|
|
|
|
* @value: new values for the bits in mask
|
2018-12-05 08:42:26 +08:00
|
|
|
* Return: 0 if successful, otherwise a negative error code.
|
|
|
|
*/
|
2019-04-20 19:22:14 +08:00
|
|
|
static int hidpp10_set_register(struct hidpp_device *hidpp_dev,
|
|
|
|
u8 register_address, u8 byte, u8 mask, u8 value)
|
2017-03-27 22:59:38 +08:00
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
|
|
|
u8 params[3] = { 0 };
|
|
|
|
|
|
|
|
ret = hidpp_send_rap_command_sync(hidpp_dev,
|
2018-12-05 08:42:26 +08:00
|
|
|
REPORT_ID_HIDPP_SHORT,
|
|
|
|
HIDPP_GET_REGISTER,
|
|
|
|
register_address,
|
|
|
|
NULL, 0, &response);
|
2017-03-27 22:59:38 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
memcpy(params, response.rap.params, 3);
|
|
|
|
|
2019-04-20 19:22:14 +08:00
|
|
|
params[byte] &= ~mask;
|
|
|
|
params[byte] |= value & mask;
|
2017-03-27 22:59:38 +08:00
|
|
|
|
|
|
|
return hidpp_send_rap_command_sync(hidpp_dev,
|
2018-12-05 08:42:26 +08:00
|
|
|
REPORT_ID_HIDPP_SHORT,
|
|
|
|
HIDPP_SET_REGISTER,
|
|
|
|
register_address,
|
|
|
|
params, 3, &response);
|
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:14 +08:00
|
|
|
#define HIDPP_REG_ENABLE_REPORTS 0x00
|
|
|
|
#define HIDPP_ENABLE_CONSUMER_REPORT BIT(0)
|
|
|
|
#define HIDPP_ENABLE_WHEEL_REPORT BIT(2)
|
|
|
|
#define HIDPP_ENABLE_MOUSE_EXTRA_BTN_REPORT BIT(3)
|
|
|
|
#define HIDPP_ENABLE_BAT_REPORT BIT(4)
|
|
|
|
#define HIDPP_ENABLE_HWHEEL_REPORT BIT(5)
|
2018-12-05 08:42:26 +08:00
|
|
|
|
|
|
|
static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
|
|
|
|
{
|
2019-04-20 19:22:14 +08:00
|
|
|
return hidpp10_set_register(hidpp_dev, HIDPP_REG_ENABLE_REPORTS, 0,
|
|
|
|
HIDPP_ENABLE_BAT_REPORT, HIDPP_ENABLE_BAT_REPORT);
|
2018-12-05 08:42:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#define HIDPP_REG_FEATURES 0x01
|
2019-04-20 19:22:14 +08:00
|
|
|
#define HIDPP_ENABLE_SPECIAL_BUTTON_FUNC BIT(1)
|
|
|
|
#define HIDPP_ENABLE_FAST_SCROLL BIT(6)
|
2018-12-05 08:42:26 +08:00
|
|
|
|
|
|
|
/* On HID++ 1.0 devices, high-res scroll was called "scrolling acceleration". */
|
|
|
|
static int hidpp10_enable_scrolling_acceleration(struct hidpp_device *hidpp_dev)
|
|
|
|
{
|
2019-04-20 19:22:14 +08:00
|
|
|
return hidpp10_set_register(hidpp_dev, HIDPP_REG_FEATURES, 0,
|
|
|
|
HIDPP_ENABLE_FAST_SCROLL, HIDPP_ENABLE_FAST_SCROLL);
|
2017-03-27 22:59:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:30 +08:00
|
|
|
#define HIDPP_REG_PAIRING_INFORMATION 0xB5
|
2017-03-27 22:59:26 +08:00
|
|
|
#define HIDPP_EXTENDED_PAIRING 0x30
|
|
|
|
#define HIDPP_DEVICE_NAME 0x40
|
2014-10-01 01:18:30 +08:00
|
|
|
|
2017-03-27 22:59:26 +08:00
|
|
|
static char *hidpp_unifying_get_name(struct hidpp_device *hidpp_dev)
|
2014-10-01 01:18:30 +08:00
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
2017-03-27 22:59:26 +08:00
|
|
|
u8 params[1] = { HIDPP_DEVICE_NAME };
|
2014-10-01 01:18:30 +08:00
|
|
|
char *name;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
ret = hidpp_send_rap_command_sync(hidpp_dev,
|
|
|
|
REPORT_ID_HIDPP_SHORT,
|
|
|
|
HIDPP_GET_LONG_REGISTER,
|
|
|
|
HIDPP_REG_PAIRING_INFORMATION,
|
|
|
|
params, 1, &response);
|
|
|
|
if (ret)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
len = response.rap.params[1];
|
|
|
|
|
2014-12-11 20:51:19 +08:00
|
|
|
if (2 + len > sizeof(response.rap.params))
|
|
|
|
return NULL;
|
|
|
|
|
2019-04-20 19:22:06 +08:00
|
|
|
if (len < 4) /* logitech devices are usually at least Xddd */
|
|
|
|
return NULL;
|
|
|
|
|
2014-10-01 01:18:30 +08:00
|
|
|
name = kzalloc(len + 1, GFP_KERNEL);
|
|
|
|
if (!name)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
memcpy(name, &response.rap.params[2], len);
|
2014-12-12 06:39:59 +08:00
|
|
|
|
|
|
|
/* include the terminating '\0' */
|
|
|
|
hidpp_prefix_name(&name, len + 1);
|
|
|
|
|
2014-10-01 01:18:30 +08:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:26 +08:00
|
|
|
static int hidpp_unifying_get_serial(struct hidpp_device *hidpp, u32 *serial)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
|
|
|
u8 params[1] = { HIDPP_EXTENDED_PAIRING };
|
|
|
|
|
|
|
|
ret = hidpp_send_rap_command_sync(hidpp,
|
|
|
|
REPORT_ID_HIDPP_SHORT,
|
|
|
|
HIDPP_GET_LONG_REGISTER,
|
|
|
|
HIDPP_REG_PAIRING_INFORMATION,
|
|
|
|
params, 1, &response);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't care about LE or BE, we will output it as a string
|
|
|
|
* with %4phD, so we need to keep the order.
|
|
|
|
*/
|
|
|
|
*serial = *((u32 *)&response.rap.params[1]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp_unifying_init(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
struct hid_device *hdev = hidpp->hid_dev;
|
|
|
|
const char *name;
|
|
|
|
u32 serial;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = hidpp_unifying_get_serial(hidpp, &serial);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
snprintf(hdev->uniq, sizeof(hdev->uniq), "%04x-%4phD",
|
|
|
|
hdev->product, &serial);
|
|
|
|
dbg_hid("HID++ Unifying: Got serial: %s\n", hdev->uniq);
|
|
|
|
|
|
|
|
name = hidpp_unifying_get_name(hidpp);
|
|
|
|
if (!name)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
snprintf(hdev->name, sizeof(hdev->name), "%s", name);
|
|
|
|
dbg_hid("HID++ Unifying: Got name: %s\n", name);
|
|
|
|
|
|
|
|
kfree(name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* 0x0000: Root */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#define HIDPP_PAGE_ROOT 0x0000
|
|
|
|
#define HIDPP_PAGE_ROOT_IDX 0x00
|
|
|
|
|
2022-08-30 21:25:49 +08:00
|
|
|
#define CMD_ROOT_GET_FEATURE 0x00
|
|
|
|
#define CMD_ROOT_GET_PROTOCOL_VERSION 0x10
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
static int hidpp_root_get_feature(struct hidpp_device *hidpp, u16 feature,
|
|
|
|
u8 *feature_index, u8 *feature_type)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
|
|
|
u8 params[2] = { feature >> 8, feature & 0x00FF };
|
|
|
|
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp,
|
|
|
|
HIDPP_PAGE_ROOT_IDX,
|
|
|
|
CMD_ROOT_GET_FEATURE,
|
|
|
|
params, 2, &response);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-03-27 22:59:32 +08:00
|
|
|
if (response.fap.params[0] == 0)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
*feature_index = response.fap.params[0];
|
|
|
|
*feature_type = response.fap.params[1];
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp)
|
|
|
|
{
|
2019-04-20 19:22:10 +08:00
|
|
|
const u8 ping_byte = 0x5a;
|
|
|
|
u8 ping_data[3] = { 0, 0, ping_byte };
|
2014-10-01 01:18:27 +08:00
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
|
|
|
|
2019-04-20 19:22:10 +08:00
|
|
|
ret = hidpp_send_rap_command_sync(hidpp,
|
|
|
|
REPORT_ID_HIDPP_SHORT,
|
2014-10-01 01:18:27 +08:00
|
|
|
HIDPP_PAGE_ROOT_IDX,
|
2022-11-12 03:45:26 +08:00
|
|
|
CMD_ROOT_GET_PROTOCOL_VERSION | LINUX_KERNEL_SW_ID,
|
2019-04-20 19:22:10 +08:00
|
|
|
ping_data, sizeof(ping_data), &response);
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2014-11-04 05:09:59 +08:00
|
|
|
if (ret == HIDPP_ERROR_INVALID_SUBID) {
|
2014-10-01 01:18:27 +08:00
|
|
|
hidpp->protocol_major = 1;
|
|
|
|
hidpp->protocol_minor = 0;
|
2019-03-22 15:41:38 +08:00
|
|
|
goto print_version;
|
2014-10-01 01:18:27 +08:00
|
|
|
}
|
|
|
|
|
2014-11-04 05:09:59 +08:00
|
|
|
/* the device might not be connected */
|
|
|
|
if (ret == HIDPP_ERROR_RESOURCE_ERROR)
|
|
|
|
return -EIO;
|
|
|
|
|
2014-11-04 05:09:58 +08:00
|
|
|
if (ret > 0) {
|
|
|
|
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
|
|
|
__func__, ret);
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
2014-10-01 01:18:27 +08:00
|
|
|
if (ret)
|
2014-11-04 05:09:58 +08:00
|
|
|
return ret;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2019-04-20 19:22:10 +08:00
|
|
|
if (response.rap.params[2] != ping_byte) {
|
|
|
|
hid_err(hidpp->hid_dev, "%s: ping mismatch 0x%02x != 0x%02x\n",
|
|
|
|
__func__, response.rap.params[2], ping_byte);
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
hidpp->protocol_major = response.rap.params[0];
|
|
|
|
hidpp->protocol_minor = response.rap.params[1];
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2019-03-22 15:41:38 +08:00
|
|
|
print_version:
|
|
|
|
hid_info(hidpp->hid_dev, "HID++ %u.%u device connected.\n",
|
|
|
|
hidpp->protocol_major, hidpp->protocol_minor);
|
|
|
|
return 0;
|
2014-10-01 01:18:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* 0x0005: GetDeviceNameType */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#define HIDPP_PAGE_GET_DEVICE_NAME_TYPE 0x0005
|
|
|
|
|
2022-08-30 21:25:49 +08:00
|
|
|
#define CMD_GET_DEVICE_NAME_TYPE_GET_COUNT 0x00
|
|
|
|
#define CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME 0x10
|
|
|
|
#define CMD_GET_DEVICE_NAME_TYPE_GET_TYPE 0x20
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
static int hidpp_devicenametype_get_count(struct hidpp_device *hidpp,
|
|
|
|
u8 feature_index, u8 *nameLength)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
|
|
CMD_GET_DEVICE_NAME_TYPE_GET_COUNT, NULL, 0, &response);
|
|
|
|
|
2014-11-04 05:09:58 +08:00
|
|
|
if (ret > 0) {
|
|
|
|
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
|
|
|
__func__, ret);
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
2014-10-01 01:18:27 +08:00
|
|
|
if (ret)
|
2014-11-04 05:09:58 +08:00
|
|
|
return ret;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
*nameLength = response.fap.params[0];
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp,
|
|
|
|
u8 feature_index, u8 char_index, char *device_name, int len_buf)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret, i;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
|
|
CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME, &char_index, 1,
|
|
|
|
&response);
|
|
|
|
|
2014-11-04 05:09:58 +08:00
|
|
|
if (ret > 0) {
|
|
|
|
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
|
|
|
__func__, ret);
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
2014-10-01 01:18:27 +08:00
|
|
|
if (ret)
|
2014-11-04 05:09:58 +08:00
|
|
|
return ret;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2015-11-20 07:42:11 +08:00
|
|
|
switch (response.report_id) {
|
|
|
|
case REPORT_ID_HIDPP_VERY_LONG:
|
2019-04-20 19:22:12 +08:00
|
|
|
count = hidpp->very_long_report_length - 4;
|
2015-11-20 07:42:11 +08:00
|
|
|
break;
|
|
|
|
case REPORT_ID_HIDPP_LONG:
|
2014-10-01 01:18:27 +08:00
|
|
|
count = HIDPP_REPORT_LONG_LENGTH - 4;
|
2015-11-20 07:42:11 +08:00
|
|
|
break;
|
|
|
|
case REPORT_ID_HIDPP_SHORT:
|
2014-10-01 01:18:27 +08:00
|
|
|
count = HIDPP_REPORT_SHORT_LENGTH - 4;
|
2015-11-20 07:42:11 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
if (len_buf < count)
|
|
|
|
count = len_buf;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
device_name[i] = response.fap.params[i];
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2014-12-11 20:51:17 +08:00
|
|
|
static char *hidpp_get_device_name(struct hidpp_device *hidpp)
|
2014-10-01 01:18:27 +08:00
|
|
|
{
|
|
|
|
u8 feature_type;
|
|
|
|
u8 feature_index;
|
|
|
|
u8 __name_length;
|
|
|
|
char *name;
|
|
|
|
unsigned index = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_GET_DEVICE_NAME_TYPE,
|
|
|
|
&feature_index, &feature_type);
|
|
|
|
if (ret)
|
2014-12-11 20:51:17 +08:00
|
|
|
return NULL;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
ret = hidpp_devicenametype_get_count(hidpp, feature_index,
|
|
|
|
&__name_length);
|
|
|
|
if (ret)
|
2014-12-11 20:51:17 +08:00
|
|
|
return NULL;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
name = kzalloc(__name_length + 1, GFP_KERNEL);
|
|
|
|
if (!name)
|
2014-12-11 20:51:17 +08:00
|
|
|
return NULL;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2014-12-11 20:51:18 +08:00
|
|
|
while (index < __name_length) {
|
|
|
|
ret = hidpp_devicenametype_get_device_name(hidpp,
|
2014-10-01 01:18:27 +08:00
|
|
|
feature_index, index, name + index,
|
|
|
|
__name_length - index);
|
2014-12-11 20:51:18 +08:00
|
|
|
if (ret <= 0) {
|
|
|
|
kfree(name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
index += ret;
|
|
|
|
}
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2014-12-12 06:39:59 +08:00
|
|
|
/* include the terminating '\0' */
|
|
|
|
hidpp_prefix_name(&name, __name_length + 1);
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2016-06-29 17:28:01 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* 0x1000: Battery level status */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#define HIDPP_PAGE_BATTERY_LEVEL_STATUS 0x1000
|
|
|
|
|
|
|
|
#define CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS 0x00
|
|
|
|
#define CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_CAPABILITY 0x10
|
|
|
|
|
|
|
|
#define EVENT_BATTERY_LEVEL_STATUS_BROADCAST 0x00
|
|
|
|
|
2017-03-27 22:59:36 +08:00
|
|
|
#define FLAG_BATTERY_LEVEL_DISABLE_OSD BIT(0)
|
|
|
|
#define FLAG_BATTERY_LEVEL_MILEAGE BIT(1)
|
|
|
|
#define FLAG_BATTERY_LEVEL_RECHARGEABLE BIT(2)
|
|
|
|
|
|
|
|
static int hidpp_map_battery_level(int capacity)
|
|
|
|
{
|
|
|
|
if (capacity < 11)
|
|
|
|
return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
|
2019-03-22 15:41:40 +08:00
|
|
|
/*
|
|
|
|
* The spec says this should be < 31 but some devices report 30
|
|
|
|
* with brand new batteries and Windows reports 30 as "Good".
|
|
|
|
*/
|
|
|
|
else if (capacity < 30)
|
2017-03-27 22:59:36 +08:00
|
|
|
return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
|
|
|
|
else if (capacity < 81)
|
|
|
|
return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
|
|
|
|
return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
|
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:35 +08:00
|
|
|
static int hidpp20_batterylevel_map_status_capacity(u8 data[3], int *capacity,
|
2017-03-27 22:59:36 +08:00
|
|
|
int *next_capacity,
|
|
|
|
int *level)
|
2016-06-29 17:28:01 +08:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
2017-03-27 22:59:35 +08:00
|
|
|
*capacity = data[0];
|
|
|
|
*next_capacity = data[1];
|
2017-03-27 22:59:36 +08:00
|
|
|
*level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
|
2016-06-29 17:28:01 +08:00
|
|
|
|
2017-03-27 22:59:35 +08:00
|
|
|
/* When discharging, we can rely on the device reported capacity.
|
|
|
|
* For all other states the device reports 0 (unknown).
|
2016-06-29 17:28:01 +08:00
|
|
|
*/
|
|
|
|
switch (data[2]) {
|
|
|
|
case 0: /* discharging (in use) */
|
|
|
|
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
2017-03-27 22:59:36 +08:00
|
|
|
*level = hidpp_map_battery_level(*capacity);
|
2016-06-29 17:28:01 +08:00
|
|
|
break;
|
|
|
|
case 1: /* recharging */
|
|
|
|
status = POWER_SUPPLY_STATUS_CHARGING;
|
|
|
|
break;
|
|
|
|
case 2: /* charge in final stage */
|
|
|
|
status = POWER_SUPPLY_STATUS_CHARGING;
|
|
|
|
break;
|
|
|
|
case 3: /* charge complete */
|
|
|
|
status = POWER_SUPPLY_STATUS_FULL;
|
2017-03-27 22:59:36 +08:00
|
|
|
*level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
|
2017-03-27 22:59:35 +08:00
|
|
|
*capacity = 100;
|
2016-06-29 17:28:01 +08:00
|
|
|
break;
|
|
|
|
case 4: /* recharging below optimal speed */
|
|
|
|
status = POWER_SUPPLY_STATUS_CHARGING;
|
|
|
|
break;
|
|
|
|
/* 5 = invalid battery type
|
|
|
|
6 = thermal error
|
|
|
|
7 = other charging error */
|
|
|
|
default:
|
|
|
|
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:35 +08:00
|
|
|
static int hidpp20_batterylevel_get_battery_capacity(struct hidpp_device *hidpp,
|
|
|
|
u8 feature_index,
|
|
|
|
int *status,
|
|
|
|
int *capacity,
|
2017-03-27 22:59:36 +08:00
|
|
|
int *next_capacity,
|
|
|
|
int *level)
|
2016-06-29 17:28:01 +08:00
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
|
|
|
u8 *params = (u8 *)response.fap.params;
|
|
|
|
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
|
|
CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS,
|
|
|
|
NULL, 0, &response);
|
2019-11-14 22:30:46 +08:00
|
|
|
/* Ignore these intermittent errors */
|
|
|
|
if (ret == HIDPP_ERROR_RESOURCE_ERROR)
|
|
|
|
return -EIO;
|
2016-06-29 17:28:01 +08:00
|
|
|
if (ret > 0) {
|
|
|
|
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
|
|
|
__func__, ret);
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-03-27 22:59:35 +08:00
|
|
|
*status = hidpp20_batterylevel_map_status_capacity(params, capacity,
|
2017-03-27 22:59:36 +08:00
|
|
|
next_capacity,
|
|
|
|
level);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp20_batterylevel_get_battery_info(struct hidpp_device *hidpp,
|
|
|
|
u8 feature_index)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
|
|
|
u8 *params = (u8 *)response.fap.params;
|
|
|
|
unsigned int level_count, flags;
|
|
|
|
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
|
|
CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_CAPABILITY,
|
|
|
|
NULL, 0, &response);
|
|
|
|
if (ret > 0) {
|
|
|
|
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
|
|
|
__func__, ret);
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
level_count = params[0];
|
|
|
|
flags = params[1];
|
|
|
|
|
|
|
|
if (level_count < 10 || !(flags & FLAG_BATTERY_LEVEL_MILEAGE))
|
|
|
|
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS;
|
|
|
|
else
|
|
|
|
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_MILEAGE;
|
2016-06-29 17:28:01 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
HID: logitech-hidpp: add support for Unified Battery (1004) feature
This new feature present in new devices replaces the old Battery Level Status
(0x1000) feature. It keeps essentially the same information for levels
(reporting critical, low, good and full) but makes these levels optional, the
device exports a capability setting which describes which levels it supports.
In addition to this, there is an optional state_of_charge paramenter that
exports the battery percentage.
This patch adds support for this new feature. There were some implementation
choices, as described below and in the code.
If the device supports the state_of_charge parameter, we will just export the
battery percentage and not the levels, which the device might still support.
Since this feature can co-exist with the Battery Voltage (0x1001) feature and
we currently only support one battery feature, I changed the battery feature
discovery to try to use 0x1000 and 0x1004 first and only then 0x1001, the
battery voltage feature.
In the future we could uncouple this and make the battery feature co-exists
with 0x1000 and 0x1004, allowing the device to export voltage information in
addition to the battery percentage or level.
I tested this patch with a MX Anywhere 3, which supports the new feature. Since
I don't have any device that doesn't support the state_of_charge parameter of
this feature, I forced the MX Anywhere 3 to use the level information, instead
of battery percentage, to test that part of the implementation.
I also tested with a MX Master 3, which supports the Battery Level Status
(0x1000) feature, and a G703 Hero, which supports the Battery Voltage (0x1001)
feature, to make sure nothing broke there.
[jkosina@suse.cz: fix comment]
Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-01-05 02:29:37 +08:00
|
|
|
static int hidpp20_query_battery_info_1000(struct hidpp_device *hidpp)
|
2016-06-29 17:28:01 +08:00
|
|
|
{
|
|
|
|
u8 feature_type;
|
|
|
|
int ret;
|
2017-03-27 22:59:36 +08:00
|
|
|
int status, capacity, next_capacity, level;
|
2016-06-29 17:28:01 +08:00
|
|
|
|
2017-03-27 22:59:37 +08:00
|
|
|
if (hidpp->battery.feature_index == 0xff) {
|
2016-06-29 17:28:01 +08:00
|
|
|
ret = hidpp_root_get_feature(hidpp,
|
|
|
|
HIDPP_PAGE_BATTERY_LEVEL_STATUS,
|
|
|
|
&hidpp->battery.feature_index,
|
|
|
|
&feature_type);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:35 +08:00
|
|
|
ret = hidpp20_batterylevel_get_battery_capacity(hidpp,
|
|
|
|
hidpp->battery.feature_index,
|
|
|
|
&status, &capacity,
|
2017-03-27 22:59:36 +08:00
|
|
|
&next_capacity, &level);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = hidpp20_batterylevel_get_battery_info(hidpp,
|
|
|
|
hidpp->battery.feature_index);
|
2016-06-29 17:28:01 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
hidpp->battery.status = status;
|
2017-03-27 22:59:35 +08:00
|
|
|
hidpp->battery.capacity = capacity;
|
2017-03-27 22:59:36 +08:00
|
|
|
hidpp->battery.level = level;
|
2017-03-27 22:59:34 +08:00
|
|
|
/* the capacity is only available when discharging or full */
|
|
|
|
hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
|
|
|
|
status == POWER_SUPPLY_STATUS_FULL;
|
2016-06-29 17:28:01 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
HID: logitech-hidpp: add support for Unified Battery (1004) feature
This new feature present in new devices replaces the old Battery Level Status
(0x1000) feature. It keeps essentially the same information for levels
(reporting critical, low, good and full) but makes these levels optional, the
device exports a capability setting which describes which levels it supports.
In addition to this, there is an optional state_of_charge paramenter that
exports the battery percentage.
This patch adds support for this new feature. There were some implementation
choices, as described below and in the code.
If the device supports the state_of_charge parameter, we will just export the
battery percentage and not the levels, which the device might still support.
Since this feature can co-exist with the Battery Voltage (0x1001) feature and
we currently only support one battery feature, I changed the battery feature
discovery to try to use 0x1000 and 0x1004 first and only then 0x1001, the
battery voltage feature.
In the future we could uncouple this and make the battery feature co-exists
with 0x1000 and 0x1004, allowing the device to export voltage information in
addition to the battery percentage or level.
I tested this patch with a MX Anywhere 3, which supports the new feature. Since
I don't have any device that doesn't support the state_of_charge parameter of
this feature, I forced the MX Anywhere 3 to use the level information, instead
of battery percentage, to test that part of the implementation.
I also tested with a MX Master 3, which supports the Battery Level Status
(0x1000) feature, and a G703 Hero, which supports the Battery Voltage (0x1001)
feature, to make sure nothing broke there.
[jkosina@suse.cz: fix comment]
Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-01-05 02:29:37 +08:00
|
|
|
static int hidpp20_battery_event_1000(struct hidpp_device *hidpp,
|
2016-06-29 17:28:01 +08:00
|
|
|
u8 *data, int size)
|
|
|
|
{
|
|
|
|
struct hidpp_report *report = (struct hidpp_report *)data;
|
2017-03-27 22:59:36 +08:00
|
|
|
int status, capacity, next_capacity, level;
|
2016-06-29 17:28:01 +08:00
|
|
|
bool changed;
|
|
|
|
|
|
|
|
if (report->fap.feature_index != hidpp->battery.feature_index ||
|
|
|
|
report->fap.funcindex_clientid != EVENT_BATTERY_LEVEL_STATUS_BROADCAST)
|
|
|
|
return 0;
|
|
|
|
|
2017-03-27 22:59:35 +08:00
|
|
|
status = hidpp20_batterylevel_map_status_capacity(report->fap.params,
|
|
|
|
&capacity,
|
2017-03-27 22:59:36 +08:00
|
|
|
&next_capacity,
|
|
|
|
&level);
|
2016-06-29 17:28:01 +08:00
|
|
|
|
2017-03-27 22:59:34 +08:00
|
|
|
/* the capacity is only available when discharging or full */
|
|
|
|
hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
|
|
|
|
status == POWER_SUPPLY_STATUS_FULL;
|
|
|
|
|
2017-03-27 22:59:35 +08:00
|
|
|
changed = capacity != hidpp->battery.capacity ||
|
2017-03-27 22:59:36 +08:00
|
|
|
level != hidpp->battery.level ||
|
2016-06-29 17:28:01 +08:00
|
|
|
status != hidpp->battery.status;
|
|
|
|
|
|
|
|
if (changed) {
|
2017-03-27 22:59:36 +08:00
|
|
|
hidpp->battery.level = level;
|
2017-03-27 22:59:35 +08:00
|
|
|
hidpp->battery.capacity = capacity;
|
2016-06-29 17:28:01 +08:00
|
|
|
hidpp->battery.status = status;
|
|
|
|
if (hidpp->battery.ps)
|
|
|
|
power_supply_changed(hidpp->battery.ps);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* 0x1001: Battery voltage */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#define HIDPP_PAGE_BATTERY_VOLTAGE 0x1001
|
|
|
|
|
|
|
|
#define CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE 0x00
|
|
|
|
|
|
|
|
#define EVENT_BATTERY_VOLTAGE_STATUS_BROADCAST 0x00
|
|
|
|
|
|
|
|
static int hidpp20_battery_map_status_voltage(u8 data[3], int *voltage,
|
|
|
|
int *level, int *charge_type)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
2020-01-12 03:24:19 +08:00
|
|
|
long flags = (long) data[2];
|
2021-05-08 03:18:19 +08:00
|
|
|
*level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
|
2020-01-12 03:24:19 +08:00
|
|
|
if (flags & 0x80)
|
|
|
|
switch (flags & 0x07) {
|
|
|
|
case 0:
|
|
|
|
status = POWER_SUPPLY_STATUS_CHARGING;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
status = POWER_SUPPLY_STATUS_FULL;
|
|
|
|
*level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
status = POWER_SUPPLY_STATUS_UNKNOWN;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
|
|
|
|
|
|
|
*charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
|
2020-01-12 03:24:19 +08:00
|
|
|
if (test_bit(3, &flags)) {
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
*charge_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
|
|
|
|
}
|
2020-01-12 03:24:19 +08:00
|
|
|
if (test_bit(4, &flags)) {
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
*charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
|
|
|
|
}
|
2020-01-12 03:24:19 +08:00
|
|
|
if (test_bit(5, &flags)) {
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
*level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*voltage = get_unaligned_be16(data);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp20_battery_get_battery_voltage(struct hidpp_device *hidpp,
|
|
|
|
u8 feature_index,
|
|
|
|
int *status, int *voltage,
|
|
|
|
int *level, int *charge_type)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
|
|
|
u8 *params = (u8 *)response.fap.params;
|
|
|
|
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
|
|
CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE,
|
|
|
|
NULL, 0, &response);
|
|
|
|
|
|
|
|
if (ret > 0) {
|
|
|
|
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
|
|
|
__func__, ret);
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_VOLTAGE;
|
|
|
|
|
|
|
|
*status = hidpp20_battery_map_status_voltage(params, voltage,
|
|
|
|
level, charge_type);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-02 20:52:32 +08:00
|
|
|
static int hidpp20_map_battery_capacity(struct hid_device *hid_dev, int voltage)
|
|
|
|
{
|
|
|
|
/* NB: This voltage curve doesn't necessarily map perfectly to all
|
|
|
|
* devices that implement the BATTERY_VOLTAGE feature. This is because
|
|
|
|
* there are a few devices that use different battery technology.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static const int voltages[] = {
|
|
|
|
4186, 4156, 4143, 4133, 4122, 4113, 4103, 4094, 4086, 4075,
|
|
|
|
4067, 4059, 4051, 4043, 4035, 4027, 4019, 4011, 4003, 3997,
|
|
|
|
3989, 3983, 3976, 3969, 3961, 3955, 3949, 3942, 3935, 3929,
|
|
|
|
3922, 3916, 3909, 3902, 3896, 3890, 3883, 3877, 3870, 3865,
|
|
|
|
3859, 3853, 3848, 3842, 3837, 3833, 3828, 3824, 3819, 3815,
|
|
|
|
3811, 3808, 3804, 3800, 3797, 3793, 3790, 3787, 3784, 3781,
|
|
|
|
3778, 3775, 3772, 3770, 3767, 3764, 3762, 3759, 3757, 3754,
|
|
|
|
3751, 3748, 3744, 3741, 3737, 3734, 3730, 3726, 3724, 3720,
|
|
|
|
3717, 3714, 3710, 3706, 3702, 3697, 3693, 3688, 3683, 3677,
|
|
|
|
3671, 3666, 3662, 3658, 3654, 3646, 3633, 3612, 3579, 3537
|
|
|
|
};
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
BUILD_BUG_ON(ARRAY_SIZE(voltages) != 100);
|
|
|
|
|
|
|
|
if (unlikely(voltage < 3500 || voltage >= 5000))
|
|
|
|
hid_warn_once(hid_dev,
|
|
|
|
"%s: possibly using the wrong voltage curve\n",
|
|
|
|
__func__);
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(voltages); i++) {
|
|
|
|
if (voltage >= voltages[i])
|
|
|
|
return ARRAY_SIZE(voltages) - i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
u8 feature_type;
|
|
|
|
int ret;
|
|
|
|
int status, voltage, level, charge_type;
|
|
|
|
|
|
|
|
if (hidpp->battery.voltage_feature_index == 0xff) {
|
|
|
|
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_BATTERY_VOLTAGE,
|
|
|
|
&hidpp->battery.voltage_feature_index,
|
|
|
|
&feature_type);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = hidpp20_battery_get_battery_voltage(hidpp,
|
|
|
|
hidpp->battery.voltage_feature_index,
|
|
|
|
&status, &voltage, &level, &charge_type);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
hidpp->battery.status = status;
|
|
|
|
hidpp->battery.voltage = voltage;
|
2021-08-02 20:52:32 +08:00
|
|
|
hidpp->battery.capacity = hidpp20_map_battery_capacity(hidpp->hid_dev,
|
|
|
|
voltage);
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
hidpp->battery.level = level;
|
|
|
|
hidpp->battery.charge_type = charge_type;
|
|
|
|
hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp20_battery_voltage_event(struct hidpp_device *hidpp,
|
|
|
|
u8 *data, int size)
|
|
|
|
{
|
|
|
|
struct hidpp_report *report = (struct hidpp_report *)data;
|
|
|
|
int status, voltage, level, charge_type;
|
|
|
|
|
|
|
|
if (report->fap.feature_index != hidpp->battery.voltage_feature_index ||
|
|
|
|
report->fap.funcindex_clientid != EVENT_BATTERY_VOLTAGE_STATUS_BROADCAST)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
status = hidpp20_battery_map_status_voltage(report->fap.params, &voltage,
|
|
|
|
&level, &charge_type);
|
|
|
|
|
|
|
|
hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
|
|
|
|
|
|
|
|
if (voltage != hidpp->battery.voltage || status != hidpp->battery.status) {
|
|
|
|
hidpp->battery.voltage = voltage;
|
2021-08-02 20:52:32 +08:00
|
|
|
hidpp->battery.capacity = hidpp20_map_battery_capacity(hidpp->hid_dev,
|
|
|
|
voltage);
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
hidpp->battery.status = status;
|
|
|
|
hidpp->battery.level = level;
|
|
|
|
hidpp->battery.charge_type = charge_type;
|
|
|
|
if (hidpp->battery.ps)
|
|
|
|
power_supply_changed(hidpp->battery.ps);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
HID: logitech-hidpp: add support for Unified Battery (1004) feature
This new feature present in new devices replaces the old Battery Level Status
(0x1000) feature. It keeps essentially the same information for levels
(reporting critical, low, good and full) but makes these levels optional, the
device exports a capability setting which describes which levels it supports.
In addition to this, there is an optional state_of_charge paramenter that
exports the battery percentage.
This patch adds support for this new feature. There were some implementation
choices, as described below and in the code.
If the device supports the state_of_charge parameter, we will just export the
battery percentage and not the levels, which the device might still support.
Since this feature can co-exist with the Battery Voltage (0x1001) feature and
we currently only support one battery feature, I changed the battery feature
discovery to try to use 0x1000 and 0x1004 first and only then 0x1001, the
battery voltage feature.
In the future we could uncouple this and make the battery feature co-exists
with 0x1000 and 0x1004, allowing the device to export voltage information in
addition to the battery percentage or level.
I tested this patch with a MX Anywhere 3, which supports the new feature. Since
I don't have any device that doesn't support the state_of_charge parameter of
this feature, I forced the MX Anywhere 3 to use the level information, instead
of battery percentage, to test that part of the implementation.
I also tested with a MX Master 3, which supports the Battery Level Status
(0x1000) feature, and a G703 Hero, which supports the Battery Voltage (0x1001)
feature, to make sure nothing broke there.
[jkosina@suse.cz: fix comment]
Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-01-05 02:29:37 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* 0x1004: Unified battery */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#define HIDPP_PAGE_UNIFIED_BATTERY 0x1004
|
|
|
|
|
|
|
|
#define CMD_UNIFIED_BATTERY_GET_CAPABILITIES 0x00
|
|
|
|
#define CMD_UNIFIED_BATTERY_GET_STATUS 0x10
|
|
|
|
|
|
|
|
#define EVENT_UNIFIED_BATTERY_STATUS_EVENT 0x00
|
|
|
|
|
|
|
|
#define FLAG_UNIFIED_BATTERY_LEVEL_CRITICAL BIT(0)
|
|
|
|
#define FLAG_UNIFIED_BATTERY_LEVEL_LOW BIT(1)
|
|
|
|
#define FLAG_UNIFIED_BATTERY_LEVEL_GOOD BIT(2)
|
|
|
|
#define FLAG_UNIFIED_BATTERY_LEVEL_FULL BIT(3)
|
|
|
|
|
|
|
|
#define FLAG_UNIFIED_BATTERY_FLAGS_RECHARGEABLE BIT(0)
|
|
|
|
#define FLAG_UNIFIED_BATTERY_FLAGS_STATE_OF_CHARGE BIT(1)
|
|
|
|
|
|
|
|
static int hidpp20_unifiedbattery_get_capabilities(struct hidpp_device *hidpp,
|
|
|
|
u8 feature_index)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
|
|
|
u8 *params = (u8 *)response.fap.params;
|
|
|
|
|
|
|
|
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS ||
|
|
|
|
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_PERCENTAGE) {
|
|
|
|
/* we have already set the device capabilities, so let's skip */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
|
|
CMD_UNIFIED_BATTERY_GET_CAPABILITIES,
|
|
|
|
NULL, 0, &response);
|
|
|
|
/* Ignore these intermittent errors */
|
|
|
|
if (ret == HIDPP_ERROR_RESOURCE_ERROR)
|
|
|
|
return -EIO;
|
|
|
|
if (ret > 0) {
|
|
|
|
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
|
|
|
__func__, ret);
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the device supports state of charge (battery percentage) we won't
|
|
|
|
* export the battery level information. there are 4 possible battery
|
|
|
|
* levels and they all are optional, this means that the device might
|
|
|
|
* not support any of them, we are just better off with the battery
|
|
|
|
* percentage.
|
|
|
|
*/
|
|
|
|
if (params[1] & FLAG_UNIFIED_BATTERY_FLAGS_STATE_OF_CHARGE) {
|
|
|
|
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_PERCENTAGE;
|
|
|
|
hidpp->battery.supported_levels_1004 = 0;
|
|
|
|
} else {
|
|
|
|
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS;
|
|
|
|
hidpp->battery.supported_levels_1004 = params[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp20_unifiedbattery_map_status(struct hidpp_device *hidpp,
|
|
|
|
u8 charging_status,
|
|
|
|
u8 external_power_status)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
switch (charging_status) {
|
|
|
|
case 0: /* discharging */
|
|
|
|
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
|
|
|
break;
|
|
|
|
case 1: /* charging */
|
|
|
|
case 2: /* charging slow */
|
|
|
|
status = POWER_SUPPLY_STATUS_CHARGING;
|
|
|
|
break;
|
|
|
|
case 3: /* complete */
|
|
|
|
status = POWER_SUPPLY_STATUS_FULL;
|
|
|
|
break;
|
|
|
|
case 4: /* error */
|
|
|
|
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
|
|
|
hid_info(hidpp->hid_dev, "%s: charging error",
|
|
|
|
hidpp->name);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp20_unifiedbattery_map_level(struct hidpp_device *hidpp,
|
|
|
|
u8 battery_level)
|
|
|
|
{
|
|
|
|
/* cler unsupported level bits */
|
|
|
|
battery_level &= hidpp->battery.supported_levels_1004;
|
|
|
|
|
|
|
|
if (battery_level & FLAG_UNIFIED_BATTERY_LEVEL_FULL)
|
|
|
|
return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
|
|
|
|
else if (battery_level & FLAG_UNIFIED_BATTERY_LEVEL_GOOD)
|
|
|
|
return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
|
|
|
|
else if (battery_level & FLAG_UNIFIED_BATTERY_LEVEL_LOW)
|
|
|
|
return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
|
|
|
|
else if (battery_level & FLAG_UNIFIED_BATTERY_LEVEL_CRITICAL)
|
|
|
|
return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
|
|
|
|
|
|
|
|
return POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp20_unifiedbattery_get_status(struct hidpp_device *hidpp,
|
|
|
|
u8 feature_index,
|
|
|
|
u8 *state_of_charge,
|
|
|
|
int *status,
|
|
|
|
int *level)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
|
|
|
u8 *params = (u8 *)response.fap.params;
|
|
|
|
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
|
|
CMD_UNIFIED_BATTERY_GET_STATUS,
|
|
|
|
NULL, 0, &response);
|
|
|
|
/* Ignore these intermittent errors */
|
|
|
|
if (ret == HIDPP_ERROR_RESOURCE_ERROR)
|
|
|
|
return -EIO;
|
|
|
|
if (ret > 0) {
|
|
|
|
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
|
|
|
__func__, ret);
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*state_of_charge = params[0];
|
|
|
|
*status = hidpp20_unifiedbattery_map_status(hidpp, params[2], params[3]);
|
|
|
|
*level = hidpp20_unifiedbattery_map_level(hidpp, params[1]);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp20_query_battery_info_1004(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
u8 feature_type;
|
|
|
|
int ret;
|
|
|
|
u8 state_of_charge;
|
|
|
|
int status, level;
|
|
|
|
|
|
|
|
if (hidpp->battery.feature_index == 0xff) {
|
|
|
|
ret = hidpp_root_get_feature(hidpp,
|
|
|
|
HIDPP_PAGE_UNIFIED_BATTERY,
|
|
|
|
&hidpp->battery.feature_index,
|
|
|
|
&feature_type);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = hidpp20_unifiedbattery_get_capabilities(hidpp,
|
|
|
|
hidpp->battery.feature_index);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = hidpp20_unifiedbattery_get_status(hidpp,
|
|
|
|
hidpp->battery.feature_index,
|
|
|
|
&state_of_charge,
|
|
|
|
&status,
|
|
|
|
&level);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
hidpp->capabilities |= HIDPP_CAPABILITY_UNIFIED_BATTERY;
|
|
|
|
hidpp->battery.capacity = state_of_charge;
|
|
|
|
hidpp->battery.status = status;
|
|
|
|
hidpp->battery.level = level;
|
|
|
|
hidpp->battery.online = true;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp20_battery_event_1004(struct hidpp_device *hidpp,
|
|
|
|
u8 *data, int size)
|
|
|
|
{
|
|
|
|
struct hidpp_report *report = (struct hidpp_report *)data;
|
|
|
|
u8 *params = (u8 *)report->fap.params;
|
|
|
|
int state_of_charge, status, level;
|
|
|
|
bool changed;
|
|
|
|
|
|
|
|
if (report->fap.feature_index != hidpp->battery.feature_index ||
|
|
|
|
report->fap.funcindex_clientid != EVENT_UNIFIED_BATTERY_STATUS_EVENT)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
state_of_charge = params[0];
|
|
|
|
status = hidpp20_unifiedbattery_map_status(hidpp, params[2], params[3]);
|
|
|
|
level = hidpp20_unifiedbattery_map_level(hidpp, params[1]);
|
|
|
|
|
|
|
|
changed = status != hidpp->battery.status ||
|
|
|
|
(state_of_charge != hidpp->battery.capacity &&
|
|
|
|
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_PERCENTAGE) ||
|
|
|
|
(level != hidpp->battery.level &&
|
|
|
|
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS);
|
|
|
|
|
|
|
|
if (changed) {
|
|
|
|
hidpp->battery.capacity = state_of_charge;
|
|
|
|
hidpp->battery.status = status;
|
|
|
|
hidpp->battery.level = level;
|
|
|
|
if (hidpp->battery.ps)
|
|
|
|
power_supply_changed(hidpp->battery.ps);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* Battery feature helpers */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
2016-06-29 17:28:01 +08:00
|
|
|
static enum power_supply_property hidpp_battery_props[] = {
|
2017-03-27 22:59:34 +08:00
|
|
|
POWER_SUPPLY_PROP_ONLINE,
|
2016-06-29 17:28:01 +08:00
|
|
|
POWER_SUPPLY_PROP_STATUS,
|
2017-03-27 22:59:22 +08:00
|
|
|
POWER_SUPPLY_PROP_SCOPE,
|
2017-03-27 22:59:30 +08:00
|
|
|
POWER_SUPPLY_PROP_MODEL_NAME,
|
|
|
|
POWER_SUPPLY_PROP_MANUFACTURER,
|
|
|
|
POWER_SUPPLY_PROP_SERIAL_NUMBER,
|
2017-03-27 22:59:36 +08:00
|
|
|
0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY, */
|
|
|
|
0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY_LEVEL, */
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
0, /* placeholder for POWER_SUPPLY_PROP_VOLTAGE_NOW, */
|
2016-06-29 17:28:01 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int hidpp_battery_get_property(struct power_supply *psy,
|
|
|
|
enum power_supply_property psp,
|
|
|
|
union power_supply_propval *val)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = power_supply_get_drvdata(psy);
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
switch(psp) {
|
|
|
|
case POWER_SUPPLY_PROP_STATUS:
|
|
|
|
val->intval = hidpp->battery.status;
|
|
|
|
break;
|
|
|
|
case POWER_SUPPLY_PROP_CAPACITY:
|
2017-03-27 22:59:35 +08:00
|
|
|
val->intval = hidpp->battery.capacity;
|
2016-06-29 17:28:01 +08:00
|
|
|
break;
|
2017-03-27 22:59:36 +08:00
|
|
|
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
|
|
|
|
val->intval = hidpp->battery.level;
|
|
|
|
break;
|
2017-03-27 22:59:22 +08:00
|
|
|
case POWER_SUPPLY_PROP_SCOPE:
|
|
|
|
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
|
|
|
|
break;
|
2017-03-27 22:59:34 +08:00
|
|
|
case POWER_SUPPLY_PROP_ONLINE:
|
|
|
|
val->intval = hidpp->battery.online;
|
|
|
|
break;
|
2017-03-27 22:59:30 +08:00
|
|
|
case POWER_SUPPLY_PROP_MODEL_NAME:
|
|
|
|
if (!strncmp(hidpp->name, "Logitech ", 9))
|
|
|
|
val->strval = hidpp->name + 9;
|
|
|
|
else
|
|
|
|
val->strval = hidpp->name;
|
|
|
|
break;
|
|
|
|
case POWER_SUPPLY_PROP_MANUFACTURER:
|
|
|
|
val->strval = "Logitech";
|
|
|
|
break;
|
|
|
|
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
|
|
|
|
val->strval = hidpp->hid_dev->uniq;
|
|
|
|
break;
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
2022-06-05 16:43:43 +08:00
|
|
|
/* hardware reports voltage in mV. sysfs expects uV */
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
val->intval = hidpp->battery.voltage * 1000;
|
|
|
|
break;
|
|
|
|
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
|
|
|
val->intval = hidpp->battery.charge_type;
|
|
|
|
break;
|
2016-06-29 17:28:01 +08:00
|
|
|
default:
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-10-28 01:44:13 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* 0x1d4b: Wireless device status */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#define HIDPP_PAGE_WIRELESS_DEVICE_STATUS 0x1d4b
|
|
|
|
|
|
|
|
static int hidpp_set_wireless_feature_index(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
u8 feature_type;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = hidpp_root_get_feature(hidpp,
|
|
|
|
HIDPP_PAGE_WIRELESS_DEVICE_STATUS,
|
|
|
|
&hidpp->wireless_feature_index,
|
|
|
|
&feature_type);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-12-05 08:42:27 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* 0x2120: Hi-resolution scrolling */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#define HIDPP_PAGE_HI_RESOLUTION_SCROLLING 0x2120
|
|
|
|
|
|
|
|
#define CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE 0x10
|
|
|
|
|
|
|
|
static int hidpp_hrs_set_highres_scrolling_mode(struct hidpp_device *hidpp,
|
|
|
|
bool enabled, u8 *multiplier)
|
|
|
|
{
|
|
|
|
u8 feature_index;
|
|
|
|
u8 feature_type;
|
|
|
|
int ret;
|
|
|
|
u8 params[1];
|
|
|
|
struct hidpp_report response;
|
|
|
|
|
|
|
|
ret = hidpp_root_get_feature(hidpp,
|
|
|
|
HIDPP_PAGE_HI_RESOLUTION_SCROLLING,
|
|
|
|
&feature_index,
|
|
|
|
&feature_type);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
params[0] = enabled ? BIT(0) : 0;
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
|
|
CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE,
|
|
|
|
params, sizeof(params), &response);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
*multiplier = response.fap.params[1];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* 0x2121: HiRes Wheel */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#define HIDPP_PAGE_HIRES_WHEEL 0x2121
|
|
|
|
|
|
|
|
#define CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY 0x00
|
|
|
|
#define CMD_HIRES_WHEEL_SET_WHEEL_MODE 0x20
|
|
|
|
|
|
|
|
static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp,
|
|
|
|
u8 *multiplier)
|
|
|
|
{
|
|
|
|
u8 feature_index;
|
|
|
|
u8 feature_type;
|
|
|
|
int ret;
|
|
|
|
struct hidpp_report response;
|
|
|
|
|
|
|
|
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
|
|
|
|
&feature_index, &feature_type);
|
|
|
|
if (ret)
|
|
|
|
goto return_default;
|
|
|
|
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
|
|
CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY,
|
|
|
|
NULL, 0, &response);
|
|
|
|
if (ret)
|
|
|
|
goto return_default;
|
|
|
|
|
|
|
|
*multiplier = response.fap.params[0];
|
|
|
|
return 0;
|
|
|
|
return_default:
|
|
|
|
hid_warn(hidpp->hid_dev,
|
|
|
|
"Couldn't get wheel multiplier (error %d)\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert,
|
|
|
|
bool high_resolution, bool use_hidpp)
|
|
|
|
{
|
|
|
|
u8 feature_index;
|
|
|
|
u8 feature_type;
|
|
|
|
int ret;
|
|
|
|
u8 params[1];
|
|
|
|
struct hidpp_report response;
|
|
|
|
|
|
|
|
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
|
|
|
|
&feature_index, &feature_type);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
params[0] = (invert ? BIT(2) : 0) |
|
|
|
|
(high_resolution ? BIT(1) : 0) |
|
|
|
|
(use_hidpp ? BIT(0) : 0);
|
|
|
|
|
|
|
|
return hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
|
|
CMD_HIRES_WHEEL_SET_WHEEL_MODE,
|
|
|
|
params, sizeof(params), &response);
|
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:37 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_MILEAGE;
|
|
|
|
|
|
|
|
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 capacity, lux, status;
|
|
|
|
u8 function;
|
|
|
|
|
|
|
|
function = report->fap.funcindex_clientid;
|
|
|
|
|
|
|
|
|
|
|
|
if (report->fap.feature_index != hidpp->battery.solar_feature_index ||
|
|
|
|
!(function == EVENT_SOLAR_BATTERY_BROADCAST ||
|
|
|
|
function == EVENT_SOLAR_BATTERY_LIGHT_MEASURE ||
|
|
|
|
function == EVENT_SOLAR_CHECK_LIGHT_BUTTON))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
capacity = report->fap.params[0];
|
|
|
|
|
|
|
|
switch (function) {
|
|
|
|
case 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;
|
|
|
|
break;
|
|
|
|
case EVENT_SOLAR_CHECK_LIGHT_BUTTON:
|
|
|
|
default:
|
|
|
|
if (capacity < hidpp->battery.capacity)
|
|
|
|
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
|
|
|
else
|
|
|
|
status = POWER_SUPPLY_STATUS_CHARGING;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (capacity == 100)
|
|
|
|
status = POWER_SUPPLY_STATUS_FULL;
|
|
|
|
|
|
|
|
hidpp->battery.online = true;
|
|
|
|
if (capacity != hidpp->battery.capacity ||
|
|
|
|
status != hidpp->battery.status) {
|
|
|
|
hidpp->battery.capacity = capacity;
|
|
|
|
hidpp->battery.status = status;
|
|
|
|
if (hidpp->battery.ps)
|
|
|
|
power_supply_changed(hidpp->battery.ps);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-03 21:08:30 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* 0x6010: Touchpad FW items */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#define HIDPP_PAGE_TOUCHPAD_FW_ITEMS 0x6010
|
|
|
|
|
|
|
|
#define CMD_TOUCHPAD_FW_ITEMS_SET 0x10
|
|
|
|
|
|
|
|
struct hidpp_touchpad_fw_items {
|
|
|
|
uint8_t presence;
|
|
|
|
uint8_t desired_state;
|
|
|
|
uint8_t state;
|
|
|
|
uint8_t persistent;
|
|
|
|
};
|
|
|
|
|
2021-03-26 22:34:49 +08:00
|
|
|
/*
|
2015-09-03 21:08:30 +08:00
|
|
|
* send a set state command to the device by reading the current items->state
|
|
|
|
* field. items is then filled with the current state.
|
|
|
|
*/
|
|
|
|
static int hidpp_touchpad_fw_items_set(struct hidpp_device *hidpp,
|
|
|
|
u8 feature_index,
|
|
|
|
struct hidpp_touchpad_fw_items *items)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
|
|
|
u8 *params = (u8 *)response.fap.params;
|
|
|
|
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
|
|
CMD_TOUCHPAD_FW_ITEMS_SET, &items->state, 1, &response);
|
|
|
|
|
|
|
|
if (ret > 0) {
|
|
|
|
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
|
|
|
__func__, ret);
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
items->presence = params[0];
|
|
|
|
items->desired_state = params[1];
|
|
|
|
items->state = params[2];
|
|
|
|
items->persistent = params[3];
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* 0x6100: TouchPadRawXY */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#define HIDPP_PAGE_TOUCHPAD_RAW_XY 0x6100
|
|
|
|
|
2022-08-30 21:25:49 +08:00
|
|
|
#define CMD_TOUCHPAD_GET_RAW_INFO 0x00
|
|
|
|
#define CMD_TOUCHPAD_SET_RAW_REPORT_STATE 0x20
|
2014-10-01 01:18:33 +08:00
|
|
|
|
|
|
|
#define EVENT_TOUCHPAD_RAW_XY 0x00
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
#define TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT 0x01
|
|
|
|
#define TOUCHPAD_RAW_XY_ORIGIN_UPPER_LEFT 0x03
|
|
|
|
|
|
|
|
struct hidpp_touchpad_raw_info {
|
|
|
|
u16 x_size;
|
|
|
|
u16 y_size;
|
|
|
|
u8 z_range;
|
|
|
|
u8 area_range;
|
|
|
|
u8 timestamp_unit;
|
|
|
|
u8 maxcontacts;
|
|
|
|
u8 origin;
|
|
|
|
u16 res;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct hidpp_touchpad_raw_xy_finger {
|
|
|
|
u8 contact_type;
|
|
|
|
u8 contact_status;
|
|
|
|
u16 x;
|
|
|
|
u16 y;
|
|
|
|
u8 z;
|
|
|
|
u8 area;
|
|
|
|
u8 finger_id;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct hidpp_touchpad_raw_xy {
|
|
|
|
u16 timestamp;
|
|
|
|
struct hidpp_touchpad_raw_xy_finger fingers[2];
|
|
|
|
u8 spurious_flag;
|
|
|
|
u8 end_of_frame;
|
|
|
|
u8 finger_count;
|
|
|
|
u8 button;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int hidpp_touchpad_get_raw_info(struct hidpp_device *hidpp,
|
|
|
|
u8 feature_index, struct hidpp_touchpad_raw_info *raw_info)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
int ret;
|
|
|
|
u8 *params = (u8 *)response.fap.params;
|
|
|
|
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
|
|
CMD_TOUCHPAD_GET_RAW_INFO, NULL, 0, &response);
|
|
|
|
|
2014-11-04 05:09:58 +08:00
|
|
|
if (ret > 0) {
|
|
|
|
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
|
|
|
__func__, ret);
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
2014-10-01 01:18:27 +08:00
|
|
|
if (ret)
|
2014-11-04 05:09:58 +08:00
|
|
|
return ret;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
raw_info->x_size = get_unaligned_be16(¶ms[0]);
|
|
|
|
raw_info->y_size = get_unaligned_be16(¶ms[2]);
|
|
|
|
raw_info->z_range = params[4];
|
|
|
|
raw_info->area_range = params[5];
|
|
|
|
raw_info->maxcontacts = params[7];
|
|
|
|
raw_info->origin = params[8];
|
|
|
|
/* res is given in unit per inch */
|
|
|
|
raw_info->res = get_unaligned_be16(¶ms[13]) * 2 / 51;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:33 +08:00
|
|
|
static int hidpp_touchpad_set_raw_report_state(struct hidpp_device *hidpp_dev,
|
|
|
|
u8 feature_index, bool send_raw_reports,
|
|
|
|
bool sensor_enhanced_settings)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Params:
|
|
|
|
* bit 0 - enable raw
|
|
|
|
* bit 1 - 16bit Z, no area
|
|
|
|
* bit 2 - enhanced sensitivity
|
|
|
|
* bit 3 - width, height (4 bits each) instead of area
|
|
|
|
* bit 4 - send raw + gestures (degrades smoothness)
|
|
|
|
* remaining bits - reserved
|
|
|
|
*/
|
|
|
|
u8 params = send_raw_reports | (sensor_enhanced_settings << 2);
|
|
|
|
|
|
|
|
return hidpp_send_fap_command_sync(hidpp_dev, feature_index,
|
|
|
|
CMD_TOUCHPAD_SET_RAW_REPORT_STATE, ¶ms, 1, &response);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hidpp_touchpad_touch_event(u8 *data,
|
|
|
|
struct hidpp_touchpad_raw_xy_finger *finger)
|
|
|
|
{
|
|
|
|
u8 x_m = data[0] << 2;
|
|
|
|
u8 y_m = data[2] << 2;
|
|
|
|
|
|
|
|
finger->x = x_m << 6 | data[1];
|
|
|
|
finger->y = y_m << 6 | data[3];
|
|
|
|
|
|
|
|
finger->contact_type = data[0] >> 6;
|
|
|
|
finger->contact_status = data[2] >> 6;
|
|
|
|
|
|
|
|
finger->z = data[4];
|
|
|
|
finger->area = data[5];
|
|
|
|
finger->finger_id = data[6] >> 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
|
|
|
|
u8 *data, struct hidpp_touchpad_raw_xy *raw_xy)
|
|
|
|
{
|
|
|
|
memset(raw_xy, 0, sizeof(struct hidpp_touchpad_raw_xy));
|
|
|
|
raw_xy->end_of_frame = data[8] & 0x01;
|
|
|
|
raw_xy->spurious_flag = (data[8] >> 1) & 0x01;
|
|
|
|
raw_xy->finger_count = data[15] & 0x0f;
|
|
|
|
raw_xy->button = (data[8] >> 2) & 0x01;
|
|
|
|
|
|
|
|
if (raw_xy->finger_count) {
|
|
|
|
hidpp_touchpad_touch_event(&data[2], &raw_xy->fingers[0]);
|
|
|
|
hidpp_touchpad_touch_event(&data[9], &raw_xy->fingers[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-11 07:25:15 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* 0x8123: Force feedback support */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#define HIDPP_FF_GET_INFO 0x01
|
|
|
|
#define HIDPP_FF_RESET_ALL 0x11
|
|
|
|
#define HIDPP_FF_DOWNLOAD_EFFECT 0x21
|
|
|
|
#define HIDPP_FF_SET_EFFECT_STATE 0x31
|
|
|
|
#define HIDPP_FF_DESTROY_EFFECT 0x41
|
|
|
|
#define HIDPP_FF_GET_APERTURE 0x51
|
|
|
|
#define HIDPP_FF_SET_APERTURE 0x61
|
|
|
|
#define HIDPP_FF_GET_GLOBAL_GAINS 0x71
|
|
|
|
#define HIDPP_FF_SET_GLOBAL_GAINS 0x81
|
|
|
|
|
|
|
|
#define HIDPP_FF_EFFECT_STATE_GET 0x00
|
|
|
|
#define HIDPP_FF_EFFECT_STATE_STOP 0x01
|
|
|
|
#define HIDPP_FF_EFFECT_STATE_PLAY 0x02
|
|
|
|
#define HIDPP_FF_EFFECT_STATE_PAUSE 0x03
|
|
|
|
|
|
|
|
#define HIDPP_FF_EFFECT_CONSTANT 0x00
|
|
|
|
#define HIDPP_FF_EFFECT_PERIODIC_SINE 0x01
|
|
|
|
#define HIDPP_FF_EFFECT_PERIODIC_SQUARE 0x02
|
|
|
|
#define HIDPP_FF_EFFECT_PERIODIC_TRIANGLE 0x03
|
|
|
|
#define HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHUP 0x04
|
|
|
|
#define HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHDOWN 0x05
|
|
|
|
#define HIDPP_FF_EFFECT_SPRING 0x06
|
|
|
|
#define HIDPP_FF_EFFECT_DAMPER 0x07
|
|
|
|
#define HIDPP_FF_EFFECT_FRICTION 0x08
|
|
|
|
#define HIDPP_FF_EFFECT_INERTIA 0x09
|
|
|
|
#define HIDPP_FF_EFFECT_RAMP 0x0A
|
|
|
|
|
|
|
|
#define HIDPP_FF_EFFECT_AUTOSTART 0x80
|
|
|
|
|
|
|
|
#define HIDPP_FF_EFFECTID_NONE -1
|
|
|
|
#define HIDPP_FF_EFFECTID_AUTOCENTER -2
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
#define HIDPP_AUTOCENTER_PARAMS_LENGTH 18
|
2016-01-11 07:25:15 +08:00
|
|
|
|
|
|
|
#define HIDPP_FF_MAX_PARAMS 20
|
|
|
|
#define HIDPP_FF_RESERVED_SLOTS 1
|
|
|
|
|
|
|
|
struct hidpp_ff_private_data {
|
|
|
|
struct hidpp_device *hidpp;
|
|
|
|
u8 feature_index;
|
|
|
|
u8 version;
|
|
|
|
u16 gain;
|
|
|
|
s16 range;
|
|
|
|
u8 slot_autocenter;
|
|
|
|
u8 num_effects;
|
|
|
|
int *effect_ids;
|
|
|
|
struct workqueue_struct *wq;
|
|
|
|
atomic_t workqueue_size;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct hidpp_ff_work_data {
|
|
|
|
struct work_struct work;
|
|
|
|
struct hidpp_ff_private_data *data;
|
|
|
|
int effect_id;
|
|
|
|
u8 command;
|
|
|
|
u8 params[HIDPP_FF_MAX_PARAMS];
|
|
|
|
u8 size;
|
|
|
|
};
|
|
|
|
|
2018-12-05 08:42:25 +08:00
|
|
|
static const signed short hidpp_ff_effects[] = {
|
2016-01-11 07:25:15 +08:00
|
|
|
FF_CONSTANT,
|
|
|
|
FF_PERIODIC,
|
|
|
|
FF_SINE,
|
|
|
|
FF_SQUARE,
|
|
|
|
FF_SAW_UP,
|
|
|
|
FF_SAW_DOWN,
|
|
|
|
FF_TRIANGLE,
|
|
|
|
FF_SPRING,
|
|
|
|
FF_DAMPER,
|
|
|
|
FF_AUTOCENTER,
|
|
|
|
FF_GAIN,
|
|
|
|
-1
|
|
|
|
};
|
|
|
|
|
2018-12-05 08:42:25 +08:00
|
|
|
static const signed short hidpp_ff_effects_v2[] = {
|
2016-01-11 07:25:15 +08:00
|
|
|
FF_RAMP,
|
|
|
|
FF_FRICTION,
|
|
|
|
FF_INERTIA,
|
|
|
|
-1
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u8 HIDPP_FF_CONDITION_CMDS[] = {
|
|
|
|
HIDPP_FF_EFFECT_SPRING,
|
|
|
|
HIDPP_FF_EFFECT_FRICTION,
|
|
|
|
HIDPP_FF_EFFECT_DAMPER,
|
|
|
|
HIDPP_FF_EFFECT_INERTIA
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *HIDPP_FF_CONDITION_NAMES[] = {
|
|
|
|
"spring",
|
|
|
|
"friction",
|
|
|
|
"damper",
|
|
|
|
"inertia"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static u8 hidpp_ff_find_effect(struct hidpp_ff_private_data *data, int effect_id)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < data->num_effects; i++)
|
|
|
|
if (data->effect_ids[i] == effect_id)
|
|
|
|
return i+1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hidpp_ff_work_handler(struct work_struct *w)
|
|
|
|
{
|
|
|
|
struct hidpp_ff_work_data *wd = container_of(w, struct hidpp_ff_work_data, work);
|
|
|
|
struct hidpp_ff_private_data *data = wd->data;
|
|
|
|
struct hidpp_report response;
|
|
|
|
u8 slot;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* add slot number if needed */
|
|
|
|
switch (wd->effect_id) {
|
|
|
|
case HIDPP_FF_EFFECTID_AUTOCENTER:
|
|
|
|
wd->params[0] = data->slot_autocenter;
|
|
|
|
break;
|
|
|
|
case HIDPP_FF_EFFECTID_NONE:
|
|
|
|
/* leave slot as zero */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* find current slot for effect */
|
|
|
|
wd->params[0] = hidpp_ff_find_effect(data, wd->effect_id);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* send command and wait for reply */
|
|
|
|
ret = hidpp_send_fap_command_sync(data->hidpp, data->feature_index,
|
|
|
|
wd->command, wd->params, wd->size, &response);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
hid_err(data->hidpp->hid_dev, "Failed to send command to device!\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse return data */
|
|
|
|
switch (wd->command) {
|
|
|
|
case HIDPP_FF_DOWNLOAD_EFFECT:
|
|
|
|
slot = response.fap.params[0];
|
|
|
|
if (slot > 0 && slot <= data->num_effects) {
|
|
|
|
if (wd->effect_id >= 0)
|
|
|
|
/* regular effect uploaded */
|
|
|
|
data->effect_ids[slot-1] = wd->effect_id;
|
|
|
|
else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER)
|
|
|
|
/* autocenter spring uploaded */
|
|
|
|
data->slot_autocenter = slot;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HIDPP_FF_DESTROY_EFFECT:
|
|
|
|
if (wd->effect_id >= 0)
|
|
|
|
/* regular effect destroyed */
|
|
|
|
data->effect_ids[wd->params[0]-1] = -1;
|
|
|
|
else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER)
|
|
|
|
/* autocenter spring destoyed */
|
|
|
|
data->slot_autocenter = 0;
|
|
|
|
break;
|
|
|
|
case HIDPP_FF_SET_GLOBAL_GAINS:
|
|
|
|
data->gain = (wd->params[0] << 8) + wd->params[1];
|
|
|
|
break;
|
|
|
|
case HIDPP_FF_SET_APERTURE:
|
|
|
|
data->range = (wd->params[0] << 8) + wd->params[1];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* no action needed */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
atomic_dec(&data->workqueue_size);
|
|
|
|
kfree(wd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp_ff_queue_work(struct hidpp_ff_private_data *data, int effect_id, u8 command, u8 *params, u8 size)
|
|
|
|
{
|
|
|
|
struct hidpp_ff_work_data *wd = kzalloc(sizeof(*wd), GFP_KERNEL);
|
|
|
|
int s;
|
|
|
|
|
|
|
|
if (!wd)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
INIT_WORK(&wd->work, hidpp_ff_work_handler);
|
|
|
|
|
|
|
|
wd->data = data;
|
|
|
|
wd->effect_id = effect_id;
|
|
|
|
wd->command = command;
|
|
|
|
wd->size = size;
|
|
|
|
memcpy(wd->params, params, size);
|
|
|
|
|
2021-07-23 23:41:52 +08:00
|
|
|
s = atomic_inc_return(&data->workqueue_size);
|
2016-01-11 07:25:15 +08:00
|
|
|
queue_work(data->wq, &wd->work);
|
|
|
|
|
|
|
|
/* warn about excessive queue size */
|
|
|
|
if (s >= 20 && s % 20 == 0)
|
|
|
|
hid_warn(data->hidpp->hid_dev, "Force feedback command queue contains %d commands, causing substantial delays!", s);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp_ff_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
|
|
|
|
{
|
|
|
|
struct hidpp_ff_private_data *data = dev->ff->private;
|
|
|
|
u8 params[20];
|
|
|
|
u8 size;
|
|
|
|
int force;
|
|
|
|
|
|
|
|
/* set common parameters */
|
|
|
|
params[2] = effect->replay.length >> 8;
|
|
|
|
params[3] = effect->replay.length & 255;
|
|
|
|
params[4] = effect->replay.delay >> 8;
|
|
|
|
params[5] = effect->replay.delay & 255;
|
|
|
|
|
|
|
|
switch (effect->type) {
|
|
|
|
case FF_CONSTANT:
|
|
|
|
force = (effect->u.constant.level * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
|
|
|
|
params[1] = HIDPP_FF_EFFECT_CONSTANT;
|
|
|
|
params[6] = force >> 8;
|
|
|
|
params[7] = force & 255;
|
|
|
|
params[8] = effect->u.constant.envelope.attack_level >> 7;
|
|
|
|
params[9] = effect->u.constant.envelope.attack_length >> 8;
|
|
|
|
params[10] = effect->u.constant.envelope.attack_length & 255;
|
|
|
|
params[11] = effect->u.constant.envelope.fade_level >> 7;
|
|
|
|
params[12] = effect->u.constant.envelope.fade_length >> 8;
|
|
|
|
params[13] = effect->u.constant.envelope.fade_length & 255;
|
|
|
|
size = 14;
|
|
|
|
dbg_hid("Uploading constant force level=%d in dir %d = %d\n",
|
|
|
|
effect->u.constant.level,
|
|
|
|
effect->direction, force);
|
|
|
|
dbg_hid(" envelope attack=(%d, %d ms) fade=(%d, %d ms)\n",
|
|
|
|
effect->u.constant.envelope.attack_level,
|
|
|
|
effect->u.constant.envelope.attack_length,
|
|
|
|
effect->u.constant.envelope.fade_level,
|
|
|
|
effect->u.constant.envelope.fade_length);
|
|
|
|
break;
|
|
|
|
case FF_PERIODIC:
|
|
|
|
{
|
|
|
|
switch (effect->u.periodic.waveform) {
|
|
|
|
case FF_SINE:
|
|
|
|
params[1] = HIDPP_FF_EFFECT_PERIODIC_SINE;
|
|
|
|
break;
|
|
|
|
case FF_SQUARE:
|
|
|
|
params[1] = HIDPP_FF_EFFECT_PERIODIC_SQUARE;
|
|
|
|
break;
|
|
|
|
case FF_SAW_UP:
|
|
|
|
params[1] = HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHUP;
|
|
|
|
break;
|
|
|
|
case FF_SAW_DOWN:
|
|
|
|
params[1] = HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHDOWN;
|
|
|
|
break;
|
|
|
|
case FF_TRIANGLE:
|
|
|
|
params[1] = HIDPP_FF_EFFECT_PERIODIC_TRIANGLE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
hid_err(data->hidpp->hid_dev, "Unexpected periodic waveform type %i!\n", effect->u.periodic.waveform);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
force = (effect->u.periodic.magnitude * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
|
|
|
|
params[6] = effect->u.periodic.magnitude >> 8;
|
|
|
|
params[7] = effect->u.periodic.magnitude & 255;
|
|
|
|
params[8] = effect->u.periodic.offset >> 8;
|
|
|
|
params[9] = effect->u.periodic.offset & 255;
|
|
|
|
params[10] = effect->u.periodic.period >> 8;
|
|
|
|
params[11] = effect->u.periodic.period & 255;
|
|
|
|
params[12] = effect->u.periodic.phase >> 8;
|
|
|
|
params[13] = effect->u.periodic.phase & 255;
|
|
|
|
params[14] = effect->u.periodic.envelope.attack_level >> 7;
|
|
|
|
params[15] = effect->u.periodic.envelope.attack_length >> 8;
|
|
|
|
params[16] = effect->u.periodic.envelope.attack_length & 255;
|
|
|
|
params[17] = effect->u.periodic.envelope.fade_level >> 7;
|
|
|
|
params[18] = effect->u.periodic.envelope.fade_length >> 8;
|
|
|
|
params[19] = effect->u.periodic.envelope.fade_length & 255;
|
|
|
|
size = 20;
|
|
|
|
dbg_hid("Uploading periodic force mag=%d/dir=%d, offset=%d, period=%d ms, phase=%d\n",
|
|
|
|
effect->u.periodic.magnitude, effect->direction,
|
|
|
|
effect->u.periodic.offset,
|
|
|
|
effect->u.periodic.period,
|
|
|
|
effect->u.periodic.phase);
|
|
|
|
dbg_hid(" envelope attack=(%d, %d ms) fade=(%d, %d ms)\n",
|
|
|
|
effect->u.periodic.envelope.attack_level,
|
|
|
|
effect->u.periodic.envelope.attack_length,
|
|
|
|
effect->u.periodic.envelope.fade_level,
|
|
|
|
effect->u.periodic.envelope.fade_length);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case FF_RAMP:
|
|
|
|
params[1] = HIDPP_FF_EFFECT_RAMP;
|
|
|
|
force = (effect->u.ramp.start_level * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
|
|
|
|
params[6] = force >> 8;
|
|
|
|
params[7] = force & 255;
|
|
|
|
force = (effect->u.ramp.end_level * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
|
|
|
|
params[8] = force >> 8;
|
|
|
|
params[9] = force & 255;
|
|
|
|
params[10] = effect->u.ramp.envelope.attack_level >> 7;
|
|
|
|
params[11] = effect->u.ramp.envelope.attack_length >> 8;
|
|
|
|
params[12] = effect->u.ramp.envelope.attack_length & 255;
|
|
|
|
params[13] = effect->u.ramp.envelope.fade_level >> 7;
|
|
|
|
params[14] = effect->u.ramp.envelope.fade_length >> 8;
|
|
|
|
params[15] = effect->u.ramp.envelope.fade_length & 255;
|
|
|
|
size = 16;
|
|
|
|
dbg_hid("Uploading ramp force level=%d -> %d in dir %d = %d\n",
|
|
|
|
effect->u.ramp.start_level,
|
|
|
|
effect->u.ramp.end_level,
|
|
|
|
effect->direction, force);
|
|
|
|
dbg_hid(" envelope attack=(%d, %d ms) fade=(%d, %d ms)\n",
|
|
|
|
effect->u.ramp.envelope.attack_level,
|
|
|
|
effect->u.ramp.envelope.attack_length,
|
|
|
|
effect->u.ramp.envelope.fade_level,
|
|
|
|
effect->u.ramp.envelope.fade_length);
|
|
|
|
break;
|
|
|
|
case FF_FRICTION:
|
|
|
|
case FF_INERTIA:
|
|
|
|
case FF_SPRING:
|
|
|
|
case FF_DAMPER:
|
|
|
|
params[1] = HIDPP_FF_CONDITION_CMDS[effect->type - FF_SPRING];
|
|
|
|
params[6] = effect->u.condition[0].left_saturation >> 9;
|
|
|
|
params[7] = (effect->u.condition[0].left_saturation >> 1) & 255;
|
|
|
|
params[8] = effect->u.condition[0].left_coeff >> 8;
|
|
|
|
params[9] = effect->u.condition[0].left_coeff & 255;
|
|
|
|
params[10] = effect->u.condition[0].deadband >> 9;
|
|
|
|
params[11] = (effect->u.condition[0].deadband >> 1) & 255;
|
|
|
|
params[12] = effect->u.condition[0].center >> 8;
|
|
|
|
params[13] = effect->u.condition[0].center & 255;
|
|
|
|
params[14] = effect->u.condition[0].right_coeff >> 8;
|
|
|
|
params[15] = effect->u.condition[0].right_coeff & 255;
|
|
|
|
params[16] = effect->u.condition[0].right_saturation >> 9;
|
|
|
|
params[17] = (effect->u.condition[0].right_saturation >> 1) & 255;
|
|
|
|
size = 18;
|
|
|
|
dbg_hid("Uploading %s force left coeff=%d, left sat=%d, right coeff=%d, right sat=%d\n",
|
|
|
|
HIDPP_FF_CONDITION_NAMES[effect->type - FF_SPRING],
|
|
|
|
effect->u.condition[0].left_coeff,
|
|
|
|
effect->u.condition[0].left_saturation,
|
|
|
|
effect->u.condition[0].right_coeff,
|
|
|
|
effect->u.condition[0].right_saturation);
|
|
|
|
dbg_hid(" deadband=%d, center=%d\n",
|
|
|
|
effect->u.condition[0].deadband,
|
|
|
|
effect->u.condition[0].center);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
hid_err(data->hidpp->hid_dev, "Unexpected force type %i!\n", effect->type);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hidpp_ff_queue_work(data, effect->id, HIDPP_FF_DOWNLOAD_EFFECT, params, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp_ff_playback(struct input_dev *dev, int effect_id, int value)
|
|
|
|
{
|
|
|
|
struct hidpp_ff_private_data *data = dev->ff->private;
|
|
|
|
u8 params[2];
|
|
|
|
|
|
|
|
params[1] = value ? HIDPP_FF_EFFECT_STATE_PLAY : HIDPP_FF_EFFECT_STATE_STOP;
|
|
|
|
|
|
|
|
dbg_hid("St%sing playback of effect %d.\n", value?"art":"opp", effect_id);
|
|
|
|
|
|
|
|
return hidpp_ff_queue_work(data, effect_id, HIDPP_FF_SET_EFFECT_STATE, params, ARRAY_SIZE(params));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp_ff_erase_effect(struct input_dev *dev, int effect_id)
|
|
|
|
{
|
|
|
|
struct hidpp_ff_private_data *data = dev->ff->private;
|
|
|
|
u8 slot = 0;
|
|
|
|
|
|
|
|
dbg_hid("Erasing effect %d.\n", effect_id);
|
|
|
|
|
|
|
|
return hidpp_ff_queue_work(data, effect_id, HIDPP_FF_DESTROY_EFFECT, &slot, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hidpp_ff_set_autocenter(struct input_dev *dev, u16 magnitude)
|
|
|
|
{
|
|
|
|
struct hidpp_ff_private_data *data = dev->ff->private;
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
u8 params[HIDPP_AUTOCENTER_PARAMS_LENGTH];
|
2016-01-11 07:25:15 +08:00
|
|
|
|
|
|
|
dbg_hid("Setting autocenter to %d.\n", magnitude);
|
|
|
|
|
|
|
|
/* start a standard spring effect */
|
|
|
|
params[1] = HIDPP_FF_EFFECT_SPRING | HIDPP_FF_EFFECT_AUTOSTART;
|
|
|
|
/* zero delay and duration */
|
|
|
|
params[2] = params[3] = params[4] = params[5] = 0;
|
|
|
|
/* set coeff to 25% of saturation */
|
|
|
|
params[8] = params[14] = magnitude >> 11;
|
|
|
|
params[9] = params[15] = (magnitude >> 3) & 255;
|
|
|
|
params[6] = params[16] = magnitude >> 9;
|
|
|
|
params[7] = params[17] = (magnitude >> 1) & 255;
|
|
|
|
/* zero deadband and center */
|
|
|
|
params[10] = params[11] = params[12] = params[13] = 0;
|
|
|
|
|
|
|
|
hidpp_ff_queue_work(data, HIDPP_FF_EFFECTID_AUTOCENTER, HIDPP_FF_DOWNLOAD_EFFECT, params, ARRAY_SIZE(params));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hidpp_ff_set_gain(struct input_dev *dev, u16 gain)
|
|
|
|
{
|
|
|
|
struct hidpp_ff_private_data *data = dev->ff->private;
|
|
|
|
u8 params[4];
|
|
|
|
|
|
|
|
dbg_hid("Setting gain to %d.\n", gain);
|
|
|
|
|
|
|
|
params[0] = gain >> 8;
|
|
|
|
params[1] = gain & 255;
|
|
|
|
params[2] = 0; /* no boost */
|
|
|
|
params[3] = 0;
|
|
|
|
|
|
|
|
hidpp_ff_queue_work(data, HIDPP_FF_EFFECTID_NONE, HIDPP_FF_SET_GLOBAL_GAINS, params, ARRAY_SIZE(params));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t hidpp_ff_range_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = to_hid_device(dev);
|
|
|
|
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
|
|
|
struct input_dev *idev = hidinput->input;
|
|
|
|
struct hidpp_ff_private_data *data = idev->ff->private;
|
|
|
|
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%u\n", data->range);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t hidpp_ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = to_hid_device(dev);
|
|
|
|
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
|
|
|
struct input_dev *idev = hidinput->input;
|
|
|
|
struct hidpp_ff_private_data *data = idev->ff->private;
|
|
|
|
u8 params[2];
|
|
|
|
int range = simple_strtoul(buf, NULL, 10);
|
|
|
|
|
|
|
|
range = clamp(range, 180, 900);
|
|
|
|
|
|
|
|
params[0] = range >> 8;
|
|
|
|
params[1] = range & 0x00FF;
|
|
|
|
|
|
|
|
hidpp_ff_queue_work(data, -1, HIDPP_FF_SET_APERTURE, params, ARRAY_SIZE(params));
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, hidpp_ff_range_show, hidpp_ff_range_store);
|
|
|
|
|
|
|
|
static void hidpp_ff_destroy(struct ff_device *ff)
|
|
|
|
{
|
|
|
|
struct hidpp_ff_private_data *data = ff->private;
|
2019-10-18 12:45:17 +08:00
|
|
|
struct hid_device *hid = data->hidpp->hid_dev;
|
2016-01-11 07:25:15 +08:00
|
|
|
|
2019-10-18 12:45:17 +08:00
|
|
|
hid_info(hid, "Unloading HID++ force feedback.\n");
|
|
|
|
|
|
|
|
device_remove_file(&hid->dev, &dev_attr_range);
|
|
|
|
destroy_workqueue(data->wq);
|
2016-01-11 07:25:15 +08:00
|
|
|
kfree(data->effect_ids);
|
|
|
|
}
|
|
|
|
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
static int hidpp_ff_init(struct hidpp_device *hidpp,
|
|
|
|
struct hidpp_ff_private_data *data)
|
2016-01-11 07:25:15 +08:00
|
|
|
{
|
|
|
|
struct hid_device *hid = hidpp->hid_dev;
|
2019-10-04 02:53:59 +08:00
|
|
|
struct hid_input *hidinput;
|
|
|
|
struct input_dev *dev;
|
2022-12-15 23:44:16 +08:00
|
|
|
struct usb_device_descriptor *udesc;
|
|
|
|
u16 bcdDevice;
|
2016-01-11 07:25:15 +08:00
|
|
|
struct ff_device *ff;
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
int error, j, num_slots = data->num_effects;
|
2016-01-11 07:25:15 +08:00
|
|
|
u8 version;
|
|
|
|
|
2022-12-15 23:44:16 +08:00
|
|
|
if (!hid_is_usb(hid)) {
|
|
|
|
hid_err(hid, "device is not USB\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2019-10-04 02:53:59 +08:00
|
|
|
if (list_empty(&hid->inputs)) {
|
|
|
|
hid_err(hid, "no inputs found\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
|
|
|
dev = hidinput->input;
|
|
|
|
|
2016-01-11 07:25:15 +08:00
|
|
|
if (!dev) {
|
|
|
|
hid_err(hid, "Struct input_dev not set!\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get firmware release */
|
2022-12-15 23:44:16 +08:00
|
|
|
udesc = &(hid_to_usb_dev(hid)->descriptor);
|
|
|
|
bcdDevice = le16_to_cpu(udesc->bcdDevice);
|
2016-01-11 07:25:15 +08:00
|
|
|
version = bcdDevice & 255;
|
|
|
|
|
|
|
|
/* Set supported force feedback capabilities */
|
2018-12-05 08:42:25 +08:00
|
|
|
for (j = 0; hidpp_ff_effects[j] >= 0; j++)
|
|
|
|
set_bit(hidpp_ff_effects[j], dev->ffbit);
|
2016-01-11 07:25:15 +08:00
|
|
|
if (version > 1)
|
2018-12-05 08:42:25 +08:00
|
|
|
for (j = 0; hidpp_ff_effects_v2[j] >= 0; j++)
|
|
|
|
set_bit(hidpp_ff_effects_v2[j], dev->ffbit);
|
2016-01-11 07:25:15 +08:00
|
|
|
|
|
|
|
error = input_ff_create(dev, num_slots);
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
hid_err(dev, "Failed to create FF device!\n");
|
|
|
|
return error;
|
|
|
|
}
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
/*
|
|
|
|
* Create a copy of passed data, so we can transfer memory
|
|
|
|
* ownership to FF core
|
|
|
|
*/
|
|
|
|
data = kmemdup(data, sizeof(*data), GFP_KERNEL);
|
2016-01-11 07:25:15 +08:00
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
|
|
|
data->effect_ids = kcalloc(num_slots, sizeof(int), GFP_KERNEL);
|
|
|
|
if (!data->effect_ids) {
|
|
|
|
kfree(data);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2019-03-14 13:24:02 +08:00
|
|
|
data->wq = create_singlethread_workqueue("hidpp-ff-sendqueue");
|
|
|
|
if (!data->wq) {
|
|
|
|
kfree(data->effect_ids);
|
|
|
|
kfree(data);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2016-01-11 07:25:15 +08:00
|
|
|
data->hidpp = hidpp;
|
|
|
|
data->version = version;
|
|
|
|
for (j = 0; j < num_slots; j++)
|
|
|
|
data->effect_ids[j] = -1;
|
|
|
|
|
|
|
|
ff = dev->ff;
|
|
|
|
ff->private = data;
|
|
|
|
|
|
|
|
ff->upload = hidpp_ff_upload_effect;
|
|
|
|
ff->erase = hidpp_ff_erase_effect;
|
|
|
|
ff->playback = hidpp_ff_playback;
|
|
|
|
ff->set_gain = hidpp_ff_set_gain;
|
|
|
|
ff->set_autocenter = hidpp_ff_set_autocenter;
|
|
|
|
ff->destroy = hidpp_ff_destroy;
|
|
|
|
|
|
|
|
/* Create sysfs interface */
|
|
|
|
error = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range);
|
|
|
|
if (error)
|
|
|
|
hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d!\n", error);
|
|
|
|
|
|
|
|
/* init the hardware command queue */
|
|
|
|
atomic_set(&data->workqueue_size, 0);
|
|
|
|
|
2017-07-14 21:37:12 +08:00
|
|
|
hid_info(hid, "Force feedback support loaded (firmware release %d).\n",
|
|
|
|
version);
|
2016-01-11 07:25:15 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
/* ************************************************************************** */
|
|
|
|
/* */
|
|
|
|
/* Device Support */
|
|
|
|
/* */
|
|
|
|
/* ************************************************************************** */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* Touchpad HID++ devices */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
2014-10-01 01:18:34 +08:00
|
|
|
#define WTP_MANUAL_RESOLUTION 39
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
struct wtp_data {
|
|
|
|
u16 x_size, y_size;
|
|
|
|
u8 finger_count;
|
|
|
|
u8 mt_feature_index;
|
|
|
|
u8 button_feature_index;
|
|
|
|
u8 maxcontacts;
|
|
|
|
bool flip_y;
|
|
|
|
unsigned int resolution;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|
|
|
struct hid_field *field, struct hid_usage *usage,
|
|
|
|
unsigned long **bit, int *max)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:32 +08:00
|
|
|
static void wtp_populate_input(struct hidpp_device *hidpp,
|
2019-04-20 19:22:09 +08:00
|
|
|
struct input_dev *input_dev)
|
2014-10-01 01:18:27 +08:00
|
|
|
{
|
|
|
|
struct wtp_data *wd = hidpp->private_data;
|
|
|
|
|
|
|
|
__set_bit(EV_ABS, input_dev->evbit);
|
|
|
|
__set_bit(EV_KEY, input_dev->evbit);
|
|
|
|
__clear_bit(EV_REL, input_dev->evbit);
|
|
|
|
__clear_bit(EV_LED, input_dev->evbit);
|
|
|
|
|
|
|
|
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, wd->x_size, 0, 0);
|
|
|
|
input_abs_set_res(input_dev, ABS_MT_POSITION_X, wd->resolution);
|
|
|
|
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, wd->y_size, 0, 0);
|
|
|
|
input_abs_set_res(input_dev, ABS_MT_POSITION_Y, wd->resolution);
|
|
|
|
|
|
|
|
/* Max pressure is not given by the devices, pick one */
|
|
|
|
input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 50, 0, 0);
|
|
|
|
|
|
|
|
input_set_capability(input_dev, EV_KEY, BTN_LEFT);
|
|
|
|
|
2014-10-01 01:18:34 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS)
|
|
|
|
input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
|
|
|
|
else
|
|
|
|
__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
input_mt_init_slots(input_dev, wd->maxcontacts, INPUT_MT_POINTER |
|
|
|
|
INPUT_MT_DROP_UNUSED);
|
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:13 +08:00
|
|
|
static void wtp_touch_event(struct hidpp_device *hidpp,
|
2014-10-01 01:18:27 +08:00
|
|
|
struct hidpp_touchpad_raw_xy_finger *touch_report)
|
|
|
|
{
|
2019-04-20 19:22:13 +08:00
|
|
|
struct wtp_data *wd = hidpp->private_data;
|
2014-10-01 01:18:27 +08:00
|
|
|
int slot;
|
|
|
|
|
|
|
|
if (!touch_report->finger_id || touch_report->contact_type)
|
|
|
|
/* no actual data */
|
|
|
|
return;
|
|
|
|
|
2019-04-20 19:22:13 +08:00
|
|
|
slot = input_mt_get_slot_by_key(hidpp->input, touch_report->finger_id);
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2019-04-20 19:22:13 +08:00
|
|
|
input_mt_slot(hidpp->input, slot);
|
|
|
|
input_mt_report_slot_state(hidpp->input, MT_TOOL_FINGER,
|
2014-10-01 01:18:27 +08:00
|
|
|
touch_report->contact_status);
|
|
|
|
if (touch_report->contact_status) {
|
2019-04-20 19:22:13 +08:00
|
|
|
input_event(hidpp->input, EV_ABS, ABS_MT_POSITION_X,
|
2014-10-01 01:18:27 +08:00
|
|
|
touch_report->x);
|
2019-04-20 19:22:13 +08:00
|
|
|
input_event(hidpp->input, EV_ABS, ABS_MT_POSITION_Y,
|
2014-10-01 01:18:27 +08:00
|
|
|
wd->flip_y ? wd->y_size - touch_report->y :
|
|
|
|
touch_report->y);
|
2019-04-20 19:22:13 +08:00
|
|
|
input_event(hidpp->input, EV_ABS, ABS_MT_PRESSURE,
|
2014-10-01 01:18:27 +08:00
|
|
|
touch_report->area);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wtp_send_raw_xy_event(struct hidpp_device *hidpp,
|
|
|
|
struct hidpp_touchpad_raw_xy *raw)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++)
|
2019-04-20 19:22:13 +08:00
|
|
|
wtp_touch_event(hidpp, &(raw->fingers[i]));
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2014-10-01 01:18:34 +08:00
|
|
|
if (raw->end_of_frame &&
|
|
|
|
!(hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS))
|
2019-04-20 19:22:13 +08:00
|
|
|
input_event(hidpp->input, EV_KEY, BTN_LEFT, raw->button);
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
if (raw->end_of_frame || raw->finger_count <= 2) {
|
2019-04-20 19:22:13 +08:00
|
|
|
input_mt_sync_frame(hidpp->input);
|
|
|
|
input_sync(hidpp->input);
|
2014-10-01 01:18:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wtp_mouse_raw_xy_event(struct hidpp_device *hidpp, u8 *data)
|
|
|
|
{
|
|
|
|
struct wtp_data *wd = hidpp->private_data;
|
|
|
|
u8 c1_area = ((data[7] & 0xf) * (data[7] & 0xf) +
|
|
|
|
(data[7] >> 4) * (data[7] >> 4)) / 2;
|
|
|
|
u8 c2_area = ((data[13] & 0xf) * (data[13] & 0xf) +
|
|
|
|
(data[13] >> 4) * (data[13] >> 4)) / 2;
|
|
|
|
struct hidpp_touchpad_raw_xy raw = {
|
|
|
|
.timestamp = data[1],
|
|
|
|
.fingers = {
|
|
|
|
{
|
|
|
|
.contact_type = 0,
|
|
|
|
.contact_status = !!data[7],
|
|
|
|
.x = get_unaligned_le16(&data[3]),
|
|
|
|
.y = get_unaligned_le16(&data[5]),
|
|
|
|
.z = c1_area,
|
|
|
|
.area = c1_area,
|
|
|
|
.finger_id = data[2],
|
|
|
|
}, {
|
|
|
|
.contact_type = 0,
|
|
|
|
.contact_status = !!data[13],
|
|
|
|
.x = get_unaligned_le16(&data[9]),
|
|
|
|
.y = get_unaligned_le16(&data[11]),
|
|
|
|
.z = c2_area,
|
|
|
|
.area = c2_area,
|
|
|
|
.finger_id = data[8],
|
|
|
|
}
|
|
|
|
},
|
|
|
|
.finger_count = wd->maxcontacts,
|
|
|
|
.spurious_flag = 0,
|
|
|
|
.end_of_frame = (data[0] >> 7) == 0,
|
|
|
|
.button = data[0] & 0x01,
|
|
|
|
};
|
|
|
|
|
|
|
|
wtp_send_raw_xy_event(hidpp, &raw);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
|
|
|
struct wtp_data *wd = hidpp->private_data;
|
2014-10-01 01:18:33 +08:00
|
|
|
struct hidpp_report *report = (struct hidpp_report *)data;
|
|
|
|
struct hidpp_touchpad_raw_xy raw;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2019-04-20 19:22:13 +08:00
|
|
|
if (!wd || !hidpp->input)
|
2014-10-01 01:18:27 +08:00
|
|
|
return 1;
|
|
|
|
|
2014-10-01 01:18:33 +08:00
|
|
|
switch (data[0]) {
|
|
|
|
case 0x02:
|
2014-12-16 23:55:22 +08:00
|
|
|
if (size < 2) {
|
|
|
|
hid_err(hdev, "Received HID report of bad size (%d)",
|
|
|
|
size);
|
|
|
|
return 1;
|
|
|
|
}
|
2014-10-01 01:18:34 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS) {
|
2019-04-20 19:22:13 +08:00
|
|
|
input_event(hidpp->input, EV_KEY, BTN_LEFT,
|
2014-10-01 01:18:34 +08:00
|
|
|
!!(data[1] & 0x01));
|
2019-04-20 19:22:13 +08:00
|
|
|
input_event(hidpp->input, EV_KEY, BTN_RIGHT,
|
2014-10-01 01:18:34 +08:00
|
|
|
!!(data[1] & 0x02));
|
2019-04-20 19:22:13 +08:00
|
|
|
input_sync(hidpp->input);
|
2014-12-16 08:50:16 +08:00
|
|
|
return 0;
|
2014-10-01 01:18:34 +08:00
|
|
|
} else {
|
|
|
|
if (size < 21)
|
|
|
|
return 1;
|
|
|
|
return wtp_mouse_raw_xy_event(hidpp, &data[7]);
|
|
|
|
}
|
2014-10-01 01:18:33 +08:00
|
|
|
case REPORT_ID_HIDPP_LONG:
|
2014-12-16 23:55:22 +08:00
|
|
|
/* size is already checked in hidpp_raw_event. */
|
2014-10-01 01:18:33 +08:00
|
|
|
if ((report->fap.feature_index != wd->mt_feature_index) ||
|
|
|
|
(report->fap.funcindex_clientid != EVENT_TOUCHPAD_RAW_XY))
|
|
|
|
return 1;
|
|
|
|
hidpp_touchpad_raw_xy_event(hidpp, data + 4, &raw);
|
|
|
|
|
|
|
|
wtp_send_raw_xy_event(hidpp, &raw);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2014-10-01 01:18:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int wtp_get_config(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
struct wtp_data *wd = hidpp->private_data;
|
|
|
|
struct hidpp_touchpad_raw_info raw_info = {0};
|
|
|
|
u8 feature_type;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_TOUCHPAD_RAW_XY,
|
|
|
|
&wd->mt_feature_index, &feature_type);
|
|
|
|
if (ret)
|
|
|
|
/* means that the device is not powered up */
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = hidpp_touchpad_get_raw_info(hidpp, wd->mt_feature_index,
|
|
|
|
&raw_info);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
wd->x_size = raw_info.x_size;
|
|
|
|
wd->y_size = raw_info.y_size;
|
|
|
|
wd->maxcontacts = raw_info.maxcontacts;
|
|
|
|
wd->flip_y = raw_info.origin == TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT;
|
|
|
|
wd->resolution = raw_info.res;
|
2014-10-01 01:18:34 +08:00
|
|
|
if (!wd->resolution)
|
|
|
|
wd->resolution = WTP_MANUAL_RESOLUTION;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
|
|
|
struct wtp_data *wd;
|
|
|
|
|
|
|
|
wd = devm_kzalloc(&hdev->dev, sizeof(struct wtp_data),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!wd)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
hidpp->private_data = wd;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
2014-12-17 06:06:01 +08:00
|
|
|
static int wtp_connect(struct hid_device *hdev, bool connected)
|
2014-10-01 01:18:33 +08:00
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
|
|
|
struct wtp_data *wd = hidpp->private_data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!wd->x_size) {
|
|
|
|
ret = wtp_get_config(hidpp);
|
|
|
|
if (ret) {
|
|
|
|
hid_err(hdev, "Can not get wtp config: %d\n", ret);
|
2014-12-17 06:06:01 +08:00
|
|
|
return ret;
|
2014-10-01 01:18:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-17 06:06:01 +08:00
|
|
|
return hidpp_touchpad_set_raw_report_state(hidpp, wd->mt_feature_index,
|
2014-10-01 01:18:33 +08:00
|
|
|
true, true);
|
|
|
|
}
|
|
|
|
|
2015-05-30 17:00:27 +08:00
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Logitech M560 devices */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Logitech M560 protocol overview
|
|
|
|
*
|
|
|
|
* The Logitech M560 mouse, is designed for windows 8. When the middle and/or
|
|
|
|
* the sides buttons are pressed, it sends some keyboard keys events
|
|
|
|
* instead of buttons ones.
|
|
|
|
* To complicate things further, the middle button keys sequence
|
|
|
|
* is different from the odd press and the even press.
|
|
|
|
*
|
|
|
|
* forward button -> Super_R
|
|
|
|
* backward button -> Super_L+'d' (press only)
|
|
|
|
* middle button -> 1st time: Alt_L+SuperL+XF86TouchpadOff (press only)
|
|
|
|
* 2nd time: left-click (press only)
|
|
|
|
* NB: press-only means that when the button is pressed, the
|
|
|
|
* KeyPress/ButtonPress and KeyRelease/ButtonRelease events are generated
|
|
|
|
* together sequentially; instead when the button is released, no event is
|
|
|
|
* generated !
|
|
|
|
*
|
|
|
|
* With the command
|
|
|
|
* 10<xx>0a 3500af03 (where <xx> is the mouse id),
|
|
|
|
* the mouse reacts differently:
|
|
|
|
* - it never sends a keyboard key event
|
|
|
|
* - for the three mouse button it sends:
|
|
|
|
* middle button press 11<xx>0a 3500af00...
|
|
|
|
* side 1 button (forward) press 11<xx>0a 3500b000...
|
|
|
|
* side 2 button (backward) press 11<xx>0a 3500ae00...
|
|
|
|
* middle/side1/side2 button release 11<xx>0a 35000000...
|
|
|
|
*/
|
|
|
|
|
|
|
|
static const u8 m560_config_parameter[] = {0x00, 0xaf, 0x03};
|
|
|
|
|
|
|
|
/* how buttons are mapped in the report */
|
|
|
|
#define M560_MOUSE_BTN_LEFT 0x01
|
|
|
|
#define M560_MOUSE_BTN_RIGHT 0x02
|
|
|
|
#define M560_MOUSE_BTN_WHEEL_LEFT 0x08
|
|
|
|
#define M560_MOUSE_BTN_WHEEL_RIGHT 0x10
|
|
|
|
|
|
|
|
#define M560_SUB_ID 0x0a
|
|
|
|
#define M560_BUTTON_MODE_REGISTER 0x35
|
|
|
|
|
|
|
|
static int m560_send_config_command(struct hid_device *hdev, bool connected)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
|
|
|
struct hidpp_device *hidpp_dev;
|
|
|
|
|
|
|
|
hidpp_dev = hid_get_drvdata(hdev);
|
|
|
|
|
|
|
|
return hidpp_send_rap_command_sync(
|
|
|
|
hidpp_dev,
|
|
|
|
REPORT_ID_HIDPP_SHORT,
|
|
|
|
M560_SUB_ID,
|
|
|
|
M560_BUTTON_MODE_REGISTER,
|
|
|
|
(u8 *)m560_config_parameter,
|
|
|
|
sizeof(m560_config_parameter),
|
|
|
|
&response
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
|
|
|
|
|
|
|
/* sanity check */
|
2019-04-20 19:22:13 +08:00
|
|
|
if (!hidpp->input) {
|
2015-05-30 17:00:27 +08:00
|
|
|
hid_err(hdev, "error in parameter\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (size < 7) {
|
|
|
|
hid_err(hdev, "error in report\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data[0] == REPORT_ID_HIDPP_LONG &&
|
|
|
|
data[2] == M560_SUB_ID && data[6] == 0x00) {
|
|
|
|
/*
|
|
|
|
* m560 mouse report for middle, forward and backward button
|
|
|
|
*
|
|
|
|
* data[0] = 0x11
|
|
|
|
* data[1] = device-id
|
|
|
|
* data[2] = 0x0a
|
|
|
|
* data[5] = 0xaf -> middle
|
|
|
|
* 0xb0 -> forward
|
|
|
|
* 0xae -> backward
|
|
|
|
* 0x00 -> release all
|
|
|
|
* data[6] = 0x00
|
|
|
|
*/
|
|
|
|
|
|
|
|
switch (data[5]) {
|
|
|
|
case 0xaf:
|
2019-04-20 19:22:13 +08:00
|
|
|
input_report_key(hidpp->input, BTN_MIDDLE, 1);
|
2015-05-30 17:00:27 +08:00
|
|
|
break;
|
|
|
|
case 0xb0:
|
2019-04-20 19:22:13 +08:00
|
|
|
input_report_key(hidpp->input, BTN_FORWARD, 1);
|
2015-05-30 17:00:27 +08:00
|
|
|
break;
|
|
|
|
case 0xae:
|
2019-04-20 19:22:13 +08:00
|
|
|
input_report_key(hidpp->input, BTN_BACK, 1);
|
2015-05-30 17:00:27 +08:00
|
|
|
break;
|
|
|
|
case 0x00:
|
2019-04-20 19:22:13 +08:00
|
|
|
input_report_key(hidpp->input, BTN_BACK, 0);
|
|
|
|
input_report_key(hidpp->input, BTN_FORWARD, 0);
|
|
|
|
input_report_key(hidpp->input, BTN_MIDDLE, 0);
|
2015-05-30 17:00:27 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
hid_err(hdev, "error in report\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2019-04-20 19:22:13 +08:00
|
|
|
input_sync(hidpp->input);
|
2015-05-30 17:00:27 +08:00
|
|
|
|
|
|
|
} else if (data[0] == 0x02) {
|
|
|
|
/*
|
|
|
|
* Logitech M560 mouse report
|
|
|
|
*
|
|
|
|
* data[0] = type (0x02)
|
|
|
|
* data[1..2] = buttons
|
|
|
|
* data[3..5] = xy
|
|
|
|
* data[6] = wheel
|
|
|
|
*/
|
|
|
|
|
|
|
|
int v;
|
|
|
|
|
2019-04-20 19:22:13 +08:00
|
|
|
input_report_key(hidpp->input, BTN_LEFT,
|
2015-05-30 17:00:27 +08:00
|
|
|
!!(data[1] & M560_MOUSE_BTN_LEFT));
|
2019-04-20 19:22:13 +08:00
|
|
|
input_report_key(hidpp->input, BTN_RIGHT,
|
2015-05-30 17:00:27 +08:00
|
|
|
!!(data[1] & M560_MOUSE_BTN_RIGHT));
|
|
|
|
|
2018-12-05 08:42:27 +08:00
|
|
|
if (data[1] & M560_MOUSE_BTN_WHEEL_LEFT) {
|
2019-04-20 19:22:13 +08:00
|
|
|
input_report_rel(hidpp->input, REL_HWHEEL, -1);
|
|
|
|
input_report_rel(hidpp->input, REL_HWHEEL_HI_RES,
|
2018-12-05 08:42:27 +08:00
|
|
|
-120);
|
|
|
|
} else if (data[1] & M560_MOUSE_BTN_WHEEL_RIGHT) {
|
2019-04-20 19:22:13 +08:00
|
|
|
input_report_rel(hidpp->input, REL_HWHEEL, 1);
|
|
|
|
input_report_rel(hidpp->input, REL_HWHEEL_HI_RES,
|
2018-12-05 08:42:27 +08:00
|
|
|
120);
|
|
|
|
}
|
2015-05-30 17:00:27 +08:00
|
|
|
|
|
|
|
v = hid_snto32(hid_field_extract(hdev, data+3, 0, 12), 12);
|
2019-04-20 19:22:13 +08:00
|
|
|
input_report_rel(hidpp->input, REL_X, v);
|
2015-05-30 17:00:27 +08:00
|
|
|
|
|
|
|
v = hid_snto32(hid_field_extract(hdev, data+3, 12, 12), 12);
|
2019-04-20 19:22:13 +08:00
|
|
|
input_report_rel(hidpp->input, REL_Y, v);
|
2015-05-30 17:00:27 +08:00
|
|
|
|
|
|
|
v = hid_snto32(data[6], 8);
|
2019-03-20 06:48:23 +08:00
|
|
|
if (v != 0)
|
2019-04-20 19:22:13 +08:00
|
|
|
hidpp_scroll_counter_handle_scroll(hidpp->input,
|
2019-03-20 06:48:23 +08:00
|
|
|
&hidpp->vertical_wheel_counter, v);
|
2015-05-30 17:00:27 +08:00
|
|
|
|
2019-04-20 19:22:13 +08:00
|
|
|
input_sync(hidpp->input);
|
2015-05-30 17:00:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void m560_populate_input(struct hidpp_device *hidpp,
|
2019-04-20 19:22:09 +08:00
|
|
|
struct input_dev *input_dev)
|
2015-05-30 17:00:27 +08:00
|
|
|
{
|
2019-04-20 19:22:13 +08:00
|
|
|
__set_bit(EV_KEY, input_dev->evbit);
|
|
|
|
__set_bit(BTN_MIDDLE, input_dev->keybit);
|
|
|
|
__set_bit(BTN_RIGHT, input_dev->keybit);
|
|
|
|
__set_bit(BTN_LEFT, input_dev->keybit);
|
|
|
|
__set_bit(BTN_BACK, input_dev->keybit);
|
|
|
|
__set_bit(BTN_FORWARD, input_dev->keybit);
|
2015-05-30 17:00:27 +08:00
|
|
|
|
2019-04-20 19:22:13 +08:00
|
|
|
__set_bit(EV_REL, input_dev->evbit);
|
|
|
|
__set_bit(REL_X, input_dev->relbit);
|
|
|
|
__set_bit(REL_Y, input_dev->relbit);
|
|
|
|
__set_bit(REL_WHEEL, input_dev->relbit);
|
|
|
|
__set_bit(REL_HWHEEL, input_dev->relbit);
|
|
|
|
__set_bit(REL_WHEEL_HI_RES, input_dev->relbit);
|
|
|
|
__set_bit(REL_HWHEEL_HI_RES, input_dev->relbit);
|
2015-05-30 17:00:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|
|
|
struct hid_field *field, struct hid_usage *usage,
|
|
|
|
unsigned long **bit, int *max)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-09-03 21:08:30 +08:00
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Logitech K400 devices */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The Logitech K400 keyboard has an embedded touchpad which is seen
|
|
|
|
* as a mouse from the OS point of view. There is a hardware shortcut to disable
|
|
|
|
* tap-to-click but the setting is not remembered accross reset, annoying some
|
|
|
|
* users.
|
|
|
|
*
|
|
|
|
* We can toggle this feature from the host by using the feature 0x6010:
|
|
|
|
* Touchpad FW items
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct k400_private_data {
|
|
|
|
u8 feature_index;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int k400_disable_tap_to_click(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
struct k400_private_data *k400 = hidpp->private_data;
|
|
|
|
struct hidpp_touchpad_fw_items items = {};
|
|
|
|
int ret;
|
|
|
|
u8 feature_type;
|
|
|
|
|
|
|
|
if (!k400->feature_index) {
|
|
|
|
ret = hidpp_root_get_feature(hidpp,
|
|
|
|
HIDPP_PAGE_TOUCHPAD_FW_ITEMS,
|
|
|
|
&k400->feature_index, &feature_type);
|
|
|
|
if (ret)
|
|
|
|
/* means that the device is not powered up */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = hidpp_touchpad_fw_items_set(hidpp, k400->feature_index, &items);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int k400_allocate(struct hid_device *hdev)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
|
|
|
struct k400_private_data *k400;
|
|
|
|
|
|
|
|
k400 = devm_kzalloc(&hdev->dev, sizeof(struct k400_private_data),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!k400)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
hidpp->private_data = k400;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int k400_connect(struct hid_device *hdev, bool connected)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
|
|
|
|
|
|
|
if (!disable_tap_to_click)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return k400_disable_tap_to_click(hidpp);
|
|
|
|
}
|
|
|
|
|
2015-11-20 07:42:13 +08:00
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Logitech G920 Driving Force Racing Wheel for Xbox One */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#define HIDPP_PAGE_G920_FORCE_FEEDBACK 0x8123
|
|
|
|
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
static int g920_ff_set_autocenter(struct hidpp_device *hidpp,
|
|
|
|
struct hidpp_ff_private_data *data)
|
2015-11-20 07:42:13 +08:00
|
|
|
{
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
struct hidpp_report response;
|
|
|
|
u8 params[HIDPP_AUTOCENTER_PARAMS_LENGTH] = {
|
|
|
|
[1] = HIDPP_FF_EFFECT_SPRING | HIDPP_FF_EFFECT_AUTOSTART,
|
|
|
|
};
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* initialize with zero autocenter to get wheel in usable state */
|
|
|
|
|
|
|
|
dbg_hid("Setting autocenter to 0.\n");
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
|
|
|
|
HIDPP_FF_DOWNLOAD_EFFECT,
|
|
|
|
params, ARRAY_SIZE(params),
|
|
|
|
&response);
|
|
|
|
if (ret)
|
|
|
|
hid_warn(hidpp->hid_dev, "Failed to autocenter device!\n");
|
|
|
|
else
|
|
|
|
data->slot_autocenter = response.fap.params[0];
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int g920_get_config(struct hidpp_device *hidpp,
|
|
|
|
struct hidpp_ff_private_data *data)
|
|
|
|
{
|
|
|
|
struct hidpp_report response;
|
2015-11-20 07:42:13 +08:00
|
|
|
u8 feature_type;
|
|
|
|
int ret;
|
|
|
|
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
memset(data, 0, sizeof(*data));
|
|
|
|
|
2015-11-20 07:42:13 +08:00
|
|
|
/* Find feature and store for later use */
|
|
|
|
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK,
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
&data->feature_index, &feature_type);
|
2015-11-20 07:42:13 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
/* Read number of slots available in device */
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
|
|
|
|
HIDPP_FF_GET_INFO,
|
|
|
|
NULL, 0,
|
|
|
|
&response);
|
|
|
|
if (ret) {
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
hid_err(hidpp->hid_dev,
|
|
|
|
"%s: received protocol error 0x%02x\n", __func__, ret);
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->num_effects = response.fap.params[0] - HIDPP_FF_RESERVED_SLOTS;
|
|
|
|
|
|
|
|
/* reset all forces */
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
|
|
|
|
HIDPP_FF_RESET_ALL,
|
|
|
|
NULL, 0,
|
|
|
|
&response);
|
2015-11-20 07:42:13 +08:00
|
|
|
if (ret)
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
hid_warn(hidpp->hid_dev, "Failed to reset all forces!\n");
|
2015-11-20 07:42:13 +08:00
|
|
|
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
|
|
|
|
HIDPP_FF_GET_APERTURE,
|
|
|
|
NULL, 0,
|
|
|
|
&response);
|
|
|
|
if (ret) {
|
|
|
|
hid_warn(hidpp->hid_dev,
|
|
|
|
"Failed to read range from device!\n");
|
|
|
|
}
|
|
|
|
data->range = ret ?
|
|
|
|
900 : get_unaligned_be16(&response.fap.params[0]);
|
|
|
|
|
|
|
|
/* Read the current gain values */
|
|
|
|
ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
|
|
|
|
HIDPP_FF_GET_GLOBAL_GAINS,
|
|
|
|
NULL, 0,
|
|
|
|
&response);
|
|
|
|
if (ret)
|
|
|
|
hid_warn(hidpp->hid_dev,
|
|
|
|
"Failed to read gain values from device!\n");
|
|
|
|
data->gain = ret ?
|
|
|
|
0xffff : get_unaligned_be16(&response.fap.params[0]);
|
|
|
|
|
|
|
|
/* ignore boost value at response.fap.params[2] */
|
|
|
|
|
|
|
|
return g920_ff_set_autocenter(hidpp, data);
|
2015-11-20 07:42:13 +08:00
|
|
|
}
|
|
|
|
|
2020-11-15 05:20:56 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* Logitech Dinovo Mini keyboard with builtin touchpad */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#define DINOVO_MINI_PRODUCT_ID 0xb30c
|
|
|
|
|
|
|
|
static int lg_dinovo_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|
|
|
struct hid_field *field, struct hid_usage *usage,
|
|
|
|
unsigned long **bit, int *max)
|
|
|
|
{
|
|
|
|
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (usage->hid & HID_USAGE) {
|
|
|
|
case 0x00d: lg_map_key_clear(KEY_MEDIA); break;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:15 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* HID++1.0 devices which use HID++ reports for their wheels */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
static int hidpp10_wheel_connect(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
return hidpp10_set_register(hidpp, HIDPP_REG_ENABLE_REPORTS, 0,
|
|
|
|
HIDPP_ENABLE_WHEEL_REPORT | HIDPP_ENABLE_HWHEEL_REPORT,
|
|
|
|
HIDPP_ENABLE_WHEEL_REPORT | HIDPP_ENABLE_HWHEEL_REPORT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp10_wheel_raw_event(struct hidpp_device *hidpp,
|
|
|
|
u8 *data, int size)
|
|
|
|
{
|
|
|
|
s8 value, hvalue;
|
|
|
|
|
|
|
|
if (!hidpp->input)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (size < 7)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (data[0] != REPORT_ID_HIDPP_SHORT || data[2] != HIDPP_SUB_ID_ROLLER)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
value = data[3];
|
|
|
|
hvalue = data[4];
|
|
|
|
|
|
|
|
input_report_rel(hidpp->input, REL_WHEEL, value);
|
|
|
|
input_report_rel(hidpp->input, REL_WHEEL_HI_RES, value * 120);
|
|
|
|
input_report_rel(hidpp->input, REL_HWHEEL, hvalue);
|
|
|
|
input_report_rel(hidpp->input, REL_HWHEEL_HI_RES, hvalue * 120);
|
|
|
|
input_sync(hidpp->input);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hidpp10_wheel_populate_input(struct hidpp_device *hidpp,
|
|
|
|
struct input_dev *input_dev)
|
|
|
|
{
|
|
|
|
__set_bit(EV_REL, input_dev->evbit);
|
|
|
|
__set_bit(REL_WHEEL, input_dev->relbit);
|
|
|
|
__set_bit(REL_WHEEL_HI_RES, input_dev->relbit);
|
|
|
|
__set_bit(REL_HWHEEL, input_dev->relbit);
|
|
|
|
__set_bit(REL_HWHEEL_HI_RES, input_dev->relbit);
|
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:16 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* HID++1.0 mice which use HID++ reports for extra mouse buttons */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
static int hidpp10_extra_mouse_buttons_connect(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
return hidpp10_set_register(hidpp, HIDPP_REG_ENABLE_REPORTS, 0,
|
|
|
|
HIDPP_ENABLE_MOUSE_EXTRA_BTN_REPORT,
|
|
|
|
HIDPP_ENABLE_MOUSE_EXTRA_BTN_REPORT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,
|
|
|
|
u8 *data, int size)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!hidpp->input)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (size < 7)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (data[0] != REPORT_ID_HIDPP_SHORT ||
|
|
|
|
data[2] != HIDPP_SUB_ID_MOUSE_EXTRA_BTNS)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Buttons are either delivered through the regular mouse report *or*
|
|
|
|
* through the extra buttons report. At least for button 6 how it is
|
|
|
|
* delivered differs per receiver firmware version. Even receivers with
|
|
|
|
* the same usb-id show different behavior, so we handle both cases.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
input_report_key(hidpp->input, BTN_MOUSE + i,
|
|
|
|
(data[3] & (1 << i)));
|
|
|
|
|
|
|
|
/* Some mice report events on button 9+, use BTN_MISC */
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
input_report_key(hidpp->input, BTN_MISC + i,
|
|
|
|
(data[4] & (1 << i)));
|
|
|
|
|
|
|
|
input_sync(hidpp->input);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hidpp10_extra_mouse_buttons_populate_input(
|
|
|
|
struct hidpp_device *hidpp, struct input_dev *input_dev)
|
|
|
|
{
|
|
|
|
/* BTN_MOUSE - BTN_MOUSE+7 are set already by the descriptor */
|
|
|
|
__set_bit(BTN_0, input_dev->keybit);
|
|
|
|
__set_bit(BTN_1, input_dev->keybit);
|
|
|
|
__set_bit(BTN_2, input_dev->keybit);
|
|
|
|
__set_bit(BTN_3, input_dev->keybit);
|
|
|
|
__set_bit(BTN_4, input_dev->keybit);
|
|
|
|
__set_bit(BTN_5, input_dev->keybit);
|
|
|
|
__set_bit(BTN_6, input_dev->keybit);
|
|
|
|
__set_bit(BTN_7, input_dev->keybit);
|
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:17 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* HID++1.0 kbds which only report 0x10xx consumer usages through sub-id 0x03 */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/* Find the consumer-page input report desc and change Maximums to 0x107f */
|
|
|
|
static u8 *hidpp10_consumer_keys_report_fixup(struct hidpp_device *hidpp,
|
|
|
|
u8 *_rdesc, unsigned int *rsize)
|
|
|
|
{
|
|
|
|
/* Note 0 terminated so we can use strnstr to search for this. */
|
2019-05-10 21:17:22 +08:00
|
|
|
static const char consumer_rdesc_start[] = {
|
2019-04-20 19:22:17 +08:00
|
|
|
0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */
|
|
|
|
0x09, 0x01, /* USAGE (Consumer Control) */
|
|
|
|
0xA1, 0x01, /* COLLECTION (Application) */
|
|
|
|
0x85, 0x03, /* REPORT_ID = 3 */
|
|
|
|
0x75, 0x10, /* REPORT_SIZE (16) */
|
|
|
|
0x95, 0x02, /* REPORT_COUNT (2) */
|
|
|
|
0x15, 0x01, /* LOGICAL_MIN (1) */
|
|
|
|
0x26, 0x00 /* LOGICAL_MAX (... */
|
|
|
|
};
|
|
|
|
char *consumer_rdesc, *rdesc = (char *)_rdesc;
|
|
|
|
unsigned int size;
|
|
|
|
|
|
|
|
consumer_rdesc = strnstr(rdesc, consumer_rdesc_start, *rsize);
|
|
|
|
size = *rsize - (consumer_rdesc - rdesc);
|
|
|
|
if (consumer_rdesc && size >= 25) {
|
|
|
|
consumer_rdesc[15] = 0x7f;
|
|
|
|
consumer_rdesc[16] = 0x10;
|
|
|
|
consumer_rdesc[20] = 0x7f;
|
|
|
|
consumer_rdesc[21] = 0x10;
|
|
|
|
}
|
|
|
|
return _rdesc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp10_consumer_keys_connect(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
return hidpp10_set_register(hidpp, HIDPP_REG_ENABLE_REPORTS, 0,
|
|
|
|
HIDPP_ENABLE_CONSUMER_REPORT,
|
|
|
|
HIDPP_ENABLE_CONSUMER_REPORT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp10_consumer_keys_raw_event(struct hidpp_device *hidpp,
|
|
|
|
u8 *data, int size)
|
|
|
|
{
|
|
|
|
u8 consumer_report[5];
|
|
|
|
|
|
|
|
if (size < 7)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (data[0] != REPORT_ID_HIDPP_SHORT ||
|
|
|
|
data[2] != HIDPP_SUB_ID_CONSUMER_VENDOR_KEYS)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build a normal consumer report (3) out of the data, this detour
|
|
|
|
* is necessary to get some keyboards to report their 0x10xx usages.
|
|
|
|
*/
|
|
|
|
consumer_report[0] = 0x03;
|
|
|
|
memcpy(&consumer_report[1], &data[3], 4);
|
|
|
|
/* We are called from atomic context */
|
|
|
|
hid_report_raw_event(hidpp->hid_dev, HID_INPUT_REPORT,
|
|
|
|
consumer_report, 5, 1);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-12-05 08:42:27 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* High-resolution scroll wheels */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static int hi_res_scroll_enable(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
u8 multiplier = 1;
|
|
|
|
|
2022-09-14 21:21:46 +08:00
|
|
|
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL) {
|
2018-12-05 08:42:27 +08:00
|
|
|
ret = hidpp_hrw_set_wheel_mode(hidpp, false, true, false);
|
|
|
|
if (ret == 0)
|
|
|
|
ret = hidpp_hrw_get_wheel_capability(hidpp, &multiplier);
|
2022-09-14 21:21:46 +08:00
|
|
|
} else if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL) {
|
2018-12-05 08:42:27 +08:00
|
|
|
ret = hidpp_hrs_set_highres_scrolling_mode(hidpp, true,
|
|
|
|
&multiplier);
|
2022-09-14 21:21:46 +08:00
|
|
|
} else /* if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL) */ {
|
2018-12-05 08:42:27 +08:00
|
|
|
ret = hidpp10_enable_scrolling_acceleration(hidpp);
|
|
|
|
multiplier = 8;
|
|
|
|
}
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (multiplier == 0)
|
|
|
|
multiplier = 1;
|
|
|
|
|
|
|
|
hidpp->vertical_wheel_counter.wheel_multiplier = multiplier;
|
2020-07-06 01:34:57 +08:00
|
|
|
hid_dbg(hidpp->hid_dev, "wheel multiplier = %d\n", multiplier);
|
2018-12-05 08:42:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-09-14 21:21:46 +08:00
|
|
|
static int hidpp_initialize_hires_scroll(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
unsigned long capabilities;
|
|
|
|
|
|
|
|
capabilities = hidpp->capabilities;
|
|
|
|
|
|
|
|
if (hidpp->protocol_major >= 2) {
|
|
|
|
u8 feature_index;
|
|
|
|
u8 feature_type;
|
|
|
|
|
|
|
|
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
|
|
|
|
&feature_index, &feature_type);
|
|
|
|
if (!ret) {
|
|
|
|
hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL;
|
|
|
|
hid_dbg(hidpp->hid_dev, "Detected HID++ 2.0 hi-res scroll wheel\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HI_RESOLUTION_SCROLLING,
|
|
|
|
&feature_index, &feature_type);
|
|
|
|
if (!ret) {
|
|
|
|
hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL;
|
|
|
|
hid_dbg(hidpp->hid_dev, "Detected HID++ 2.0 hi-res scrolling\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
struct hidpp_report response;
|
|
|
|
|
|
|
|
ret = hidpp_send_rap_command_sync(hidpp,
|
|
|
|
REPORT_ID_HIDPP_SHORT,
|
|
|
|
HIDPP_GET_REGISTER,
|
|
|
|
HIDPP_ENABLE_FAST_SCROLL,
|
|
|
|
NULL, 0, &response);
|
|
|
|
if (!ret) {
|
|
|
|
hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL;
|
|
|
|
hid_dbg(hidpp->hid_dev, "Detected HID++ 1.0 fast scroll\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hidpp->capabilities == capabilities)
|
|
|
|
hid_dbg(hidpp->hid_dev, "Did not detect HID++ hi-res scrolling hardware support\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* Generic HID++ devices */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
2019-04-20 19:22:17 +08:00
|
|
|
static u8 *hidpp_report_fixup(struct hid_device *hdev, u8 *rdesc,
|
|
|
|
unsigned int *rsize)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
|
|
|
|
|
|
|
if (!hidpp)
|
|
|
|
return rdesc;
|
|
|
|
|
|
|
|
/* For 27 MHz keyboards the quirk gets set after hid_parse. */
|
|
|
|
if (hdev->group == HID_GROUP_LOGITECH_27MHZ_DEVICE ||
|
|
|
|
(hidpp->quirks & HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS))
|
|
|
|
rdesc = hidpp10_consumer_keys_report_fixup(hidpp, rdesc, rsize);
|
|
|
|
|
|
|
|
return rdesc;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|
|
|
struct hid_field *field, struct hid_usage *usage,
|
|
|
|
unsigned long **bit, int *max)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
|
|
|
|
2019-04-20 19:22:04 +08:00
|
|
|
if (!hidpp)
|
|
|
|
return 0;
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
|
|
|
|
return wtp_input_mapping(hdev, hi, field, usage, bit, max);
|
2015-05-30 17:00:27 +08:00
|
|
|
else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560 &&
|
|
|
|
field->application != HID_GD_MOUSE)
|
|
|
|
return m560_input_mapping(hdev, hi, field, usage, bit, max);
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2020-11-15 05:20:56 +08:00
|
|
|
if (hdev->product == DINOVO_MINI_PRODUCT_ID)
|
|
|
|
return lg_dinovo_input_mapping(hdev, hi, field, usage, bit, max);
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-20 07:42:15 +08:00
|
|
|
static int hidpp_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
|
|
|
struct hid_field *field, struct hid_usage *usage,
|
|
|
|
unsigned long **bit, int *max)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
|
|
|
|
2019-04-20 19:22:04 +08:00
|
|
|
if (!hidpp)
|
|
|
|
return 0;
|
|
|
|
|
2015-11-20 07:42:15 +08:00
|
|
|
/* Ensure that Logitech G920 is not given a default fuzz/flat value */
|
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
|
|
|
|
if (usage->type == EV_ABS && (usage->code == ABS_X ||
|
|
|
|
usage->code == ABS_Y || usage->code == ABS_Z ||
|
|
|
|
usage->code == ABS_RZ)) {
|
|
|
|
field->application = HID_GD_MULTIAXIS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-10-01 01:18:32 +08:00
|
|
|
static void hidpp_populate_input(struct hidpp_device *hidpp,
|
2019-04-20 19:22:09 +08:00
|
|
|
struct input_dev *input)
|
2014-10-01 01:18:32 +08:00
|
|
|
{
|
2019-04-20 19:22:13 +08:00
|
|
|
hidpp->input = input;
|
|
|
|
|
2014-10-01 01:18:32 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
|
2019-04-20 19:22:09 +08:00
|
|
|
wtp_populate_input(hidpp, input);
|
2015-05-30 17:00:27 +08:00
|
|
|
else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
|
2019-04-20 19:22:09 +08:00
|
|
|
m560_populate_input(hidpp, input);
|
2019-04-20 19:22:15 +08:00
|
|
|
|
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_HIDPP_WHEELS)
|
|
|
|
hidpp10_wheel_populate_input(hidpp, input);
|
2019-04-20 19:22:16 +08:00
|
|
|
|
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS)
|
|
|
|
hidpp10_extra_mouse_buttons_populate_input(hidpp, input);
|
2014-10-01 01:18:32 +08:00
|
|
|
}
|
|
|
|
|
2015-09-30 06:52:59 +08:00
|
|
|
static int hidpp_input_configured(struct hid_device *hdev,
|
2014-10-01 01:18:27 +08:00
|
|
|
struct hid_input *hidinput)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
2014-10-01 01:18:32 +08:00
|
|
|
struct input_dev *input = hidinput->input;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2019-04-20 19:22:04 +08:00
|
|
|
if (!hidpp)
|
|
|
|
return 0;
|
|
|
|
|
2019-04-20 19:22:09 +08:00
|
|
|
hidpp_populate_input(hidpp, input);
|
2015-09-30 06:52:59 +08:00
|
|
|
|
|
|
|
return 0;
|
2014-10-01 01:18:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
|
|
|
|
int size)
|
|
|
|
{
|
|
|
|
struct hidpp_report *question = hidpp->send_receive_buf;
|
|
|
|
struct hidpp_report *answer = hidpp->send_receive_buf;
|
|
|
|
struct hidpp_report *report = (struct hidpp_report *)data;
|
2017-03-27 22:59:29 +08:00
|
|
|
int ret;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the mutex is locked then we have a pending answer from a
|
2014-12-17 07:23:51 +08:00
|
|
|
* previously sent command.
|
2014-10-01 01:18:27 +08:00
|
|
|
*/
|
|
|
|
if (unlikely(mutex_is_locked(&hidpp->send_mutex))) {
|
|
|
|
/*
|
|
|
|
* Check for a correct hidpp20 answer or the corresponding
|
|
|
|
* error
|
|
|
|
*/
|
|
|
|
if (hidpp_match_answer(question, report) ||
|
|
|
|
hidpp_match_error(question, report)) {
|
|
|
|
*answer = *report;
|
|
|
|
hidpp->answer_available = true;
|
|
|
|
wake_up(&hidpp->wait);
|
|
|
|
/*
|
|
|
|
* This was an answer to a command that this driver sent
|
|
|
|
* We return 1 to hid-core to avoid forwarding the
|
|
|
|
* command upstream as it has been treated by the driver
|
|
|
|
*/
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 01:44:13 +08:00
|
|
|
if (unlikely(hidpp_report_is_connect_event(hidpp, report))) {
|
2014-10-01 01:18:32 +08:00
|
|
|
atomic_set(&hidpp->connected,
|
|
|
|
!(report->rap.params[0] & (1 << 6)));
|
2016-06-29 17:28:02 +08:00
|
|
|
if (schedule_work(&hidpp->work) == 0)
|
2014-10-01 01:18:32 +08:00
|
|
|
dbg_hid("%s: connect event already queued\n", __func__);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-04-05 02:56:09 +08:00
|
|
|
if (hidpp->hid_dev->group == HID_GROUP_LOGITECH_27MHZ_DEVICE &&
|
|
|
|
data[0] == REPORT_ID_HIDPP_SHORT &&
|
|
|
|
data[2] == HIDPP_SUB_ID_USER_IFACE_EVENT &&
|
|
|
|
(data[3] & HIDPP_USER_IFACE_EVENT_ENCRYPTION_KEY_LOST)) {
|
|
|
|
dev_err_ratelimited(&hidpp->hid_dev->dev,
|
|
|
|
"Error the keyboard's wireless encryption key has been lost, your keyboard will not work unless you re-configure encryption.\n");
|
|
|
|
dev_err_ratelimited(&hidpp->hid_dev->dev,
|
|
|
|
"See: https://gitlab.freedesktop.org/jwrdegoede/logitech-27mhz-keyboard-encryption-setup/\n");
|
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:29 +08:00
|
|
|
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) {
|
HID: logitech-hidpp: add support for Unified Battery (1004) feature
This new feature present in new devices replaces the old Battery Level Status
(0x1000) feature. It keeps essentially the same information for levels
(reporting critical, low, good and full) but makes these levels optional, the
device exports a capability setting which describes which levels it supports.
In addition to this, there is an optional state_of_charge paramenter that
exports the battery percentage.
This patch adds support for this new feature. There were some implementation
choices, as described below and in the code.
If the device supports the state_of_charge parameter, we will just export the
battery percentage and not the levels, which the device might still support.
Since this feature can co-exist with the Battery Voltage (0x1001) feature and
we currently only support one battery feature, I changed the battery feature
discovery to try to use 0x1000 and 0x1004 first and only then 0x1001, the
battery voltage feature.
In the future we could uncouple this and make the battery feature co-exists
with 0x1000 and 0x1004, allowing the device to export voltage information in
addition to the battery percentage or level.
I tested this patch with a MX Anywhere 3, which supports the new feature. Since
I don't have any device that doesn't support the state_of_charge parameter of
this feature, I forced the MX Anywhere 3 to use the level information, instead
of battery percentage, to test that part of the implementation.
I also tested with a MX Master 3, which supports the Battery Level Status
(0x1000) feature, and a G703 Hero, which supports the Battery Voltage (0x1001)
feature, to make sure nothing broke there.
[jkosina@suse.cz: fix comment]
Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-01-05 02:29:37 +08:00
|
|
|
ret = hidpp20_battery_event_1000(hidpp, data, size);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
ret = hidpp20_battery_event_1004(hidpp, data, size);
|
2017-03-27 22:59:29 +08:00
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
2017-03-27 22:59:37 +08:00
|
|
|
ret = hidpp_solar_battery_event(hidpp, data, size);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
ret = hidpp20_battery_voltage_event(hidpp, data, size);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
2017-03-27 22:59:29 +08:00
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:38 +08:00
|
|
|
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
|
|
|
|
ret = hidpp10_battery_event(hidpp, data, size);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:15 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_HIDPP_WHEELS) {
|
|
|
|
ret = hidpp10_wheel_raw_event(hidpp, data, size);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:16 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS) {
|
|
|
|
ret = hidpp10_extra_mouse_buttons_raw_event(hidpp, data, size);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:17 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS) {
|
|
|
|
ret = hidpp10_consumer_keys_raw_event(hidpp, data, size);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
|
|
|
|
u8 *data, int size)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
2014-12-17 07:23:51 +08:00
|
|
|
int ret = 0;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2019-04-20 19:22:04 +08:00
|
|
|
if (!hidpp)
|
|
|
|
return 0;
|
|
|
|
|
2014-12-17 07:23:51 +08:00
|
|
|
/* Generic HID++ processing. */
|
2014-10-01 01:18:27 +08:00
|
|
|
switch (data[0]) {
|
2015-11-20 07:42:11 +08:00
|
|
|
case REPORT_ID_HIDPP_VERY_LONG:
|
2019-04-20 19:22:12 +08:00
|
|
|
if (size != hidpp->very_long_report_length) {
|
2015-11-20 07:42:11 +08:00
|
|
|
hid_err(hdev, "received hid++ report of bad size (%d)",
|
|
|
|
size);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
ret = hidpp_raw_hidpp_event(hidpp, data, size);
|
|
|
|
break;
|
2014-10-01 01:18:27 +08:00
|
|
|
case REPORT_ID_HIDPP_LONG:
|
|
|
|
if (size != HIDPP_REPORT_LONG_LENGTH) {
|
|
|
|
hid_err(hdev, "received hid++ report of bad size (%d)",
|
|
|
|
size);
|
|
|
|
return 1;
|
|
|
|
}
|
2014-12-17 07:23:51 +08:00
|
|
|
ret = hidpp_raw_hidpp_event(hidpp, data, size);
|
|
|
|
break;
|
2014-10-01 01:18:27 +08:00
|
|
|
case REPORT_ID_HIDPP_SHORT:
|
|
|
|
if (size != HIDPP_REPORT_SHORT_LENGTH) {
|
|
|
|
hid_err(hdev, "received hid++ report of bad size (%d)",
|
|
|
|
size);
|
|
|
|
return 1;
|
|
|
|
}
|
2014-12-17 07:23:51 +08:00
|
|
|
ret = hidpp_raw_hidpp_event(hidpp, data, size);
|
|
|
|
break;
|
2014-10-01 01:18:27 +08:00
|
|
|
}
|
|
|
|
|
2014-12-17 07:23:51 +08:00
|
|
|
/* If no report is available for further processing, skip calling
|
|
|
|
* raw_event of subclasses. */
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
|
|
|
|
return wtp_raw_event(hdev, data, size);
|
2015-05-30 17:00:27 +08:00
|
|
|
else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
|
|
|
|
return m560_raw_event(hdev, data, size);
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-12-05 08:42:27 +08:00
|
|
|
static int hidpp_event(struct hid_device *hdev, struct hid_field *field,
|
|
|
|
struct hid_usage *usage, __s32 value)
|
|
|
|
{
|
|
|
|
/* This function will only be called for scroll events, due to the
|
|
|
|
* restriction imposed in hidpp_usages.
|
|
|
|
*/
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
2019-04-20 19:22:04 +08:00
|
|
|
struct hidpp_scroll_counter *counter;
|
|
|
|
|
|
|
|
if (!hidpp)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
counter = &hidpp->vertical_wheel_counter;
|
2018-12-05 08:42:27 +08:00
|
|
|
/* A scroll event may occur before the multiplier has been retrieved or
|
|
|
|
* the input device set, or high-res scroll enabling may fail. In such
|
|
|
|
* cases we must return early (falling back to default behaviour) to
|
|
|
|
* avoid a crash in hidpp_scroll_counter_handle_scroll.
|
|
|
|
*/
|
2022-09-14 21:21:46 +08:00
|
|
|
if (!(hidpp->capabilities & HIDPP_CAPABILITY_HI_RES_SCROLL)
|
|
|
|
|| value == 0 || hidpp->input == NULL
|
|
|
|
|| counter->wheel_multiplier == 0)
|
2018-12-05 08:42:27 +08:00
|
|
|
return 0;
|
|
|
|
|
2019-04-20 19:22:13 +08:00
|
|
|
hidpp_scroll_counter_handle_scroll(hidpp->input, counter, value);
|
2018-12-05 08:42:27 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:31 +08:00
|
|
|
static int hidpp_initialize_battery(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
static atomic_t battery_no = ATOMIC_INIT(0);
|
|
|
|
struct power_supply_config cfg = { .drv_data = hidpp };
|
|
|
|
struct power_supply_desc *desc = &hidpp->battery.desc;
|
2017-03-27 22:59:36 +08:00
|
|
|
enum power_supply_property *battery_props;
|
2017-03-27 22:59:31 +08:00
|
|
|
struct hidpp_battery *battery;
|
2017-03-27 22:59:36 +08:00
|
|
|
unsigned int num_battery_props;
|
2017-03-27 22:59:31 +08:00
|
|
|
unsigned long n;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (hidpp->battery.ps)
|
|
|
|
return 0;
|
|
|
|
|
2017-03-27 22:59:37 +08:00
|
|
|
hidpp->battery.feature_index = 0xff;
|
|
|
|
hidpp->battery.solar_feature_index = 0xff;
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
hidpp->battery.voltage_feature_index = 0xff;
|
2017-03-27 22:59:37 +08:00
|
|
|
|
2017-03-27 22:59:31 +08:00
|
|
|
if (hidpp->protocol_major >= 2) {
|
2017-03-27 22:59:37 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_CLASS_K750)
|
|
|
|
ret = hidpp_solar_request_battery_event(hidpp);
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
else {
|
HID: logitech-hidpp: add support for Unified Battery (1004) feature
This new feature present in new devices replaces the old Battery Level Status
(0x1000) feature. It keeps essentially the same information for levels
(reporting critical, low, good and full) but makes these levels optional, the
device exports a capability setting which describes which levels it supports.
In addition to this, there is an optional state_of_charge paramenter that
exports the battery percentage.
This patch adds support for this new feature. There were some implementation
choices, as described below and in the code.
If the device supports the state_of_charge parameter, we will just export the
battery percentage and not the levels, which the device might still support.
Since this feature can co-exist with the Battery Voltage (0x1001) feature and
we currently only support one battery feature, I changed the battery feature
discovery to try to use 0x1000 and 0x1004 first and only then 0x1001, the
battery voltage feature.
In the future we could uncouple this and make the battery feature co-exists
with 0x1000 and 0x1004, allowing the device to export voltage information in
addition to the battery percentage or level.
I tested this patch with a MX Anywhere 3, which supports the new feature. Since
I don't have any device that doesn't support the state_of_charge parameter of
this feature, I forced the MX Anywhere 3 to use the level information, instead
of battery percentage, to test that part of the implementation.
I also tested with a MX Master 3, which supports the Battery Level Status
(0x1000) feature, and a G703 Hero, which supports the Battery Voltage (0x1001)
feature, to make sure nothing broke there.
[jkosina@suse.cz: fix comment]
Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-01-05 02:29:37 +08:00
|
|
|
/* we only support one battery feature right now, so let's
|
|
|
|
first check the ones that support battery level first
|
|
|
|
and leave voltage for last */
|
|
|
|
ret = hidpp20_query_battery_info_1000(hidpp);
|
|
|
|
if (ret)
|
|
|
|
ret = hidpp20_query_battery_info_1004(hidpp);
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
if (ret)
|
HID: logitech-hidpp: add support for Unified Battery (1004) feature
This new feature present in new devices replaces the old Battery Level Status
(0x1000) feature. It keeps essentially the same information for levels
(reporting critical, low, good and full) but makes these levels optional, the
device exports a capability setting which describes which levels it supports.
In addition to this, there is an optional state_of_charge paramenter that
exports the battery percentage.
This patch adds support for this new feature. There were some implementation
choices, as described below and in the code.
If the device supports the state_of_charge parameter, we will just export the
battery percentage and not the levels, which the device might still support.
Since this feature can co-exist with the Battery Voltage (0x1001) feature and
we currently only support one battery feature, I changed the battery feature
discovery to try to use 0x1000 and 0x1004 first and only then 0x1001, the
battery voltage feature.
In the future we could uncouple this and make the battery feature co-exists
with 0x1000 and 0x1004, allowing the device to export voltage information in
addition to the battery percentage or level.
I tested this patch with a MX Anywhere 3, which supports the new feature. Since
I don't have any device that doesn't support the state_of_charge parameter of
this feature, I forced the MX Anywhere 3 to use the level information, instead
of battery percentage, to test that part of the implementation.
I also tested with a MX Master 3, which supports the Battery Level Status
(0x1000) feature, and a G703 Hero, which supports the Battery Voltage (0x1001)
feature, to make sure nothing broke there.
[jkosina@suse.cz: fix comment]
Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-01-05 02:29:37 +08:00
|
|
|
ret = hidpp20_query_battery_voltage_info(hidpp);
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
}
|
2017-03-27 22:59:37 +08:00
|
|
|
|
2017-03-27 22:59:31 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP20_BATTERY;
|
|
|
|
} else {
|
2017-03-27 22:59:38 +08:00
|
|
|
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;
|
2017-03-27 22:59:31 +08:00
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:36 +08:00
|
|
|
battery_props = devm_kmemdup(&hidpp->hid_dev->dev,
|
|
|
|
hidpp_battery_props,
|
|
|
|
sizeof(hidpp_battery_props),
|
|
|
|
GFP_KERNEL);
|
2017-07-07 13:12:13 +08:00
|
|
|
if (!battery_props)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 3;
|
2017-03-27 22:59:36 +08:00
|
|
|
|
HID: logitech-hidpp: add support for Unified Battery (1004) feature
This new feature present in new devices replaces the old Battery Level Status
(0x1000) feature. It keeps essentially the same information for levels
(reporting critical, low, good and full) but makes these levels optional, the
device exports a capability setting which describes which levels it supports.
In addition to this, there is an optional state_of_charge paramenter that
exports the battery percentage.
This patch adds support for this new feature. There were some implementation
choices, as described below and in the code.
If the device supports the state_of_charge parameter, we will just export the
battery percentage and not the levels, which the device might still support.
Since this feature can co-exist with the Battery Voltage (0x1001) feature and
we currently only support one battery feature, I changed the battery feature
discovery to try to use 0x1000 and 0x1004 first and only then 0x1001, the
battery voltage feature.
In the future we could uncouple this and make the battery feature co-exists
with 0x1000 and 0x1004, allowing the device to export voltage information in
addition to the battery percentage or level.
I tested this patch with a MX Anywhere 3, which supports the new feature. Since
I don't have any device that doesn't support the state_of_charge parameter of
this feature, I forced the MX Anywhere 3 to use the level information, instead
of battery percentage, to test that part of the implementation.
I also tested with a MX Master 3, which supports the Battery Level Status
(0x1000) feature, and a G703 Hero, which supports the Battery Voltage (0x1001)
feature, to make sure nothing broke there.
[jkosina@suse.cz: fix comment]
Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-01-05 02:29:37 +08:00
|
|
|
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE ||
|
2021-08-02 20:52:32 +08:00
|
|
|
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_PERCENTAGE ||
|
|
|
|
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
|
2017-03-27 22:59:36 +08:00
|
|
|
battery_props[num_battery_props++] =
|
|
|
|
POWER_SUPPLY_PROP_CAPACITY;
|
|
|
|
|
|
|
|
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS)
|
|
|
|
battery_props[num_battery_props++] =
|
|
|
|
POWER_SUPPLY_PROP_CAPACITY_LEVEL;
|
|
|
|
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
|
|
|
|
battery_props[num_battery_props++] =
|
|
|
|
POWER_SUPPLY_PROP_VOLTAGE_NOW;
|
|
|
|
|
2017-03-27 22:59:31 +08:00
|
|
|
battery = &hidpp->battery;
|
|
|
|
|
|
|
|
n = atomic_inc_return(&battery_no) - 1;
|
2017-03-27 22:59:36 +08:00
|
|
|
desc->properties = battery_props;
|
|
|
|
desc->num_properties = num_battery_props;
|
2017-03-27 22:59:31 +08:00
|
|
|
desc->get_property = hidpp_battery_get_property;
|
|
|
|
sprintf(battery->name, "hidpp_battery_%ld", n);
|
|
|
|
desc->name = battery->name;
|
|
|
|
desc->type = POWER_SUPPLY_TYPE_BATTERY;
|
|
|
|
desc->use_for_apm = 0;
|
|
|
|
|
|
|
|
battery->ps = devm_power_supply_register(&hidpp->hid_dev->dev,
|
|
|
|
&battery->desc,
|
|
|
|
&cfg);
|
|
|
|
if (IS_ERR(battery->ps))
|
|
|
|
return PTR_ERR(battery->ps);
|
|
|
|
|
|
|
|
power_supply_powers(battery->ps, &hidpp->hid_dev->dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:26 +08:00
|
|
|
static void hidpp_overwrite_name(struct hid_device *hdev)
|
2014-10-01 01:18:27 +08:00
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
|
|
|
char *name;
|
|
|
|
|
2017-03-27 22:59:26 +08:00
|
|
|
if (hidpp->protocol_major < 2)
|
2017-03-27 22:59:24 +08:00
|
|
|
return;
|
2017-03-27 22:59:26 +08:00
|
|
|
|
|
|
|
name = hidpp_get_device_name(hidpp);
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2015-11-20 07:42:12 +08:00
|
|
|
if (!name) {
|
2014-10-01 01:18:27 +08:00
|
|
|
hid_err(hdev, "unable to retrieve the name of the device");
|
2015-11-20 07:42:12 +08:00
|
|
|
} else {
|
|
|
|
dbg_hid("HID++: Got name: %s\n", name);
|
2014-10-01 01:18:27 +08:00
|
|
|
snprintf(hdev->name, sizeof(hdev->name), "%s", name);
|
2015-11-20 07:42:12 +08:00
|
|
|
}
|
2014-10-01 01:18:27 +08:00
|
|
|
|
|
|
|
kfree(name);
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:32 +08:00
|
|
|
static int hidpp_input_open(struct input_dev *dev)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = input_get_drvdata(dev);
|
|
|
|
|
|
|
|
return hid_hw_open(hid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hidpp_input_close(struct input_dev *dev)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = input_get_drvdata(dev);
|
|
|
|
|
|
|
|
hid_hw_close(hid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct input_dev *hidpp_allocate_input(struct hid_device *hdev)
|
|
|
|
{
|
|
|
|
struct input_dev *input_dev = devm_input_allocate_device(&hdev->dev);
|
2015-01-09 03:37:12 +08:00
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
2014-10-01 01:18:32 +08:00
|
|
|
|
|
|
|
if (!input_dev)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
input_set_drvdata(input_dev, hdev);
|
|
|
|
input_dev->open = hidpp_input_open;
|
|
|
|
input_dev->close = hidpp_input_close;
|
|
|
|
|
2015-01-09 03:37:12 +08:00
|
|
|
input_dev->name = hidpp->name;
|
2014-10-01 01:18:32 +08:00
|
|
|
input_dev->phys = hdev->phys;
|
|
|
|
input_dev->uniq = hdev->uniq;
|
|
|
|
input_dev->id.bustype = hdev->bus;
|
|
|
|
input_dev->id.vendor = hdev->vendor;
|
|
|
|
input_dev->id.product = hdev->product;
|
|
|
|
input_dev->id.version = hdev->version;
|
|
|
|
input_dev->dev.parent = &hdev->dev;
|
|
|
|
|
|
|
|
return input_dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hidpp_connect_event(struct hidpp_device *hidpp)
|
|
|
|
{
|
|
|
|
struct hid_device *hdev = hidpp->hid_dev;
|
|
|
|
int ret = 0;
|
|
|
|
bool connected = atomic_read(&hidpp->connected);
|
|
|
|
struct input_dev *input;
|
|
|
|
char *name, *devm_name;
|
|
|
|
|
2017-03-27 22:59:34 +08:00
|
|
|
if (!connected) {
|
|
|
|
if (hidpp->battery.ps) {
|
|
|
|
hidpp->battery.online = false;
|
|
|
|
hidpp->battery.status = POWER_SUPPLY_STATUS_UNKNOWN;
|
2017-03-27 22:59:36 +08:00
|
|
|
hidpp->battery.level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
|
2017-03-27 22:59:34 +08:00
|
|
|
power_supply_changed(hidpp->battery.ps);
|
|
|
|
}
|
2017-03-27 22:59:28 +08:00
|
|
|
return;
|
2017-03-27 22:59:34 +08:00
|
|
|
}
|
2017-03-27 22:59:28 +08:00
|
|
|
|
2014-12-17 06:06:01 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
|
|
|
|
ret = wtp_connect(hdev, connected);
|
|
|
|
if (ret)
|
|
|
|
return;
|
2015-05-30 17:00:27 +08:00
|
|
|
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
|
|
|
|
ret = m560_send_config_command(hdev, connected);
|
|
|
|
if (ret)
|
|
|
|
return;
|
2015-09-03 21:08:30 +08:00
|
|
|
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) {
|
|
|
|
ret = k400_connect(hdev, connected);
|
|
|
|
if (ret)
|
|
|
|
return;
|
2014-12-17 06:06:01 +08:00
|
|
|
}
|
2014-10-01 01:18:33 +08:00
|
|
|
|
2019-04-20 19:22:15 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_HIDPP_WHEELS) {
|
|
|
|
ret = hidpp10_wheel_connect(hidpp);
|
|
|
|
if (ret)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:16 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS) {
|
|
|
|
ret = hidpp10_extra_mouse_buttons_connect(hidpp);
|
|
|
|
if (ret)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:17 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS) {
|
|
|
|
ret = hidpp10_consumer_keys_connect(hidpp);
|
|
|
|
if (ret)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-09-03 21:08:29 +08:00
|
|
|
/* the device is already connected, we can ask for its name and
|
|
|
|
* protocol */
|
2014-10-01 01:18:32 +08:00
|
|
|
if (!hidpp->protocol_major) {
|
2019-03-22 15:41:39 +08:00
|
|
|
ret = hidpp_root_get_protocol_version(hidpp);
|
2014-10-01 01:18:32 +08:00
|
|
|
if (ret) {
|
|
|
|
hid_err(hdev, "Can not get the protocol version.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:27 +08:00
|
|
|
if (hidpp->name == hdev->name && hidpp->protocol_major >= 2) {
|
2015-01-09 03:37:12 +08:00
|
|
|
name = hidpp_get_device_name(hidpp);
|
2019-04-20 19:22:07 +08:00
|
|
|
if (name) {
|
|
|
|
devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
|
|
|
|
"%s", name);
|
|
|
|
kfree(name);
|
|
|
|
if (!devm_name)
|
|
|
|
return;
|
2015-01-09 03:37:12 +08:00
|
|
|
|
2019-04-20 19:22:07 +08:00
|
|
|
hidpp->name = devm_name;
|
|
|
|
}
|
2015-01-09 03:37:12 +08:00
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:27 +08:00
|
|
|
hidpp_initialize_battery(hidpp);
|
2022-09-14 21:21:46 +08:00
|
|
|
hidpp_initialize_hires_scroll(hidpp);
|
2017-03-27 22:59:27 +08:00
|
|
|
|
2017-03-27 22:59:33 +08:00
|
|
|
/* forward current battery state */
|
2017-03-27 22:59:38 +08:00
|
|
|
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) {
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
|
|
|
|
hidpp20_query_battery_voltage_info(hidpp);
|
HID: logitech-hidpp: add support for Unified Battery (1004) feature
This new feature present in new devices replaces the old Battery Level Status
(0x1000) feature. It keeps essentially the same information for levels
(reporting critical, low, good and full) but makes these levels optional, the
device exports a capability setting which describes which levels it supports.
In addition to this, there is an optional state_of_charge paramenter that
exports the battery percentage.
This patch adds support for this new feature. There were some implementation
choices, as described below and in the code.
If the device supports the state_of_charge parameter, we will just export the
battery percentage and not the levels, which the device might still support.
Since this feature can co-exist with the Battery Voltage (0x1001) feature and
we currently only support one battery feature, I changed the battery feature
discovery to try to use 0x1000 and 0x1004 first and only then 0x1001, the
battery voltage feature.
In the future we could uncouple this and make the battery feature co-exists
with 0x1000 and 0x1004, allowing the device to export voltage information in
addition to the battery percentage or level.
I tested this patch with a MX Anywhere 3, which supports the new feature. Since
I don't have any device that doesn't support the state_of_charge parameter of
this feature, I forced the MX Anywhere 3 to use the level information, instead
of battery percentage, to test that part of the implementation.
I also tested with a MX Master 3, which supports the Battery Level Status
(0x1000) feature, and a G703 Hero, which supports the Battery Voltage (0x1001)
feature, to make sure nothing broke there.
[jkosina@suse.cz: fix comment]
Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-01-05 02:29:37 +08:00
|
|
|
else if (hidpp->capabilities & HIDPP_CAPABILITY_UNIFIED_BATTERY)
|
|
|
|
hidpp20_query_battery_info_1004(hidpp);
|
hid-logitech-hidpp: read battery voltage from newer devices
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.
When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its charge status. The device will respond with three bytes: the first
two are its voltage, and the last one is a bitset, reporting if the
battery is charging, fast or slow, in critical level or discharging.
If everything went correctly, record the fact that we're capable
of querying battery voltage.
Note that the protocol only gives us the current voltage in mV.
We store that as-is, but when queried, we report it in uV as
expected by sysfs.
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.
Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.
Signed-off-by: Pedro Vanzella <pedro@pedrovanzella.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-27 06:25:06 +08:00
|
|
|
else
|
HID: logitech-hidpp: add support for Unified Battery (1004) feature
This new feature present in new devices replaces the old Battery Level Status
(0x1000) feature. It keeps essentially the same information for levels
(reporting critical, low, good and full) but makes these levels optional, the
device exports a capability setting which describes which levels it supports.
In addition to this, there is an optional state_of_charge paramenter that
exports the battery percentage.
This patch adds support for this new feature. There were some implementation
choices, as described below and in the code.
If the device supports the state_of_charge parameter, we will just export the
battery percentage and not the levels, which the device might still support.
Since this feature can co-exist with the Battery Voltage (0x1001) feature and
we currently only support one battery feature, I changed the battery feature
discovery to try to use 0x1000 and 0x1004 first and only then 0x1001, the
battery voltage feature.
In the future we could uncouple this and make the battery feature co-exists
with 0x1000 and 0x1004, allowing the device to export voltage information in
addition to the battery percentage or level.
I tested this patch with a MX Anywhere 3, which supports the new feature. Since
I don't have any device that doesn't support the state_of_charge parameter of
this feature, I forced the MX Anywhere 3 to use the level information, instead
of battery percentage, to test that part of the implementation.
I also tested with a MX Master 3, which supports the Battery Level Status
(0x1000) feature, and a G703 Hero, which supports the Battery Voltage (0x1001)
feature, to make sure nothing broke there.
[jkosina@suse.cz: fix comment]
Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-01-05 02:29:37 +08:00
|
|
|
hidpp20_query_battery_info_1000(hidpp);
|
2017-03-27 22:59:33 +08:00
|
|
|
}
|
2017-03-27 22:59:38 +08:00
|
|
|
if (hidpp->battery.ps)
|
|
|
|
power_supply_changed(hidpp->battery.ps);
|
2017-03-27 22:59:33 +08:00
|
|
|
|
2022-09-14 21:21:46 +08:00
|
|
|
if (hidpp->capabilities & HIDPP_CAPABILITY_HI_RES_SCROLL)
|
2018-12-05 08:42:27 +08:00
|
|
|
hi_res_scroll_enable(hidpp);
|
|
|
|
|
2017-03-27 22:59:28 +08:00
|
|
|
if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)
|
|
|
|
/* if the input nodes are already created, we can stop now */
|
2017-03-27 22:59:27 +08:00
|
|
|
return;
|
|
|
|
|
2014-10-01 01:18:32 +08:00
|
|
|
input = hidpp_allocate_input(hdev);
|
|
|
|
if (!input) {
|
|
|
|
hid_err(hdev, "cannot allocate new input device: %d\n", ret);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:09 +08:00
|
|
|
hidpp_populate_input(hidpp, input);
|
2014-10-01 01:18:32 +08:00
|
|
|
|
|
|
|
ret = input_register_device(input);
|
2022-08-12 10:55:15 +08:00
|
|
|
if (ret) {
|
2014-10-01 01:18:32 +08:00
|
|
|
input_free_device(input);
|
2022-08-12 10:55:15 +08:00
|
|
|
return;
|
|
|
|
}
|
2014-10-01 01:18:32 +08:00
|
|
|
|
|
|
|
hidpp->delayed_input = input;
|
|
|
|
}
|
|
|
|
|
2017-03-27 22:59:39 +08:00
|
|
|
static DEVICE_ATTR(builtin_power_supply, 0000, NULL, NULL);
|
|
|
|
|
|
|
|
static struct attribute *sysfs_attrs[] = {
|
|
|
|
&dev_attr_builtin_power_supply.attr,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2017-08-03 19:29:04 +08:00
|
|
|
static const struct attribute_group ps_attribute_group = {
|
2017-03-27 22:59:39 +08:00
|
|
|
.attrs = sysfs_attrs
|
|
|
|
};
|
|
|
|
|
2019-04-20 19:22:12 +08:00
|
|
|
static int hidpp_get_report_length(struct hid_device *hdev, int id)
|
2019-04-20 19:22:04 +08:00
|
|
|
{
|
|
|
|
struct hid_report_enum *re;
|
|
|
|
struct hid_report *report;
|
|
|
|
|
2019-04-20 19:22:12 +08:00
|
|
|
re = &(hdev->report_enum[HID_OUTPUT_REPORT]);
|
|
|
|
report = re->report_id_hash[id];
|
|
|
|
if (!report)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return report->field[0]->report_count + 1;
|
|
|
|
}
|
|
|
|
|
2019-10-28 01:44:06 +08:00
|
|
|
static u8 hidpp_validate_device(struct hid_device *hdev)
|
2019-04-20 19:22:12 +08:00
|
|
|
{
|
2019-10-18 12:45:16 +08:00
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
2019-10-28 01:44:06 +08:00
|
|
|
int id, report_length;
|
|
|
|
u8 supported_reports = 0;
|
2019-10-18 12:45:16 +08:00
|
|
|
|
|
|
|
id = REPORT_ID_HIDPP_SHORT;
|
|
|
|
report_length = hidpp_get_report_length(hdev, id);
|
|
|
|
if (report_length) {
|
|
|
|
if (report_length < HIDPP_REPORT_SHORT_LENGTH)
|
|
|
|
goto bad_device;
|
2019-04-20 19:22:12 +08:00
|
|
|
|
2019-10-28 01:44:06 +08:00
|
|
|
supported_reports |= HIDPP_REPORT_SHORT_SUPPORTED;
|
2019-04-20 19:22:04 +08:00
|
|
|
}
|
|
|
|
|
2019-10-18 12:45:16 +08:00
|
|
|
id = REPORT_ID_HIDPP_LONG;
|
2019-04-20 19:22:12 +08:00
|
|
|
report_length = hidpp_get_report_length(hdev, id);
|
2019-10-18 12:45:16 +08:00
|
|
|
if (report_length) {
|
|
|
|
if (report_length < HIDPP_REPORT_LONG_LENGTH)
|
|
|
|
goto bad_device;
|
2019-04-20 19:22:04 +08:00
|
|
|
|
2019-10-28 01:44:06 +08:00
|
|
|
supported_reports |= HIDPP_REPORT_LONG_SUPPORTED;
|
2019-04-20 19:22:04 +08:00
|
|
|
}
|
|
|
|
|
2019-10-18 12:45:16 +08:00
|
|
|
id = REPORT_ID_HIDPP_VERY_LONG;
|
|
|
|
report_length = hidpp_get_report_length(hdev, id);
|
|
|
|
if (report_length) {
|
|
|
|
if (report_length < HIDPP_REPORT_LONG_LENGTH ||
|
|
|
|
report_length > HIDPP_REPORT_VERY_LONG_MAX_LENGTH)
|
|
|
|
goto bad_device;
|
2019-04-20 19:22:04 +08:00
|
|
|
|
2019-10-28 01:44:06 +08:00
|
|
|
supported_reports |= HIDPP_REPORT_VERY_LONG_SUPPORTED;
|
2019-10-18 12:45:16 +08:00
|
|
|
hidpp->very_long_report_length = report_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
return supported_reports;
|
|
|
|
|
|
|
|
bad_device:
|
|
|
|
hid_warn(hdev, "not enough values in hidpp report %d\n", id);
|
|
|
|
return false;
|
2019-04-20 19:22:04 +08:00
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:15 +08:00
|
|
|
static bool hidpp_application_equals(struct hid_device *hdev,
|
|
|
|
unsigned int application)
|
|
|
|
{
|
|
|
|
struct list_head *report_list;
|
|
|
|
struct hid_report *report;
|
|
|
|
|
|
|
|
report_list = &hdev->report_enum[HID_INPUT_REPORT].report_list;
|
|
|
|
report = list_first_entry_or_null(report_list, struct hid_report, list);
|
|
|
|
return report && report->application == application;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp;
|
|
|
|
int ret;
|
|
|
|
bool connected;
|
2014-10-01 01:18:32 +08:00
|
|
|
unsigned int connect_mask = HID_CONNECT_DEFAULT;
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
struct hidpp_ff_private_data data;
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2019-04-20 19:22:17 +08:00
|
|
|
/* report_fixup needs drvdata to be set before we call hid_parse */
|
|
|
|
hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL);
|
|
|
|
if (!hidpp)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
hidpp->hid_dev = hdev;
|
|
|
|
hidpp->name = hdev->name;
|
|
|
|
hidpp->quirks = id->driver_data;
|
|
|
|
hid_set_drvdata(hdev, hidpp);
|
|
|
|
|
2019-04-20 19:22:04 +08:00
|
|
|
ret = hid_parse(hdev);
|
|
|
|
if (ret) {
|
|
|
|
hid_err(hdev, "%s:parse failed\n", __func__);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the device is HID++ capable, otherwise treat as generic HID
|
|
|
|
*/
|
2019-10-28 01:44:06 +08:00
|
|
|
hidpp->supported_reports = hidpp_validate_device(hdev);
|
|
|
|
|
|
|
|
if (!hidpp->supported_reports) {
|
2019-04-20 19:22:17 +08:00
|
|
|
hid_set_drvdata(hdev, NULL);
|
|
|
|
devm_kfree(&hdev->dev, hidpp);
|
2019-04-20 19:22:04 +08:00
|
|
|
return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
2019-04-20 19:22:17 +08:00
|
|
|
}
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2017-03-27 22:59:26 +08:00
|
|
|
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
|
|
|
|
hidpp->quirks |= HIDPP_QUIRK_UNIFYING;
|
|
|
|
|
2019-04-20 19:22:15 +08:00
|
|
|
if (id->group == HID_GROUP_LOGITECH_27MHZ_DEVICE &&
|
|
|
|
hidpp_application_equals(hdev, HID_GD_MOUSE))
|
2019-04-20 19:22:16 +08:00
|
|
|
hidpp->quirks |= HIDPP_QUIRK_HIDPP_WHEELS |
|
|
|
|
HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS;
|
2019-04-20 19:22:15 +08:00
|
|
|
|
2019-04-20 19:22:17 +08:00
|
|
|
if (id->group == HID_GROUP_LOGITECH_27MHZ_DEVICE &&
|
|
|
|
hidpp_application_equals(hdev, HID_GD_KEYBOARD))
|
|
|
|
hidpp->quirks |= HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS;
|
|
|
|
|
2015-03-27 00:41:57 +08:00
|
|
|
if (disable_raw_mode) {
|
|
|
|
hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP;
|
2015-09-03 21:08:29 +08:00
|
|
|
hidpp->quirks &= ~HIDPP_QUIRK_NO_HIDINPUT;
|
2015-03-27 00:41:57 +08:00
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
|
|
|
|
ret = wtp_allocate(hdev, id);
|
|
|
|
if (ret)
|
2019-04-20 19:21:42 +08:00
|
|
|
return ret;
|
2015-09-03 21:08:30 +08:00
|
|
|
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) {
|
|
|
|
ret = k400_allocate(hdev);
|
|
|
|
if (ret)
|
2019-04-20 19:21:42 +08:00
|
|
|
return ret;
|
2014-10-01 01:18:27 +08:00
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:32 +08:00
|
|
|
INIT_WORK(&hidpp->work, delayed_work_cb);
|
2014-10-01 01:18:27 +08:00
|
|
|
mutex_init(&hidpp->send_mutex);
|
|
|
|
init_waitqueue_head(&hidpp->wait);
|
|
|
|
|
2017-03-27 22:59:39 +08:00
|
|
|
/* indicates we are handling the battery properties in the kernel */
|
|
|
|
ret = sysfs_create_group(&hdev->dev.kobj, &ps_attribute_group);
|
|
|
|
if (ret)
|
|
|
|
hid_warn(hdev, "Cannot allocate sysfs group for %s\n",
|
|
|
|
hdev->name);
|
|
|
|
|
2019-04-20 19:22:05 +08:00
|
|
|
/*
|
|
|
|
* Plain USB connections need to actually call start and open
|
|
|
|
* on the transport driver to allow incoming data.
|
|
|
|
*/
|
|
|
|
ret = hid_hw_start(hdev, 0);
|
|
|
|
if (ret) {
|
|
|
|
hid_err(hdev, "hw start failed\n");
|
|
|
|
goto hid_hw_start_fail;
|
2015-11-20 07:42:12 +08:00
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:05 +08:00
|
|
|
ret = hid_hw_open(hdev);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(&hdev->dev, "%s:hid_hw_open returned error:%d\n",
|
|
|
|
__func__, ret);
|
|
|
|
goto hid_hw_open_fail;
|
|
|
|
}
|
2015-11-20 07:42:12 +08:00
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
/* Allow incoming packets */
|
|
|
|
hid_device_io_start(hdev);
|
|
|
|
|
2017-03-27 22:59:26 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_UNIFYING)
|
|
|
|
hidpp_unifying_init(hidpp);
|
|
|
|
|
2019-03-22 15:41:39 +08:00
|
|
|
connected = hidpp_root_get_protocol_version(hidpp) == 0;
|
2017-03-27 22:59:26 +08:00
|
|
|
atomic_set(&hidpp->connected, connected);
|
|
|
|
if (!(hidpp->quirks & HIDPP_QUIRK_UNIFYING)) {
|
2014-10-01 01:18:28 +08:00
|
|
|
if (!connected) {
|
2015-04-05 20:06:29 +08:00
|
|
|
ret = -ENODEV;
|
2014-10-01 01:18:28 +08:00
|
|
|
hid_err(hdev, "Device not connected");
|
2019-04-20 19:22:05 +08:00
|
|
|
goto hid_hw_init_fail;
|
2014-10-01 01:18:28 +08:00
|
|
|
}
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2017-03-27 22:59:26 +08:00
|
|
|
hidpp_overwrite_name(hdev);
|
|
|
|
}
|
2014-10-01 01:18:30 +08:00
|
|
|
|
2019-10-28 01:44:13 +08:00
|
|
|
if (connected && hidpp->protocol_major >= 2) {
|
|
|
|
ret = hidpp_set_wireless_feature_index(hidpp);
|
|
|
|
if (ret == -ENOENT)
|
|
|
|
hidpp->wireless_feature_index = 0;
|
|
|
|
else if (ret)
|
|
|
|
goto hid_hw_init_fail;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:32 +08:00
|
|
|
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
|
2014-10-01 01:18:27 +08:00
|
|
|
ret = wtp_get_config(hidpp);
|
|
|
|
if (ret)
|
2019-04-20 19:22:05 +08:00
|
|
|
goto hid_hw_init_fail;
|
2015-11-20 07:42:13 +08:00
|
|
|
} else if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
ret = g920_get_config(hidpp, &data);
|
2015-11-20 07:42:13 +08:00
|
|
|
if (ret)
|
2019-04-20 19:22:05 +08:00
|
|
|
goto hid_hw_init_fail;
|
2014-10-01 01:18:27 +08:00
|
|
|
}
|
|
|
|
|
2019-04-20 19:22:05 +08:00
|
|
|
hidpp_connect_event(hidpp);
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2019-04-20 19:22:05 +08:00
|
|
|
/* Reset the HID node state */
|
|
|
|
hid_device_io_stop(hdev);
|
|
|
|
hid_hw_close(hdev);
|
|
|
|
hid_hw_stop(hdev);
|
2014-10-01 01:18:27 +08:00
|
|
|
|
2019-04-20 19:22:05 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT)
|
|
|
|
connect_mask &= ~HID_CONNECT_HIDINPUT;
|
2014-10-01 01:18:32 +08:00
|
|
|
|
2019-04-20 19:22:05 +08:00
|
|
|
/* Now export the actual inputs and hidraw nodes to the world */
|
|
|
|
ret = hid_hw_start(hdev, connect_mask);
|
|
|
|
if (ret) {
|
|
|
|
hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
|
|
|
|
goto hid_hw_start_fail;
|
|
|
|
}
|
2014-10-01 01:18:32 +08:00
|
|
|
|
HID: logitech-hidpp: split g920_get_config()
Original version of g920_get_config() contained two kind of actions:
1. Device specific communication to query/set some parameters
which requires active communication channel with the device,
or, put in other way, for the call to be sandwiched between
hid_device_io_start() and hid_device_io_stop().
2. Input subsystem specific FF controller initialization which, in
order to access a valid 'struct hid_input' via
'hid->inputs.next', requires claimed hidinput which means be
executed after the call to hid_hw_start() with connect_mask
containing HID_CONNECT_HIDINPUT.
Location of g920_get_config() can only fulfill requirements for #1 and
not #2, which might result in following backtrace:
[ 88.312258] logitech-hidpp-device 0003:046D:C262.0005: HID++ 4.2 device connected.
[ 88.320298] BUG: kernel NULL pointer dereference, address: 0000000000000018
[ 88.320304] #PF: supervisor read access in kernel mode
[ 88.320307] #PF: error_code(0x0000) - not-present page
[ 88.320309] PGD 0 P4D 0
[ 88.320315] Oops: 0000 [#1] SMP PTI
[ 88.320320] CPU: 1 PID: 3080 Comm: systemd-udevd Not tainted 5.4.0-rc1+ #31
[ 88.320322] Hardware name: Apple Inc. MacBookPro11,1/Mac-189A3D4F975D5FFC, BIOS 149.0.0.0.0 09/17/2018
[ 88.320334] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320338] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320341] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320345] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320347] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320350] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320352] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320354] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320358] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320363] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
[ 88.320366] Call Trace:
[ 88.320377] ? _cond_resched+0x15/0x30
[ 88.320387] ? create_pinctrl+0x2f/0x3c0
[ 88.320393] ? kernfs_link_sibling+0x94/0xe0
[ 88.320398] ? _cond_resched+0x15/0x30
[ 88.320402] ? kernfs_activate+0x5f/0x80
[ 88.320406] ? kernfs_add_one+0xe2/0x130
[ 88.320411] hid_device_probe+0x106/0x170
[ 88.320419] really_probe+0x147/0x3c0
[ 88.320424] driver_probe_device+0xb6/0x100
[ 88.320428] device_driver_attach+0x53/0x60
[ 88.320433] __driver_attach+0x8a/0x150
[ 88.320437] ? device_driver_attach+0x60/0x60
[ 88.320440] bus_for_each_dev+0x78/0xc0
[ 88.320445] bus_add_driver+0x14d/0x1f0
[ 88.320450] driver_register+0x6c/0xc0
[ 88.320453] ? 0xffffffffc0d67000
[ 88.320457] __hid_register_driver+0x4c/0x80
[ 88.320464] do_one_initcall+0x46/0x1f4
[ 88.320469] ? _cond_resched+0x15/0x30
[ 88.320474] ? kmem_cache_alloc_trace+0x162/0x220
[ 88.320481] ? do_init_module+0x23/0x230
[ 88.320486] do_init_module+0x5c/0x230
[ 88.320491] load_module+0x26e1/0x2990
[ 88.320502] ? ima_post_read_file+0xf0/0x100
[ 88.320508] ? __do_sys_finit_module+0xaa/0x110
[ 88.320512] __do_sys_finit_module+0xaa/0x110
[ 88.320520] do_syscall_64+0x5b/0x180
[ 88.320525] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 88.320528] RIP: 0033:0x7f8d8d1f01fd
[ 88.320532] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 5b 8c 0c 00 f7 d8 64 89 01 48
[ 88.320535] RSP: 002b:00007ffefa3bb068 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 88.320539] RAX: ffffffffffffffda RBX: 000055922040cb40 RCX: 00007f8d8d1f01fd
[ 88.320541] RDX: 0000000000000000 RSI: 00007f8d8ce4984d RDI: 0000000000000006
[ 88.320543] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
[ 88.320545] R10: 0000000000000006 R11: 0000000000000246 R12: 00007f8d8ce4984d
[ 88.320547] R13: 0000000000000000 R14: 000055922040efc0 R15: 000055922040cb40
[ 88.320551] Modules linked in: hid_logitech_hidpp(+) fuse rfcomm ccm xt_CHECKSUM xt_MASQUERADE bridge stp llc nf_nat_tftp nf_conntrack_tftp nf_conntrack_netbios_ns nf_conntrack_broadcast xt_CT ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ip6table_nat ip6table_mangle ip6table_raw ip6table_security iptable_nat nf_nat tun iptable_mangle iptable_raw iptable_security nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c ip_set nfnetlink ebtable_filter ebtables ip6table_filter ip6_tables cmac bnep sunrpc dm_crypt nls_utf8 hfsplus intel_rapl_msr intel_rapl_common ath9k_htc ath9k_common x86_pkg_temp_thermal intel_powerclamp b43 ath9k_hw coretemp snd_hda_codec_hdmi cordic kvm_intel snd_hda_codec_cirrus mac80211 snd_hda_codec_generic ledtrig_audio kvm snd_hda_intel snd_intel_nhlt irqbypass snd_hda_codec btusb btrtl snd_hda_core ath btbcm ssb snd_hwdep btintel snd_seq crct10dif_pclmul iTCO_wdt snd_seq_device crc32_pclmul bluetooth mmc_core iTCO_vendor_support joydev cfg80211
[ 88.320602] applesmc ghash_clmulni_intel ecdh_generic snd_pcm input_polldev intel_cstate ecc intel_uncore thunderbolt snd_timer i2c_i801 libarc4 rfkill intel_rapl_perf lpc_ich mei_me pcspkr bcm5974 snd bcma mei soundcore acpi_als sbs kfifo_buf sbshc industrialio apple_bl i915 i2c_algo_bit drm_kms_helper drm uas crc32c_intel usb_storage video hid_apple
[ 88.320630] CR2: 0000000000000018
[ 88.320633] ---[ end trace 933491c8a4fadeb7 ]---
[ 88.320642] RIP: 0010:hidpp_probe+0x61f/0x948 [hid_logitech_hidpp]
[ 88.320645] Code: 81 00 00 48 89 ef e8 f0 d6 ff ff 41 89 c6 85 c0 75 b5 0f b6 44 24 28 48 8b 5d 00 88 44 24 1e 89 44 24 0c 48 8b 83 18 1c 00 00 <48> 8b 48 18 48 8b 83 10 19 00 00 48 8b 40 40 48 89 0c 24 0f b7 80
[ 88.320647] RSP: 0018:ffffb0a6824aba68 EFLAGS: 00010246
[ 88.320650] RAX: 0000000000000000 RBX: ffff93a50756e000 RCX: 0000000000010408
[ 88.320652] RDX: 0000000000000000 RSI: ffff93a51f0ad0a0 RDI: 000000000002d0a0
[ 88.320655] RBP: ffff93a50416da28 R08: ffff93a50416da70 R09: ffff93a50416da70
[ 88.320657] R10: 000000148ae9e60c R11: 00000000000f1525 R12: ffff93a50756e000
[ 88.320659] R13: ffff93a50756f8d0 R14: 0000000000000000 R15: ffff93a50756fc38
[ 88.320662] FS: 00007f8d8c1e0940(0000) GS:ffff93a51f080000(0000) knlGS:0000000000000000
[ 88.320664] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 88.320667] CR2: 0000000000000018 CR3: 00000003996d8003 CR4: 00000000001606e0
To solve this issue:
1. Split g920_get_config() such that all of the device specific
communication remains a part of the function and input subsystem
initialization bits go to hidpp_ff_init()
2. Move call to hidpp_ff_init() from being a part of
g920_get_config() to be the last step of .probe(), right after a
call to hid_hw_start() with connect_mask containing
HID_CONNECT_HIDINPUT.
Fixes: 91cf9a98ae41 ("HID: logitech-hidpp: make .probe usbhid capable")
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Tested-by: Sam Bazley <sambazley@fastmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Henrik Rydberg <rydberg@bitmath.org>
Cc: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
Cc: Austin Palmer <austinp@valvesoftware.com>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # 5.2+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2019-10-18 12:45:15 +08:00
|
|
|
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
|
|
|
|
ret = hidpp_ff_init(hidpp, &data);
|
|
|
|
if (ret)
|
|
|
|
hid_warn(hidpp->hid_dev,
|
|
|
|
"Unable to initialize force feedback support, errno %d\n",
|
|
|
|
ret);
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
return ret;
|
|
|
|
|
2019-04-20 19:22:05 +08:00
|
|
|
hid_hw_init_fail:
|
|
|
|
hid_hw_close(hdev);
|
|
|
|
hid_hw_open_fail:
|
|
|
|
hid_hw_stop(hdev);
|
2014-10-01 01:18:27 +08:00
|
|
|
hid_hw_start_fail:
|
2017-03-27 22:59:39 +08:00
|
|
|
sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group);
|
2014-10-01 01:18:32 +08:00
|
|
|
cancel_work_sync(&hidpp->work);
|
2014-10-01 01:18:27 +08:00
|
|
|
mutex_destroy(&hidpp->send_mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hidpp_remove(struct hid_device *hdev)
|
|
|
|
{
|
|
|
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
|
|
|
|
2019-04-20 19:22:04 +08:00
|
|
|
if (!hidpp)
|
|
|
|
return hid_hw_stop(hdev);
|
|
|
|
|
2017-03-27 22:59:39 +08:00
|
|
|
sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group);
|
|
|
|
|
2015-11-20 07:42:12 +08:00
|
|
|
hid_hw_stop(hdev);
|
2014-10-01 01:18:32 +08:00
|
|
|
cancel_work_sync(&hidpp->work);
|
2014-10-01 01:18:27 +08:00
|
|
|
mutex_destroy(&hidpp->send_mutex);
|
|
|
|
}
|
|
|
|
|
2018-12-05 08:42:27 +08:00
|
|
|
#define LDJ_DEVICE(product) \
|
|
|
|
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, \
|
|
|
|
USB_VENDOR_ID_LOGITECH, (product))
|
|
|
|
|
2019-04-20 19:22:11 +08:00
|
|
|
#define L27MHZ_DEVICE(product) \
|
|
|
|
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_27MHZ_DEVICE, \
|
|
|
|
USB_VENDOR_ID_LOGITECH, (product))
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
static const struct hid_device_id hidpp_devices[] = {
|
2014-10-01 01:18:34 +08:00
|
|
|
{ /* wireless touchpad */
|
2018-12-05 08:42:28 +08:00
|
|
|
LDJ_DEVICE(0x4011),
|
2014-10-01 01:18:34 +08:00
|
|
|
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |
|
|
|
|
HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS },
|
2014-10-01 01:18:33 +08:00
|
|
|
{ /* wireless touchpad T650 */
|
2018-12-05 08:42:28 +08:00
|
|
|
LDJ_DEVICE(0x4101),
|
2014-10-01 01:18:33 +08:00
|
|
|
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT },
|
2014-10-01 01:18:27 +08:00
|
|
|
{ /* wireless touchpad T651 */
|
|
|
|
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
|
|
|
|
USB_DEVICE_ID_LOGITECH_T651),
|
|
|
|
.driver_data = HIDPP_QUIRK_CLASS_WTP },
|
2015-05-30 17:00:27 +08:00
|
|
|
{ /* Mouse logitech M560 */
|
2018-12-05 08:42:27 +08:00
|
|
|
LDJ_DEVICE(0x402d),
|
2022-09-14 21:21:46 +08:00
|
|
|
.driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
|
2015-09-03 21:08:30 +08:00
|
|
|
{ /* Keyboard logitech K400 */
|
2018-12-05 08:42:28 +08:00
|
|
|
LDJ_DEVICE(0x4024),
|
2016-06-29 17:28:02 +08:00
|
|
|
.driver_data = HIDPP_QUIRK_CLASS_K400 },
|
2017-03-27 22:59:37 +08:00
|
|
|
{ /* Solar Keyboard Logitech K750 */
|
2018-12-05 08:42:28 +08:00
|
|
|
LDJ_DEVICE(0x4002),
|
2017-03-27 22:59:37 +08:00
|
|
|
.driver_data = HIDPP_QUIRK_CLASS_K750 },
|
2019-04-20 19:22:17 +08:00
|
|
|
{ /* Keyboard MX5000 (Bluetooth-receiver in HID proxy mode) */
|
|
|
|
LDJ_DEVICE(0xb305),
|
|
|
|
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
|
2020-11-02 21:36:57 +08:00
|
|
|
{ /* Dinovo Edge (Bluetooth-receiver in HID proxy mode) */
|
|
|
|
LDJ_DEVICE(0xb309),
|
|
|
|
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
|
2019-04-29 03:25:52 +08:00
|
|
|
{ /* Keyboard MX5500 (Bluetooth-receiver in HID proxy mode) */
|
|
|
|
LDJ_DEVICE(0xb30b),
|
|
|
|
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
|
2014-10-01 01:18:28 +08:00
|
|
|
|
2018-12-05 08:42:28 +08:00
|
|
|
{ LDJ_DEVICE(HID_ANY_ID) },
|
2015-11-20 07:42:12 +08:00
|
|
|
|
2019-04-20 19:22:15 +08:00
|
|
|
{ /* Keyboard LX501 (Y-RR53) */
|
|
|
|
L27MHZ_DEVICE(0x0049),
|
|
|
|
.driver_data = HIDPP_QUIRK_KBD_ZOOM_WHEEL },
|
|
|
|
{ /* Keyboard MX3000 (Y-RAM74) */
|
|
|
|
L27MHZ_DEVICE(0x0057),
|
|
|
|
.driver_data = HIDPP_QUIRK_KBD_SCROLL_WHEEL },
|
|
|
|
{ /* Keyboard MX3200 (Y-RAV80) */
|
|
|
|
L27MHZ_DEVICE(0x005c),
|
|
|
|
.driver_data = HIDPP_QUIRK_KBD_ZOOM_WHEEL },
|
2019-05-12 18:36:02 +08:00
|
|
|
{ /* S510 Media Remote */
|
|
|
|
L27MHZ_DEVICE(0x00fe),
|
|
|
|
.driver_data = HIDPP_QUIRK_KBD_SCROLL_WHEEL },
|
2019-04-20 19:22:15 +08:00
|
|
|
|
2019-04-20 19:22:11 +08:00
|
|
|
{ L27MHZ_DEVICE(HID_ANY_ID) },
|
|
|
|
|
2019-07-16 15:37:26 +08:00
|
|
|
{ /* Logitech G403 Wireless Gaming Mouse over USB */
|
2019-04-20 19:22:05 +08:00
|
|
|
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC082) },
|
2019-07-16 15:37:26 +08:00
|
|
|
{ /* Logitech G703 Gaming Mouse over USB */
|
|
|
|
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC087) },
|
|
|
|
{ /* Logitech G703 Hero Gaming Mouse over USB */
|
|
|
|
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC090) },
|
2019-04-20 19:22:05 +08:00
|
|
|
{ /* Logitech G900 Gaming Mouse over USB */
|
|
|
|
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC081) },
|
2019-07-16 15:37:26 +08:00
|
|
|
{ /* Logitech G903 Gaming Mouse over USB */
|
|
|
|
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC086) },
|
|
|
|
{ /* Logitech G903 Hero Gaming Mouse over USB */
|
|
|
|
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC091) },
|
2019-04-20 19:22:05 +08:00
|
|
|
{ /* Logitech G920 Wheel over USB */
|
|
|
|
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
|
2015-11-20 07:42:12 +08:00
|
|
|
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
|
2019-07-16 15:37:26 +08:00
|
|
|
{ /* Logitech G Pro Gaming Mouse over USB */
|
|
|
|
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) },
|
2019-04-20 19:22:17 +08:00
|
|
|
|
|
|
|
{ /* MX5000 keyboard over Bluetooth */
|
|
|
|
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb305),
|
|
|
|
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
|
2020-11-02 21:36:57 +08:00
|
|
|
{ /* Dinovo Edge keyboard over Bluetooth */
|
|
|
|
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb309),
|
|
|
|
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
|
2019-04-29 03:25:52 +08:00
|
|
|
{ /* MX5500 keyboard over Bluetooth */
|
|
|
|
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb30b),
|
|
|
|
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
|
2022-12-07 22:24:32 +08:00
|
|
|
{ /* M-RCQ142 V470 Cordless Laser Mouse over Bluetooth */
|
|
|
|
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb008) },
|
|
|
|
{ /* MX Master mouse over Bluetooth */
|
|
|
|
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb012) },
|
|
|
|
{ /* MX Ergo trackball over Bluetooth */
|
|
|
|
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01d) },
|
|
|
|
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01e) },
|
|
|
|
{ /* MX Master 3 mouse over Bluetooth */
|
|
|
|
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb023) },
|
2014-10-01 01:18:27 +08:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(hid, hidpp_devices);
|
|
|
|
|
2018-12-05 08:42:27 +08:00
|
|
|
static const struct hid_usage_id hidpp_usages[] = {
|
|
|
|
{ HID_GD_WHEEL, EV_REL, REL_WHEEL_HI_RES },
|
|
|
|
{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
|
|
|
|
};
|
|
|
|
|
2014-10-01 01:18:27 +08:00
|
|
|
static struct hid_driver hidpp_driver = {
|
|
|
|
.name = "logitech-hidpp-device",
|
|
|
|
.id_table = hidpp_devices,
|
2019-04-20 19:22:17 +08:00
|
|
|
.report_fixup = hidpp_report_fixup,
|
2014-10-01 01:18:27 +08:00
|
|
|
.probe = hidpp_probe,
|
|
|
|
.remove = hidpp_remove,
|
|
|
|
.raw_event = hidpp_raw_event,
|
2018-12-05 08:42:27 +08:00
|
|
|
.usage_table = hidpp_usages,
|
|
|
|
.event = hidpp_event,
|
2014-10-01 01:18:27 +08:00
|
|
|
.input_configured = hidpp_input_configured,
|
|
|
|
.input_mapping = hidpp_input_mapping,
|
2015-11-20 07:42:15 +08:00
|
|
|
.input_mapped = hidpp_input_mapped,
|
2014-10-01 01:18:27 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
module_hid_driver(hidpp_driver);
|