Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

Pull HID updates from Jiri Kosina:

 - Fix in i2c-hid driver for Elan touchpad quirk regression (Jim
   Broadus)

 - Quirk preventing ASUS Claymore from accidentally suspending whole
   system (Luke D. Jones)

 - Updates to the existing FW reporting mechanism, MP2 FW status checks,
   adding proper power management support for amd-sfh (Basavaraj
   Natikar)

 - Regression fix for an issue in HID core that got uncovered by recent
   USB core cleanup leading to issues when transfer_buffer_length is not
   in line with wLength (Alan Stern)

 - Memory leak fix in USB HID core (Anirudh Rayabharam)

 - Improvement of stylus battery reporting (Dmitry Torokhov)

 - Power management improvement for Goodix driver (Douglas Anderson)

 - High-resolution scroll support for Magicmouse devices (José Expósito)

 - Support for GHLive PS4 dongles (Daniel Nguyen)

 - Support proper EV_MSC emissions to hid-apple (Vincent Lefevre)

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (31 commits)
  HID: usbhid: Simplify code in hid_submit_ctrl()
  HID: usbhid: Fix warning caused by 0-length input reports
  HID: usbhid: Fix flood of "control queue full" messages
  HID: sony: Fix more ShanWan clone gamepads to not rumble when plugged in.
  HID: sony: support for the ghlive ps4 dongles
  HID: thrustmaster: clean up Makefile and adapt quirks
  HID: i2c-hid: Fix Elan touchpad regression
  HID: asus: Prevent Claymore sending suspend event
  HID: amd_sfh: Add dyndbg prints for debugging
  HID: amd_sfh: Add support for PM suspend and resume
  HID: amd_sfh: Move hid probe after sensor is enabled
  HID: amd_sfh: Add command response to check command status
  HID: amd_sfh: Fix period data field to enable sensor
  HID: logitech-hidpp: battery: provide CAPACITY property for newer devices
  HID: thrustmaster: Fix memory leak in thrustmaster_interrupts()
  HID: thrustmaster: Fix memory leak in remove
  HID: thrustmaster: Fix memory leaks in probe
  HID: elo: update the reference count of the usb device structure
  HID: logitech-hidpp: Use 'atomic_inc_return' instead of hand-writing it
  HID: apple: Add missing scan code event for keys handled by hid-apple
  ...
This commit is contained in:
Linus Torvalds 2021-09-02 14:30:46 -07:00
commit 83ec916974
22 changed files with 564 additions and 96 deletions

View File

@ -259,10 +259,11 @@ config HID_PRODIKEYS
and some additional multimedia keys.
config HID_CMEDIA
tristate "CMedia CM6533 HID audio jack controls"
tristate "CMedia audio chips"
depends on HID
help
Support for CMedia CM6533 HID audio jack controls.
Support for CMedia CM6533 HID audio jack controls
and HS100B mute buttons.
config HID_CP2112
tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
@ -952,7 +953,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
* Guitar Hero Live PS3, Wii U and PS4 guitar dongles
* Guitar Hero PS3 and PC guitar dongles
config SONY_FF

View File

@ -115,7 +115,6 @@ obj-$(CONFIG_HID_STEELSERIES) += hid-steelseries.o
obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o
obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o
obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o hid-thrustmaster.o
obj-$(CONFIG_HID_TMINIT) += hid-tminit.o
obj-$(CONFIG_HID_TIVO) += hid-tivo.o
obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o

View File

@ -17,7 +17,6 @@
#include "amd_sfh_pcie.h"
#include "amd_sfh_hid.h"
#define AMD_SFH_IDLE_LOOP 200
struct request_list {
struct hid_device *hid;
@ -123,14 +122,24 @@ static void amd_sfh_work_buffer(struct work_struct *work)
int i;
for (i = 0; i < cli_data->num_hid_devices; i++) {
report_size = get_input_report(i, cli_data->sensor_idx[i], cli_data->report_id[i],
in_data);
hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,
in_data->input_report[i], report_size, 0);
if (cli_data->sensor_sts[i] == SENSOR_ENABLED) {
report_size = get_input_report
(i, cli_data->sensor_idx[i], cli_data->report_id[i], in_data);
hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,
in_data->input_report[i], report_size, 0);
}
}
schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
}
u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
{
if (mp2->mp2_ops->response)
sensor_sts = mp2->mp2_ops->response(mp2, sid, sensor_sts);
return sensor_sts;
}
int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
{
struct amd_input_data *in_data = &privdata->in_data;
@ -139,8 +148,8 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
struct device *dev;
u32 feature_report_size;
u32 input_report_size;
int rc, i, status;
u8 cl_idx;
int rc, i;
dev = &privdata->pdev->dev;
@ -155,7 +164,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
in_data->sensor_virt_addr[i] = dma_alloc_coherent(dev, sizeof(int) * 8,
&cl_data->sensor_dma_addr[i],
GFP_KERNEL);
cl_data->sensor_sts[i] = 0;
cl_data->sensor_sts[i] = SENSOR_DISABLED;
cl_data->sensor_requested_cnt[i] = 0;
cl_data->cur_hid_dev = i;
cl_idx = cl_data->sensor_idx[i];
@ -184,7 +193,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
rc = -ENOMEM;
goto cleanup;
}
info.period = msecs_to_jiffies(AMD_SFH_IDLE_LOOP);
info.period = AMD_SFH_IDLE_LOOP;
info.sensor_idx = cl_idx;
info.dma_address = cl_data->sensor_dma_addr[i];
@ -195,13 +204,27 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
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;
privdata->mp2_ops->start(privdata, info);
cl_data->sensor_sts[i] = 1;
status = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], SENSOR_ENABLED);
if (status == SENSOR_ENABLED) {
cl_data->sensor_sts[i] = SENSOR_ENABLED;
rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data);
if (rc) {
privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
status = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);
if (status != SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(dev, "sid 0x%x status 0x%x\n",
cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
goto cleanup;
}
}
dev_dbg(dev, "sid 0x%x status 0x%x\n",
cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
}
schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
return 0;
@ -224,10 +247,19 @@ int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)
{
struct amdtp_cl_data *cl_data = privdata->cl_data;
struct amd_input_data *in_data = cl_data->in_data;
int i;
int i, status;
for (i = 0; i < cl_data->num_hid_devices; i++)
privdata->mp2_ops->stop(privdata, i);
for (i = 0; i < cl_data->num_hid_devices; i++) {
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
status = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);
if (status != SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x status 0x%x\n",
cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
}
}
cancel_delayed_work_sync(&cl_data->work);
cancel_delayed_work_sync(&cl_data->work_buffer);

View File

@ -13,6 +13,7 @@
#include <linux/dmi.h>
#include <linux/interrupt.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/slab.h>
@ -31,6 +32,20 @@ static int sensor_mask_override = -1;
module_param_named(sensor_mask, sensor_mask_override, int, 0444);
MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask");
static int amd_sfh_wait_response_v2(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
{
union cmd_response cmd_resp;
/* Get response with status within a max of 800 ms timeout */
if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp,
(cmd_resp.response_v2.response == sensor_sts &&
cmd_resp.response_v2.status == 0 && (sid == 0xff ||
cmd_resp.response_v2.sensor_id == sid)), 500, 800000))
return cmd_resp.response_v2.response;
return SENSOR_DISABLED;
}
static void amd_start_sensor_v2(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
{
union sfh_cmd_base cmd_base;
@ -183,6 +198,7 @@ static const struct amd_mp2_ops amd_sfh_ops_v2 = {
.start = amd_start_sensor_v2,
.stop = amd_stop_sensor_v2,
.stop_all = amd_stop_all_sensor_v2,
.response = amd_sfh_wait_response_v2,
};
static const struct amd_mp2_ops amd_sfh_ops = {
@ -248,6 +264,58 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
return amd_sfh_hid_client_init(privdata);
}
static int __maybe_unused amd_mp2_pci_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev);
struct amdtp_cl_data *cl_data = mp2->cl_data;
struct amd_mp2_sensor_info info;
int i, status;
for (i = 0; i < cl_data->num_hid_devices; i++) {
if (cl_data->sensor_sts[i] == SENSOR_DISABLED) {
info.period = AMD_SFH_IDLE_LOOP;
info.sensor_idx = cl_data->sensor_idx[i];
info.dma_address = cl_data->sensor_dma_addr[i];
mp2->mp2_ops->start(mp2, info);
status = amd_sfh_wait_for_response
(mp2, cl_data->sensor_idx[i], SENSOR_ENABLED);
if (status == SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_ENABLED;
dev_dbg(dev, "resume sid 0x%x status 0x%x\n",
cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
}
}
return 0;
}
static int __maybe_unused amd_mp2_pci_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev);
struct amdtp_cl_data *cl_data = mp2->cl_data;
int i, status;
for (i = 0; i < cl_data->num_hid_devices; i++) {
if (cl_data->sensor_idx[i] != HPD_IDX &&
cl_data->sensor_sts[i] == SENSOR_ENABLED) {
mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);
status = amd_sfh_wait_for_response
(mp2, cl_data->sensor_idx[i], SENSOR_DISABLED);
if (status != SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(dev, "suspend sid 0x%x status 0x%x\n",
cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
}
}
return 0;
}
static SIMPLE_DEV_PM_OPS(amd_mp2_pm_ops, amd_mp2_pci_suspend,
amd_mp2_pci_resume);
static const struct pci_device_id amd_mp2_pci_tbl[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) },
{ }
@ -258,6 +326,7 @@ static struct pci_driver amd_mp2_pci_driver = {
.name = DRIVER_NAME,
.id_table = amd_mp2_pci_tbl,
.probe = amd_mp2_pci_probe,
.driver.pm = &amd_mp2_pm_ops,
};
module_pci_driver(amd_mp2_pci_driver);

View File

@ -24,14 +24,20 @@
#define AMD_C2P_MSG2 0x10508
#define AMD_C2P_MSG(regno) (0x10500 + ((regno) * 4))
#define AMD_P2C_MSG(regno) (0x10680 + ((regno) * 4))
/* MP2 P2C Message Registers */
#define AMD_P2C_MSG3 0x1068C /* Supported Sensors info */
#define V2_STATUS 0x2
#define SENSOR_ENABLED 4
#define SENSOR_DISABLED 5
#define HPD_IDX 16
#define AMD_SFH_IDLE_LOOP 200
/* SFH Command register */
union sfh_cmd_base {
u32 ul;
@ -51,6 +57,19 @@ union sfh_cmd_base {
} cmd_v2;
};
union cmd_response {
u32 resp;
struct {
u32 status : 2;
u32 out_in_c2p : 1;
u32 rsvd1 : 1;
u32 response : 4;
u32 sub_cmd : 8;
u32 sensor_id : 6;
u32 rsvd2 : 10;
} response_v2;
};
union sfh_cmd_param {
u32 ul;
struct {
@ -112,10 +131,14 @@ 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);
u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts);
void amd_mp2_suspend(struct amd_mp2_dev *mp2);
void amd_mp2_resume(struct amd_mp2_dev *mp2);
struct amd_mp2_ops {
void (*start)(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info);
void (*stop)(struct amd_mp2_dev *privdata, u16 sensor_idx);
void (*stop_all)(struct amd_mp2_dev *privdata);
int (*response)(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts);
};
#endif

View File

@ -187,6 +187,15 @@ static const struct apple_key_translation *apple_find_translation(
return NULL;
}
static void input_event_with_scancode(struct input_dev *input,
__u8 type, __u16 code, unsigned int hid, __s32 value)
{
if (type == EV_KEY &&
(!test_bit(code, input->key)) == value)
input_event(input, EV_MSC, MSC_SCAN, hid);
input_event(input, type, code, value);
}
static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
struct hid_usage *usage, __s32 value)
{
@ -199,7 +208,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
if (usage->code == fn_keycode) {
asc->fn_on = !!value;
input_event(input, usage->type, KEY_FN, value);
input_event_with_scancode(input, usage->type, KEY_FN,
usage->hid, value);
return 1;
}
@ -240,7 +250,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
code = do_translate ? trans->to : trans->from;
}
input_event(input, usage->type, code, value);
input_event_with_scancode(input, usage->type, code,
usage->hid, value);
return 1;
}
@ -258,8 +269,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
clear_bit(usage->code,
asc->pressed_numlock);
input_event(input, usage->type, trans->to,
value);
input_event_with_scancode(input, usage->type,
trans->to, usage->hid, value);
}
return 1;
@ -270,7 +281,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
if (hid->country == HID_COUNTRY_INTERNATIONAL_ISO) {
trans = apple_find_translation(apple_iso_keyboard, usage->code);
if (trans) {
input_event(input, usage->type, trans->to, value);
input_event_with_scancode(input, usage->type,
trans->to, usage->hid, value);
return 1;
}
}
@ -279,7 +291,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
if (swap_opt_cmd) {
trans = apple_find_translation(swapped_option_cmd_keys, usage->code);
if (trans) {
input_event(input, usage->type, trans->to, value);
input_event_with_scancode(input, usage->type,
trans->to, usage->hid, value);
return 1;
}
}
@ -287,7 +300,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
if (swap_fn_leftctrl) {
trans = apple_find_translation(swapped_fn_leftctrl_keys, usage->code);
if (trans) {
input_event(input, usage->type, trans->to, value);
input_event_with_scancode(input, usage->type,
trans->to, usage->hid, value);
return 1;
}
}
@ -306,8 +320,8 @@ static int apple_event(struct hid_device *hdev, struct hid_field *field,
if ((asc->quirks & APPLE_INVERT_HWHEEL) &&
usage->code == REL_HWHEEL) {
input_event(field->hidinput->input, usage->type, usage->code,
-value);
input_event_with_scancode(field->hidinput->input, usage->type,
usage->code, usage->hid, -value);
return 1;
}

View File

@ -82,6 +82,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define QUIRK_T90CHI BIT(9)
#define QUIRK_MEDION_E1239T BIT(10)
#define QUIRK_ROG_NKEY_KEYBOARD BIT(11)
#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12)
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
QUIRK_NO_INIT_REPORTS | \
@ -366,6 +367,17 @@ static int asus_raw_event(struct hid_device *hdev,
}
if (drvdata->quirks & QUIRK_ROG_CLAYMORE_II_KEYBOARD) {
/*
* CLAYMORE II keyboard sends this packet when it goes to sleep
* this causes the whole system to go into suspend.
*/
if(size == 2 && data[0] == 0x02 && data[1] == 0x00) {
return -1;
}
}
return 0;
}
@ -1225,6 +1237,9 @@ static const struct hid_device_id asus_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2),
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD),
QUIRK_ROG_CLAYMORE_II_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD),
QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },

View File

@ -1,8 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* HID driver for CMedia CM6533 audio jack controls
* and HS100B mute buttons
*
* Copyright (C) 2015 Ben Chen <ben_chen@bizlinktech.com>
* Copyright (C) 2021 Thomas Weißschuh <linux@weissschuh.net>
*/
#include <linux/device.h>
@ -11,13 +13,53 @@
#include "hid-ids.h"
MODULE_AUTHOR("Ben Chen");
MODULE_DESCRIPTION("CM6533 HID jack controls");
MODULE_AUTHOR("Thomas Weißschuh");
MODULE_DESCRIPTION("CM6533 HID jack controls and HS100B mute button");
MODULE_LICENSE("GPL");
#define CM6533_JD_TYPE_COUNT 1
#define CM6533_JD_RAWEV_LEN 16
#define CM6533_JD_SFX_OFFSET 8
#define HS100B_RDESC_ORIG_SIZE 60
/* Fixed report descriptor of HS-100B audio chip
* Bit 4 is an abolute Microphone mute usage instead of being unassigned.
*/
static __u8 hs100b_rdesc_fixed[] = {
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x09, 0xE9, /* Usage (Volume Inc), */
0x09, 0xEA, /* Usage (Volume Dec), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x02, /* Input (Variable), */
0x09, 0xE2, /* Usage (Mute), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x06, /* Input (Variable, Relative), */
0x05, 0x0B, /* Usage Page (Telephony), */
0x09, 0x2F, /* Usage (2Fh), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x20, /* Usage (20h), */
0x81, 0x06, /* Input (Variable, Relative), */
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x00, /* Usage (00h), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x09, 0x00, /* Usage (00h), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x00, /* Usage (00h), */
0x95, 0x04, /* Report Count (4), */
0x91, 0x02, /* Output (Variable), */
0xC0 /* End Collection */
};
/*
*
*CM6533 audio jack HID raw events:
@ -156,5 +198,49 @@ static struct hid_driver cmhid_driver = {
.remove = cmhid_remove,
.input_mapping = cmhid_input_mapping,
};
module_hid_driver(cmhid_driver);
static __u8 *cmhid_hs100b_report_fixup(struct hid_device *hid, __u8 *rdesc,
unsigned int *rsize)
{
if (*rsize == HS100B_RDESC_ORIG_SIZE) {
hid_info(hid, "Fixing CMedia HS-100B report descriptor\n");
rdesc = hs100b_rdesc_fixed;
*rsize = sizeof(hs100b_rdesc_fixed);
}
return rdesc;
}
static const struct hid_device_id cmhid_hs100b_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CMEDIA_HS100B) },
{ }
};
MODULE_DEVICE_TABLE(hid, cmhid_hs100b_devices);
static struct hid_driver cmhid_hs100b_driver = {
.name = "cmedia_hs100b",
.id_table = cmhid_hs100b_devices,
.report_fixup = cmhid_hs100b_report_fixup,
};
static int cmedia_init(void)
{
int ret;
ret = hid_register_driver(&cmhid_driver);
if (ret)
return ret;
ret = hid_register_driver(&cmhid_hs100b_driver);
if (ret)
hid_unregister_driver(&cmhid_driver);
return ret;
}
module_init(cmedia_init);
static void cmedia_exit(void)
{
hid_unregister_driver(&cmhid_driver);
hid_unregister_driver(&cmhid_hs100b_driver);
}
module_exit(cmedia_exit);

View File

@ -228,13 +228,15 @@ static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct elo_priv *priv;
int ret;
struct usb_device *udev;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
INIT_DELAYED_WORK(&priv->work, elo_work);
priv->usbdev = interface_to_usbdev(to_usb_interface(hdev->dev.parent));
udev = interface_to_usbdev(to_usb_interface(hdev->dev.parent));
priv->usbdev = usb_get_dev(udev);
hid_set_drvdata(hdev, priv);
@ -265,6 +267,8 @@ static void elo_remove(struct hid_device *hdev)
{
struct elo_priv *priv = hid_get_drvdata(hdev);
usb_put_dev(priv->usbdev);
hid_hw_stop(hdev);
cancel_delayed_work_sync(&priv->work);
kfree(priv);

View File

@ -41,9 +41,6 @@
#define USB_VENDOR_ID_ACTIONSTAR 0x2101
#define USB_DEVICE_ID_ACTIONSTAR_1011 0x1011
#define USB_VENDOR_ID_ACTIVISION 0x1430
#define USB_DEVICE_ID_ACTIVISION_GUITAR_DONGLE 0x474c
#define USB_VENDOR_ID_ADS_TECH 0x06e1
#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X 0xa155
@ -197,6 +194,7 @@
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822
#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866
#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2 0x19b6
#define USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD 0x196b
#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
#define USB_VENDOR_ID_ATEN 0x0557
@ -292,6 +290,7 @@
#define USB_VENDOR_ID_CMEDIA 0x0d8c
#define USB_DEVICE_ID_CM109 0x000e
#define USB_DEVICE_ID_CMEDIA_HS100B 0x0014
#define USB_DEVICE_ID_CM6533 0x0022
#define USB_VENDOR_ID_CODEMERCS 0x07c0
@ -1018,6 +1017,10 @@
#define USB_VENDOR_ID_REALTEK 0x0bda
#define USB_DEVICE_ID_REALTEK_READER 0x0152
#define USB_VENDOR_ID_REDOCTANE 0x1430
#define USB_DEVICE_ID_REDOCTANE_GUITAR_DONGLE 0x474c
#define USB_DEVICE_ID_REDOCTANE_PS4_GHLIVE_DONGLE 0x07bb
#define USB_VENDOR_ID_RETROUSB 0xf000
#define USB_DEVICE_ID_RETROUSB_SNES_RETROPAD 0x0003
#define USB_DEVICE_ID_RETROUSB_SNES_RETROPORT 0x00f1

View File

@ -419,8 +419,6 @@ static int hidinput_get_battery_property(struct power_supply *psy,
if (dev->battery_status == HID_BATTERY_UNKNOWN)
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
else if (dev->battery_capacity == 100)
val->intval = POWER_SUPPLY_STATUS_FULL;
else
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
break;

View File

@ -1331,6 +1331,43 @@ static int hidpp20_battery_get_battery_voltage(struct hidpp_device *hidpp,
return 0;
}
static int hidpp20_map_battery_capacity(struct hid_device *hid_dev, int voltage)
{
/* NB: This voltage curve doesn't necessarily map perfectly to all
* devices that implement the BATTERY_VOLTAGE feature. This is because
* there are a few devices that use different battery technology.
*/
static const int voltages[] = {
4186, 4156, 4143, 4133, 4122, 4113, 4103, 4094, 4086, 4075,
4067, 4059, 4051, 4043, 4035, 4027, 4019, 4011, 4003, 3997,
3989, 3983, 3976, 3969, 3961, 3955, 3949, 3942, 3935, 3929,
3922, 3916, 3909, 3902, 3896, 3890, 3883, 3877, 3870, 3865,
3859, 3853, 3848, 3842, 3837, 3833, 3828, 3824, 3819, 3815,
3811, 3808, 3804, 3800, 3797, 3793, 3790, 3787, 3784, 3781,
3778, 3775, 3772, 3770, 3767, 3764, 3762, 3759, 3757, 3754,
3751, 3748, 3744, 3741, 3737, 3734, 3730, 3726, 3724, 3720,
3717, 3714, 3710, 3706, 3702, 3697, 3693, 3688, 3683, 3677,
3671, 3666, 3662, 3658, 3654, 3646, 3633, 3612, 3579, 3537
};
int i;
BUILD_BUG_ON(ARRAY_SIZE(voltages) != 100);
if (unlikely(voltage < 3500 || voltage >= 5000))
hid_warn_once(hid_dev,
"%s: possibly using the wrong voltage curve\n",
__func__);
for (i = 0; i < ARRAY_SIZE(voltages); i++) {
if (voltage >= voltages[i])
return ARRAY_SIZE(voltages) - i;
}
return 0;
}
static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp)
{
u8 feature_type;
@ -1354,6 +1391,8 @@ static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp)
hidpp->battery.status = status;
hidpp->battery.voltage = voltage;
hidpp->battery.capacity = hidpp20_map_battery_capacity(hidpp->hid_dev,
voltage);
hidpp->battery.level = level;
hidpp->battery.charge_type = charge_type;
hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
@ -1378,6 +1417,8 @@ static int hidpp20_battery_voltage_event(struct hidpp_device *hidpp,
if (voltage != hidpp->battery.voltage || status != hidpp->battery.status) {
hidpp->battery.voltage = voltage;
hidpp->battery.capacity = hidpp20_map_battery_capacity(hidpp->hid_dev,
voltage);
hidpp->battery.status = status;
hidpp->battery.level = level;
hidpp->battery.charge_type = charge_type;
@ -2240,11 +2281,10 @@ static int hidpp_ff_queue_work(struct hidpp_ff_private_data *data, int effect_id
wd->size = size;
memcpy(wd->params, params, size);
atomic_inc(&data->workqueue_size);
s = atomic_inc_return(&data->workqueue_size);
queue_work(data->wq, &wd->work);
/* warn about excessive queue size */
s = atomic_read(&data->workqueue_size);
if (s >= 20 && s % 20 == 0)
hid_warn(data->hidpp->hid_dev, "Force feedback command queue contains %d commands, causing substantial delays!", s);
@ -3717,7 +3757,8 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 3;
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE ||
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_PERCENTAGE)
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_PERCENTAGE ||
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
battery_props[num_battery_props++] =
POWER_SUPPLY_PROP_CAPACITY;

View File

@ -68,6 +68,10 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
#define TOUCH_STATE_START 0x30
#define TOUCH_STATE_DRAG 0x40
/* Number of high-resolution events for each low-resolution detent. */
#define SCROLL_HR_STEPS 10
#define SCROLL_HR_MULT (120 / SCROLL_HR_STEPS)
#define SCROLL_HR_THRESHOLD 90 /* units */
#define SCROLL_ACCEL_DEFAULT 7
/* Touch surface information. Dimension is in hundredths of a mm, min and max
@ -126,7 +130,11 @@ struct magicmouse_sc {
short y;
short scroll_x;
short scroll_y;
short scroll_x_hr;
short scroll_y_hr;
u8 size;
bool scroll_x_active;
bool scroll_y_active;
} touches[16];
int tracking_ids[16];
@ -248,12 +256,20 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
unsigned long now = jiffies;
int step_x = msc->touches[id].scroll_x - x;
int step_y = msc->touches[id].scroll_y - y;
int step_hr = ((64 - (int)scroll_speed) * msc->scroll_accel) /
SCROLL_HR_STEPS;
int step_x_hr = msc->touches[id].scroll_x_hr - x;
int step_y_hr = msc->touches[id].scroll_y_hr - y;
/* Calculate and apply the scroll motion. */
switch (state) {
case TOUCH_STATE_START:
msc->touches[id].scroll_x = x;
msc->touches[id].scroll_y = y;
msc->touches[id].scroll_x_hr = x;
msc->touches[id].scroll_y_hr = y;
msc->touches[id].scroll_x_active = false;
msc->touches[id].scroll_y_active = false;
/* Reset acceleration after half a second. */
if (scroll_acceleration && time_before(now,
@ -280,6 +296,40 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
msc->scroll_jiffies = now;
input_report_rel(input, REL_WHEEL, step_y);
}
if (!msc->touches[id].scroll_x_active &&
abs(step_x_hr) > SCROLL_HR_THRESHOLD) {
msc->touches[id].scroll_x_active = true;
msc->touches[id].scroll_x_hr = x;
step_x_hr = 0;
}
step_x_hr /= step_hr;
if (step_x_hr != 0 &&
msc->touches[id].scroll_x_active) {
msc->touches[id].scroll_x_hr -= step_x_hr *
step_hr;
input_report_rel(input,
REL_HWHEEL_HI_RES,
-step_x_hr * SCROLL_HR_MULT);
}
if (!msc->touches[id].scroll_y_active &&
abs(step_y_hr) > SCROLL_HR_THRESHOLD) {
msc->touches[id].scroll_y_active = true;
msc->touches[id].scroll_y_hr = y;
step_y_hr = 0;
}
step_y_hr /= step_hr;
if (step_y_hr != 0 &&
msc->touches[id].scroll_y_active) {
msc->touches[id].scroll_y_hr -= step_y_hr *
step_hr;
input_report_rel(input,
REL_WHEEL_HI_RES,
step_y_hr * SCROLL_HR_MULT);
}
break;
}
}
@ -481,6 +531,8 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
if (emulate_scroll_wheel) {
__set_bit(REL_WHEEL, input->relbit);
__set_bit(REL_HWHEEL, input->relbit);
__set_bit(REL_WHEEL_HI_RES, input->relbit);
__set_bit(REL_HWHEEL_HI_RES, input->relbit);
}
} else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
/* setting the device name to ensure the same driver settings

View File

@ -662,8 +662,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb653) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65a) },
#endif
#if IS_ENABLED(CONFIG_HID_TMINIT)
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65d) },
#endif
#if IS_ENABLED(CONFIG_HID_TIVO)

View File

@ -11,8 +11,9 @@
* 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>
* Copyright (c) 2020-2021 Pascal Giard <pascal.giard@etsmtl.ca>
* Copyright (c) 2020 Sanjay Govind <sanjay.govind9@gmail.com>
* Copyright (c) 2021 Daniel Nguyen <daniel.nguyen.1@ens.etsmtl.ca>
*/
/*
@ -62,6 +63,7 @@
#define SHANWAN_GAMEPAD BIT(16)
#define GH_GUITAR_CONTROLLER BIT(17)
#define GHL_GUITAR_PS3WIIU BIT(18)
#define GHL_GUITAR_PS4 BIT(19)
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
@ -85,18 +87,27 @@
#define NSG_MRXU_MAX_X 1667
#define NSG_MRXU_MAX_Y 1868
#define GHL_GUITAR_POKE_INTERVAL 10 /* In seconds */
/* The PS3/Wii U dongles require a poke every 10 seconds, but the PS4
* requires one every 8 seconds. Using 8 seconds for all for simplicity.
*/
#define GHL_GUITAR_POKE_INTERVAL 8 /* In seconds */
#define GUITAR_TILT_USAGE 44
/* Magic value and data taken from GHLtarUtility:
/* Magic 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
};
/* Magic data for the PS4 dongles sniffed with a USB protocol
* analyzer.
*/
static const char ghl_ps4_magic_data[] = {
0x30, 0x02, 0x08, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00
};
/* PS/3 Motion controller */
static u8 motion_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
@ -642,14 +653,14 @@ static void ghl_magic_poke(struct timer_list *t)
hid_err(sc->hdev, "usb_submit_urb failed: %d", ret);
}
static int ghl_init_urb(struct sony_sc *sc, struct usb_device *usbdev)
static int ghl_init_urb(struct sony_sc *sc, struct usb_device *usbdev,
const char ghl_magic_data[], u16 poke_size)
{
struct usb_ctrlrequest *cr;
u16 poke_size;
u8 *databuf;
unsigned int pipe;
u16 ghl_magic_value = (((HID_OUTPUT_REPORT + 1) << 8) | ghl_magic_data[0]);
poke_size = ARRAY_SIZE(ghl_ps3wiiu_magic_data);
pipe = usb_sndctrlpipe(usbdev, 0);
cr = devm_kzalloc(&sc->hdev->dev, sizeof(*cr), GFP_ATOMIC);
@ -663,10 +674,10 @@ static int ghl_init_urb(struct sony_sc *sc, struct usb_device *usbdev)
cr->bRequestType =
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT;
cr->bRequest = USB_REQ_SET_CONFIGURATION;
cr->wValue = cpu_to_le16(ghl_ps3wiiu_magic_value);
cr->wValue = cpu_to_le16(ghl_magic_value);
cr->wIndex = 0;
cr->wLength = cpu_to_le16(poke_size);
memcpy(databuf, ghl_ps3wiiu_magic_data, poke_size);
memcpy(databuf, ghl_magic_data, poke_size);
usb_fill_control_urb(
sc->ghl_urb, usbdev, pipe,
(unsigned char *) cr, databuf, poke_size,
@ -2974,7 +2985,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (!strcmp(hdev->name, "FutureMax Dance Mat"))
quirks |= FUTUREMAX_DANCE_MAT;
if (!strcmp(hdev->name, "SHANWAN PS3 GamePad"))
if (!strcmp(hdev->name, "SHANWAN PS3 GamePad") ||
!strcmp(hdev->name, "ShanWan PS(R) Ga`epad"))
quirks |= SHANWAN_GAMEPAD;
sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL);
@ -3030,11 +3042,17 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
return -ENODEV;
}
if (sc->quirks & GHL_GUITAR_PS3WIIU) {
if (sc->quirks & (GHL_GUITAR_PS3WIIU | GHL_GUITAR_PS4)) {
sc->ghl_urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!sc->ghl_urb)
return -ENOMEM;
ret = ghl_init_urb(sc, usbdev);
if (sc->quirks & GHL_GUITAR_PS3WIIU)
ret = ghl_init_urb(sc, usbdev, ghl_ps3wiiu_magic_data,
ARRAY_SIZE(ghl_ps3wiiu_magic_data));
else if (sc->quirks & GHL_GUITAR_PS4)
ret = ghl_init_urb(sc, usbdev, ghl_ps4_magic_data,
ARRAY_SIZE(ghl_ps4_magic_data));
if (ret) {
hid_err(hdev, "error preparing URB\n");
return ret;
@ -3052,7 +3070,7 @@ static void sony_remove(struct hid_device *hdev)
{
struct sony_sc *sc = hid_get_drvdata(hdev);
if (sc->quirks & GHL_GUITAR_PS3WIIU) {
if (sc->quirks & (GHL_GUITAR_PS3WIIU | GHL_GUITAR_PS4)) {
del_timer_sync(&sc->ghl_poke_timer);
usb_free_urb(sc->ghl_urb);
}
@ -3172,11 +3190,14 @@ static const struct hid_device_id sony_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE),
.driver_data = GHL_GUITAR_PS3WIIU | GH_GUITAR_CONTROLLER },
/* Guitar Hero PC Guitar Dongle */
{ HID_USB_DEVICE(USB_VENDOR_ID_ACTIVISION, USB_DEVICE_ID_ACTIVISION_GUITAR_DONGLE),
{ HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_GUITAR_DONGLE),
.driver_data = GH_GUITAR_CONTROLLER },
/* Guitar Hero PS3 World Tour Guitar Dongle */
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GUITAR_DONGLE),
.driver_data = GH_GUITAR_CONTROLLER },
/* Guitar Hero Live PS4 guitar dongles */
{ HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_PS4_GHLIVE_DONGLE),
.driver_data = GHL_GUITAR_PS4 | GH_GUITAR_CONTROLLER },
{ }
};
MODULE_DEVICE_TABLE(hid, sony_devices);

View File

@ -173,6 +173,7 @@ static void thrustmaster_interrupts(struct hid_device *hdev)
if (ret) {
hid_err(hdev, "setup data couldn't be sent\n");
kfree(send_buf);
return;
}
}
@ -253,6 +254,7 @@ static void thrustmaster_remove(struct hid_device *hdev)
usb_kill_urb(tm_wheel->urb);
kfree(tm_wheel->change_request);
kfree(tm_wheel->response);
kfree(tm_wheel->model_request);
usb_free_urb(tm_wheel->urb);
@ -336,11 +338,14 @@ static int thrustmaster_probe(struct hid_device *hdev, const struct hid_device_i
);
ret = usb_submit_urb(tm_wheel->urb, GFP_ATOMIC);
if (ret)
if (ret) {
hid_err(hdev, "Error %d while submitting the URB. I am unable to initialize this wheel...\n", ret);
goto error6;
}
return ret;
error6: kfree(tm_wheel->change_request);
error5: kfree(tm_wheel->response);
error4: kfree(tm_wheel->model_request);
error3: usb_free_urb(tm_wheel->urb);

View File

@ -171,8 +171,6 @@ static const struct i2c_hid_quirks {
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
{ I2C_VENDOR_ID_RAYDIUM, I2C_PRODUCT_ID_RAYDIUM_3118,
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
{ USB_VENDOR_ID_ELAN, HID_ANY_ID,
I2C_HID_QUIRK_BOGUS_IRQ },
{ USB_VENDOR_ID_ALPS_JP, HID_ANY_ID,
I2C_HID_QUIRK_RESET_ON_RESUME },
{ I2C_VENDOR_ID_SYNAPTICS, I2C_PRODUCT_ID_SYNAPTICS_SYNA2393,
@ -183,7 +181,8 @@ static const struct i2c_hid_quirks {
* Sending the wakeup after reset actually break ELAN touchscreen controller
*/
{ USB_VENDOR_ID_ELAN, HID_ANY_ID,
I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET },
I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET |
I2C_HID_QUIRK_BOGUS_IRQ },
{ 0, 0 }
};

View File

@ -26,28 +26,29 @@ struct i2c_hid_of_goodix {
struct i2chid_ops ops;
struct regulator *vdd;
struct notifier_block nb;
struct mutex regulator_mutex;
struct gpio_desc *reset_gpio;
const struct goodix_i2c_hid_timing_data *timings;
};
static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
static void goodix_i2c_hid_deassert_reset(struct i2c_hid_of_goodix *ihid_goodix,
bool regulator_just_turned_on)
{
struct i2c_hid_of_goodix *ihid_goodix =
container_of(ops, struct i2c_hid_of_goodix, ops);
int ret;
ret = regulator_enable(ihid_goodix->vdd);
if (ret)
return ret;
if (ihid_goodix->timings->post_power_delay_ms)
if (regulator_just_turned_on && ihid_goodix->timings->post_power_delay_ms)
msleep(ihid_goodix->timings->post_power_delay_ms);
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 0);
if (ihid_goodix->timings->post_gpio_reset_delay_ms)
msleep(ihid_goodix->timings->post_gpio_reset_delay_ms);
}
return 0;
static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
{
struct i2c_hid_of_goodix *ihid_goodix =
container_of(ops, struct i2c_hid_of_goodix, ops);
return regulator_enable(ihid_goodix->vdd);
}
static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
@ -55,20 +56,54 @@ static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
struct i2c_hid_of_goodix *ihid_goodix =
container_of(ops, struct i2c_hid_of_goodix, ops);
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
regulator_disable(ihid_goodix->vdd);
}
static int ihid_goodix_vdd_notify(struct notifier_block *nb,
unsigned long event,
void *ignored)
{
struct i2c_hid_of_goodix *ihid_goodix =
container_of(nb, struct i2c_hid_of_goodix, nb);
int ret = NOTIFY_OK;
mutex_lock(&ihid_goodix->regulator_mutex);
switch (event) {
case REGULATOR_EVENT_PRE_DISABLE:
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
break;
case REGULATOR_EVENT_ENABLE:
goodix_i2c_hid_deassert_reset(ihid_goodix, true);
break;
case REGULATOR_EVENT_ABORT_DISABLE:
goodix_i2c_hid_deassert_reset(ihid_goodix, false);
break;
default:
ret = NOTIFY_DONE;
break;
}
mutex_unlock(&ihid_goodix->regulator_mutex);
return ret;
}
static int i2c_hid_of_goodix_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_hid_of_goodix *ihid_goodix;
int ret;
ihid_goodix = devm_kzalloc(&client->dev, sizeof(*ihid_goodix),
GFP_KERNEL);
if (!ihid_goodix)
return -ENOMEM;
mutex_init(&ihid_goodix->regulator_mutex);
ihid_goodix->ops.power_up = goodix_i2c_hid_power_up;
ihid_goodix->ops.power_down = goodix_i2c_hid_power_down;
@ -84,6 +119,37 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client,
ihid_goodix->timings = device_get_match_data(&client->dev);
/*
* We need to control the "reset" line in lockstep with the regulator
* actually turning on an off instead of just when we make the request.
* This matters if the regulator is shared with another consumer.
* - If the regulator is off then we must assert reset. The reset
* line is active low and on some boards it could cause a current
* leak if left high.
* - If the regulator is on then we don't want reset asserted for very
* long. Holding the controller in reset apparently draws extra
* power.
*/
mutex_lock(&ihid_goodix->regulator_mutex);
ihid_goodix->nb.notifier_call = ihid_goodix_vdd_notify;
ret = devm_regulator_register_notifier(ihid_goodix->vdd, &ihid_goodix->nb);
if (ret) {
mutex_unlock(&ihid_goodix->regulator_mutex);
return dev_err_probe(&client->dev, ret,
"regulator notifier request failed\n");
}
/*
* If someone else is holding the regulator on (or the regulator is
* an always-on one) we might never be told to deassert reset. Do it
* now. Here we'll assume that someone else might have _just
* barely_ turned the regulator on so we'll do the full
* "post_power_delay" just in case.
*/
if (ihid_goodix->reset_gpio && regulator_is_enabled(ihid_goodix->vdd))
goodix_i2c_hid_deassert_reset(ihid_goodix, true);
mutex_unlock(&ihid_goodix->regulator_mutex);
return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001);
}

View File

@ -377,27 +377,23 @@ static int hid_submit_ctrl(struct hid_device *hid)
len = hid_report_len(report);
if (dir == USB_DIR_OUT) {
usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
usbhid->urbctrl->transfer_buffer_length = len;
if (raw_report) {
memcpy(usbhid->ctrlbuf, raw_report, len);
kfree(raw_report);
usbhid->ctrl[usbhid->ctrltail].raw_report = NULL;
}
} else {
int maxpacket, padlen;
int maxpacket;
usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
maxpacket = usb_maxpacket(hid_to_usb_dev(hid),
usbhid->urbctrl->pipe, 0);
if (maxpacket > 0) {
padlen = DIV_ROUND_UP(len, maxpacket);
padlen *= maxpacket;
if (padlen > usbhid->bufsize)
padlen = usbhid->bufsize;
} else
padlen = 0;
usbhid->urbctrl->transfer_buffer_length = padlen;
len += (len == 0); /* Don't allow 0-length reports */
len = round_up(len, maxpacket);
if (len > usbhid->bufsize)
len = usbhid->bufsize;
}
usbhid->urbctrl->transfer_buffer_length = len;
usbhid->urbctrl->dev = hid_to_usb_dev(hid);
usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
@ -505,7 +501,7 @@ static void hid_ctrl(struct urb *urb)
if (unplug) {
usbhid->ctrltail = usbhid->ctrlhead;
} else {
} else if (usbhid->ctrlhead != usbhid->ctrltail) {
usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
if (usbhid->ctrlhead != usbhid->ctrltail &&
@ -1223,9 +1219,20 @@ static void usbhid_stop(struct hid_device *hid)
mutex_lock(&usbhid->mutex);
clear_bit(HID_STARTED, &usbhid->iofl);
spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */
set_bit(HID_DISCONNECTED, &usbhid->iofl);
while (usbhid->ctrltail != usbhid->ctrlhead) {
if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_OUT) {
kfree(usbhid->ctrl[usbhid->ctrltail].raw_report);
usbhid->ctrl[usbhid->ctrltail].raw_report = NULL;
}
usbhid->ctrltail = (usbhid->ctrltail + 1) &
(HID_CONTROL_FIFO_SIZE - 1);
}
spin_unlock_irq(&usbhid->lock);
usb_kill_urb(usbhid->urbin);
usb_kill_urb(usbhid->urbout);
usb_kill_urb(usbhid->urbctrl);

View File

@ -2287,7 +2287,13 @@ static void wacom_set_shared_values(struct wacom_wac *wacom_wac)
if (wacom_wac->has_mute_touch_switch) {
wacom_wac->shared->has_mute_touch_switch = true;
wacom_wac->shared->is_touch_on = true;
/* Hardware touch switch may be off. Wait until
* we know the switch state to decide is_touch_on.
* Softkey state should be initialized to "on" to
* match historic default.
*/
if (wacom_wac->is_soft_touch_switch)
wacom_wac->shared->is_touch_on = true;
}
if (wacom_wac->shared->has_mute_touch_switch &&
@ -2791,6 +2797,7 @@ static int wacom_probe(struct hid_device *hdev,
error);
}
wacom_wac->probe_complete = true;
return 0;
}

View File

@ -824,6 +824,13 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
return 0;
}
static inline bool touch_is_muted(struct wacom_wac *wacom_wac)
{
return wacom_wac->probe_complete &&
wacom_wac->shared->has_mute_touch_switch &&
!wacom_wac->shared->is_touch_on;
}
static inline bool report_touch_events(struct wacom_wac *wacom)
{
return (touch_arbitration ? !wacom->shared->stylus_in_proximity : 1);
@ -1525,11 +1532,8 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
int byte_per_packet = WACOM_BYTES_PER_24HDT_PACKET;
int y_offset = 2;
if (wacom->shared->has_mute_touch_switch &&
!wacom->shared->is_touch_on) {
if (!wacom->shared->touch_down)
return 0;
}
if (touch_is_muted(wacom) && !wacom->shared->touch_down)
return 0;
if (wacom->features.type == WACOM_27QHDT) {
current_num_contacts = data[63];
@ -1987,14 +1991,17 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
features->numbered_buttons++;
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
case WACOM_HID_WD_TOUCHONOFF:
case WACOM_HID_WD_MUTE_DEVICE:
/* softkey touch switch */
wacom_wac->is_soft_touch_switch = true;
fallthrough;
case WACOM_HID_WD_TOUCHONOFF:
/*
* This usage, which is used to mute touch events, comes
* from the pad packet, but is reported on the touch
* These two usages, which are used to mute touch events, come
* from the pad packet, but are reported on the touch
* interface. Because the touch interface may not have
* been created yet, we cannot call wacom_map_usage(). In
* order to process this usage when we receive it, we set
* order to process the usages when we receive them, we set
* the usage type and code directly.
*/
wacom_wac->has_mute_touch_switch = true;
@ -2533,8 +2540,7 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
bool prox = hid_data->tipswitch &&
report_touch_events(wacom_wac);
if (wacom_wac->shared->has_mute_touch_switch &&
!wacom_wac->shared->is_touch_on) {
if (touch_is_muted(wacom_wac)) {
if (!wacom_wac->shared->touch_down)
return;
prox = false;
@ -2548,8 +2554,17 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
int slot;
slot = input_mt_get_slot_by_key(input, hid_data->id);
if (slot < 0)
if (slot < 0) {
return;
} else {
struct input_mt_slot *ps = &input->mt->slots[slot];
int mt_id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
if (!prox && mt_id < 0) {
// No data to send for this slot; short-circuit
return;
}
}
input_mt_slot(input, slot);
input_mt_report_slot_state(input, MT_TOOL_FINGER, prox);
@ -2581,6 +2596,9 @@ static void wacom_wac_finger_event(struct hid_device *hdev,
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
struct wacom_features *features = &wacom->wacom_wac.features;
if (touch_is_muted(wacom_wac) && !wacom_wac->shared->touch_down)
return;
if (wacom_wac->is_invalid_bt_frame)
return;
@ -2630,6 +2648,9 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev,
struct hid_data* hid_data = &wacom_wac->hid_data;
int i;
if (touch_is_muted(wacom_wac) && !wacom_wac->shared->touch_down)
return;
wacom_wac->is_invalid_bt_frame = false;
for (i = 0; i < report->maxfield; i++) {
@ -2681,6 +2702,10 @@ static void wacom_wac_finger_report(struct hid_device *hdev,
struct input_dev *input = wacom_wac->touch_input;
unsigned touch_max = wacom_wac->features.touch_max;
/* if there was nothing to process, don't send an empty sync */
if (wacom_wac->hid_data.num_expected == 0)
return;
/* If more packets of data are expected, give us a chance to
* process them rather than immediately syncing a partial
* update.
@ -3835,6 +3860,7 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
input_dev->evbit[0] |= BIT_MASK(EV_SW);
__set_bit(SW_MUTE_DEVICE, input_dev->swbit);
wacom_wac->has_mute_touch_switch = true;
wacom_wac->is_soft_touch_switch = true;
}
fallthrough;

View File

@ -337,6 +337,7 @@ struct wacom_wac {
int tool[2];
int id[2];
__u64 serial[2];
bool probe_complete;
bool reporting_data;
struct wacom_features features;
struct wacom_shared *shared;
@ -352,6 +353,7 @@ struct wacom_wac {
int mode_value;
struct hid_data hid_data;
bool has_mute_touch_switch;
bool is_soft_touch_switch;
bool has_mode_change;
bool is_direct_mode;
bool is_invalid_bt_frame;