Commit de925e2f authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 62746f92 85a69473
.. SPDX-License-Identifier: GPL-2.0
AMD Sensor Fusion Hub
=====================
AMD Sensor Fusion Hub (SFH) is part of an SOC starting from Ryzen based platforms.
The solution is working well on several OEM products. AMD SFH uses HID over PCIe bus.
In terms of architecture it resembles ISH, however the major difference is all
the HID reports are generated as part of the kernel driver.
1. Block Diagram
================
::
---------------------------------
| HID User Space Applications |
- -------------------------------
---------------------------------------------
---------------------------------
| HID Core |
---------------------------------
---------------------------------
| AMD HID Transport |
---------------------------------
--------------------------------
| AMD HID Client |
| with HID Report Generator|
--------------------------------
--------------------------------
| AMD MP2 PCIe Driver |
--------------------------------
OS
---------------------------------------------
Hardware + Firmware
--------------------------------
| SFH MP2 Processor |
--------------------------------
AMD HID Transport Layer
-----------------------
AMD SFH transport is also implemented as a bus. Each client application executing in the AMD MP2 is
registered as a device on this bus. Here: MP2 which is an ARM core connected to x86 for processing
sensor data. The layer, which binds each device (AMD SFH HID driver) identifies the device type and
registers with the hid core. Transport layer attach a constant "struct hid_ll_driver" object with
each device. Once a device is registered with HID core, the callbacks provided via this struct are
used by HID core to communicate with the device. AMD HID Transport layer implements the synchronous calls.
AMD HID Client Layer
--------------------
This layer is responsible to implement HID request and descriptors. As firmware is OS agnostic, HID
client layer fills the HID request structure and descriptors. HID client layer is complex as it is
interface between MP2 PCIe layer and HID. HID client layer initialized the MP2 PCIe layer and holds
the instance of MP2 layer. It identifies the number of sensors connected using MP2-PCIe layer. Base
on that allocates the DRAM address for each and every sensor and pass it to MP2-PCIe driver.On
enumeration of each the sensor, client layer fills the HID Descriptor structure and HID input repor
structure. HID Feature report structure is optional. The report descriptor structure varies from
sensor to sensor.
AMD MP2 PCIe layer
------------------
MP2 PCIe Layer is responsible for making all transactions with the firmware over PCIe.
The connection establishment between firmware and PCIe happens here.
The communication between X86 and MP2 is split into three parts.
1. Command transfer via the C2P mailbox registers.
2. Data transfer via DRAM.
3. Supported sensor info via P2C registers.
Commands are sent to MP2 using C2P Mailbox registers. Writing into C2P Message registers generate
interrupt to MP2. The client layer allocates the physical memory and the same is sent to MP2 via
the PCI layer. MP2 firmware writes the command output to the access DRAM memory which the client
layer has allocated. Firmware always writes minimum of 32 bytes into DRAM. So as a protocol driver
shall allocate minimum of 32 bytes DRAM space.
Enumeration and Probing flow
----------------------------
::
HID AMD AMD AMD -PCIe MP2
Core Transport Client layer layer FW
| | | | |
| | | on Boot Driver Loaded |
| | | | |
| | | MP2-PCIe Int |
| | | | |
| | |---Get Number of sensors-> | |
| | | Read P2C |
| | | Register |
| | | | |
| | | Loop(for No of Sensors) | |
| | |----------------------| | |
| | | Create HID Descriptor| | |
| | | Create Input report | | |
| | | Descriptor Map | | |
| | | the MP2 FW Index to | | |
| | | HID Index | | |
| | | Allocate the DRAM | Enable |
| | | address | Sensors |
| | |----------------------| | |
| | HID transport| | Enable |
| |<--Probe------| |---Sensor CMD--> |
| | Create the | | |
| | HID device | | |
| | (MFD) | | |
| | by Populating| | |
| | the HID | | |
| | ll_driver | | |
| HID | | | |
| add | | | |
|Device | | | |
|<------------- | | | |
Data Flow from Application to the AMD SFH Driver
------------------------------------------------
::
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
|HID_req | | | |
|get_report | | | |
|------------->| | | |
| | HID_get_input| | |
| | report | | |
| |------------->|------------------------| | |
| | | Read the DRAM data for| | |
| | | requested sensor and | | |
| | | create the HID input | | |
| | | report | | |
| | |------------------------| | |
| |Data received | | |
| | in HID report| | |
To |<-------------|<-------------| | |
Applications| | | | |
<-------| | | | |
......@@ -123,8 +123,49 @@ HIDIOCGFEATURE(len):
This ioctl will request a feature report from the device using the control
endpoint. The first byte of the supplied buffer should be set to the report
number of the requested report. For devices which do not use numbered
reports, set the first byte to 0. The report will be returned starting at
the first byte of the buffer (ie: the report number is not returned).
reports, set the first byte to 0. The returned report buffer will contain the
report number in the first byte, followed by the report data read from the
device. For devices which do not use numbered reports, the report data will
begin at the first byte of the returned buffer.
HIDIOCSINPUT(len):
Send an Input Report
This ioctl will send an input report to the device, using the control endpoint.
In most cases, setting an input HID report on a device is meaningless and has
no effect, but some devices may choose to use this to set or reset an initial
state of a report. The format of the buffer issued with this report is identical
to that of HIDIOCSFEATURE.
HIDIOCGINPUT(len):
Get an Input Report
This ioctl will request an input report from the device using the control
endpoint. This is slower on most devices where a dedicated In endpoint exists
for regular input reports, but allows the host to request the value of a
specific report number. Typically, this is used to request the initial states of
an input report of a device, before an application listens for normal reports via
the regular device read() interface. The format of the buffer issued with this report
is identical to that of HIDIOCGFEATURE.
HIDIOCSOUTPUT(len):
Send an Output Report
This ioctl will send an output report to the device, using the control endpoint.
This is slower on most devices where a dedicated Out endpoint exists for regular
output reports, but is added for completeness. Typically, this is used to set
the initial states of an output report of a device, before an application sends
updates via the regular device write() interface. The format of the buffer issued
with this report is identical to that of HIDIOCSFEATURE.
HIDIOCGOUTPUT(len):
Get an Output Report
This ioctl will request an output report from the device using the control
endpoint. Typically, this is used to retrive the initial state of
an output report of a device, before an application updates it as necessary either
via a HIDIOCSOUTPUT request, or the regular device write() interface. The format
of the buffer issued with this report is identical to that of HIDIOCGFEATURE.
Example
-------
......
......@@ -16,3 +16,4 @@ Human Interface Devices (HID)
hid-alps
intel-ish-hid
amd-sfh-hid
......@@ -956,6 +956,14 @@ S: Supported
F: arch/arm64/boot/dts/amd/amd-seattle-xgbe*.dtsi
F: drivers/net/ethernet/amd/xgbe/
AMD SENSOR FUSION HUB DRIVER
M: Nehal Shah <nehal-bakulchandra.shah@amd.com>
M: Sandeep Singh <sandeep.singh@amd.com>
L: linux-input@vger.kernel.org
S: Maintained
F: Documentation/hid/amd-sfh*
F: drivers/hid/amd-sfh-hid/
AMS AS73211 DRIVER
M: Christian Eggers <ceggers@arri.de>
L: linux-iio@vger.kernel.org
......
......@@ -907,6 +907,7 @@ config HID_SONY
* Buzz controllers
* Sony PS3 Blue-ray Disk Remote Control (Bluetooth)
* Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
* Guitar Hero Live PS3 and Wii U guitar dongles
config SONY_FF
bool "Sony PS2/3/4 accessories force feedback support"
......@@ -1183,4 +1184,6 @@ source "drivers/hid/i2c-hid/Kconfig"
source "drivers/hid/intel-ish-hid/Kconfig"
source "drivers/hid/amd-sfh-hid/Kconfig"
endmenu
......@@ -142,3 +142,5 @@ obj-$(CONFIG_I2C_HID) += i2c-hid/
obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/
obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/
obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/
# 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
# 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)/
// 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;
}
// 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;
}
}
}
/* 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
// 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>");
/* 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
// 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;
}
/* 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
......@@ -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,6 +534,11 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
unsigned char kbd_func;
int 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)
......@@ -473,6 +552,7 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
/* Check for backlight support */
if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
return -ENODEV;
}
drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
sizeof(struct asus_kbd_leds),
......@@ -751,8 +831,8 @@ static int asus_input_mapping(struct hid_device *hdev,
usage->hid == (HID_UP_GENDEVCTRLS | 0x0026)))
return -1;
/* ASUS-specific keyboard hotkeys */
if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) {
/* ASUS-specific keyboard hotkeys and led backlight */
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR) {
switch (usage->hid & HID_USAGE) {
case 0x10: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break;
case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break;
......@@ -780,6 +860,18 @@ static int asus_input_mapping(struct hid_device *hdev,
/* Fn+F5 "fan" symbol on FX503VD */
case 0x99: asus_map_key_clear(KEY_PROG4); break;
/* Fn+F5 "fan" symbol on N-Key keyboard */
case 0xae: asus_map_key_clear(KEY_PROG4); break;
/* Fn+Ret "Calc" symbol on N-Key keyboard */
case 0x92: asus_map_key_clear(KEY_CALC); break;
/* Fn+Left Aura mode previous on N-Key keyboard */
case 0xb2: asus_map_key_clear(KEY_PROG2); break;
/* Fn+Right Aura mode next on N-Key keyboard */
case 0xb3: asus_map_key_clear(KEY_PROG3); break;
default:
/* ASUS lazily declares 256 usages, ignore the rest,
* as some make the keyboard appear as a pointer device. */
......@@ -1126,6 +1218,9 @@ static const struct hid_device_id asus_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD),
QUIRK_USE_KBD_BACKLIGHT },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD),
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD),
QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
......
......@@ -11,6 +11,7 @@
* Copyright (c) 2017 Diego Elio Pettenò <flameeyes@flameeyes.eu>
* Copyright (c) 2017 Alex Manoussakis <amanou@gnu.org>
* Copyright (c) 2017 Tomasz Kramkowski <tk@the-tk.com>
* Copyright (c) 2020 YOSHIOKA Takuma <lo48576@hard-wi.red>
*/
/*
......@@ -29,25 +30,26 @@
* report descriptor but it does not appear that these enable software to
* control what the extra buttons map to. The only simple and straightforward
* solution seems to involve fixing up the report descriptor.
*
* Report descriptor format:
* Positions 13, 15, 21 and 31 store the button bit count, button usage minimum,
* button usage maximum and padding bit count respectively.
*/
#define MOUSE_BUTTONS_MAX 8
static void mouse_button_fixup(struct hid_device *hdev,
__u8 *rdesc, unsigned int rsize,
unsigned int button_bit_count,
unsigned int padding_bit,
unsigned int button_report_size,
unsigned int button_usage_maximum,
int nbuttons)
{
if (rsize < 32 || rdesc[12] != 0x95 ||
rdesc[14] != 0x75 || rdesc[15] != 0x01 ||
rdesc[20] != 0x29 || rdesc[30] != 0x75)
if (rsize < 32 || rdesc[button_bit_count] != 0x95 ||
rdesc[button_report_size] != 0x75 ||
rdesc[button_report_size + 1] != 0x01 ||
rdesc[button_usage_maximum] != 0x29 || rdesc[padding_bit] != 0x75)
return;
hid_info(hdev, "Fixing up Elecom mouse button count\n");
nbuttons = clamp(nbuttons, 0, MOUSE_BUTTONS_MAX);
rdesc[13] = nbuttons;
rdesc[21] = nbuttons;
rdesc[31] = MOUSE_BUTTONS_MAX - nbuttons;
rdesc[button_bit_count + 1] = nbuttons;
rdesc[button_usage_maximum + 1] = nbuttons;
rdesc[padding_bit + 1] = MOUSE_BUTTONS_MAX - nbuttons;
}
static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
......@@ -62,16 +64,40 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
rdesc[47] = 0x00;
}
break;
case USB_DEVICE_ID_ELECOM_M_XGL20DLBK:
/*
* Report descriptor format:
* 20: button bit count
* 28: padding bit count
* 22: button report size
* 14: button usage maximum
*/
mouse_button_fixup(hdev, rdesc, *rsize, 20, 28, 22, 14, 8);
break;
case USB_DEVICE_ID_ELECOM_M_XT3URBK:
case USB_DEVICE_ID_ELECOM_M_XT3DRBK:
case USB_DEVICE_ID_ELECOM_M_XT4DRBK:
mouse_button_fixup(hdev, rdesc, *rsize, 6);
/*
* Report descriptor format:
* 12: button bit count
* 30: padding bit count
* 14: button report size
* 20: button usage maximum
*/
mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 6);
break;
case USB_DEVICE_ID_ELECOM_M_DT1URBK:
case USB_DEVICE_ID_ELECOM_M_DT1DRBK:
case USB_DEVICE_ID_ELECOM_M_HT1URBK:
case USB_DEVICE_ID_ELECOM_M_HT1DRBK:
mouse_button_fixup(hdev, rdesc, *rsize, 8);
/*
* Report descriptor format:
* 12: button bit count
* 30: padding bit count
* 14: button report size
* 20: button usage maximum
*/
mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 8);
break;
}
return rdesc;
......@@ -79,6 +105,7 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
static const struct hid_device_id elecom_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) },
......
......@@ -190,6 +190,7 @@
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822
#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866
#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
#define USB_VENDOR_ID_ATEN 0x0557
......@@ -359,6 +360,7 @@
#define USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR 0x1803
#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE1 0x1843
#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE2 0x1844
#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE3 0x1846
#define USB_VENDOR_ID_DWAV 0x0eef
#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
......@@ -390,6 +392,7 @@
#define USB_VENDOR_ID_ELECOM 0x056e
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
#define USB_DEVICE_ID_ELECOM_M_XGL20DLBK 0x00e6
#define USB_DEVICE_ID_ELECOM_M_XT3URBK 0x00fb
#define USB_DEVICE_ID_ELECOM_M_XT3DRBK 0x00fc
#define USB_DEVICE_ID_ELECOM_M_XT4DRBK 0x00fd
......@@ -1074,6 +1077,9 @@
#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002
#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000
#define USB_VENDOR_ID_SONY_GHLIVE 0x12ba
#define USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE 0x074b
#define USB_VENDOR_ID_SINO_LITE 0x1345
#define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008
......@@ -1133,6 +1139,7 @@
#define USB_DEVICE_ID_SYNAPTICS_DELL_K12A 0x2819
#define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5_012 0x2968
#define USB_DEVICE_ID_SYNAPTICS_TP_V103 0x5710
#define USB_DEVICE_ID_SYNAPTICS_ACER_ONE_S1002 0x73f4
#define USB_DEVICE_ID_SYNAPTICS_ACER_ONE_S1003 0x73f5
#define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5 0x81a7
......
......@@ -537,9 +537,12 @@ static void hidinput_update_battery(struct hid_device *dev, int value)
capacity = hidinput_scale_battery_capacity(dev, value);
if (dev->battery_status != HID_BATTERY_REPORTED ||
capacity != dev->battery_capacity) {
capacity != dev->battery_capacity ||
ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) {
dev->battery_capacity = capacity;
dev->battery_status = HID_BATTERY_REPORTED;
dev->battery_ratelimit_time =
ktime_add_ms(ktime_get_coarse(), 30 * 1000);
power_supply_changed(dev->battery);
}
}
......@@ -746,6 +749,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
field->flags |= HID_MAIN_ITEM_RELATIVE;
break;
}
goto unknown;
default: goto unknown;
}
......
......@@ -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;
......@@ -101,6 +107,11 @@ static const struct hid_device_id ite_devices[] = {
USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5_012),
.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_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) },
......
......@@ -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 },
......
......@@ -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);
......
......@@ -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) },
......
This diff is collapsed.
......@@ -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;
......
......@@ -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)
......
......@@ -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 */
};
......
......@@ -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) {
......
......@@ -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 */
......
......@@ -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, \
}
......
......@@ -494,7 +494,7 @@ struct hid_report_enum {
};
#define HID_MIN_BUFFER_SIZE 64 /* make sure there is at least a packet size of space */
#define HID_MAX_BUFFER_SIZE 8192 /* 8kb */
#define HID_MAX_BUFFER_SIZE 16384 /* 16kb */
#define HID_CONTROL_FIFO_SIZE 256 /* to init devices with >100 reports */
#define HID_OUTPUT_FIFO_SIZE 64
......@@ -585,6 +585,7 @@ struct hid_device { /* device report descriptor */
__s32 battery_report_id;
enum hid_battery_status battery_status;
bool battery_avoid_query;
ktime_t battery_ratelimit_time;
#endif
unsigned long status; /* see STAT flags above */
......
......@@ -40,6 +40,12 @@ struct hidraw_devinfo {
#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
#define HIDIOCGRAWUNIQ(len) _IOC(_IOC_READ, 'H', 0x08, len)
/* The first byte of SINPUT and GINPUT is the report number */
#define HIDIOCSINPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x09, len)
#define HIDIOCGINPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0A, len)
/* The first byte of SOUTPUT and GOUTPUT is the report number */
#define HIDIOCSOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0B, len)
#define HIDIOCGOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0C, len)
#define HIDRAW_FIRST_MINOR 0
#define HIDRAW_MAX_DEVICES 64
......
......@@ -128,7 +128,7 @@ int main(int argc, char **argv)
perror("HIDIOCGFEATURE");
} else {
printf("ioctl HIDIOCGFEATURE returned: %d\n", res);
printf("Report data (not containing the report number):\n\t");
printf("Report data:\n\t");
for (i = 0; i < res; i++)
printf("%hhx ", buf[i]);
puts("\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment