You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
Pull HID updates from Jiri Kosina: - AMD SFH (Sensor Fusion Hub) support (Sandeep Singh) - increase of maximum HID report size to 16KB in order to support some of the modern devices (Dean Camera) - control interface support for hidraw (Dean Camera) - Sony DS4 power and firmware reporting fixes (Roderick Colenbrander) - support for ghlive PS3/WII U dongles (Pascal Giard) * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (27 commits) HID: i2c-hid: add Vero K147 to descriptor override HID: ite: Add support for Acer S1002 keyboard-dock HID: sony: support for ghlive ps3/wii u dongles HID: hidraw: Add additional hidraw input/output report ioctls. HID: Increase HID maximum report size to 16KB HID: elecom: drop stray comment HID: mf: add support for 0079:1846 Mayflash/Dragonrise USB Gamecube Adapter HID: elecom: add support for EX-G M-XGL20DLBK wireless mouse HID: elecom: rewrite report based on model specific parameters HID: wacom: Constify attribute_groups HID: input: Fix fall-through warnings for Clang HID: usbhid: Fix fall-through warnings for Clang HID: logitech-hidpp: Add hid_device_id for V470 bluetooth mouse HID: intel-ish-hid: Remove unnecessary assignment to variable rv HID: sony: Workaround for DS4 dongle hotplug kernel crash. HID: sony: Don't use fw_version/hw_version for sysfs cleanup. HID: sony: Report more accurate DS4 power status. SFH: fix error return check for -ERESTARTSYS HID: SFH: Add documentation HID: hid-input: occasionally report stylus battery even if not changed ...
This commit is contained in:
145
Documentation/hid/amd-sfh-hid.rst
Normal file
145
Documentation/hid/amd-sfh-hid.rst
Normal file
@@ -0,0 +1,145 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
|
||||
AMD Sensor Fusion Hub
|
||||
=====================
|
||||
AMD Sensor Fusion Hub (SFH) is part of an SOC starting from Ryzen based platforms.
|
||||
The solution is working well on several OEM products. AMD SFH uses HID over PCIe bus.
|
||||
In terms of architecture it resembles ISH, however the major difference is all
|
||||
the HID reports are generated as part of the kernel driver.
|
||||
|
||||
1. Block Diagram
|
||||
================
|
||||
|
||||
::
|
||||
|
||||
---------------------------------
|
||||
| HID User Space Applications |
|
||||
- -------------------------------
|
||||
|
||||
---------------------------------------------
|
||||
---------------------------------
|
||||
| HID Core |
|
||||
---------------------------------
|
||||
|
||||
---------------------------------
|
||||
| AMD HID Transport |
|
||||
---------------------------------
|
||||
|
||||
--------------------------------
|
||||
| AMD HID Client |
|
||||
| with HID Report Generator|
|
||||
--------------------------------
|
||||
|
||||
--------------------------------
|
||||
| AMD MP2 PCIe Driver |
|
||||
--------------------------------
|
||||
OS
|
||||
---------------------------------------------
|
||||
Hardware + Firmware
|
||||
--------------------------------
|
||||
| SFH MP2 Processor |
|
||||
--------------------------------
|
||||
|
||||
|
||||
AMD HID Transport Layer
|
||||
-----------------------
|
||||
AMD SFH transport is also implemented as a bus. Each client application executing in the AMD MP2 is
|
||||
registered as a device on this bus. Here: MP2 which is an ARM core connected to x86 for processing
|
||||
sensor data. The layer, which binds each device (AMD SFH HID driver) identifies the device type and
|
||||
registers with the hid core. Transport layer attach a constant "struct hid_ll_driver" object with
|
||||
each device. Once a device is registered with HID core, the callbacks provided via this struct are
|
||||
used by HID core to communicate with the device. AMD HID Transport layer implements the synchronous calls.
|
||||
|
||||
AMD HID Client Layer
|
||||
--------------------
|
||||
This layer is responsible to implement HID request and descriptors. As firmware is OS agnostic, HID
|
||||
client layer fills the HID request structure and descriptors. HID client layer is complex as it is
|
||||
interface between MP2 PCIe layer and HID. HID client layer initialized the MP2 PCIe layer and holds
|
||||
the instance of MP2 layer. It identifies the number of sensors connected using MP2-PCIe layer. Base
|
||||
on that allocates the DRAM address for each and every sensor and pass it to MP2-PCIe driver.On
|
||||
enumeration of each the sensor, client layer fills the HID Descriptor structure and HID input repor
|
||||
structure. HID Feature report structure is optional. The report descriptor structure varies from
|
||||
sensor to sensor.
|
||||
|
||||
AMD MP2 PCIe layer
|
||||
------------------
|
||||
MP2 PCIe Layer is responsible for making all transactions with the firmware over PCIe.
|
||||
The connection establishment between firmware and PCIe happens here.
|
||||
|
||||
The communication between X86 and MP2 is split into three parts.
|
||||
1. Command transfer via the C2P mailbox registers.
|
||||
2. Data transfer via DRAM.
|
||||
3. Supported sensor info via P2C registers.
|
||||
|
||||
Commands are sent to MP2 using C2P Mailbox registers. Writing into C2P Message registers generate
|
||||
interrupt to MP2. The client layer allocates the physical memory and the same is sent to MP2 via
|
||||
the PCI layer. MP2 firmware writes the command output to the access DRAM memory which the client
|
||||
layer has allocated. Firmware always writes minimum of 32 bytes into DRAM. So as a protocol driver
|
||||
shall allocate minimum of 32 bytes DRAM space.
|
||||
|
||||
Enumeration and Probing flow
|
||||
----------------------------
|
||||
::
|
||||
|
||||
HID AMD AMD AMD -PCIe MP2
|
||||
Core Transport Client layer layer FW
|
||||
| | | | |
|
||||
| | | on Boot Driver Loaded |
|
||||
| | | | |
|
||||
| | | MP2-PCIe Int |
|
||||
| | | | |
|
||||
| | |---Get Number of sensors-> | |
|
||||
| | | Read P2C |
|
||||
| | | Register |
|
||||
| | | | |
|
||||
| | | Loop(for No of Sensors) | |
|
||||
| | |----------------------| | |
|
||||
| | | Create HID Descriptor| | |
|
||||
| | | Create Input report | | |
|
||||
| | | Descriptor Map | | |
|
||||
| | | the MP2 FW Index to | | |
|
||||
| | | HID Index | | |
|
||||
| | | Allocate the DRAM | Enable |
|
||||
| | | address | Sensors |
|
||||
| | |----------------------| | |
|
||||
| | HID transport| | Enable |
|
||||
| |<--Probe------| |---Sensor CMD--> |
|
||||
| | Create the | | |
|
||||
| | HID device | | |
|
||||
| | (MFD) | | |
|
||||
| | by Populating| | |
|
||||
| | the HID | | |
|
||||
| | ll_driver | | |
|
||||
| HID | | | |
|
||||
| add | | | |
|
||||
|Device | | | |
|
||||
|<------------- | | | |
|
||||
|
||||
|
||||
Data Flow from Application to the AMD SFH Driver
|
||||
------------------------------------------------
|
||||
|
||||
::
|
||||
|
||||
| | | | |
|
||||
| | | | |
|
||||
| | | | |
|
||||
| | | | |
|
||||
| | | | |
|
||||
|HID_req | | | |
|
||||
|get_report | | | |
|
||||
|------------->| | | |
|
||||
| | HID_get_input| | |
|
||||
| | report | | |
|
||||
| |------------->|------------------------| | |
|
||||
| | | Read the DRAM data for| | |
|
||||
| | | requested sensor and | | |
|
||||
| | | create the HID input | | |
|
||||
| | | report | | |
|
||||
| | |------------------------| | |
|
||||
| |Data received | | |
|
||||
| | in HID report| | |
|
||||
To |<-------------|<-------------| | |
|
||||
Applications| | | | |
|
||||
<-------| | | | |
|
||||
@@ -123,8 +123,49 @@ HIDIOCGFEATURE(len):
|
||||
This ioctl will request a feature report from the device using the control
|
||||
endpoint. The first byte of the supplied buffer should be set to the report
|
||||
number of the requested report. For devices which do not use numbered
|
||||
reports, set the first byte to 0. The report will be returned starting at
|
||||
the first byte of the buffer (ie: the report number is not returned).
|
||||
reports, set the first byte to 0. The returned report buffer will contain the
|
||||
report number in the first byte, followed by the report data read from the
|
||||
device. For devices which do not use numbered reports, the report data will
|
||||
begin at the first byte of the returned buffer.
|
||||
|
||||
HIDIOCSINPUT(len):
|
||||
Send an Input Report
|
||||
|
||||
This ioctl will send an input report to the device, using the control endpoint.
|
||||
In most cases, setting an input HID report on a device is meaningless and has
|
||||
no effect, but some devices may choose to use this to set or reset an initial
|
||||
state of a report. The format of the buffer issued with this report is identical
|
||||
to that of HIDIOCSFEATURE.
|
||||
|
||||
HIDIOCGINPUT(len):
|
||||
Get an Input Report
|
||||
|
||||
This ioctl will request an input report from the device using the control
|
||||
endpoint. This is slower on most devices where a dedicated In endpoint exists
|
||||
for regular input reports, but allows the host to request the value of a
|
||||
specific report number. Typically, this is used to request the initial states of
|
||||
an input report of a device, before an application listens for normal reports via
|
||||
the regular device read() interface. The format of the buffer issued with this report
|
||||
is identical to that of HIDIOCGFEATURE.
|
||||
|
||||
HIDIOCSOUTPUT(len):
|
||||
Send an Output Report
|
||||
|
||||
This ioctl will send an output report to the device, using the control endpoint.
|
||||
This is slower on most devices where a dedicated Out endpoint exists for regular
|
||||
output reports, but is added for completeness. Typically, this is used to set
|
||||
the initial states of an output report of a device, before an application sends
|
||||
updates via the regular device write() interface. The format of the buffer issued
|
||||
with this report is identical to that of HIDIOCSFEATURE.
|
||||
|
||||
HIDIOCGOUTPUT(len):
|
||||
Get an Output Report
|
||||
|
||||
This ioctl will request an output report from the device using the control
|
||||
endpoint. Typically, this is used to retrive the initial state of
|
||||
an output report of a device, before an application updates it as necessary either
|
||||
via a HIDIOCSOUTPUT request, or the regular device write() interface. The format
|
||||
of the buffer issued with this report is identical to that of HIDIOCGFEATURE.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
@@ -16,3 +16,4 @@ Human Interface Devices (HID)
|
||||
|
||||
hid-alps
|
||||
intel-ish-hid
|
||||
amd-sfh-hid
|
||||
|
||||
@@ -956,6 +956,14 @@ S: Supported
|
||||
F: arch/arm64/boot/dts/amd/amd-seattle-xgbe*.dtsi
|
||||
F: drivers/net/ethernet/amd/xgbe/
|
||||
|
||||
AMD SENSOR FUSION HUB DRIVER
|
||||
M: Nehal Shah <nehal-bakulchandra.shah@amd.com>
|
||||
M: Sandeep Singh <sandeep.singh@amd.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hid/amd-sfh*
|
||||
F: drivers/hid/amd-sfh-hid/
|
||||
|
||||
AMS AS73211 DRIVER
|
||||
M: Christian Eggers <ceggers@arri.de>
|
||||
L: linux-iio@vger.kernel.org
|
||||
|
||||
@@ -907,6 +907,7 @@ config HID_SONY
|
||||
* Buzz controllers
|
||||
* Sony PS3 Blue-ray Disk Remote Control (Bluetooth)
|
||||
* Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
|
||||
* Guitar Hero Live PS3 and Wii U guitar dongles
|
||||
|
||||
config SONY_FF
|
||||
bool "Sony PS2/3/4 accessories force feedback support"
|
||||
@@ -1183,4 +1184,6 @@ source "drivers/hid/i2c-hid/Kconfig"
|
||||
|
||||
source "drivers/hid/intel-ish-hid/Kconfig"
|
||||
|
||||
source "drivers/hid/amd-sfh-hid/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -142,3 +142,5 @@ obj-$(CONFIG_I2C_HID) += i2c-hid/
|
||||
|
||||
obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/
|
||||
obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/
|
||||
|
||||
obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/
|
||||
|
||||
18
drivers/hid/amd-sfh-hid/Kconfig
Normal file
18
drivers/hid/amd-sfh-hid/Kconfig
Normal file
@@ -0,0 +1,18 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
menu "AMD SFH HID Support"
|
||||
depends on X86_64 || COMPILE_TEST
|
||||
depends on PCI
|
||||
depends on HID
|
||||
|
||||
config AMD_SFH_HID
|
||||
tristate "AMD Sensor Fusion Hub"
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
AMD Sensor Fusion Hub.
|
||||
This driver will enable sensors functionality on AMD platforms
|
||||
starting from 17h family of RYZEN parts.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called amd-sfh.
|
||||
Say Y or M here if you want to support AMD SFH. If unsure, say N.
|
||||
endmenu
|
||||
13
drivers/hid/amd-sfh-hid/Makefile
Normal file
13
drivers/hid/amd-sfh-hid/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# Makefile - AMD SFH HID drivers
|
||||
# Copyright (c) 2019-2020, Advanced Micro Devices, Inc.
|
||||
#
|
||||
#
|
||||
obj-$(CONFIG_AMD_SFH_HID) += amd_sfh.o
|
||||
amd_sfh-objs := amd_sfh_hid.o
|
||||
amd_sfh-objs += amd_sfh_client.o
|
||||
amd_sfh-objs += amd_sfh_pcie.o
|
||||
amd_sfh-objs += hid_descriptor/amd_sfh_hid_desc.o
|
||||
|
||||
ccflags-y += -I $(srctree)/$(src)/
|
||||
246
drivers/hid/amd-sfh-hid/amd_sfh_client.c
Normal file
246
drivers/hid/amd-sfh-hid/amd_sfh_client.c
Normal file
@@ -0,0 +1,246 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* AMD SFH Client Layer
|
||||
* Copyright 2020 Advanced Micro Devices, Inc.
|
||||
* Authors: Nehal Bakulchandra Shah <Nehal-Bakulchandra.Shah@amd.com>
|
||||
* Sandeep Singh <Sandeep.singh@amd.com>
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#include "hid_descriptor/amd_sfh_hid_desc.h"
|
||||
#include "amd_sfh_pcie.h"
|
||||
#include "amd_sfh_hid.h"
|
||||
|
||||
#define AMD_SFH_IDLE_LOOP 200
|
||||
|
||||
struct request_list {
|
||||
struct hid_device *hid;
|
||||
struct list_head list;
|
||||
u8 report_id;
|
||||
u8 sensor_idx;
|
||||
u8 report_type;
|
||||
u8 current_index;
|
||||
};
|
||||
|
||||
static struct request_list req_list;
|
||||
|
||||
void amd_sfh_set_report(struct hid_device *hid, int report_id,
|
||||
int report_type)
|
||||
{
|
||||
struct amdtp_hid_data *hid_data = hid->driver_data;
|
||||
struct amdtp_cl_data *cli_data = hid_data->cli_data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cli_data->num_hid_devices; i++) {
|
||||
if (cli_data->hid_sensor_hubs[i] == hid) {
|
||||
cli_data->cur_hid_dev = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
amdtp_hid_wakeup(hid);
|
||||
}
|
||||
|
||||
int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type)
|
||||
{
|
||||
struct amdtp_hid_data *hid_data = hid->driver_data;
|
||||
struct amdtp_cl_data *cli_data = hid_data->cli_data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cli_data->num_hid_devices; i++) {
|
||||
if (cli_data->hid_sensor_hubs[i] == hid) {
|
||||
struct request_list *new = kzalloc(sizeof(*new), GFP_KERNEL);
|
||||
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
new->current_index = i;
|
||||
new->sensor_idx = cli_data->sensor_idx[i];
|
||||
new->hid = hid;
|
||||
new->report_type = report_type;
|
||||
new->report_id = report_id;
|
||||
cli_data->report_id[i] = report_id;
|
||||
cli_data->request_done[i] = false;
|
||||
list_add(&new->list, &req_list.list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
schedule_delayed_work(&cli_data->work, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void amd_sfh_work(struct work_struct *work)
|
||||
{
|
||||
struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work.work);
|
||||
struct request_list *req_node;
|
||||
u8 current_index, sensor_index;
|
||||
u8 report_id, node_type;
|
||||
u8 report_size = 0;
|
||||
|
||||
req_node = list_last_entry(&req_list.list, struct request_list, list);
|
||||
list_del(&req_node->list);
|
||||
current_index = req_node->current_index;
|
||||
sensor_index = req_node->sensor_idx;
|
||||
report_id = req_node->report_id;
|
||||
node_type = req_node->report_type;
|
||||
|
||||
if (node_type == HID_FEATURE_REPORT) {
|
||||
report_size = get_feature_report(sensor_index, report_id,
|
||||
cli_data->feature_report[current_index]);
|
||||
if (report_size)
|
||||
hid_input_report(cli_data->hid_sensor_hubs[current_index],
|
||||
cli_data->report_type[current_index],
|
||||
cli_data->feature_report[current_index], report_size, 0);
|
||||
else
|
||||
pr_err("AMDSFH: Invalid report size\n");
|
||||
|
||||
} else if (node_type == HID_INPUT_REPORT) {
|
||||
report_size = get_input_report(sensor_index, report_id,
|
||||
cli_data->input_report[current_index],
|
||||
cli_data->sensor_virt_addr[current_index]);
|
||||
if (report_size)
|
||||
hid_input_report(cli_data->hid_sensor_hubs[current_index],
|
||||
cli_data->report_type[current_index],
|
||||
cli_data->input_report[current_index], report_size, 0);
|
||||
else
|
||||
pr_err("AMDSFH: Invalid report size\n");
|
||||
}
|
||||
cli_data->cur_hid_dev = current_index;
|
||||
cli_data->sensor_requested_cnt[current_index] = 0;
|
||||
amdtp_hid_wakeup(cli_data->hid_sensor_hubs[current_index]);
|
||||
}
|
||||
|
||||
static void amd_sfh_work_buffer(struct work_struct *work)
|
||||
{
|
||||
struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work_buffer.work);
|
||||
u8 report_size;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cli_data->num_hid_devices; i++) {
|
||||
report_size = get_input_report(cli_data->sensor_idx[i], cli_data->report_id[i],
|
||||
cli_data->input_report[i],
|
||||
cli_data->sensor_virt_addr[i]);
|
||||
hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,
|
||||
cli_data->input_report[i], report_size, 0);
|
||||
}
|
||||
schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
|
||||
}
|
||||
|
||||
int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
|
||||
{
|
||||
struct amdtp_cl_data *cl_data = privdata->cl_data;
|
||||
struct amd_mp2_sensor_info info;
|
||||
struct device *dev;
|
||||
u32 feature_report_size;
|
||||
u32 input_report_size;
|
||||
u8 cl_idx;
|
||||
int rc, i;
|
||||
|
||||
dev = &privdata->pdev->dev;
|
||||
cl_data = kzalloc(sizeof(*cl_data), GFP_KERNEL);
|
||||
if (!cl_data)
|
||||
return -ENOMEM;
|
||||
|
||||
cl_data->num_hid_devices = amd_mp2_get_sensor_num(privdata, &cl_data->sensor_idx[0]);
|
||||
|
||||
INIT_DELAYED_WORK(&cl_data->work, amd_sfh_work);
|
||||
INIT_DELAYED_WORK(&cl_data->work_buffer, amd_sfh_work_buffer);
|
||||
INIT_LIST_HEAD(&req_list.list);
|
||||
|
||||
for (i = 0; i < cl_data->num_hid_devices; i++) {
|
||||
cl_data->sensor_virt_addr[i] = dma_alloc_coherent(dev, sizeof(int) * 8,
|
||||
&cl_data->sensor_phys_addr[i],
|
||||
GFP_KERNEL);
|
||||
cl_data->sensor_sts[i] = 0;
|
||||
cl_data->sensor_requested_cnt[i] = 0;
|
||||
cl_data->cur_hid_dev = i;
|
||||
cl_idx = cl_data->sensor_idx[i];
|
||||
cl_data->report_descr_sz[i] = get_descr_sz(cl_idx, descr_size);
|
||||
if (!cl_data->report_descr_sz[i]) {
|
||||
rc = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
feature_report_size = get_descr_sz(cl_idx, feature_size);
|
||||
if (!feature_report_size) {
|
||||
rc = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
input_report_size = get_descr_sz(cl_idx, input_size);
|
||||
if (!input_report_size) {
|
||||
rc = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
cl_data->feature_report[i] = kzalloc(feature_report_size, GFP_KERNEL);
|
||||
if (!cl_data->feature_report[i]) {
|
||||
rc = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
cl_data->input_report[i] = kzalloc(input_report_size, GFP_KERNEL);
|
||||
if (!cl_data->input_report[i]) {
|
||||
rc = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
info.period = msecs_to_jiffies(AMD_SFH_IDLE_LOOP);
|
||||
info.sensor_idx = cl_idx;
|
||||
info.phys_address = cl_data->sensor_phys_addr[i];
|
||||
|
||||
cl_data->report_descr[i] = kzalloc(cl_data->report_descr_sz[i], GFP_KERNEL);
|
||||
if (!cl_data->report_descr[i]) {
|
||||
rc = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
rc = get_report_descriptor(cl_idx, cl_data->report_descr[i]);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data);
|
||||
if (rc)
|
||||
return rc;
|
||||
amd_start_sensor(privdata, info);
|
||||
cl_data->sensor_sts[i] = 1;
|
||||
}
|
||||
privdata->cl_data = cl_data;
|
||||
schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
for (i = 0; i < cl_data->num_hid_devices; i++) {
|
||||
if (cl_data->sensor_virt_addr[i]) {
|
||||
dma_free_coherent(&privdata->pdev->dev, 8 * sizeof(int),
|
||||
cl_data->sensor_virt_addr[i],
|
||||
cl_data->sensor_phys_addr[i]);
|
||||
}
|
||||
kfree(cl_data->feature_report[i]);
|
||||
kfree(cl_data->input_report[i]);
|
||||
kfree(cl_data->report_descr[i]);
|
||||
}
|
||||
kfree(cl_data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)
|
||||
{
|
||||
struct amdtp_cl_data *cl_data = privdata->cl_data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cl_data->num_hid_devices; i++)
|
||||
amd_stop_sensor(privdata, i);
|
||||
|
||||
cancel_delayed_work_sync(&cl_data->work);
|
||||
cancel_delayed_work_sync(&cl_data->work_buffer);
|
||||
amdtp_hid_remove(cl_data);
|
||||
|
||||
for (i = 0; i < cl_data->num_hid_devices; i++) {
|
||||
if (cl_data->sensor_virt_addr[i]) {
|
||||
dma_free_coherent(&privdata->pdev->dev, 8 * sizeof(int),
|
||||
cl_data->sensor_virt_addr[i],
|
||||
cl_data->sensor_phys_addr[i]);
|
||||
}
|
||||
}
|
||||
kfree(cl_data);
|
||||
return 0;
|
||||
}
|
||||
174
drivers/hid/amd-sfh-hid/amd_sfh_hid.c
Normal file
174
drivers/hid/amd-sfh-hid/amd_sfh_hid.c
Normal file
@@ -0,0 +1,174 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* AMD MP2 Sensors transport driver
|
||||
*
|
||||
* Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com>
|
||||
* Sandeep Singh <sandeep.singh@amd.com>
|
||||
*/
|
||||
#include <linux/hid.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "amd_sfh_hid.h"
|
||||
|
||||
#define AMD_SFH_RESPONSE_TIMEOUT 1500
|
||||
|
||||
/**
|
||||
* amdtp_hid_parse() - hid-core .parse() callback
|
||||
* @hid: hid device instance
|
||||
*
|
||||
* This function gets called during call to hid_add_device
|
||||
*
|
||||
* Return: 0 on success and non zero on error
|
||||
*/
|
||||
static int amdtp_hid_parse(struct hid_device *hid)
|
||||
{
|
||||
struct amdtp_hid_data *hid_data = hid->driver_data;
|
||||
struct amdtp_cl_data *cli_data = hid_data->cli_data;
|
||||
|
||||
return hid_parse_report(hid, cli_data->report_descr[hid_data->index],
|
||||
cli_data->report_descr_sz[hid_data->index]);
|
||||
}
|
||||
|
||||
/* Empty callbacks with success return code */
|
||||
static int amdtp_hid_start(struct hid_device *hid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void amdtp_hid_stop(struct hid_device *hid)
|
||||
{
|
||||
}
|
||||
|
||||
static int amdtp_hid_open(struct hid_device *hid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void amdtp_hid_close(struct hid_device *hid)
|
||||
{
|
||||
}
|
||||
|
||||
static int amdtp_raw_request(struct hid_device *hdev, u8 reportnum,
|
||||
u8 *buf, size_t len, u8 rtype, int reqtype)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void amdtp_hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (reqtype) {
|
||||
case HID_REQ_GET_REPORT:
|
||||
rc = amd_sfh_get_report(hid, rep->id, rep->type);
|
||||
if (rc)
|
||||
dev_err(&hid->dev, "AMDSFH get report error\n");
|
||||
break;
|
||||
case HID_REQ_SET_REPORT:
|
||||
amd_sfh_set_report(hid, rep->id, reqtype);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int amdtp_wait_for_response(struct hid_device *hid)
|
||||
{
|
||||
struct amdtp_hid_data *hid_data = hid->driver_data;
|
||||
struct amdtp_cl_data *cli_data = hid_data->cli_data;
|
||||
int i, ret = 0;
|
||||
|
||||
for (i = 0; i < cli_data->num_hid_devices; i++) {
|
||||
if (cli_data->hid_sensor_hubs[i] == hid)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cli_data->request_done[i])
|
||||
ret = wait_event_interruptible_timeout(hid_data->hid_wait,
|
||||
cli_data->request_done[i],
|
||||
msecs_to_jiffies(AMD_SFH_RESPONSE_TIMEOUT));
|
||||
if (ret == -ERESTARTSYS)
|
||||
return -ERESTARTSYS;
|
||||
else if (ret < 0)
|
||||
return -ETIMEDOUT;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void amdtp_hid_wakeup(struct hid_device *hid)
|
||||
{
|
||||
struct amdtp_hid_data *hid_data = hid->driver_data;
|
||||
struct amdtp_cl_data *cli_data = hid_data->cli_data;
|
||||
|
||||
cli_data->request_done[cli_data->cur_hid_dev] = true;
|
||||
wake_up_interruptible(&hid_data->hid_wait);
|
||||
}
|
||||
|
||||
static struct hid_ll_driver amdtp_hid_ll_driver = {
|
||||
.parse = amdtp_hid_parse,
|
||||
.start = amdtp_hid_start,
|
||||
.stop = amdtp_hid_stop,
|
||||
.open = amdtp_hid_open,
|
||||
.close = amdtp_hid_close,
|
||||
.request = amdtp_hid_request,
|
||||
.wait = amdtp_wait_for_response,
|
||||
.raw_request = amdtp_raw_request,
|
||||
};
|
||||
|
||||
int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data)
|
||||
{
|
||||
struct hid_device *hid;
|
||||
struct amdtp_hid_data *hid_data;
|
||||
int rc;
|
||||
|
||||
hid = hid_allocate_device();
|
||||
if (IS_ERR(hid))
|
||||
return PTR_ERR(hid);
|
||||
|
||||
hid_data = kzalloc(sizeof(*hid_data), GFP_KERNEL);
|
||||
if (!hid_data) {
|
||||
rc = -ENOMEM;
|
||||
goto err_hid_data;
|
||||
}
|
||||
|
||||
hid->ll_driver = &amdtp_hid_ll_driver;
|
||||
hid_data->index = cur_hid_dev;
|
||||
hid_data->cli_data = cli_data;
|
||||
init_waitqueue_head(&hid_data->hid_wait);
|
||||
|
||||
hid->driver_data = hid_data;
|
||||
cli_data->hid_sensor_hubs[cur_hid_dev] = hid;
|
||||
hid->bus = BUS_AMD_AMDTP;
|
||||
hid->vendor = AMD_SFH_HID_VENDOR;
|
||||
hid->product = AMD_SFH_HID_PRODUCT;
|
||||
snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-amdtp",
|
||||
hid->vendor, hid->product);
|
||||
|
||||
rc = hid_add_device(hid);
|
||||
if (rc)
|
||||
goto err_hid_device;
|
||||
return 0;
|
||||
|
||||
err_hid_device:
|
||||
kfree(hid_data);
|
||||
err_hid_data:
|
||||
hid_destroy_device(hid);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void amdtp_hid_remove(struct amdtp_cl_data *cli_data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cli_data->num_hid_devices; ++i) {
|
||||
kfree(cli_data->feature_report[i]);
|
||||
kfree(cli_data->input_report[i]);
|
||||
kfree(cli_data->report_descr[i]);
|
||||
if (cli_data->hid_sensor_hubs[i]) {
|
||||
kfree(cli_data->hid_sensor_hubs[i]->driver_data);
|
||||
hid_destroy_device(cli_data->hid_sensor_hubs[i]);
|
||||
cli_data->hid_sensor_hubs[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
67
drivers/hid/amd-sfh-hid/amd_sfh_hid.h
Normal file
67
drivers/hid/amd-sfh-hid/amd_sfh_hid.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* AMD MP2 Sensors transport driver
|
||||
*
|
||||
* Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com>
|
||||
* Sandeep Singh <sandeep.singh@amd.com>
|
||||
*/
|
||||
|
||||
#ifndef AMDSFH_HID_H
|
||||
#define AMDSFH_HID_H
|
||||
|
||||
#define MAX_HID_DEVICES 4
|
||||
#define BUS_AMD_AMDTP 0x20
|
||||
#define AMD_SFH_HID_VENDOR 0x1022
|
||||
#define AMD_SFH_HID_PRODUCT 0x0001
|
||||
|
||||
struct amdtp_cl_data {
|
||||
u8 init_done;
|
||||
u32 cur_hid_dev;
|
||||
u32 hid_dev_count;
|
||||
u32 num_hid_devices;
|
||||
struct device_info *hid_devices;
|
||||
u8 *report_descr[MAX_HID_DEVICES];
|
||||
int report_descr_sz[MAX_HID_DEVICES];
|
||||
struct hid_device *hid_sensor_hubs[MAX_HID_DEVICES];
|
||||
u8 *hid_descr[MAX_HID_DEVICES];
|
||||
int hid_descr_size[MAX_HID_DEVICES];
|
||||
phys_addr_t phys_addr_base;
|
||||
u32 *sensor_virt_addr[MAX_HID_DEVICES];
|
||||
phys_addr_t sensor_phys_addr[MAX_HID_DEVICES];
|
||||
u32 sensor_sts[MAX_HID_DEVICES];
|
||||
u32 sensor_requested_cnt[MAX_HID_DEVICES];
|
||||
u8 report_type[MAX_HID_DEVICES];
|
||||
u8 report_id[MAX_HID_DEVICES];
|
||||
u8 sensor_idx[MAX_HID_DEVICES];
|
||||
u8 *feature_report[MAX_HID_DEVICES];
|
||||
u8 *input_report[MAX_HID_DEVICES];
|
||||
u8 request_done[MAX_HID_DEVICES];
|
||||
struct delayed_work work;
|
||||
struct delayed_work work_buffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct amdtp_hid_data - Per instance HID data
|
||||
* @index: Device index in the order of enumeration
|
||||
* @request_done: Get Feature/Input report complete flag
|
||||
* used during get/set request from hid core
|
||||
* @cli_data: Link to the client instance
|
||||
* @hid_wait: Completion waitq
|
||||
*
|
||||
* Used to tie hid->driver data to driver client instance
|
||||
*/
|
||||
struct amdtp_hid_data {
|
||||
int index;
|
||||
struct amdtp_cl_data *cli_data;
|
||||
wait_queue_head_t hid_wait;
|
||||
};
|
||||
|
||||
/* Interface functions between HID LL driver and AMD SFH client */
|
||||
void hid_amdtp_set_feature(struct hid_device *hid, char *buf, u32 len, int report_id);
|
||||
void hid_amdtp_get_report(struct hid_device *hid, int report_id, int report_type);
|
||||
int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data);
|
||||
void amdtp_hid_remove(struct amdtp_cl_data *cli_data);
|
||||
int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type);
|
||||
void amd_sfh_set_report(struct hid_device *hid, int report_id, int report_type);
|
||||
void amdtp_hid_wakeup(struct hid_device *hid);
|
||||
#endif
|
||||
152
drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
Normal file
152
drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
Normal file
@@ -0,0 +1,152 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* AMD MP2 PCIe communication driver
|
||||
* Copyright 2020 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
|
||||
* Sandeep Singh <Sandeep.singh@amd.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "amd_sfh_pcie.h"
|
||||
|
||||
#define DRIVER_NAME "pcie_mp2_amd"
|
||||
#define DRIVER_DESC "AMD(R) PCIe MP2 Communication Driver"
|
||||
|
||||
#define ACEL_EN BIT(0)
|
||||
#define GYRO_EN BIT(1)
|
||||
#define MAGNO_EN BIT(2)
|
||||
#define ALS_EN BIT(19)
|
||||
|
||||
void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
|
||||
{
|
||||
union sfh_cmd_param cmd_param;
|
||||
union sfh_cmd_base cmd_base;
|
||||
|
||||
/* fill up command register */
|
||||
memset(&cmd_base, 0, sizeof(cmd_base));
|
||||
cmd_base.s.cmd_id = ENABLE_SENSOR;
|
||||
cmd_base.s.period = info.period;
|
||||
cmd_base.s.sensor_id = info.sensor_idx;
|
||||
|
||||
/* fill up command param register */
|
||||
memset(&cmd_param, 0, sizeof(cmd_param));
|
||||
cmd_param.s.buf_layout = 1;
|
||||
cmd_param.s.buf_length = 16;
|
||||
|
||||
writeq(info.phys_address, privdata->mmio + AMD_C2P_MSG2);
|
||||
writel(cmd_param.ul, privdata->mmio + AMD_C2P_MSG1);
|
||||
writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
|
||||
}
|
||||
|
||||
void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx)
|
||||
{
|
||||
union sfh_cmd_base cmd_base;
|
||||
|
||||
/* fill up command register */
|
||||
memset(&cmd_base, 0, sizeof(cmd_base));
|
||||
cmd_base.s.cmd_id = DISABLE_SENSOR;
|
||||
cmd_base.s.period = 0;
|
||||
cmd_base.s.sensor_id = sensor_idx;
|
||||
|
||||
writeq(0x0, privdata->mmio + AMD_C2P_MSG2);
|
||||
writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
|
||||
}
|
||||
|
||||
void amd_stop_all_sensors(struct amd_mp2_dev *privdata)
|
||||
{
|
||||
union sfh_cmd_base cmd_base;
|
||||
|
||||
/* fill up command register */
|
||||
memset(&cmd_base, 0, sizeof(cmd_base));
|
||||
cmd_base.s.cmd_id = STOP_ALL_SENSORS;
|
||||
cmd_base.s.period = 0;
|
||||
cmd_base.s.sensor_id = 0;
|
||||
|
||||
writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
|
||||
}
|
||||
|
||||
int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id)
|
||||
{
|
||||
int activestatus, num_of_sensors = 0;
|
||||
|
||||
privdata->activecontrolstatus = readl(privdata->mmio + AMD_P2C_MSG3);
|
||||
activestatus = privdata->activecontrolstatus >> 4;
|
||||
if (ACEL_EN & activestatus)
|
||||
sensor_id[num_of_sensors++] = accel_idx;
|
||||
|
||||
if (GYRO_EN & activestatus)
|
||||
sensor_id[num_of_sensors++] = gyro_idx;
|
||||
|
||||
if (MAGNO_EN & activestatus)
|
||||
sensor_id[num_of_sensors++] = mag_idx;
|
||||
|
||||
if (ALS_EN & activestatus)
|
||||
sensor_id[num_of_sensors++] = als_idx;
|
||||
|
||||
return num_of_sensors;
|
||||
}
|
||||
|
||||
static void amd_mp2_pci_remove(void *privdata)
|
||||
{
|
||||
amd_sfh_hid_client_deinit(privdata);
|
||||
amd_stop_all_sensors(privdata);
|
||||
}
|
||||
|
||||
static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct amd_mp2_dev *privdata;
|
||||
int rc;
|
||||
|
||||
privdata = devm_kzalloc(&pdev->dev, sizeof(*privdata), GFP_KERNEL);
|
||||
if (!privdata)
|
||||
return -ENOMEM;
|
||||
|
||||
privdata->pdev = pdev;
|
||||
pci_set_drvdata(pdev, privdata);
|
||||
rc = pcim_enable_device(pdev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = pcim_iomap_regions(pdev, BIT(2), DRIVER_NAME);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
privdata->mmio = pcim_iomap_table(pdev)[2];
|
||||
pci_set_master(pdev);
|
||||
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
|
||||
if (rc) {
|
||||
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
||||
return rc;
|
||||
}
|
||||
rc = devm_add_action_or_reset(&pdev->dev, amd_mp2_pci_remove, privdata);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return amd_sfh_hid_client_init(privdata);
|
||||
}
|
||||
|
||||
static const struct pci_device_id amd_mp2_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl);
|
||||
|
||||
static struct pci_driver amd_mp2_pci_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = amd_mp2_pci_tbl,
|
||||
.probe = amd_mp2_pci_probe,
|
||||
};
|
||||
module_pci_driver(amd_mp2_pci_driver);
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>");
|
||||
MODULE_AUTHOR("Sandeep Singh <Sandeep.singh@amd.com>");
|
||||
79
drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
Normal file
79
drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* AMD MP2 PCIe communication driver
|
||||
* Copyright 2020 Advanced Micro Devices, Inc.
|
||||
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
|
||||
* Sandeep Singh <Sandeep.singh@amd.com>
|
||||
*/
|
||||
|
||||
#ifndef PCIE_MP2_AMD_H
|
||||
#define PCIE_MP2_AMD_H
|
||||
|
||||
#include <linux/pci.h>
|
||||
|
||||
#define PCI_DEVICE_ID_AMD_MP2 0x15E4
|
||||
|
||||
#define ENABLE_SENSOR 1
|
||||
#define DISABLE_SENSOR 2
|
||||
#define STOP_ALL_SENSORS 8
|
||||
|
||||
/* MP2 C2P Message Registers */
|
||||
#define AMD_C2P_MSG0 0x10500
|
||||
#define AMD_C2P_MSG1 0x10504
|
||||
#define AMD_C2P_MSG2 0x10508
|
||||
|
||||
/* MP2 P2C Message Registers */
|
||||
#define AMD_P2C_MSG3 0x1068C /* Supported Sensors info */
|
||||
|
||||
/* SFH Command register */
|
||||
union sfh_cmd_base {
|
||||
u32 ul;
|
||||
struct {
|
||||
u32 cmd_id : 8;
|
||||
u32 sensor_id : 8;
|
||||
u32 period : 16;
|
||||
} s;
|
||||
};
|
||||
|
||||
union sfh_cmd_param {
|
||||
u32 ul;
|
||||
struct {
|
||||
u32 buf_layout : 2;
|
||||
u32 buf_length : 6;
|
||||
u32 rsvd : 24;
|
||||
} s;
|
||||
};
|
||||
|
||||
struct sfh_cmd_reg {
|
||||
union sfh_cmd_base cmd_base;
|
||||
union sfh_cmd_param cmd_param;
|
||||
phys_addr_t phys_addr;
|
||||
};
|
||||
|
||||
enum sensor_idx {
|
||||
accel_idx = 0,
|
||||
gyro_idx = 1,
|
||||
mag_idx = 2,
|
||||
als_idx = 19
|
||||
};
|
||||
|
||||
struct amd_mp2_dev {
|
||||
struct pci_dev *pdev;
|
||||
struct amdtp_cl_data *cl_data;
|
||||
void __iomem *mmio;
|
||||
u32 activecontrolstatus;
|
||||
};
|
||||
|
||||
struct amd_mp2_sensor_info {
|
||||
u8 sensor_idx;
|
||||
u32 period;
|
||||
phys_addr_t phys_address;
|
||||
};
|
||||
|
||||
void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info);
|
||||
void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx);
|
||||
void amd_stop_all_sensors(struct amd_mp2_dev *privdata);
|
||||
int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id);
|
||||
int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata);
|
||||
int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata);
|
||||
#endif
|
||||
224
drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c
Normal file
224
drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c
Normal file
@@ -0,0 +1,224 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* AMD SFH Report Descriptor generator
|
||||
* Copyright 2020 Advanced Micro Devices, Inc.
|
||||
* Authors: Nehal Bakulchandra Shah <Nehal-Bakulchandra.Shah@amd.com>
|
||||
* Sandeep Singh <sandeep.singh@amd.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include "amd_sfh_pcie.h"
|
||||
#include "amd_sfh_hid_desc.h"
|
||||
#include "amd_sfh_hid_report_desc.h"
|
||||
|
||||
#define AMD_SFH_FW_MULTIPLIER (1000)
|
||||
#define HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM 0x41
|
||||
#define HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM 0x51
|
||||
#define HID_DEFAULT_REPORT_INTERVAL 0x50
|
||||
#define HID_DEFAULT_MIN_VALUE 0X7F
|
||||
#define HID_DEFAULT_MAX_VALUE 0x80
|
||||
#define HID_DEFAULT_SENSITIVITY 0x7F
|
||||
#define HID_USAGE_SENSOR_PROPERTY_CONNECTION_TYPE_PC_INTEGRATED_ENUM 0x01
|
||||
/* state enums */
|
||||
#define HID_USAGE_SENSOR_STATE_READY_ENUM 0x02
|
||||
#define HID_USAGE_SENSOR_STATE_INITIALIZING_ENUM 0x05
|
||||
#define HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM 0x04
|
||||
|
||||
int get_report_descriptor(int sensor_idx, u8 *rep_desc)
|
||||
{
|
||||
switch (sensor_idx) {
|
||||
case accel_idx: /* accel */
|
||||
memset(rep_desc, 0, sizeof(accel3_report_descriptor));
|
||||
memcpy(rep_desc, accel3_report_descriptor,
|
||||
sizeof(accel3_report_descriptor));
|
||||
break;
|
||||
case gyro_idx: /* gyro */
|
||||
memset(rep_desc, 0, sizeof(gyro3_report_descriptor));
|
||||
memcpy(rep_desc, gyro3_report_descriptor,
|
||||
sizeof(gyro3_report_descriptor));
|
||||
break;
|
||||
case mag_idx: /* Magnetometer */
|
||||
memset(rep_desc, 0, sizeof(comp3_report_descriptor));
|
||||
memcpy(rep_desc, comp3_report_descriptor,
|
||||
sizeof(comp3_report_descriptor));
|
||||
break;
|
||||
case als_idx: /* ambient light sensor */
|
||||
memset(rep_desc, 0, sizeof(als_report_descriptor));
|
||||
memcpy(rep_desc, als_report_descriptor,
|
||||
sizeof(als_report_descriptor));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 get_descr_sz(int sensor_idx, int descriptor_name)
|
||||
{
|
||||
switch (sensor_idx) {
|
||||
case accel_idx:
|
||||
switch (descriptor_name) {
|
||||
case descr_size:
|
||||
return sizeof(accel3_report_descriptor);
|
||||
case input_size:
|
||||
return sizeof(struct accel3_input_report);
|
||||
case feature_size:
|
||||
return sizeof(struct accel3_feature_report);
|
||||
}
|
||||
break;
|
||||
case gyro_idx:
|
||||
switch (descriptor_name) {
|
||||
case descr_size:
|
||||
return sizeof(gyro3_report_descriptor);
|
||||
case input_size:
|
||||
return sizeof(struct gyro_input_report);
|
||||
case feature_size:
|
||||
return sizeof(struct gyro_feature_report);
|
||||
}
|
||||
break;
|
||||
case mag_idx:
|
||||
switch (descriptor_name) {
|
||||
case descr_size:
|
||||
return sizeof(comp3_report_descriptor);
|
||||
case input_size:
|
||||
return sizeof(struct magno_input_report);
|
||||
case feature_size:
|
||||
return sizeof(struct magno_feature_report);
|
||||
}
|
||||
break;
|
||||
case als_idx:
|
||||
switch (descriptor_name) {
|
||||
case descr_size:
|
||||
return sizeof(als_report_descriptor);
|
||||
case input_size:
|
||||
return sizeof(struct als_input_report);
|
||||
case feature_size:
|
||||
return sizeof(struct als_feature_report);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_common_features(struct common_feature_property *common, int report_id)
|
||||
{
|
||||
common->report_id = report_id;
|
||||
common->connection_type = HID_USAGE_SENSOR_PROPERTY_CONNECTION_TYPE_PC_INTEGRATED_ENUM;
|
||||
common->report_state = HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM;
|
||||
common->power_state = HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM;
|
||||
common->sensor_state = HID_USAGE_SENSOR_STATE_INITIALIZING_ENUM;
|
||||
common->report_interval = HID_DEFAULT_REPORT_INTERVAL;
|
||||
}
|
||||
|
||||
u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report)
|
||||
{
|
||||
struct accel3_feature_report acc_feature;
|
||||
struct gyro_feature_report gyro_feature;
|
||||
struct magno_feature_report magno_feature;
|
||||
struct als_feature_report als_feature;
|
||||
u8 report_size = 0;
|
||||
|
||||
if (!feature_report)
|
||||
return report_size;
|
||||
|
||||
switch (sensor_idx) {
|
||||
case accel_idx: /* accel */
|
||||
get_common_features(&acc_feature.common_property, report_id);
|
||||
acc_feature.accel_change_sesnitivity = HID_DEFAULT_SENSITIVITY;
|
||||
acc_feature.accel_sensitivity_min = HID_DEFAULT_MIN_VALUE;
|
||||
acc_feature.accel_sensitivity_max = HID_DEFAULT_MAX_VALUE;
|
||||
memcpy(feature_report, &acc_feature, sizeof(acc_feature));
|
||||
report_size = sizeof(acc_feature);
|
||||
break;
|
||||
case gyro_idx: /* gyro */
|
||||
get_common_features(&gyro_feature.common_property, report_id);
|
||||
gyro_feature.gyro_change_sesnitivity = HID_DEFAULT_SENSITIVITY;
|
||||
gyro_feature.gyro_sensitivity_min = HID_DEFAULT_MIN_VALUE;
|
||||
gyro_feature.gyro_sensitivity_max = HID_DEFAULT_MAX_VALUE;
|
||||
memcpy(feature_report, &gyro_feature, sizeof(gyro_feature));
|
||||
report_size = sizeof(gyro_feature);
|
||||
break;
|
||||
case mag_idx: /* Magnetometer */
|
||||
get_common_features(&magno_feature.common_property, report_id);
|
||||
magno_feature.magno_headingchange_sensitivity = HID_DEFAULT_SENSITIVITY;
|
||||
magno_feature.heading_min = HID_DEFAULT_MIN_VALUE;
|
||||
magno_feature.heading_max = HID_DEFAULT_MAX_VALUE;
|
||||
magno_feature.flux_change_sensitivity = HID_DEFAULT_MIN_VALUE;
|
||||
magno_feature.flux_min = HID_DEFAULT_MIN_VALUE;
|
||||
magno_feature.flux_max = HID_DEFAULT_MAX_VALUE;
|
||||
memcpy(feature_report, &magno_feature, sizeof(magno_feature));
|
||||
report_size = sizeof(magno_feature);
|
||||
break;
|
||||
case als_idx: /* ambient light sensor */
|
||||
get_common_features(&als_feature.common_property, report_id);
|
||||
als_feature.als_change_sesnitivity = HID_DEFAULT_SENSITIVITY;
|
||||
als_feature.als_sensitivity_min = HID_DEFAULT_MIN_VALUE;
|
||||
als_feature.als_sensitivity_max = HID_DEFAULT_MAX_VALUE;
|
||||
memcpy(feature_report, &als_feature, sizeof(als_feature));
|
||||
report_size = sizeof(als_feature);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return report_size;
|
||||
}
|
||||
|
||||
static void get_common_inputs(struct common_input_property *common, int report_id)
|
||||
{
|
||||
common->report_id = report_id;
|
||||
common->sensor_state = HID_USAGE_SENSOR_STATE_READY_ENUM;
|
||||
common->event_type = HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM;
|
||||
}
|
||||
|
||||
u8 get_input_report(int sensor_idx, int report_id, u8 *input_report, u32 *sensor_virt_addr)
|
||||
{
|
||||
struct accel3_input_report acc_input;
|
||||
struct gyro_input_report gyro_input;
|
||||
struct magno_input_report magno_input;
|
||||
struct als_input_report als_input;
|
||||
u8 report_size = 0;
|
||||
|
||||
if (!sensor_virt_addr || !input_report)
|
||||
return report_size;
|
||||
|
||||
switch (sensor_idx) {
|
||||
case accel_idx: /* accel */
|
||||
get_common_inputs(&acc_input.common_property, report_id);
|
||||
acc_input.in_accel_x_value = (int)sensor_virt_addr[0] / AMD_SFH_FW_MULTIPLIER;
|
||||
acc_input.in_accel_y_value = (int)sensor_virt_addr[1] / AMD_SFH_FW_MULTIPLIER;
|
||||
acc_input.in_accel_z_value = (int)sensor_virt_addr[2] / AMD_SFH_FW_MULTIPLIER;
|
||||
memcpy(input_report, &acc_input, sizeof(acc_input));
|
||||
report_size = sizeof(acc_input);
|
||||
break;
|
||||
case gyro_idx: /* gyro */
|
||||
get_common_inputs(&gyro_input.common_property, report_id);
|
||||
gyro_input.in_angel_x_value = (int)sensor_virt_addr[0] / AMD_SFH_FW_MULTIPLIER;
|
||||
gyro_input.in_angel_y_value = (int)sensor_virt_addr[1] / AMD_SFH_FW_MULTIPLIER;
|
||||
gyro_input.in_angel_z_value = (int)sensor_virt_addr[2] / AMD_SFH_FW_MULTIPLIER;
|
||||
memcpy(input_report, &gyro_input, sizeof(gyro_input));
|
||||
report_size = sizeof(gyro_input);
|
||||
break;
|
||||
case mag_idx: /* Magnetometer */
|
||||
get_common_inputs(&magno_input.common_property, report_id);
|
||||
magno_input.in_magno_x = (int)sensor_virt_addr[0] / AMD_SFH_FW_MULTIPLIER;
|
||||
magno_input.in_magno_y = (int)sensor_virt_addr[1] / AMD_SFH_FW_MULTIPLIER;
|
||||
magno_input.in_magno_z = (int)sensor_virt_addr[2] / AMD_SFH_FW_MULTIPLIER;
|
||||
magno_input.in_magno_accuracy = (u16)sensor_virt_addr[3] / AMD_SFH_FW_MULTIPLIER;
|
||||
memcpy(input_report, &magno_input, sizeof(magno_input));
|
||||
report_size = sizeof(magno_input);
|
||||
break;
|
||||
case als_idx: /* Als */
|
||||
get_common_inputs(&als_input.common_property, report_id);
|
||||
als_input.illuminance_value = (int)sensor_virt_addr[0] / AMD_SFH_FW_MULTIPLIER;
|
||||
report_size = sizeof(als_input);
|
||||
memcpy(input_report, &als_input, sizeof(als_input));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return report_size;
|
||||
}
|
||||
107
drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h
Normal file
107
drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* HID report descriptors, structures and routines
|
||||
* Copyright 2020 Advanced Micro Devices, Inc.
|
||||
* Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com>
|
||||
* Sandeep Singh <Sandeep.singh@amd.com>
|
||||
*/
|
||||
|
||||
#ifndef AMD_SFH_HID_DESCRIPTOR_H
|
||||
#define AMD_SFH_HID_DESCRIPTOR_H
|
||||
|
||||
enum desc_type {
|
||||
/* Report descriptor name */
|
||||
descr_size = 1,
|
||||
input_size,
|
||||
feature_size,
|
||||
};
|
||||
|
||||
struct common_feature_property {
|
||||
/* common properties */
|
||||
u8 report_id;
|
||||
u8 connection_type;
|
||||
u8 report_state;
|
||||
u8 power_state;
|
||||
u8 sensor_state;
|
||||
u32 report_interval;
|
||||
} __packed;
|
||||
|
||||
struct common_input_property {
|
||||
/* common properties */
|
||||
u8 report_id;
|
||||
u8 sensor_state;
|
||||
u8 event_type;
|
||||
} __packed;
|
||||
|
||||
struct accel3_feature_report {
|
||||
struct common_feature_property common_property;
|
||||
/* properties specific to this sensor */
|
||||
u16 accel_change_sesnitivity;
|
||||
s16 accel_sensitivity_max;
|
||||
s16 accel_sensitivity_min;
|
||||
} __packed;
|
||||
|
||||
struct accel3_input_report {
|
||||
struct common_input_property common_property;
|
||||
/* values specific to this sensor */
|
||||
int in_accel_x_value;
|
||||
int in_accel_y_value;
|
||||
int in_accel_z_value;
|
||||
/* include if required to support the "shake" event */
|
||||
u8 in_accel_shake_detection;
|
||||
} __packed;
|
||||
|
||||
struct gyro_feature_report {
|
||||
struct common_feature_property common_property;
|
||||
/* properties specific to this sensor */
|
||||
u16 gyro_change_sesnitivity;
|
||||
s16 gyro_sensitivity_max;
|
||||
s16 gyro_sensitivity_min;
|
||||
} __packed;
|
||||
|
||||
struct gyro_input_report {
|
||||
struct common_input_property common_property;
|
||||
/* values specific to this sensor */
|
||||
int in_angel_x_value;
|
||||
int in_angel_y_value;
|
||||
int in_angel_z_value;
|
||||
} __packed;
|
||||
|
||||
struct magno_feature_report {
|
||||
struct common_feature_property common_property;
|
||||
/*properties specific to this sensor */
|
||||
u16 magno_headingchange_sensitivity;
|
||||
s16 heading_min;
|
||||
s16 heading_max;
|
||||
u16 flux_change_sensitivity;
|
||||
s16 flux_min;
|
||||
s16 flux_max;
|
||||
} __packed;
|
||||
|
||||
struct magno_input_report {
|
||||
struct common_input_property common_property;
|
||||
int in_magno_x;
|
||||
int in_magno_y;
|
||||
int in_magno_z;
|
||||
int in_magno_accuracy;
|
||||
} __packed;
|
||||
|
||||
struct als_feature_report {
|
||||
struct common_feature_property common_property;
|
||||
/* properties specific to this sensor */
|
||||
u16 als_change_sesnitivity;
|
||||
s16 als_sensitivity_max;
|
||||
s16 als_sensitivity_min;
|
||||
} __packed;
|
||||
|
||||
struct als_input_report {
|
||||
struct common_input_property common_property;
|
||||
/* values specific to this sensor */
|
||||
int illuminance_value;
|
||||
} __packed;
|
||||
|
||||
int get_report_descriptor(int sensor_idx, u8 rep_desc[]);
|
||||
u32 get_descr_sz(int sensor_idx, int descriptor_name);
|
||||
u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report);
|
||||
u8 get_input_report(int sensor_idx, int report_id, u8 *input_report, u32 *sensor_virt_addr);
|
||||
#endif
|
||||
645
drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h
Normal file
645
drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -48,6 +48,8 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
||||
#define INPUT_REPORT_ID 0x5d
|
||||
#define FEATURE_KBD_REPORT_ID 0x5a
|
||||
#define FEATURE_KBD_REPORT_SIZE 16
|
||||
#define FEATURE_KBD_LED_REPORT_ID1 0x5d
|
||||
#define FEATURE_KBD_LED_REPORT_ID2 0x5e
|
||||
|
||||
#define SUPPORT_KBD_BACKLIGHT BIT(0)
|
||||
|
||||
@@ -80,6 +82,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
||||
#define QUIRK_T101HA_DOCK BIT(9)
|
||||
#define QUIRK_T90CHI BIT(10)
|
||||
#define QUIRK_MEDION_E1239T BIT(11)
|
||||
#define QUIRK_ROG_NKEY_KEYBOARD BIT(12)
|
||||
|
||||
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
|
||||
QUIRK_NO_INIT_REPORTS | \
|
||||
@@ -332,6 +335,28 @@ static int asus_raw_event(struct hid_device *hdev,
|
||||
if (drvdata->quirks & QUIRK_MEDION_E1239T)
|
||||
return asus_e1239t_event(drvdata, data, size);
|
||||
|
||||
if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
/*
|
||||
* Skip these report ID, the device emits a continuous stream associated
|
||||
* with the AURA mode it is in which looks like an 'echo'.
|
||||
*/
|
||||
if (report->id == FEATURE_KBD_LED_REPORT_ID1 ||
|
||||
report->id == FEATURE_KBD_LED_REPORT_ID2) {
|
||||
return -1;
|
||||
/* Additional report filtering */
|
||||
} else if (report->id == FEATURE_KBD_REPORT_ID) {
|
||||
/*
|
||||
* G14 and G15 send these codes on some keypresses with no
|
||||
* discernable reason for doing so. We'll filter them out to avoid
|
||||
* unmapped warning messages later.
|
||||
*/
|
||||
if (data[1] == 0xea || data[1] == 0xec || data[1] == 0x02 ||
|
||||
data[1] == 0x8a || data[1] == 0x9e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -344,7 +369,11 @@ static int asus_kbd_set_report(struct hid_device *hdev, u8 *buf, size_t buf_size
|
||||
if (!dmabuf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, dmabuf,
|
||||
/*
|
||||
* The report ID should be set from the incoming buffer due to LED and key
|
||||
* interfaces having different pages
|
||||
*/
|
||||
ret = hid_hw_raw_request(hdev, buf[0], dmabuf,
|
||||
buf_size, HID_FEATURE_REPORT,
|
||||
HID_REQ_SET_REPORT);
|
||||
kfree(dmabuf);
|
||||
@@ -397,6 +426,51 @@ static int asus_kbd_get_functions(struct hid_device *hdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rog_nkey_led_init(struct hid_device *hdev)
|
||||
{
|
||||
u8 buf_init_start[] = { FEATURE_KBD_LED_REPORT_ID1, 0xB9 };
|
||||
u8 buf_init2[] = { FEATURE_KBD_LED_REPORT_ID1, 0x41, 0x53, 0x55, 0x53, 0x20,
|
||||
0x54, 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
|
||||
u8 buf_init3[] = { FEATURE_KBD_LED_REPORT_ID1,
|
||||
0x05, 0x20, 0x31, 0x00, 0x08 };
|
||||
int ret;
|
||||
|
||||
hid_info(hdev, "Asus initialise N-KEY Device");
|
||||
/* The first message is an init start */
|
||||
ret = asus_kbd_set_report(hdev, buf_init_start, sizeof(buf_init_start));
|
||||
if (ret < 0) {
|
||||
hid_warn(hdev, "Asus failed to send init start command: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
/* Followed by a string */
|
||||
ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2));
|
||||
if (ret < 0) {
|
||||
hid_warn(hdev, "Asus failed to send init command 1.0: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
/* Followed by a string */
|
||||
ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3));
|
||||
if (ret < 0) {
|
||||
hid_warn(hdev, "Asus failed to send init command 1.1: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* begin second report ID with same data */
|
||||
buf_init2[0] = FEATURE_KBD_LED_REPORT_ID2;
|
||||
buf_init3[0] = FEATURE_KBD_LED_REPORT_ID2;
|
||||
|
||||
ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2));
|
||||
if (ret < 0) {
|
||||
hid_warn(hdev, "Asus failed to send init command 2.0: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3));
|
||||
if (ret < 0)
|
||||
hid_warn(hdev, "Asus failed to send init command 2.1: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
@@ -460,19 +534,25 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
|
||||
unsigned char kbd_func;
|
||||
int ret;
|
||||
|
||||
/* Initialize keyboard */
|
||||
ret = asus_kbd_init(hdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
ret = rog_nkey_led_init(hdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
/* Initialize keyboard */
|
||||
ret = asus_kbd_init(hdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Get keyboard functions */
|
||||
ret = asus_kbd_get_functions(hdev, &kbd_func);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* Get keyboard functions */
|
||||
ret = asus_kbd_get_functions(hdev, &kbd_func);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Check for backlight support */
|
||||
if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
|
||||
return -ENODEV;
|
||||
/* Check for backlight support */
|
||||
if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
|
||||
sizeof(struct asus_kbd_leds),
|
||||
@@ -751,8 +831,8 @@ static int asus_input_mapping(struct hid_device *hdev,
|
||||
usage->hid == (HID_UP_GENDEVCTRLS | 0x0026)))
|
||||
return -1;
|
||||
|
||||
/* ASUS-specific keyboard hotkeys */
|
||||
if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) {
|
||||
/* ASUS-specific keyboard hotkeys and led backlight */
|
||||
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR) {
|
||||
switch (usage->hid & HID_USAGE) {
|
||||
case 0x10: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break;
|
||||
case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break;
|
||||
@@ -780,6 +860,18 @@ static int asus_input_mapping(struct hid_device *hdev,
|
||||
/* Fn+F5 "fan" symbol on FX503VD */
|
||||
case 0x99: asus_map_key_clear(KEY_PROG4); break;
|
||||
|
||||
/* Fn+F5 "fan" symbol on N-Key keyboard */
|
||||
case 0xae: asus_map_key_clear(KEY_PROG4); break;
|
||||
|
||||
/* Fn+Ret "Calc" symbol on N-Key keyboard */
|
||||
case 0x92: asus_map_key_clear(KEY_CALC); break;
|
||||
|
||||
/* Fn+Left Aura mode previous on N-Key keyboard */
|
||||
case 0xb2: asus_map_key_clear(KEY_PROG2); break;
|
||||
|
||||
/* Fn+Right Aura mode next on N-Key keyboard */
|
||||
case 0xb3: asus_map_key_clear(KEY_PROG3); break;
|
||||
|
||||
default:
|
||||
/* ASUS lazily declares 256 usages, ignore the rest,
|
||||
* as some make the keyboard appear as a pointer device. */
|
||||
@@ -1126,6 +1218,9 @@ static const struct hid_device_id asus_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD),
|
||||
QUIRK_USE_KBD_BACKLIGHT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD),
|
||||
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD),
|
||||
QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
* Copyright (c) 2017 Diego Elio Pettenò <flameeyes@flameeyes.eu>
|
||||
* Copyright (c) 2017 Alex Manoussakis <amanou@gnu.org>
|
||||
* Copyright (c) 2017 Tomasz Kramkowski <tk@the-tk.com>
|
||||
* Copyright (c) 2020 YOSHIOKA Takuma <lo48576@hard-wi.red>
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -29,25 +30,26 @@
|
||||
* report descriptor but it does not appear that these enable software to
|
||||
* control what the extra buttons map to. The only simple and straightforward
|
||||
* solution seems to involve fixing up the report descriptor.
|
||||
*
|
||||
* Report descriptor format:
|
||||
* Positions 13, 15, 21 and 31 store the button bit count, button usage minimum,
|
||||
* button usage maximum and padding bit count respectively.
|
||||
*/
|
||||
#define MOUSE_BUTTONS_MAX 8
|
||||
static void mouse_button_fixup(struct hid_device *hdev,
|
||||
__u8 *rdesc, unsigned int rsize,
|
||||
unsigned int button_bit_count,
|
||||
unsigned int padding_bit,
|
||||
unsigned int button_report_size,
|
||||
unsigned int button_usage_maximum,
|
||||
int nbuttons)
|
||||
{
|
||||
if (rsize < 32 || rdesc[12] != 0x95 ||
|
||||
rdesc[14] != 0x75 || rdesc[15] != 0x01 ||
|
||||
rdesc[20] != 0x29 || rdesc[30] != 0x75)
|
||||
if (rsize < 32 || rdesc[button_bit_count] != 0x95 ||
|
||||
rdesc[button_report_size] != 0x75 ||
|
||||
rdesc[button_report_size + 1] != 0x01 ||
|
||||
rdesc[button_usage_maximum] != 0x29 || rdesc[padding_bit] != 0x75)
|
||||
return;
|
||||
hid_info(hdev, "Fixing up Elecom mouse button count\n");
|
||||
nbuttons = clamp(nbuttons, 0, MOUSE_BUTTONS_MAX);
|
||||
rdesc[13] = nbuttons;
|
||||
rdesc[21] = nbuttons;
|
||||
rdesc[31] = MOUSE_BUTTONS_MAX - nbuttons;
|
||||
rdesc[button_bit_count + 1] = nbuttons;
|
||||
rdesc[button_usage_maximum + 1] = nbuttons;
|
||||
rdesc[padding_bit + 1] = MOUSE_BUTTONS_MAX - nbuttons;
|
||||
}
|
||||
|
||||
static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
@@ -62,16 +64,40 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
rdesc[47] = 0x00;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_ELECOM_M_XGL20DLBK:
|
||||
/*
|
||||
* Report descriptor format:
|
||||
* 20: button bit count
|
||||
* 28: padding bit count
|
||||
* 22: button report size
|
||||
* 14: button usage maximum
|
||||
*/
|
||||
mouse_button_fixup(hdev, rdesc, *rsize, 20, 28, 22, 14, 8);
|
||||
break;
|
||||
case USB_DEVICE_ID_ELECOM_M_XT3URBK:
|
||||
case USB_DEVICE_ID_ELECOM_M_XT3DRBK:
|
||||
case USB_DEVICE_ID_ELECOM_M_XT4DRBK:
|
||||
mouse_button_fixup(hdev, rdesc, *rsize, 6);
|
||||
/*
|
||||
* Report descriptor format:
|
||||
* 12: button bit count
|
||||
* 30: padding bit count
|
||||
* 14: button report size
|
||||
* 20: button usage maximum
|
||||
*/
|
||||
mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 6);
|
||||
break;
|
||||
case USB_DEVICE_ID_ELECOM_M_DT1URBK:
|
||||
case USB_DEVICE_ID_ELECOM_M_DT1DRBK:
|
||||
case USB_DEVICE_ID_ELECOM_M_HT1URBK:
|
||||
case USB_DEVICE_ID_ELECOM_M_HT1DRBK:
|
||||
mouse_button_fixup(hdev, rdesc, *rsize, 8);
|
||||
/*
|
||||
* Report descriptor format:
|
||||
* 12: button bit count
|
||||
* 30: padding bit count
|
||||
* 14: button report size
|
||||
* 20: button usage maximum
|
||||
*/
|
||||
mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 8);
|
||||
break;
|
||||
}
|
||||
return rdesc;
|
||||
@@ -79,6 +105,7 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
|
||||
static const struct hid_device_id elecom_devices[] = {
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) },
|
||||
|
||||
@@ -190,6 +190,7 @@
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866
|
||||
#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
|
||||
|
||||
#define USB_VENDOR_ID_ATEN 0x0557
|
||||
@@ -359,6 +360,7 @@
|
||||
#define USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR 0x1803
|
||||
#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE1 0x1843
|
||||
#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE2 0x1844
|
||||
#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE3 0x1846
|
||||
|
||||
#define USB_VENDOR_ID_DWAV 0x0eef
|
||||
#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
|
||||
@@ -390,6 +392,7 @@
|
||||
|
||||
#define USB_VENDOR_ID_ELECOM 0x056e
|
||||
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
|
||||
#define USB_DEVICE_ID_ELECOM_M_XGL20DLBK 0x00e6
|
||||
#define USB_DEVICE_ID_ELECOM_M_XT3URBK 0x00fb
|
||||
#define USB_DEVICE_ID_ELECOM_M_XT3DRBK 0x00fc
|
||||
#define USB_DEVICE_ID_ELECOM_M_XT4DRBK 0x00fd
|
||||
@@ -1074,6 +1077,9 @@
|
||||
#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002
|
||||
#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000
|
||||
|
||||
#define USB_VENDOR_ID_SONY_GHLIVE 0x12ba
|
||||
#define USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE 0x074b
|
||||
|
||||
#define USB_VENDOR_ID_SINO_LITE 0x1345
|
||||
#define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008
|
||||
|
||||
@@ -1133,6 +1139,7 @@
|
||||
#define USB_DEVICE_ID_SYNAPTICS_DELL_K12A 0x2819
|
||||
#define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5_012 0x2968
|
||||
#define USB_DEVICE_ID_SYNAPTICS_TP_V103 0x5710
|
||||
#define USB_DEVICE_ID_SYNAPTICS_ACER_ONE_S1002 0x73f4
|
||||
#define USB_DEVICE_ID_SYNAPTICS_ACER_ONE_S1003 0x73f5
|
||||
#define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5 0x81a7
|
||||
|
||||
|
||||
@@ -537,9 +537,12 @@ static void hidinput_update_battery(struct hid_device *dev, int value)
|
||||
capacity = hidinput_scale_battery_capacity(dev, value);
|
||||
|
||||
if (dev->battery_status != HID_BATTERY_REPORTED ||
|
||||
capacity != dev->battery_capacity) {
|
||||
capacity != dev->battery_capacity ||
|
||||
ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) {
|
||||
dev->battery_capacity = capacity;
|
||||
dev->battery_status = HID_BATTERY_REPORTED;
|
||||
dev->battery_ratelimit_time =
|
||||
ktime_add_ms(ktime_get_coarse(), 30 * 1000);
|
||||
power_supply_changed(dev->battery);
|
||||
}
|
||||
}
|
||||
@@ -746,6 +749,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
field->flags |= HID_MAIN_ITEM_RELATIVE;
|
||||
break;
|
||||
}
|
||||
goto unknown;
|
||||
|
||||
default: goto unknown;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user