summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2020-12-16 15:44:44 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2020-12-16 15:44:44 -0800
commitde925e2fbb44eed8a2a7ce166b485fed5eae01aa (patch)
tree078ddd7b31e1cde13f554d741140c8f519cd8f90 /drivers
parent62746f92b10a4add6a7db87ff59b901276746b11 (diff)
parent85a694738f6e7ad36918a3fa0569701b102d06c2 (diff)
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 ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hid/Kconfig3
-rw-r--r--drivers/hid/Makefile2
-rw-r--r--drivers/hid/amd-sfh-hid/Kconfig18
-rw-r--r--drivers/hid/amd-sfh-hid/Makefile13
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_client.c246
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_hid.c174
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_hid.h67
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_pcie.c152
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_pcie.h79
-rw-r--r--drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c224
-rw-r--r--drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h107
-rw-r--r--drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h645
-rw-r--r--drivers/hid/hid-asus.c123
-rw-r--r--drivers/hid/hid-elecom.c51
-rw-r--r--drivers/hid/hid-ids.h7
-rw-r--r--drivers/hid/hid-input.c6
-rw-r--r--drivers/hid/hid-ite.c13
-rw-r--r--drivers/hid/hid-logitech-hidpp.c2
-rw-r--r--drivers/hid/hid-mf.c2
-rw-r--r--drivers/hid/hid-quirks.c3
-rw-r--r--drivers/hid/hid-sony.c247
-rw-r--r--drivers/hid/hidraw.c24
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-core.c5
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c8
-rw-r--r--drivers/hid/intel-ish-hid/ishtp-hid.c6
-rw-r--r--drivers/hid/usbhid/hid-core.c2
-rw-r--r--drivers/hid/wacom_sys.c16
27 files changed, 2154 insertions, 91 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 612629678c84..7bdda1b5b221 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -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
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 4acb583c92a6..014d21fe7dac 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -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/
diff --git a/drivers/hid/amd-sfh-hid/Kconfig b/drivers/hid/amd-sfh-hid/Kconfig
new file mode 100644
index 000000000000..db069a83e9a2
--- /dev/null
+++ b/drivers/hid/amd-sfh-hid/Kconfig
@@ -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
diff --git a/drivers/hid/amd-sfh-hid/Makefile b/drivers/hid/amd-sfh-hid/Makefile
new file mode 100644
index 000000000000..35e704da5612
--- /dev/null
+++ b/drivers/hid/amd-sfh-hid/Makefile
@@ -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)/
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
new file mode 100644
index 000000000000..3d1ccac5d99a
--- /dev/null
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
@@ -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;
+}
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
new file mode 100644
index 000000000000..4f989483aa03
--- /dev/null
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
@@ -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;
+ }
+ }
+}
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
new file mode 100644
index 000000000000..6be0783d885c
--- /dev/null
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
@@ -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
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
new file mode 100644
index 000000000000..a51c7b76283b
--- /dev/null
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
@@ -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>");
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
new file mode 100644
index 000000000000..e8be94f935b7
--- /dev/null
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
@@ -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
diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c
new file mode 100644
index 000000000000..6e3ad66e57a4
--- /dev/null
+++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c
@@ -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;
+}
diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h
new file mode 100644
index 000000000000..095c471d8fd6
--- /dev/null
+++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h
@@ -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
diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h
new file mode 100644
index 000000000000..44271d39b322
--- /dev/null
+++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h
@@ -0,0 +1,645 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * HID descriptor stuructures
+ * 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_REPORT_DESCRIPTOR_H
+#define AMD_SFH_HID_REPORT_DESCRIPTOR_H
+
+// Accelerometer 3D Sensor
+static const u8 accel3_report_descriptor[] = {
+0x05, 0x20, /* Usage page */
+0x09, 0x73, /* Motion type Accel 3D */
+0xA1, 0x00, /* HID Collection (Physical) */
+
+//feature reports(xmit/receive)
+0x85, 1, /* HID Report ID */
+0x05, 0x20, /* HID usage page sensor */
+0x0A, 0x09, 0x03, /* Sensor property and sensor connection type */
+0x15, 0, /* HID logical MIN_8(0) */
+0x25, 2, /* HID logical MAX_8(2) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection (logical) */
+0x0A, 0x30, 0x08, /* Sensor property connection type intergated sel*/
+0x0A, 0x31, 0x08, /* Sensor property connection type attached sel */
+0x0A, 0x32, 0x08, /* Sensor property connection type external sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x16, 0x03, /* HID usage sensor property reporting state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 5, /* HID logical Max_8(5) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection(logical) */
+0x0A, 0x40, 0x08, /* Sensor property report state no events sel */
+0x0A, 0x41, 0x08, /* Sensor property report state all events sel */
+0x0A, 0x42, 0x08, /* Sensor property report state threshold events sel */
+0x0A, 0x43, 0x08, /* Sensor property report state no events wake sel */
+0x0A, 0x44, 0x08, /* Sensor property report state all events wake sel */
+0x0A, 0x45, 0x08, /* Sensor property report state threshold events wake sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x19, 0x03, /* HID usage sensor property power state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 5, /* HID logical Max_8(5) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection(logical) */
+0x0A, 0x50, 0x08, /* Sensor property power state undefined sel */
+0x0A, 0x51, 0x08, /* Sensor property power state D0 full power sel */
+0x0A, 0x52, 0x08, /* Sensor property power state D1 low power sel */
+0x0A, 0x53, 0x08, /* Sensor property power state D2 standby with wake sel */
+0x0A, 0x54, 0x08, /* Sensor property power state D3 sleep with wake sel */
+0x0A, 0x55, 0x08, /* Sensor property power state D4 power off sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x01, 0x02, /* HID usage sensor state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 6, /* HID logical Max_8(6) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection(logical) */
+0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
+0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
+0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
+0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
+0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
+0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
+0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x0E, 0x03, /* HID usage sensor property report interval */
+0x15, 0, /* HID logical Min_8(0) */
+0x27, 0xFF, 0xFF, 0xFF, 0xFF, /* HID logical Max_32 */
+
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count(1) */
+0x55, 0, /* HID unit exponent(0) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0x52, 0x14, /* Sensor data motion accel and mod change sensitivity ABS) */
+
+0x15, 0, /* HID logical Min_8(0) */
+0x26, 0xFF, 0xFF, /* HID logical Max_16(0xFF,0xFF) */
+
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0x52, 0x24, /* HID usage sensor data (motion accel and mod max) */
+
+0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
+
+0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
+
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0x52, 0x34, /* HID usage sensor data (motion accel and mod min) */
+
+0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
+
+0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
+
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+
+//input report (transmit)
+0x05, 0x20, /* HID usage page sensors */
+0x0A, 0x01, 0x02, /* HID usage sensor state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 6, /* HID logical Max_8(6) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count (1) */
+0xA1, 0x02, /* HID end collection (logical) */
+0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
+0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
+0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
+0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
+0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
+0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
+0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
+0X81, 0x00, /* HID Input (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x02, 0x02, /* HID usage sensor event */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 5, /* HID logical Max_8(5) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count (1) */
+0xA1, 0x02, /* HID end collection (logical) */
+0x0A, 0x10, 0x08, /* HID usage sensor event unknown sel */
+0x0A, 0x11, 0x08, /* HID usage sensor event state changed sel */
+0x0A, 0x12, 0x08, /* HID usage sensor event property changed sel */
+0x0A, 0x13, 0x08, /* HID usage sensor event data updated sel */
+0x0A, 0x14, 0x08, /* HID usage sensor event poll response sel */
+0x0A, 0x15, 0x08, /* HID usage sensor event change sensitivity sel */
+0X81, 0x00, /* HID Input (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x53, 0x04, /* HID usage sensor data motion Acceleration X axis */
+0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
+
+0x27, 0xFF, 0xff, 0XFF, 0XFF, /* HID logical Max_32 */
+
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count (1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0X81, 0x02, /* HID Input (Data_Arr_Abs) */
+0x0A, 0x54, 0x04, /* HID usage sensor data motion Acceleration Y axis */
+0x17, 0X00, 0X00, 0x01, 0x80, /* HID logical Min_32 */
+
+0x27, 0xFF, 0xFF, 0XFF, 0XFF, /* HID logical Max_32 */
+
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count (1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0X81, 0x02, /* HID Input (Data_Arr_Abs) */
+0x0A, 0x55, 0x04, /* HID usage sensor data motion Acceleration Z axis */
+0x17, 0X00, 0X00, 0x01, 0x80, /* HID logical Min_32 */
+
+0x27, 0XFF, 0XFF, 0xFF, 0x7F, /* HID logical Max_32 */
+
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count (1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0X81, 0x02, /* HID Input (Data_Arr_Abs) */
+
+0x0A, 0x51, 0x04, /* HID usage sensor data motion state */
+0x15, 0, /* HID logical Min_8(0) False = Still*/
+0x25, 1, /* HID logical Min_8(1) True = In motion */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count (1) */
+0X81, 0x02, /* HID Input (Data_Arr_Abs) */
+0xC0 /* HID end collection */
+};
+
+const u8 gyro3_report_descriptor[] = {
+0x05, 0x20, /* Usage page */
+0x09, 0x76, /* Motion type Gyro3D */
+0xA1, 0x00, /* HID Collection (Physical) */
+
+0x85, 2, /* HID Report ID */
+0x05, 0x20, /* HID usage page sensor */
+0x0A, 0x09, 0x03, /* Sensor property and sensor connection type */
+0x15, 0, /* HID logical MIN_8(0) */
+0x25, 2, /* HID logical MAX_8(2) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection (logical) */
+0x0A, 0x30, 0x08, /* Sensor property connection type intergated sel */
+0x0A, 0x31, 0x08, /* Sensor property connection type attached sel */
+0x0A, 0x32, 0x08, /* Sensor property connection type external sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x16, 0x03, /* HID usage sensor property reporting state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 5, /* HID logical Max_8(5) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection(logical) */
+0x0A, 0x40, 0x08, /* Sensor reporting state no events sel */
+0x0A, 0x41, 0x08, /* Sensor reporting state all events sel */
+0x0A, 0x42, 0x08, /* Sensor reporting state threshold events sel */
+0x0A, 0x43, 0x08, /* Sensor reporting state no events wake sel */
+0x0A, 0x44, 0x08, /* Sensor reporting state all events wake sel */
+0x0A, 0x45, 0x08, /* Sensor reporting state threshold events wake sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x19, 0x03, /* HID usage sensor property power state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 5, /* HID logical Max_8(5) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection(logical) */
+0x0A, 0x50, 0x08, /* Sensor power state undefined sel */
+0x0A, 0x51, 0x08, /* Sensor power state D0 full power sel */
+0x0A, 0x52, 0x08, /* Sensor power state D1 low power sel */
+0x0A, 0x53, 0x08, /* Sensor power state D2 standby with wake sel */
+0x0A, 0x54, 0x08, /* Sensor power state D3 sleep with wake sel */
+0x0A, 0x55, 0x08, /* Sensor power state D4 power off sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x01, 0x02, /* HID usage sensor state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 6, /* HID logical Max_8(6) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection(logical) */
+0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
+0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
+0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
+0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
+0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
+0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
+0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x0E, 0x03, /* HID usage sensor property report interval */
+0x15, 0, /* HID logical Min_8(0) */
+0x27, 0xFF, 0xFF, 0xFF, 0xFF, /* HID logical Max_32 */
+
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count(1) */
+0x55, 0, /* HID unit exponent(0) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0x56, 0x14, /* Angular velocity and mod change sensitivity ABS)*/
+
+0x15, 0, /* HID logical Min_8(0) */
+0x26, 0xFF, 0xFF, /* HID logical Max_16(0xFF,0xFF) */
+
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0x56, 0x24, /* Sensor data (motion angular velocity and mod max) */
+
+0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
+
+0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
+
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0x56, 0x34, /* HID usage sensor data (motion accel and mod min) */
+
+0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
+
+0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
+
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+
+//Input reports(transmit)
+0x05, 0x20, /* HID usage page sensors */
+0x0A, 0x01, 0x02, /* HID usage sensor state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 6, /* HID logical Max_8(6) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count (1) */
+0xA1, 0x02, /* HID end collection (logical) */
+0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
+0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
+0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
+0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
+0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
+0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
+0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
+0X81, 0x00, /* HID Input (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x02, 0x02, /* HID usage sensor event */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 5, /* HID logical Max_8(5) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count (1) */
+0xA1, 0x02, /* HID end collection (logical) */
+0x0A, 0x10, 0x08, /* HID usage sensor event unknown sel */
+0x0A, 0x11, 0x08, /* HID usage sensor event state changed sel */
+0x0A, 0x12, 0x08, /* HID usage sensor event property changed sel */
+0x0A, 0x13, 0x08, /* HID usage sensor event data updated sel */
+0x0A, 0x14, 0x08, /* HID usage sensor event poll response sel */
+0x0A, 0x15, 0x08, /* HID usage sensor event change sensitivity sel */
+0X81, 0x00, /* HID Input (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x57, 0x04, /* Sensor data motion Angular velocity X axis */
+0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
+
+0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
+
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count (1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0X81, 0x02, /* HID Input (Data_Arr_Abs) */
+0x0A, 0x58, 0x04, /* Sensor data motion Angular velocity Y axis */
+0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
+
+0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
+
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count (1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0X81, 0x02, /* HID Input (Data_Arr_Abs) */
+0x0A, 0x59, 0x04, /* Sensor data motion Angular velocity Z axis */
+0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
+
+0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
+
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count (1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0X81, 0x02, /* HID Input (Data_Arr_Abs) */
+
+0xC0, /* HID end collection */
+};
+
+const u8 comp3_report_descriptor[] = {
+0x05, 0x20, /* Usage page */
+0x09, 0x83, /* Motion type Orientation compass 3D */
+0xA1, 0x00, /* HID Collection (Physical) */
+
+0x85, 3, /* HID Report ID */
+0x05, 0x20, /* HID usage page sensor */
+0x0A, 0x09, 0x03, /* Sensor property and sensor connection type */
+0x15, 0, /* HID logical MIN_8(0) */
+0x25, 2, /* HID logical MAX_8(2) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection (logical) */
+0x0A, 0x30, 0x08, /* Sensor property connection type intergated sel */
+0x0A, 0x31, 0x08, /* Sensor property connection type attached sel */
+0x0A, 0x32, 0x08, /* Sensor property connection type external sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x16, 0x03, /* HID usage sensor property reporting state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 5, /* HID logical Max_8(5) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection(logical) */
+0x0A, 0x40, 0x08, /* Sensor reporting state no events sel */
+0x0A, 0x41, 0x08, /* Sensor reporting state all events sel */
+0x0A, 0x42, 0x08, /* Sensor reporting state threshold events sel */
+0x0A, 0x43, 0x08, /* Sensor reporting state no events wake sel */
+0x0A, 0x44, 0x08, /* Sensor reporting state all events wake sel */
+0x0A, 0x45, 0x08, /* Sensor reporting state threshold events wake sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x19, 0x03, /* HID usage sensor property power state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 5, /* HID logical Max_8(5) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection(logical) */
+0x0A, 0x50, 0x08, /* Sensor power state undefined sel */
+0x0A, 0x51, 0x08, /* Sensor power state D0 full power sel */
+0x0A, 0x52, 0x08, /* Sensor power state D1 low power sel */
+0x0A, 0x53, 0x08, /* Sensor power state D2 standby with wake sel */
+0x0A, 0x54, 0x08, /* Sensor power state D3 sleep with wake sel */
+0x0A, 0x55, 0x08, /* Sensor power state D4 power off sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x01, 0x02, /* HID usage sensor state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 6, /* HID logical Max_8(6) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection(logical) */
+0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
+0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
+0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
+0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
+0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
+0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
+0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x0E, 0x03, /* HID usage sensor property report interval */
+0x15, 0, /* HID logical Min_8(0) */
+0x27, 0xFF, 0xFF, 0xFF, 0xFF, /* HID logical Max_32 */
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count(1) */
+0x55, 0, /* HID unit exponent(0) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0x71, 0x14, /* Orientation and mod change sensitivity ABS)*/
+0x15, 0, /* HID logical Min_8(0) */
+0x26, 0xFF, 0xFF, /* HID logical Max_16(0xFF,0xFF) */
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0x71, 0x24, /* Sensor data (motion orientation and mod max) */
+0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
+0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0F, /* HID unit exponent(0x0F) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0x71, 0x34, /* Sensor data (motion orientation and mod min) */
+0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
+0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0F, /* HID unit exponent(0x0F) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0x84, 0x14, /* Maganetic flux and change sensitivity ABS) */
+0x15, 0, /* HID logical Min_8(0) */
+0x26, 0xFF, 0xFF, /* HID logical Max_16(0xFF,0xFF) */
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0x84, 0x24, /* Maganetic flux and mod change sensitivity Max) */
+0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
+0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0F, /* HID unit exponent(0x0F) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0x84, 0x34, /* Maganetic flux and mod change sensitivity Min */
+0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
+0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0F, /* HID unit exponent(0x0F) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+
+//Input reports(transmit)
+0x05, 0x20, /* HID usage page sensors */
+0x0A, 0x01, 0x02, /* HID usage sensor state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 6, /* HID logical Max_8(6) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count (1) */
+0xA1, 0x02, /* HID end collection (logical) */
+0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
+0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
+0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
+0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
+0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
+0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
+0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
+0X81, 0x00, /* HID Input (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x02, 0x02, /* HID usage sensor event */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 5, /* HID logical Max_8(5) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count (1) */
+0xA1, 0x02, /* HID end collection (logical) */
+0x0A, 0x10, 0x08, /* HID usage sensor event unknown sel */
+0x0A, 0x11, 0x08, /* HID usage sensor event state changed sel */
+0x0A, 0x12, 0x08, /* HID usage sensor event property changed sel */
+0x0A, 0x13, 0x08, /* HID usage sensor event data updated sel */
+0x0A, 0x14, 0x08, /* HID usage sensor event poll response sel */
+0x0A, 0x15, 0x08, /* HID usage sensor event change sensitivity sel */
+0X81, 0x00, /* HID Input (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x85, 0x04, /* Sensor data orientation magnetic flux X axis */
+0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
+0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count (1) */
+0x55, 0x0D, /* HID unit exponent(0x0D) */
+0X81, 0x02, /* HID Input (Data_Arr_Abs) */
+0x0A, 0x86, 0x04, /* Sensor data orientation magnetic flux Y axis */
+0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
+0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count (1) */
+0x55, 0x0D, /* HID unit exponent(0x0D) */
+0X81, 0x02, /* HID Input (Data_Arr_Abs) */
+0x0A, 0x87, 0x04, /* Sensor data orientation magnetic flux Z axis */
+0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
+0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count (1) */
+0x55, 0x0D, /* HID unit exponent(0x0D) */
+0X81, 0x02, /* HID Input (Data_Arr_Abs) */
+0x0A, 0x88, 0x04, /* Sensor data orientation magnetometer accuracy */
+0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
+0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count (1) */
+0X81, 0x02, /* HID Input (Data_Arr_Abs) */
+0xC0 /* HID end collection */
+};
+
+const u8 als_report_descriptor[] = {
+0x05, 0x20, /* HID usage page sensor */
+0x09, 0x41, /* HID usage sensor type Ambientlight */
+0xA1, 0x00, /* HID Collection (Physical) */
+
+//feature reports(xmit/receive)//
+0x85, 4, /* HID Report ID */
+0x05, 0x20, /* HID usage page sensor */
+0x0A, 0x09, 0x03, /* Sensor property and sensor connection type */
+0x15, 0, /* HID logical MIN_8(0) */
+0x25, 2, /* HID logical MAX_8(2) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection (logical) */
+0x0A, 0x30, 0x08, /* Sensor property connection type intergated sel */
+0x0A, 0x31, 0x08, /* Sensor property connection type attached sel */
+0x0A, 0x32, 0x08, /* Sensor property connection type external sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x16, 0x03, /* HID usage sensor property reporting state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 5, /* HID logical Max_8(5) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection(logical) */
+0x0A, 0x40, 0x08, /* Sensor reporting state no events sel */
+0x0A, 0x41, 0x08, /* Sensor reporting state all events sel */
+0x0A, 0x42, 0x08, /* Sensor reporting state threshold events sel */
+0x0A, 0x43, 0x08, /* Sensor reporting state no events wake sel */
+0x0A, 0x44, 0x08, /* Sensor reporting state all events wake sel */
+0x0A, 0x45, 0x08, /* Sensor reporting state threshold events wake sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x19, 0x03, /* HID usage sensor property power state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 5, /* HID logical Max_8(5) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection(logical) */
+0x0A, 0x50, 0x08, /* Sensor power state undefined sel */
+0x0A, 0x51, 0x08, /* Sensor power state D0 full power sel */
+0x0A, 0x52, 0x08, /* Sensor power state D1 low power sel */
+0x0A, 0x53, 0x08, /* Sensor power state D2 standby with wake sel */
+0x0A, 0x54, 0x08, /* Sensor power state D3 sleep with wake sel */
+0x0A, 0x55, 0x08, /* Sensor power state D4 power off sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x01, 0x02, /* HID usage sensor state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 6, /* HID logical Max_8(6) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count(1) */
+0xA1, 0x02, /* HID collection(logical) */
+0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
+0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
+0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
+0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
+0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
+0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
+0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
+0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x0E, 0x03, /* HID usage sensor property report interval */
+0x15, 0, /* HID logical Min_8(0) */
+0x27, 0xFF, 0xFF, 0xFF, 0xFF, /* HID logical Max_32 */
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count(1) */
+0x55, 0, /* HID unit exponent(0) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0xD1, 0xE4, /* Light illuminance and sensitivity REL PCT) */
+0x15, 0, /* HID logical Min_8(0) */
+0x26, 0x10, 0x27, /* HID logical Max_16(0x10,0x27) */
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0E, /* HID unit exponent(0x0E) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0xD1, 0x24, /* Sensor data (Light illuminance and mod max) */
+0x15, 0, /* HID logical Min_8(0) */
+0x26, 0xFF, 0xFF, /* HID logical Max_16(0xFF,0xFF) */
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0F, /* HID unit exponent(0x0F) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+0x0A, 0xD1, 0x34, /* Sensor data (Light illuminance and mod min) */
+0x15, 0, /* HID logical Min_8(0) */
+0x26, 0xFF, 0xFF, /* HID logical Max_16(0xFF,0xFF) */
+0x75, 16, /* HID report size(16) */
+0x95, 1, /* HID report count(1) */
+0x55, 0x0F, /* HID unit exponent(0x0F) */
+0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
+
+//Input reports (transmit)
+0x05, 0x20, /* HID usage page sensors */
+0x0A, 0x01, 0x02, /* HID usage sensor state */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 6, /* HID logical Max_8(6) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count (1) */
+0xA1, 0x02, /* HID end collection (logical) */
+0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
+0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
+0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
+0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
+0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
+0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
+0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
+0X81, 0x00, /* HID Input (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0x02, 0x02, /* HID usage sensor event */
+0x15, 0, /* HID logical Min_8(0) */
+0x25, 5, /* HID logical Max_8(5) */
+0x75, 8, /* HID report size(8) */
+0x95, 1, /* HID report count (1) */
+0xA1, 0x02, /* HID end collection (logical) */
+0x0A, 0x10, 0x08, /* HID usage sensor event unknown sel */
+0x0A, 0x11, 0x08, /* HID usage sensor event state changed sel */
+0x0A, 0x12, 0x08, /* HID usage sensor event property changed sel */
+0x0A, 0x13, 0x08, /* HID usage sensor event data updated sel */
+0x0A, 0x14, 0x08, /* HID usage sensor event poll response sel */
+0x0A, 0x15, 0x08, /* HID usage sensor event change sensitivity sel */
+0X81, 0x00, /* HID Input (Data_Arr_Abs) */
+0xC0, /* HID end collection */
+0x0A, 0xD1, 0x04, /* HID usage sensor data light illuminance */
+0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
+0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
+0x55, 0x0F, /* HID unit exponent(0x0F) */
+0x75, 32, /* HID report size(32) */
+0x95, 1, /* HID report count (1) */
+0X81, 0x02, /* HID Input (Data_Arr_Abs) */
+0xC0 /* HID end collection */
+};
+#endif
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index c183caf89d49..1dfe184ebf5a 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -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. */
@@ -1127,6 +1219,9 @@ static const struct hid_device_id asus_devices[] = {
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 },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c
index 8c712d4bc075..e59e9911fc37 100644
--- a/drivers/hid/hid-elecom.c
+++ b/drivers/hid/hid-elecom.c
@@ -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) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index f170feaac40b..4c5f23640f9c 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -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
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 4dca11392459..dc7f6b4a775c 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -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;
}
diff --git a/drivers/hid/hid-ite.c b/drivers/hid/hid-ite.c
index 742c052b0110..22bfbebceaf4 100644
--- a/drivers/hid/hid-ite.c
+++ b/drivers/hid/hid-ite.c
@@ -18,10 +18,16 @@ static __u8 *ite_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
if (quirks & QUIRK_TOUCHPAD_ON_OFF_REPORT) {
+ /* For Acer Aspire Switch 10 SW5-012 keyboard-dock */
if (*rsize == 188 && rdesc[162] == 0x81 && rdesc[163] == 0x02) {
- hid_info(hdev, "Fixing up ITE keyboard report descriptor\n");
+ hid_info(hdev, "Fixing up Acer Sw5-012 ITE keyboard report descriptor\n");
rdesc[163] = HID_MAIN_ITEM_RELATIVE;
}
+ /* For Acer One S1002 keyboard-dock */
+ if (*rsize == 188 && rdesc[185] == 0x81 && rdesc[186] == 0x02) {
+ hid_info(hdev, "Fixing up Acer S1002 ITE keyboard report descriptor\n");
+ rdesc[186] = HID_MAIN_ITEM_RELATIVE;
+ }
}
return rdesc;
@@ -103,6 +109,11 @@ static const struct hid_device_id ite_devices[] = {
/* ITE8910 USB kbd ctlr, with Synaptics touchpad connected to it. */
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
USB_VENDOR_ID_SYNAPTICS,
+ USB_DEVICE_ID_SYNAPTICS_ACER_ONE_S1002),
+ .driver_data = QUIRK_TOUCHPAD_ON_OFF_REPORT },
+ /* ITE8910 USB kbd ctlr, with Synaptics touchpad connected to it. */
+ { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
+ USB_VENDOR_ID_SYNAPTICS,
USB_DEVICE_ID_SYNAPTICS_ACER_ONE_S1003) },
{ }
};
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 0ca723119547..f85781464807 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -4048,6 +4048,8 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* MX5500 keyboard over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb30b),
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
+ { /* 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),
.driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
diff --git a/drivers/hid/hid-mf.c b/drivers/hid/hid-mf.c
index fc75f30f537c..92d7ecd41a78 100644
--- a/drivers/hid/hid-mf.c
+++ b/drivers/hid/hid-mf.c
@@ -153,6 +153,8 @@ static const struct hid_device_id mf_devices[] = {
.driver_data = HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE2),
.driver_data = 0 }, /* No quirk required */
+ { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE3),
+ .driver_data = HID_QUIRK_MULTI_INPUT },
{ }
};
MODULE_DEVICE_TABLE(hid, mf_devices);
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index bf7ecab5d9e5..d9ca874dffac 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -72,6 +72,7 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_REDRAGON_SEYMUR2), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE1), HID_QUIRK_MULTI_INPUT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE3), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER), HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET },
@@ -366,6 +367,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
#endif
#if IS_ENABLED(CONFIG_HID_ELECOM)
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
+ { HID_BLUETOOTH_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) },
@@ -484,6 +486,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE3) },
#endif
#if IS_ENABLED(CONFIG_HID_MICROSOFT)
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 2f073f536070..e3a557dc9ffd 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -11,6 +11,7 @@
* Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
* Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com>
* Copyright (c) 2018 Todd Kelner
+ * Copyright (c) 2020 Pascal Giard <pascal.giard@etsmtl.ca>
*/
/*
@@ -35,6 +36,8 @@
#include <linux/idr.h>
#include <linux/input/mt.h>
#include <linux/crc32.h>
+#include <linux/usb.h>
+#include <linux/timer.h>
#include <asm/unaligned.h>
#include "hid-ids.h"
@@ -56,6 +59,7 @@
#define NSG_MR5U_REMOTE_BT BIT(14)
#define NSG_MR7U_REMOTE_BT BIT(15)
#define SHANWAN_GAMEPAD BIT(16)
+#define GHL_GUITAR_PS3WIIU BIT(17)
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
@@ -79,6 +83,17 @@
#define NSG_MRXU_MAX_X 1667
#define NSG_MRXU_MAX_Y 1868
+#define GHL_GUITAR_POKE_INTERVAL 10 /* In seconds */
+#define GHL_GUITAR_TILT_USAGE 44
+
+/* Magic value and data taken from GHLtarUtility:
+ * https://github.com/ghlre/GHLtarUtility/blob/master/PS3Guitar.cs
+ * Note: The Wii U and PS3 dongles happen to share the same!
+ */
+static const u16 ghl_ps3wiiu_magic_value = 0x201;
+static const char ghl_ps3wiiu_magic_data[] = {
+ 0x02, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00
+};
/* PS/3 Motion controller */
static u8 motion_rdesc[] = {
@@ -550,7 +565,9 @@ struct sony_sc {
struct power_supply_desc battery_desc;
int device_id;
unsigned fw_version;
+ bool fw_version_created;
unsigned hw_version;
+ bool hw_version_created;
u8 *output_report_dmabuf;
#ifdef CONFIG_SONY_FF
@@ -562,9 +579,8 @@ struct sony_sc {
u8 hotplug_worker_initialized;
u8 state_worker_initialized;
u8 defer_initialization;
- u8 cable_state;
- u8 battery_charging;
u8 battery_capacity;
+ int battery_status;
u8 led_state[MAX_LEDS];
u8 led_delay_on[MAX_LEDS];
u8 led_delay_off[MAX_LEDS];
@@ -578,6 +594,10 @@ struct sony_sc {
enum ds4_dongle_state ds4_dongle_state;
/* DS4 calibration data */
struct ds4_calibration_data ds4_calib_data[6];
+ /* GH Live */
+ struct timer_list ghl_poke_timer;
+ struct usb_ctrlrequest *ghl_cr;
+ u8 *ghl_databuf;
};
static void sony_set_leds(struct sony_sc *sc);
@@ -601,6 +621,85 @@ static inline void sony_schedule_work(struct sony_sc *sc,
}
}
+static void ghl_magic_poke_cb(struct urb *urb)
+{
+ if (urb) {
+ /* Free sc->ghl_cr and sc->ghl_databuf allocated in
+ * ghl_magic_poke()
+ */
+ kfree(urb->setup_packet);
+ kfree(urb->transfer_buffer);
+ }
+}
+
+static void ghl_magic_poke(struct timer_list *t)
+{
+ struct sony_sc *sc = from_timer(sc, t, ghl_poke_timer);
+
+ int ret;
+ unsigned int pipe;
+ struct urb *urb;
+ struct usb_device *usbdev = to_usb_device(sc->hdev->dev.parent->parent);
+ const u16 poke_size =
+ ARRAY_SIZE(ghl_ps3wiiu_magic_data);
+
+ pipe = usb_sndctrlpipe(usbdev, 0);
+
+ if (!sc->ghl_cr) {
+ sc->ghl_cr = kzalloc(sizeof(*sc->ghl_cr), GFP_ATOMIC);
+ if (!sc->ghl_cr)
+ goto resched;
+ }
+
+ if (!sc->ghl_databuf) {
+ sc->ghl_databuf = kzalloc(poke_size, GFP_ATOMIC);
+ if (!sc->ghl_databuf)
+ goto resched;
+ }
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ goto resched;
+
+ sc->ghl_cr->bRequestType =
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT;
+ sc->ghl_cr->bRequest = USB_REQ_SET_CONFIGURATION;
+ sc->ghl_cr->wValue = cpu_to_le16(ghl_ps3wiiu_magic_value);
+ sc->ghl_cr->wIndex = 0;
+ sc->ghl_cr->wLength = cpu_to_le16(poke_size);
+ memcpy(sc->ghl_databuf, ghl_ps3wiiu_magic_data, poke_size);
+
+ usb_fill_control_urb(
+ urb, usbdev, pipe,
+ (unsigned char *) sc->ghl_cr, sc->ghl_databuf,
+ poke_size, ghl_magic_poke_cb, NULL);
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ kfree(sc->ghl_databuf);
+ kfree(sc->ghl_cr);
+ }
+ usb_free_urb(urb);
+
+resched:
+ /* Reschedule for next time */
+ mod_timer(&sc->ghl_poke_timer, jiffies + GHL_GUITAR_POKE_INTERVAL*HZ);
+}
+
+static int guitar_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_MSVENDOR) {
+ unsigned int abs = usage->hid & HID_USAGE;
+
+ if (abs == GHL_GUITAR_TILT_USAGE) {
+ hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RY);
+ return 1;
+ }
+ }
+ return 0;
+}
+
static ssize_t ds4_show_poll_interval(struct device *dev,
struct device_attribute
*attr, char *buf)
@@ -892,7 +991,8 @@ static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size)
static const u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
unsigned long flags;
int offset;
- u8 cable_state, battery_capacity, battery_charging;
+ u8 battery_capacity;
+ int battery_status;
/*
* The sixaxis is charging if the battery value is 0xee
@@ -904,19 +1004,16 @@ static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size)
if (rd[offset] >= 0xee) {
battery_capacity = 100;
- battery_charging = !(rd[offset] & 0x01);
- cable_state = 1;
+ battery_status = (rd[offset] & 0x01) ? POWER_SUPPLY_STATUS_FULL : POWER_SUPPLY_STATUS_CHARGING;
} else {
u8 index = rd[offset] <= 5 ? rd[offset] : 5;
battery_capacity = sixaxis_battery_capacity[index];
- battery_charging = 0;
- cable_state = 0;
+ battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
}
spin_lock_irqsave(&sc->lock, flags);
- sc->cable_state = cable_state;
sc->battery_capacity = battery_capacity;
- sc->battery_charging = battery_charging;
+ sc->battery_status = battery_status;
spin_unlock_irqrestore(&sc->lock, flags);
if (sc->quirks & SIXAXIS_CONTROLLER) {
@@ -944,7 +1041,8 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
struct input_dev *input_dev = hidinput->input;
unsigned long flags;
int n, m, offset, num_touch_data, max_touch_data;
- u8 cable_state, battery_capacity, battery_charging;
+ u8 cable_state, battery_capacity;
+ int battery_status;
u16 timestamp;
/* When using Bluetooth the header is 2 bytes longer, so skip these. */
@@ -1049,29 +1147,52 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
*/
offset = data_offset + DS4_INPUT_REPORT_BATTERY_OFFSET;
cable_state = (rd[offset] >> 4) & 0x01;
- battery_capacity = rd[offset] & 0x0F;
/*
- * When a USB power source is connected the battery level ranges from
- * 0 to 10, and when running on battery power it ranges from 0 to 9.
- * A battery level above 10 when plugged in means charge completed.
+ * Interpretation of the battery_capacity data depends on the cable state.
+ * When no cable is connected (bit4 is 0):
+ * - 0:10: percentage in units of 10%.
+ * When a cable is plugged in:
+ * - 0-10: percentage in units of 10%.
+ * - 11: battery is full
+ * - 14: not charging due to Voltage or temperature error
+ * - 15: charge error
*/
- if (!cable_state || battery_capacity > 10)
- battery_charging = 0;
- else
- battery_charging = 1;
+ if (cable_state) {
+ u8 battery_data = rd[offset] & 0xf;
+
+ if (battery_data < 10) {
+ /* Take the mid-point for each battery capacity value,
+ * because on the hardware side 0 = 0-9%, 1=10-19%, etc.
+ * This matches official platform behavior, which does
+ * the same.
+ */
+ battery_capacity = battery_data * 10 + 5;
+ battery_status = POWER_SUPPLY_STATUS_CHARGING;
+ } else if (battery_data == 10) {
+ battery_capacity = 100;
+ battery_status = POWER_SUPPLY_STATUS_CHARGING;
+ } else if (battery_data == 11) {
+ battery_capacity = 100;
+ battery_status = POWER_SUPPLY_STATUS_FULL;
+ } else { /* 14, 15 and undefined values */
+ battery_capacity = 0;
+ battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+ } else {
+ u8 battery_data = rd[offset] & 0xf;
- if (!cable_state)
- battery_capacity++;
- if (battery_capacity > 10)
- battery_capacity = 10;
+ if (battery_data < 10)
+ battery_capacity = battery_data * 10 + 5;
+ else /* 10 */
+ battery_capacity = 100;
- battery_capacity *= 10;
+ battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
spin_lock_irqsave(&sc->lock, flags);
- sc->cable_state = cable_state;
sc->battery_capacity = battery_capacity;
- sc->battery_charging = battery_charging;
+ sc->battery_status = battery_status;
spin_unlock_irqrestore(&sc->lock, flags);
/*
@@ -1360,6 +1481,8 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
if (sc->quirks & DUALSHOCK4_CONTROLLER)
return ds4_mapping(hdev, hi, field, usage, bit, max);
+ if (sc->quirks & GHL_GUITAR_PS3WIIU)
+ return guitar_mapping(hdev, hi, field, usage, bit, max);
/* Let hid-core decide for the others */
return 0;
@@ -1597,16 +1720,38 @@ static int dualshock4_get_calibration_data(struct sony_sc *sc)
* of the controller, so that it sends input reports of type 0x11.
*/
if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE)) {
+ int retries;
+
buf = kmalloc(DS4_FEATURE_REPORT_0x02_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- ret = hid_hw_raw_request(sc->hdev, 0x02, buf,
- DS4_FEATURE_REPORT_0x02_SIZE,
- HID_FEATURE_REPORT,
- HID_REQ_GET_REPORT);
- if (ret < 0)
- goto err_stop;
+ /* We should normally receive the feature report data we asked
+ * for, but hidraw applications such as Steam can issue feature
+ * reports as well. In particular for Dongle reconnects, Steam
+ * and this function are competing resulting in often receiving
+ * data for a different HID report, so retry a few times.
+ */
+ for (retries = 0; retries < 3; retries++) {
+ ret = hid_hw_raw_request(sc->hdev, 0x02, buf,
+ DS4_FEATURE_REPORT_0x02_SIZE,
+ HID_FEATURE_REPORT,
+ HID_REQ_GET_REPORT);
+ if (ret < 0)
+ goto err_stop;
+
+ if (buf[0] != 0x02) {
+ if (retries < 2) {
+ hid_warn(sc->hdev, "Retrying DualShock 4 get calibration report (0x02) request\n");
+ continue;
+ } else {
+ ret = -EILSEQ;
+ goto err_stop;
+ }
+ } else {
+ break;
+ }
+ }
} else {
u8 bthdr = 0xA3;
u32 crc;
@@ -2300,12 +2445,12 @@ static int sony_battery_get_property(struct power_supply *psy,
struct sony_sc *sc = power_supply_get_drvdata(psy);
unsigned long flags;
int ret = 0;
- u8 battery_charging, battery_capacity, cable_state;
+ u8 battery_capacity;
+ int battery_status;
spin_lock_irqsave(&sc->lock, flags);
- battery_charging = sc->battery_charging;
battery_capacity = sc->battery_capacity;
- cable_state = sc->cable_state;
+ battery_status = sc->battery_status;
spin_unlock_irqrestore(&sc->lock, flags);
switch (psp) {
@@ -2319,13 +2464,7 @@ static int sony_battery_get_property(struct power_supply *psy,
val->intval = battery_capacity;
break;
case POWER_SUPPLY_PROP_STATUS:
- if (battery_charging)
- val->intval = POWER_SUPPLY_STATUS_CHARGING;
- else
- if (battery_capacity == 100 && cable_state)
- val->intval = POWER_SUPPLY_STATUS_FULL;
- else
- val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ val->intval = battery_status;
break;
default:
ret = -EINVAL;
@@ -2723,19 +2862,17 @@ static int sony_input_configured(struct hid_device *hdev,
ret = device_create_file(&sc->hdev->dev, &dev_attr_firmware_version);
if (ret) {
- /* Make zero for cleanup reasons of sysfs entries. */
- sc->fw_version = 0;
- sc->hw_version = 0;
hid_err(sc->hdev, "can't create sysfs firmware_version attribute err: %d\n", ret);
goto err_stop;
}
+ sc->fw_version_created = true;
ret = device_create_file(&sc->hdev->dev, &dev_attr_hardware_version);
if (ret) {
- sc->hw_version = 0;
hid_err(sc->hdev, "can't create sysfs hardware_version attribute err: %d\n", ret);
goto err_stop;
}
+ sc->hw_version_created = true;
/*
* The Dualshock 4 touchpad supports 2 touches and has a
@@ -2827,9 +2964,9 @@ err_stop:
*/
if (sc->ds4_bt_poll_interval)
device_remove_file(&sc->hdev->dev, &dev_attr_bt_poll_interval);
- if (sc->fw_version)
+ if (sc->fw_version_created)
device_remove_file(&sc->hdev->dev, &dev_attr_firmware_version);
- if (sc->hw_version)
+ if (sc->hw_version_created)
device_remove_file(&sc->hdev->dev, &dev_attr_hardware_version);
sony_cancel_work_sync(sc);
sony_remove_dev_list(sc);
@@ -2902,6 +3039,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
return -ENODEV;
}
+ if (sc->quirks & GHL_GUITAR_PS3WIIU) {
+ timer_setup(&sc->ghl_poke_timer, ghl_magic_poke, 0);
+ mod_timer(&sc->ghl_poke_timer,
+ jiffies + GHL_GUITAR_POKE_INTERVAL*HZ);
+ }
+
return ret;
}
@@ -2909,15 +3052,18 @@ static void sony_remove(struct hid_device *hdev)
{
struct sony_sc *sc = hid_get_drvdata(hdev);
+ if (sc->quirks & GHL_GUITAR_PS3WIIU)
+ del_timer_sync(&sc->ghl_poke_timer);
+
hid_hw_close(hdev);
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
device_remove_file(&sc->hdev->dev, &dev_attr_bt_poll_interval);
- if (sc->fw_version)
+ if (sc->fw_version_created)
device_remove_file(&sc->hdev->dev, &dev_attr_firmware_version);
- if (sc->hw_version)
+ if (sc->hw_version_created)
device_remove_file(&sc->hdev->dev, &dev_attr_hardware_version);
sony_cancel_work_sync(sc);
@@ -3020,6 +3166,9 @@ static const struct hid_device_id sony_devices[] = {
/* SMK-Link NSG-MR7U Remote Control */
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE),
.driver_data = NSG_MR7U_REMOTE_BT },
+ /* Guitar Hero Live PS3 and Wii U guitar dongles */
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_GHLIVE, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE),
+ .driver_data = GHL_GUITAR_PS3WIIU},
{ }
};
MODULE_DEVICE_TABLE(hid, sony_devices);
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 2eee5e31c2b7..79faac87a06f 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -170,7 +170,7 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t
/*
* This function performs a Get_Report transfer over the control endpoint
* per section 7.2.1 of the HID specification, version 1.1. The first byte
- * of buffer is the report number to request, or 0x0 if the defice does not
+ * of buffer is the report number to request, or 0x0 if the device does not
* use numbered reports. The report_type parameter can be HID_FEATURE_REPORT
* or HID_INPUT_REPORT.
*/
@@ -428,6 +428,28 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
break;
}
+ if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSINPUT(0))) {
+ int len = _IOC_SIZE(cmd);
+ ret = hidraw_send_report(file, user_arg, len, HID_INPUT_REPORT);
+ break;
+ }
+ if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGINPUT(0))) {
+ int len = _IOC_SIZE(cmd);
+ ret = hidraw_get_report(file, user_arg, len, HID_INPUT_REPORT);
+ break;
+ }
+
+ if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSOUTPUT(0))) {
+ int len = _IOC_SIZE(cmd);
+ ret = hidraw_send_report(file, user_arg, len, HID_OUTPUT_REPORT);
+ break;
+ }
+ if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGOUTPUT(0))) {
+ int len = _IOC_SIZE(cmd);
+ ret = hidraw_get_report(file, user_arg, len, HID_OUTPUT_REPORT);
+ break;
+ }
+
/* Begin Read-only ioctls. */
if (_IOC_DIR(cmd) != _IOC_READ) {
ret = -EINVAL;
diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
index aeff1ffb0c8b..bfe716d7ea44 100644
--- a/drivers/hid/i2c-hid/i2c-hid-core.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -1106,8 +1106,11 @@ static int i2c_hid_probe(struct i2c_client *client,
}
ret = i2c_hid_fetch_hid_descriptor(ihid);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to fetch the HID Descriptor\n");
goto err_regulator;
+ }
ret = i2c_hid_init_irq(client);
if (ret < 0)
diff --git a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
index 35f3bfc3e6f5..8e0f67455c09 100644
--- a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
+++ b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
@@ -405,6 +405,14 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
},
.driver_data = (void *)&sipodev_desc
},
+ {
+ .ident = "Vero K147",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VERO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "K147"),
+ },
+ .driver_data = (void *)&sipodev_desc
+ },
{ } /* Terminate list */
};
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.c b/drivers/hid/intel-ish-hid/ishtp-hid.c
index b8aae69ad15d..393bed0abee9 100644
--- a/drivers/hid/intel-ish-hid/ishtp-hid.c
+++ b/drivers/hid/intel-ish-hid/ishtp-hid.c
@@ -211,10 +211,8 @@ int ishtp_hid_probe(unsigned int cur_hid_dev,
struct ishtp_hid_data *hid_data;
hid = hid_allocate_device();
- if (IS_ERR(hid)) {
- rv = PTR_ERR(hid);
- return -ENOMEM;
- }
+ if (IS_ERR(hid))
+ return PTR_ERR(hid);
hid_data = kzalloc(sizeof(*hid_data), GFP_KERNEL);
if (!hid_data) {
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 17a29ee0ac6c..86257ce6d619 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -438,6 +438,7 @@ static void hid_irq_out(struct urb *urb)
break;
case -ESHUTDOWN: /* unplug */
unplug = 1;
+ break;
case -EILSEQ: /* protocol error or unplug */
case -EPROTO: /* protocol error or unplug */
case -ECONNRESET: /* unlink */
@@ -489,6 +490,7 @@ static void hid_ctrl(struct urb *urb)
break;
case -ESHUTDOWN: /* unplug */
unplug = 1;
+ break;
case -EILSEQ: /* protocol error or unplug */
case -EPROTO: /* protocol error or unplug */
case -ECONNRESET: /* unlink */
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index cd71e7133944..045c464228d9 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -1173,7 +1173,7 @@ static struct attribute *cintiq_led_attrs[] = {
NULL
};
-static struct attribute_group cintiq_led_attr_group = {
+static const struct attribute_group cintiq_led_attr_group = {
.name = "wacom_led",
.attrs = cintiq_led_attrs,
};
@@ -1194,7 +1194,7 @@ static struct attribute *intuos4_led_attrs[] = {
NULL
};
-static struct attribute_group intuos4_led_attr_group = {
+static const struct attribute_group intuos4_led_attr_group = {
.name = "wacom_led",
.attrs = intuos4_led_attrs,
};
@@ -1205,7 +1205,7 @@ static struct attribute *intuos5_led_attrs[] = {
NULL
};
-static struct attribute_group intuos5_led_attr_group = {
+static const struct attribute_group intuos5_led_attr_group = {
.name = "wacom_led",
.attrs = intuos5_led_attrs,
};
@@ -1216,13 +1216,13 @@ static struct attribute *generic_led_attrs[] = {
NULL
};
-static struct attribute_group generic_led_attr_group = {
+static const struct attribute_group generic_led_attr_group = {
.name = "wacom_led",
.attrs = generic_led_attrs,
};
struct wacom_sysfs_group_devres {
- struct attribute_group *group;
+ const struct attribute_group *group;
struct kobject *root;
};
@@ -1238,7 +1238,7 @@ static void wacom_devm_sysfs_group_release(struct device *dev, void *res)
static int __wacom_devm_sysfs_create_group(struct wacom *wacom,
struct kobject *root,
- struct attribute_group *group)
+ const struct attribute_group *group)
{
struct wacom_sysfs_group_devres *devres;
int error;
@@ -1264,7 +1264,7 @@ static int __wacom_devm_sysfs_create_group(struct wacom *wacom,
}
static int wacom_devm_sysfs_create_group(struct wacom *wacom,
- struct attribute_group *group)
+ const struct attribute_group *group)
{
return __wacom_devm_sysfs_create_group(wacom, &wacom->hdev->dev.kobj,
group);
@@ -1847,7 +1847,7 @@ static struct attribute *remote##SET_ID##_serial_attrs[] = { \
&remote##SET_ID##_mode_attr.attr, \
NULL \
}; \
-static struct attribute_group remote##SET_ID##_serial_group = { \
+static const struct attribute_group remote##SET_ID##_serial_group = { \
.name = NULL, \
.attrs = remote##SET_ID##_serial_attrs, \
}