You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID updates from Jiri Kosina: - support for new Wacom "MobileStudio Pro" class of tablets from Jason Gerecke - Microsoft Surface 3 support from Benjamin Tissoires and Microsoft Surface 4 support from Daniel Keller - uDraw PS3 tablet support from Bastien Nocera - timeout scheduling fixes for intel-ish-hid from Even Xu - HID_QUIRK_MULTI_INPUT in order to simplify LED handling from Benjamin Tissoires - support for Sony DS4 dongle and various other fixes to Sony driver from Roderick Colenbrander - other assorted smaller fixes and device ID additions * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (63 commits) HID: fix missing irq field HID: i2c-hid: fix build HID: i2c-hid: Disable IRQ before freeing buffers HID: usbhid: fix improper return value HID: wacom: generic: Don't sync input on empty input packets HID: wacom: generic: Pad supports more than buttons HID: wacom: generic: Send data only when the interface is defined HID: wacom: generic: Don't return a value for wacom_wac_event HID: intel_ish-hid: use %pUL for uuid formatting HID: cp2112: explicitly require irqchip support in gpiolib HID: asus: Add i2c touchpad support HID: intel-ish-hid: Fix potential race condition HID: sony: Support DS4 dongle HID: sony: Comply to Linux gamepad spec for DS4 HID: sony: Make the DS4 touchpad a separate device HID: sony: Fix memory issue when connecting device using both Bluetooth and USB HID: cp2112: add IRQ chip handling HID: i2c-hid: force the IRQ level trigger only when not set HID: multitouch: do not retrieve all reports for all devices HID: multitouch: enable the Surface 3 Type Cover to report multitouch data ...
This commit is contained in:
@@ -12488,6 +12488,12 @@ S: Maintained
|
||||
F: Documentation/filesystems/udf.txt
|
||||
F: fs/udf/
|
||||
|
||||
UDRAW TABLET
|
||||
M: Bastien Nocera <hadess@hadess.net>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/hid/hid-udraw.c
|
||||
|
||||
UFS FILESYSTEM
|
||||
M: Evgeniy Dushistov <dushistov@mail.ru>
|
||||
S: Maintained
|
||||
|
||||
+17
-2
@@ -138,7 +138,7 @@ config HID_ASUS
|
||||
tristate "Asus"
|
||||
depends on I2C_HID
|
||||
---help---
|
||||
Support for Asus notebook built-in keyboard via i2c.
|
||||
Support for Asus notebook built-in keyboard and touchpad via i2c.
|
||||
|
||||
Supported devices:
|
||||
- EeeBook X205TA
|
||||
@@ -214,7 +214,7 @@ config HID_CMEDIA
|
||||
|
||||
config HID_CP2112
|
||||
tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
|
||||
depends on USB_HID && I2C && GPIOLIB
|
||||
depends on USB_HID && I2C && GPIOLIB && GPIOLIB_IRQCHIP
|
||||
---help---
|
||||
Support for Silicon Labs CP2112 HID USB to SMBus Master Bridge.
|
||||
This is a HID device driver which registers as an i2c adapter
|
||||
@@ -512,6 +512,14 @@ config HID_MAGICMOUSE
|
||||
Say Y here if you want support for the multi-touch features of the
|
||||
Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad.
|
||||
|
||||
config HID_MAYFLASH
|
||||
tristate "Mayflash game controller adapter force feedback"
|
||||
depends on HID
|
||||
select INPUT_FF_MEMLESS
|
||||
---help---
|
||||
Say Y here if you have HJZ Mayflash PS3 game controller adapters
|
||||
and want to enable force feedback support.
|
||||
|
||||
config HID_MICROSOFT
|
||||
tristate "Microsoft non-fully HID-compliant devices"
|
||||
depends on HID
|
||||
@@ -861,6 +869,13 @@ config THRUSTMASTER_FF
|
||||
a THRUSTMASTER Dual Trigger 3-in-1 or a THRUSTMASTER Ferrari GT
|
||||
Rumble Force or Force Feedback Wheel.
|
||||
|
||||
config HID_UDRAW_PS3
|
||||
tristate "THQ PS3 uDraw tablet"
|
||||
depends on HID
|
||||
---help---
|
||||
Say Y here if you want to use the THQ uDraw gaming tablet for
|
||||
the PS3.
|
||||
|
||||
config HID_WACOM
|
||||
tristate "Wacom Intuos/Graphire tablet support (USB)"
|
||||
depends on HID
|
||||
|
||||
@@ -58,6 +58,7 @@ obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
|
||||
obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
|
||||
obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o
|
||||
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
|
||||
obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o
|
||||
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
|
||||
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
|
||||
obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
|
||||
@@ -96,6 +97,7 @@ obj-$(CONFIG_HID_TIVO) += hid-tivo.o
|
||||
obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
|
||||
obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
|
||||
obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
|
||||
obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o
|
||||
obj-$(CONFIG_HID_LED) += hid-led.o
|
||||
obj-$(CONFIG_HID_XINMO) += hid-xinmo.o
|
||||
obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
|
||||
|
||||
+293
-6
@@ -11,6 +11,12 @@
|
||||
* This module based on hid-ortek by
|
||||
* Copyright (c) 2010 Johnathon Harris <jmharris@gmail.com>
|
||||
* Copyright (c) 2011 Jiri Kosina
|
||||
*
|
||||
* This module has been updated to add support for Asus i2c touchpad.
|
||||
*
|
||||
* Copyright (c) 2016 Brendan McGrath <redmcg@redmandi.dyndns.org>
|
||||
* Copyright (c) 2016 Victor Vlasenko <victor.vlasenko@sysgears.com>
|
||||
* Copyright (c) 2016 Frederik Wenigwieser <frederik.wenigwieser@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -20,16 +26,287 @@
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input/mt.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
MODULE_AUTHOR("Yusuke Fujimaki <usk.fujimaki@gmail.com>");
|
||||
MODULE_AUTHOR("Brendan McGrath <redmcg@redmandi.dyndns.org>");
|
||||
MODULE_AUTHOR("Victor Vlasenko <victor.vlasenko@sysgears.com>");
|
||||
MODULE_AUTHOR("Frederik Wenigwieser <frederik.wenigwieser@gmail.com>");
|
||||
MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
||||
|
||||
#define FEATURE_REPORT_ID 0x0d
|
||||
#define INPUT_REPORT_ID 0x5d
|
||||
|
||||
#define INPUT_REPORT_SIZE 28
|
||||
|
||||
#define MAX_CONTACTS 5
|
||||
|
||||
#define MAX_X 2794
|
||||
#define MAX_Y 1758
|
||||
#define MAX_TOUCH_MAJOR 8
|
||||
#define MAX_PRESSURE 128
|
||||
|
||||
#define CONTACT_DATA_SIZE 5
|
||||
|
||||
#define BTN_LEFT_MASK 0x01
|
||||
#define CONTACT_TOOL_TYPE_MASK 0x80
|
||||
#define CONTACT_X_MSB_MASK 0xf0
|
||||
#define CONTACT_Y_MSB_MASK 0x0f
|
||||
#define CONTACT_TOUCH_MAJOR_MASK 0x07
|
||||
#define CONTACT_PRESSURE_MASK 0x7f
|
||||
|
||||
#define QUIRK_FIX_NOTEBOOK_REPORT BIT(0)
|
||||
#define QUIRK_NO_INIT_REPORTS BIT(1)
|
||||
#define QUIRK_SKIP_INPUT_MAPPING BIT(2)
|
||||
#define QUIRK_IS_MULTITOUCH BIT(3)
|
||||
|
||||
#define NOTEBOOK_QUIRKS QUIRK_FIX_NOTEBOOK_REPORT
|
||||
#define TOUCHPAD_QUIRKS (QUIRK_NO_INIT_REPORTS | \
|
||||
QUIRK_SKIP_INPUT_MAPPING | \
|
||||
QUIRK_IS_MULTITOUCH)
|
||||
|
||||
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
|
||||
|
||||
struct asus_drvdata {
|
||||
unsigned long quirks;
|
||||
struct input_dev *input;
|
||||
};
|
||||
|
||||
static void asus_report_contact_down(struct input_dev *input,
|
||||
int toolType, u8 *data)
|
||||
{
|
||||
int touch_major, pressure;
|
||||
int x = (data[0] & CONTACT_X_MSB_MASK) << 4 | data[1];
|
||||
int y = MAX_Y - ((data[0] & CONTACT_Y_MSB_MASK) << 8 | data[2]);
|
||||
|
||||
if (toolType == MT_TOOL_PALM) {
|
||||
touch_major = MAX_TOUCH_MAJOR;
|
||||
pressure = MAX_PRESSURE;
|
||||
} else {
|
||||
touch_major = (data[3] >> 4) & CONTACT_TOUCH_MAJOR_MASK;
|
||||
pressure = data[4] & CONTACT_PRESSURE_MASK;
|
||||
}
|
||||
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major);
|
||||
input_report_abs(input, ABS_MT_PRESSURE, pressure);
|
||||
}
|
||||
|
||||
/* Required for Synaptics Palm Detection */
|
||||
static void asus_report_tool_width(struct input_dev *input)
|
||||
{
|
||||
struct input_mt *mt = input->mt;
|
||||
struct input_mt_slot *oldest;
|
||||
int oldid, count, i;
|
||||
|
||||
oldest = NULL;
|
||||
oldid = mt->trkid;
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < mt->num_slots; ++i) {
|
||||
struct input_mt_slot *ps = &mt->slots[i];
|
||||
int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
|
||||
|
||||
if (id < 0)
|
||||
continue;
|
||||
if ((id - oldid) & TRKID_SGN) {
|
||||
oldest = ps;
|
||||
oldid = id;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
if (oldest) {
|
||||
input_report_abs(input, ABS_TOOL_WIDTH,
|
||||
input_mt_get_value(oldest, ABS_MT_TOUCH_MAJOR));
|
||||
}
|
||||
}
|
||||
|
||||
static void asus_report_input(struct input_dev *input, u8 *data)
|
||||
{
|
||||
int i;
|
||||
u8 *contactData = data + 2;
|
||||
|
||||
for (i = 0; i < MAX_CONTACTS; i++) {
|
||||
bool down = !!(data[1] & BIT(i+3));
|
||||
int toolType = contactData[3] & CONTACT_TOOL_TYPE_MASK ?
|
||||
MT_TOOL_PALM : MT_TOOL_FINGER;
|
||||
|
||||
input_mt_slot(input, i);
|
||||
input_mt_report_slot_state(input, toolType, down);
|
||||
|
||||
if (down) {
|
||||
asus_report_contact_down(input, toolType, contactData);
|
||||
contactData += CONTACT_DATA_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
input_report_key(input, BTN_LEFT, data[1] & BTN_LEFT_MASK);
|
||||
asus_report_tool_width(input);
|
||||
|
||||
input_mt_sync_frame(input);
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static int asus_raw_event(struct hid_device *hdev,
|
||||
struct hid_report *report, u8 *data, int size)
|
||||
{
|
||||
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
||||
if (drvdata->quirks & QUIRK_IS_MULTITOUCH &&
|
||||
data[0] == INPUT_REPORT_ID &&
|
||||
size == INPUT_REPORT_SIZE) {
|
||||
asus_report_input(drvdata->input, data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
|
||||
{
|
||||
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
||||
if (drvdata->quirks & QUIRK_IS_MULTITOUCH) {
|
||||
int ret;
|
||||
struct input_dev *input = hi->input;
|
||||
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0, MAX_X, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, MAX_Y, 0, 0);
|
||||
input_set_abs_params(input, ABS_TOOL_WIDTH, 0, MAX_TOUCH_MAJOR, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, MAX_TOUCH_MAJOR, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_PRESSURE, 0, MAX_PRESSURE, 0, 0);
|
||||
|
||||
__set_bit(BTN_LEFT, input->keybit);
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
||||
|
||||
ret = input_mt_init_slots(input, MAX_CONTACTS, INPUT_MT_POINTER);
|
||||
|
||||
if (ret) {
|
||||
hid_err(hdev, "Asus input mt init slots failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drvdata->input = input;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asus_input_mapping(struct hid_device *hdev,
|
||||
struct hid_input *hi, struct hid_field *field,
|
||||
struct hid_usage *usage, unsigned long **bit,
|
||||
int *max)
|
||||
{
|
||||
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
||||
if (drvdata->quirks & QUIRK_SKIP_INPUT_MAPPING) {
|
||||
/* Don't map anything from the HID report.
|
||||
* We do it all manually in asus_input_configured
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asus_start_multitouch(struct hid_device *hdev)
|
||||
{
|
||||
int ret;
|
||||
const unsigned char buf[] = { FEATURE_REPORT_ID, 0x00, 0x03, 0x01, 0x00 };
|
||||
unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL);
|
||||
|
||||
if (!dmabuf) {
|
||||
ret = -ENOMEM;
|
||||
hid_err(hdev, "Asus failed to alloc dma buf: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, sizeof(buf),
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
|
||||
kfree(dmabuf);
|
||||
|
||||
if (ret != sizeof(buf)) {
|
||||
hid_err(hdev, "Asus failed to start multitouch: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
|
||||
{
|
||||
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
||||
if (drvdata->quirks & QUIRK_IS_MULTITOUCH)
|
||||
return asus_start_multitouch(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct asus_drvdata *drvdata;
|
||||
|
||||
drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (drvdata == NULL) {
|
||||
hid_err(hdev, "Can't alloc Asus descriptor\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hid_set_drvdata(hdev, drvdata);
|
||||
|
||||
drvdata->quirks = id->driver_data;
|
||||
|
||||
if (drvdata->quirks & QUIRK_NO_INIT_REPORTS)
|
||||
hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Asus hid parse failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Asus hw start failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!drvdata->input) {
|
||||
hid_err(hdev, "Asus input not registered\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_stop_hw;
|
||||
}
|
||||
|
||||
drvdata->input->name = "Asus TouchPad";
|
||||
|
||||
if (drvdata->quirks & QUIRK_IS_MULTITOUCH) {
|
||||
ret = asus_start_multitouch(hdev);
|
||||
if (ret)
|
||||
goto err_stop_hw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_stop_hw:
|
||||
hid_hw_stop(hdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x65) {
|
||||
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
||||
if (drvdata->quirks & QUIRK_FIX_NOTEBOOK_REPORT &&
|
||||
*rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x65) {
|
||||
hid_info(hdev, "Fixing up Asus notebook report descriptor\n");
|
||||
rdesc[55] = 0xdd;
|
||||
}
|
||||
@@ -37,15 +314,25 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
}
|
||||
|
||||
static const struct hid_device_id asus_devices[] = {
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD) },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD), NOTEBOOK_QUIRKS},
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_TOUCHPAD), TOUCHPAD_QUIRKS },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, asus_devices);
|
||||
|
||||
static struct hid_driver asus_driver = {
|
||||
.name = "asus",
|
||||
.id_table = asus_devices,
|
||||
.report_fixup = asus_report_fixup
|
||||
.name = "asus",
|
||||
.id_table = asus_devices,
|
||||
.report_fixup = asus_report_fixup,
|
||||
.probe = asus_probe,
|
||||
.input_mapping = asus_input_mapping,
|
||||
.input_configured = asus_input_configured,
|
||||
#ifdef CONFIG_PM
|
||||
.reset_resume = asus_reset_resume,
|
||||
#endif
|
||||
.raw_event = asus_raw_event
|
||||
};
|
||||
module_hid_driver(asus_driver);
|
||||
|
||||
|
||||
+12
-2
@@ -727,8 +727,9 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
|
||||
(hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 ||
|
||||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 ||
|
||||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP ||
|
||||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4 ||
|
||||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2 ||
|
||||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP ||
|
||||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 ||
|
||||
hid->product == USB_DEVICE_ID_MS_POWER_COVER) &&
|
||||
hid->group == HID_GROUP_MULTITOUCH)
|
||||
hid->group = HID_GROUP_GENERIC;
|
||||
@@ -1857,6 +1858,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD) },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_TOUCHPAD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185BFM, 0x2208) },
|
||||
@@ -1883,6 +1885,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DELCOM, USB_DEVICE_ID_DELCOM_VISUAL_IND) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
|
||||
#if IS_ENABLED(CONFIG_HID_MAYFLASH)
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3) },
|
||||
#endif
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_WN) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_FA) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
|
||||
@@ -1983,8 +1988,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1) },
|
||||
@@ -2059,6 +2065,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
|
||||
@@ -2086,6 +2095,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) },
|
||||
|
||||
+202
-3
@@ -24,6 +24,7 @@
|
||||
* http://www.silabs.com/Support%20Documents/TechnicalDocs/AN495.pdf
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/i2c.h>
|
||||
@@ -168,6 +169,12 @@ struct cp2112_device {
|
||||
struct gpio_chip gc;
|
||||
u8 *in_out_buffer;
|
||||
spinlock_t lock;
|
||||
|
||||
struct gpio_desc *desc[8];
|
||||
bool gpio_poll;
|
||||
struct delayed_work gpio_poll_worker;
|
||||
unsigned long irq_mask;
|
||||
u8 gpio_prev_state;
|
||||
};
|
||||
|
||||
static int gpio_push_pull = 0xFF;
|
||||
@@ -233,7 +240,7 @@ static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
}
|
||||
|
||||
static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
static int cp2112_gpio_get_all(struct gpio_chip *chip)
|
||||
{
|
||||
struct cp2112_device *dev = gpiochip_get_data(chip);
|
||||
struct hid_device *hdev = dev->hdev;
|
||||
@@ -252,7 +259,7 @@ static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = (buf[1] >> offset) & 1;
|
||||
ret = buf[1];
|
||||
|
||||
exit:
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
@@ -260,6 +267,17 @@ exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cp2112_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = cp2112_gpio_get_all(chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (ret >> offset) & 1;
|
||||
}
|
||||
|
||||
static int cp2112_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
@@ -1041,6 +1059,166 @@ static void chmod_sysfs_attrs(struct hid_device *hdev)
|
||||
}
|
||||
}
|
||||
|
||||
static void cp2112_gpio_irq_ack(struct irq_data *d)
|
||||
{
|
||||
}
|
||||
|
||||
static void cp2112_gpio_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct cp2112_device *dev = gpiochip_get_data(gc);
|
||||
|
||||
__clear_bit(d->hwirq, &dev->irq_mask);
|
||||
}
|
||||
|
||||
static void cp2112_gpio_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct cp2112_device *dev = gpiochip_get_data(gc);
|
||||
|
||||
__set_bit(d->hwirq, &dev->irq_mask);
|
||||
}
|
||||
|
||||
static void cp2112_gpio_poll_callback(struct work_struct *work)
|
||||
{
|
||||
struct cp2112_device *dev = container_of(work, struct cp2112_device,
|
||||
gpio_poll_worker.work);
|
||||
struct irq_data *d;
|
||||
u8 gpio_mask;
|
||||
u8 virqs = (u8)dev->irq_mask;
|
||||
u32 irq_type;
|
||||
int irq, virq, ret;
|
||||
|
||||
ret = cp2112_gpio_get_all(&dev->gc);
|
||||
if (ret == -ENODEV) /* the hardware has been disconnected */
|
||||
return;
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
gpio_mask = ret;
|
||||
|
||||
while (virqs) {
|
||||
virq = ffs(virqs) - 1;
|
||||
virqs &= ~BIT(virq);
|
||||
|
||||
if (!dev->gc.to_irq)
|
||||
break;
|
||||
|
||||
irq = dev->gc.to_irq(&dev->gc, virq);
|
||||
|
||||
d = irq_get_irq_data(irq);
|
||||
if (!d)
|
||||
continue;
|
||||
|
||||
irq_type = irqd_get_trigger_type(d);
|
||||
|
||||
if (gpio_mask & BIT(virq)) {
|
||||
/* Level High */
|
||||
|
||||
if (irq_type & IRQ_TYPE_LEVEL_HIGH)
|
||||
handle_nested_irq(irq);
|
||||
|
||||
if ((irq_type & IRQ_TYPE_EDGE_RISING) &&
|
||||
!(dev->gpio_prev_state & BIT(virq)))
|
||||
handle_nested_irq(irq);
|
||||
} else {
|
||||
/* Level Low */
|
||||
|
||||
if (irq_type & IRQ_TYPE_LEVEL_LOW)
|
||||
handle_nested_irq(irq);
|
||||
|
||||
if ((irq_type & IRQ_TYPE_EDGE_FALLING) &&
|
||||
(dev->gpio_prev_state & BIT(virq)))
|
||||
handle_nested_irq(irq);
|
||||
}
|
||||
}
|
||||
|
||||
dev->gpio_prev_state = gpio_mask;
|
||||
|
||||
exit:
|
||||
if (dev->gpio_poll)
|
||||
schedule_delayed_work(&dev->gpio_poll_worker, 10);
|
||||
}
|
||||
|
||||
|
||||
static unsigned int cp2112_gpio_irq_startup(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct cp2112_device *dev = gpiochip_get_data(gc);
|
||||
|
||||
INIT_DELAYED_WORK(&dev->gpio_poll_worker, cp2112_gpio_poll_callback);
|
||||
|
||||
cp2112_gpio_direction_input(gc, d->hwirq);
|
||||
|
||||
if (!dev->gpio_poll) {
|
||||
dev->gpio_poll = true;
|
||||
schedule_delayed_work(&dev->gpio_poll_worker, 0);
|
||||
}
|
||||
|
||||
cp2112_gpio_irq_unmask(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cp2112_gpio_irq_shutdown(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct cp2112_device *dev = gpiochip_get_data(gc);
|
||||
|
||||
cancel_delayed_work_sync(&dev->gpio_poll_worker);
|
||||
}
|
||||
|
||||
static int cp2112_gpio_irq_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_chip cp2112_gpio_irqchip = {
|
||||
.name = "cp2112-gpio",
|
||||
.irq_startup = cp2112_gpio_irq_startup,
|
||||
.irq_shutdown = cp2112_gpio_irq_shutdown,
|
||||
.irq_ack = cp2112_gpio_irq_ack,
|
||||
.irq_mask = cp2112_gpio_irq_mask,
|
||||
.irq_unmask = cp2112_gpio_irq_unmask,
|
||||
.irq_set_type = cp2112_gpio_irq_type,
|
||||
};
|
||||
|
||||
static int __maybe_unused cp2112_allocate_irq(struct cp2112_device *dev,
|
||||
int pin)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dev->desc[pin])
|
||||
return -EINVAL;
|
||||
|
||||
dev->desc[pin] = gpiochip_request_own_desc(&dev->gc, pin,
|
||||
"HID/I2C:Event");
|
||||
if (IS_ERR(dev->desc[pin])) {
|
||||
dev_err(dev->gc.parent, "Failed to request GPIO\n");
|
||||
return PTR_ERR(dev->desc[pin]);
|
||||
}
|
||||
|
||||
ret = gpiochip_lock_as_irq(&dev->gc, pin);
|
||||
if (ret) {
|
||||
dev_err(dev->gc.parent, "Failed to lock GPIO as interrupt\n");
|
||||
goto err_desc;
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(dev->desc[pin]);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->gc.parent, "Failed to translate GPIO to IRQ\n");
|
||||
goto err_lock;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err_lock:
|
||||
gpiochip_unlock_as_irq(&dev->gc, pin);
|
||||
err_desc:
|
||||
gpiochip_free_own_desc(dev->desc[pin]);
|
||||
dev->desc[pin] = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
struct cp2112_device *dev;
|
||||
@@ -1163,8 +1341,17 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
chmod_sysfs_attrs(hdev);
|
||||
hid_hw_power(hdev, PM_HINT_NORMAL);
|
||||
|
||||
ret = gpiochip_irqchip_add(&dev->gc, &cp2112_gpio_irqchip, 0,
|
||||
handle_simple_irq, IRQ_TYPE_NONE);
|
||||
if (ret) {
|
||||
dev_err(dev->gc.parent, "failed to add IRQ chip\n");
|
||||
goto err_sysfs_remove;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err_sysfs_remove:
|
||||
sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group);
|
||||
err_gpiochip_remove:
|
||||
gpiochip_remove(&dev->gc);
|
||||
err_free_i2c:
|
||||
@@ -1181,10 +1368,22 @@ err_hid_stop:
|
||||
static void cp2112_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct cp2112_device *dev = hid_get_drvdata(hdev);
|
||||
int i;
|
||||
|
||||
sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group);
|
||||
gpiochip_remove(&dev->gc);
|
||||
i2c_del_adapter(&dev->adap);
|
||||
|
||||
if (dev->gpio_poll) {
|
||||
dev->gpio_poll = false;
|
||||
cancel_delayed_work_sync(&dev->gpio_poll_worker);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dev->desc); i++) {
|
||||
gpiochip_unlock_as_irq(&dev->gc, i);
|
||||
gpiochip_free_own_desc(dev->desc[i]);
|
||||
}
|
||||
|
||||
gpiochip_remove(&dev->gc);
|
||||
/* i2c_del_adapter has finished removing all i2c devices from our
|
||||
* adapter. Well behaved devices should no longer call our cp2112_xfer
|
||||
* and should have waited for any pending calls to finish. It has also
|
||||
|
||||
+16
-3
@@ -171,6 +171,7 @@
|
||||
#define USB_DEVICE_ID_ASUSTEK_LCM 0x1726
|
||||
#define USB_DEVICE_ID_ASUSTEK_LCM2 0x175b
|
||||
#define USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD 0x8585
|
||||
#define USB_DEVICE_ID_ASUSTEK_TOUCHPAD 0x0101
|
||||
|
||||
#define USB_VENDOR_ID_ATEN 0x0557
|
||||
#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
|
||||
@@ -315,8 +316,10 @@
|
||||
#define USB_VENDOR_ID_DMI 0x0c0b
|
||||
#define USB_DEVICE_ID_DMI_ENC 0x5fab
|
||||
|
||||
#define USB_VENDOR_ID_DRAGONRISE 0x0079
|
||||
#define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800
|
||||
#define USB_VENDOR_ID_DRAGONRISE 0x0079
|
||||
#define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800
|
||||
#define USB_DEVICE_ID_DRAGONRISE_PS3 0x1801
|
||||
#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE 0x1843
|
||||
|
||||
#define USB_VENDOR_ID_DWAV 0x0eef
|
||||
#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
|
||||
@@ -718,8 +721,9 @@
|
||||
#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 0x07dc
|
||||
#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2
|
||||
#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd
|
||||
#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4 0x07e4
|
||||
#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2 0x07e8
|
||||
#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP 0x07e9
|
||||
#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de
|
||||
#define USB_DEVICE_ID_MS_POWER_COVER 0x07da
|
||||
|
||||
#define USB_VENDOR_ID_MOJO 0x8282
|
||||
@@ -903,6 +907,8 @@
|
||||
#define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306
|
||||
#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268
|
||||
#define USB_DEVICE_ID_SONY_PS4_CONTROLLER 0x05c4
|
||||
#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 0x09cc
|
||||
#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE 0x0ba0
|
||||
#define USB_DEVICE_ID_SONY_MOTION_CONTROLLER 0x03d5
|
||||
#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f
|
||||
#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002
|
||||
@@ -959,6 +965,9 @@
|
||||
#define USB_VENDOR_ID_THINGM 0x27b8
|
||||
#define USB_DEVICE_ID_BLINK1 0x01ed
|
||||
|
||||
#define USB_VENDOR_ID_THQ 0x20d6
|
||||
#define USB_DEVICE_ID_THQ_PS3_UDRAW 0xcb17
|
||||
|
||||
#define USB_VENDOR_ID_THRUSTMASTER 0x044f
|
||||
|
||||
#define USB_VENDOR_ID_TIVO 0x150a
|
||||
@@ -1034,6 +1043,10 @@
|
||||
#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH 0x0500
|
||||
#define USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET 0x0502
|
||||
|
||||
#define USB_VENDOR_ID_WEIDA 0x2575
|
||||
#define USB_DEVICE_ID_WEIDA_8752 0xC300
|
||||
#define USB_DEVICE_ID_WEIDA_8755 0xC301
|
||||
|
||||
#define USB_VENDOR_ID_WISEGROUP 0x0925
|
||||
#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
|
||||
#define USB_DEVICE_ID_SUPER_JOY_BOX_3 0x8888
|
||||
|
||||
+55
-41
@@ -253,6 +253,7 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
|
||||
case ABS_RX:
|
||||
case ABS_RY:
|
||||
case ABS_RZ:
|
||||
case ABS_WHEEL:
|
||||
case ABS_TILT_X:
|
||||
case ABS_TILT_Y:
|
||||
if (field->unit == 0x14) { /* If degrees */
|
||||
@@ -1468,6 +1469,31 @@ static void hidinput_cleanup_hidinput(struct hid_device *hid,
|
||||
kfree(hidinput);
|
||||
}
|
||||
|
||||
static struct hid_input *hidinput_match(struct hid_report *report)
|
||||
{
|
||||
struct hid_device *hid = report->device;
|
||||
struct hid_input *hidinput;
|
||||
|
||||
list_for_each_entry(hidinput, &hid->inputs, list) {
|
||||
if (hidinput->report &&
|
||||
hidinput->report->id == report->id)
|
||||
return hidinput;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void hidinput_configure_usages(struct hid_input *hidinput,
|
||||
struct hid_report *report)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < report->maxfield; i++)
|
||||
for (j = 0; j < report->field[i]->maxusage; j++)
|
||||
hidinput_configure_usage(hidinput, report->field[i],
|
||||
report->field[i]->usage + j);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register the input device; print a message.
|
||||
* Configure the input layer interface
|
||||
@@ -1478,8 +1504,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
|
||||
{
|
||||
struct hid_driver *drv = hid->driver;
|
||||
struct hid_report *report;
|
||||
struct hid_input *hidinput = NULL;
|
||||
int i, j, k;
|
||||
struct hid_input *next, *hidinput = NULL;
|
||||
int i, k;
|
||||
|
||||
INIT_LIST_HEAD(&hid->inputs);
|
||||
INIT_WORK(&hid->led_work, hidinput_led_worker);
|
||||
@@ -1509,43 +1535,40 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
|
||||
if (!report->maxfield)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Find the previous hidinput report attached
|
||||
* to this report id.
|
||||
*/
|
||||
if (hid->quirks & HID_QUIRK_MULTI_INPUT)
|
||||
hidinput = hidinput_match(report);
|
||||
|
||||
if (!hidinput) {
|
||||
hidinput = hidinput_allocate(hid);
|
||||
if (!hidinput)
|
||||
goto out_unwind;
|
||||
}
|
||||
|
||||
for (i = 0; i < report->maxfield; i++)
|
||||
for (j = 0; j < report->field[i]->maxusage; j++)
|
||||
hidinput_configure_usage(hidinput, report->field[i],
|
||||
report->field[i]->usage + j);
|
||||
hidinput_configure_usages(hidinput, report);
|
||||
|
||||
if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
|
||||
!hidinput_has_been_populated(hidinput))
|
||||
continue;
|
||||
|
||||
if (hid->quirks & HID_QUIRK_MULTI_INPUT) {
|
||||
/* This will leave hidinput NULL, so that it
|
||||
* allocates another one if we have more inputs on
|
||||
* the same interface. Some devices (e.g. Happ's
|
||||
* UGCI) cram a lot of unrelated inputs into the
|
||||
* same interface. */
|
||||
if (hid->quirks & HID_QUIRK_MULTI_INPUT)
|
||||
hidinput->report = report;
|
||||
if (drv->input_configured &&
|
||||
drv->input_configured(hid, hidinput))
|
||||
goto out_cleanup;
|
||||
if (input_register_device(hidinput->input))
|
||||
goto out_cleanup;
|
||||
hidinput = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hidinput && (hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
|
||||
!hidinput_has_been_populated(hidinput)) {
|
||||
/* no need to register an input device not populated */
|
||||
hidinput_cleanup_hidinput(hid, hidinput);
|
||||
hidinput = NULL;
|
||||
list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
|
||||
if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
|
||||
!hidinput_has_been_populated(hidinput)) {
|
||||
/* no need to register an input device not populated */
|
||||
hidinput_cleanup_hidinput(hid, hidinput);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (drv->input_configured &&
|
||||
drv->input_configured(hid, hidinput))
|
||||
goto out_unwind;
|
||||
if (input_register_device(hidinput->input))
|
||||
goto out_unwind;
|
||||
hidinput->registered = true;
|
||||
}
|
||||
|
||||
if (list_empty(&hid->inputs)) {
|
||||
@@ -1553,20 +1576,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
|
||||
goto out_unwind;
|
||||
}
|
||||
|
||||
if (hidinput) {
|
||||
if (drv->input_configured &&
|
||||
drv->input_configured(hid, hidinput))
|
||||
goto out_cleanup;
|
||||
if (input_register_device(hidinput->input))
|
||||
goto out_cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_cleanup:
|
||||
list_del(&hidinput->list);
|
||||
input_free_device(hidinput->input);
|
||||
kfree(hidinput);
|
||||
out_unwind:
|
||||
/* unwind the ones we already registered */
|
||||
hidinput_disconnect(hid);
|
||||
@@ -1583,7 +1594,10 @@ void hidinput_disconnect(struct hid_device *hid)
|
||||
|
||||
list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
|
||||
list_del(&hidinput->list);
|
||||
input_unregister_device(hidinput->input);
|
||||
if (hidinput->registered)
|
||||
input_unregister_device(hidinput->input);
|
||||
else
|
||||
input_free_device(hidinput->input);
|
||||
kfree(hidinput);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Force feedback support for Mayflash game controller adapters.
|
||||
*
|
||||
* These devices are manufactured by Mayflash but identify themselves
|
||||
* using the vendor ID of DragonRise Inc.
|
||||
*
|
||||
* Tested with:
|
||||
* 0079:1801 "DragonRise Inc. Mayflash PS3 Game Controller Adapter"
|
||||
*
|
||||
* The following adapters probably work too, but need to be tested:
|
||||
* 0079:1800 "DragonRise Inc. Mayflash WIIU Game Controller Adapter"
|
||||
* 0079:1843 "DragonRise Inc. Mayflash GameCube Game Controller Adapter"
|
||||
*
|
||||
* Copyright (c) 2016 Marcel Hasler <mahasler@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
struct mf_device {
|
||||
struct hid_report *report;
|
||||
};
|
||||
|
||||
static int mf_play(struct input_dev *dev, void *data, struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = input_get_drvdata(dev);
|
||||
struct mf_device *mf = data;
|
||||
int strong, weak;
|
||||
|
||||
strong = effect->u.rumble.strong_magnitude;
|
||||
weak = effect->u.rumble.weak_magnitude;
|
||||
|
||||
dbg_hid("Called with 0x%04x 0x%04x.\n", strong, weak);
|
||||
|
||||
strong = strong * 0xff / 0xffff;
|
||||
weak = weak * 0xff / 0xffff;
|
||||
|
||||
dbg_hid("Running with 0x%02x 0x%02x.\n", strong, weak);
|
||||
|
||||
mf->report->field[0]->value[0] = weak;
|
||||
mf->report->field[0]->value[1] = strong;
|
||||
hid_hw_request(hid, mf->report, HID_REQ_SET_REPORT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mf_init(struct hid_device *hid)
|
||||
{
|
||||
struct mf_device *mf;
|
||||
|
||||
struct list_head *report_list =
|
||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
|
||||
struct list_head *report_ptr;
|
||||
struct hid_report *report;
|
||||
|
||||
struct list_head *input_ptr = &hid->inputs;
|
||||
struct hid_input *input;
|
||||
|
||||
struct input_dev *dev;
|
||||
|
||||
int error;
|
||||
|
||||
/* Setup each of the four inputs */
|
||||
list_for_each(report_ptr, report_list) {
|
||||
report = list_entry(report_ptr, struct hid_report, list);
|
||||
|
||||
if (report->maxfield < 1 || report->field[0]->report_count < 2) {
|
||||
hid_err(hid, "Invalid report, this should never happen!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (list_is_last(input_ptr, &hid->inputs)) {
|
||||
hid_err(hid, "Missing input, this should never happen!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
input_ptr = input_ptr->next;
|
||||
input = list_entry(input_ptr, struct hid_input, list);
|
||||
|
||||
mf = kzalloc(sizeof(struct mf_device), GFP_KERNEL);
|
||||
if (!mf)
|
||||
return -ENOMEM;
|
||||
|
||||
dev = input->input;
|
||||
set_bit(FF_RUMBLE, dev->ffbit);
|
||||
|
||||
error = input_ff_create_memless(dev, mf, mf_play);
|
||||
if (error) {
|
||||
kfree(mf);
|
||||
return error;
|
||||
}
|
||||
|
||||
mf->report = report;
|
||||
mf->report->field[0]->value[0] = 0x00;
|
||||
mf->report->field[0]->value[1] = 0x00;
|
||||
hid_hw_request(hid, mf->report, HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
||||
hid_info(hid, "Force feedback for HJZ Mayflash game controller "
|
||||
"adapters by Marcel Hasler <mahasler@gmail.com>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mf_probe(struct hid_device *hid, const struct hid_device_id *id)
|
||||
{
|
||||
int error;
|
||||
|
||||
dev_dbg(&hid->dev, "Mayflash HID hardware probe...\n");
|
||||
|
||||
/* Split device into four inputs */
|
||||
hid->quirks |= HID_QUIRK_MULTI_INPUT;
|
||||
|
||||
error = hid_parse(hid);
|
||||
if (error) {
|
||||
hid_err(hid, "HID parse failed.\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
|
||||
if (error) {
|
||||
hid_err(hid, "HID hw start failed\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = mf_init(hid);
|
||||
if (error) {
|
||||
hid_err(hid, "Force feedback init failed.\n");
|
||||
hid_hw_stop(hid);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_device_id mf_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3), },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, mf_devices);
|
||||
|
||||
static struct hid_driver mf_driver = {
|
||||
.name = "hid_mf",
|
||||
.id_table = mf_devices,
|
||||
.probe = mf_probe,
|
||||
};
|
||||
module_hid_driver(mf_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -280,9 +280,11 @@ static const struct hid_device_id ms_devices[] = {
|
||||
.driver_data = MS_HIDINPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP),
|
||||
.driver_data = MS_HIDINPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP),
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4),
|
||||
.driver_data = MS_HIDINPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3),
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2),
|
||||
.driver_data = MS_HIDINPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP),
|
||||
.driver_data = MS_HIDINPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
|
||||
.driver_data = MS_HIDINPUT },
|
||||
|
||||
@@ -108,6 +108,7 @@ struct mt_device {
|
||||
int cc_value_index; /* contact count value index in the field */
|
||||
unsigned last_slot_field; /* the last field of a slot */
|
||||
unsigned mt_report_id; /* the report ID of the multitouch device */
|
||||
unsigned long initial_quirks; /* initial quirks state */
|
||||
__s16 inputmode; /* InputMode HID feature, -1 if non-existent */
|
||||
__s16 inputmode_index; /* InputMode HID feature index in the report */
|
||||
__s16 maxcontact_report_id; /* Maximum Contact Number HID feature,
|
||||
@@ -318,13 +319,10 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
|
||||
u8 *buf;
|
||||
|
||||
/*
|
||||
* Only fetch the feature report if initial reports are not already
|
||||
* been retrieved. Currently this is only done for Windows 8 touch
|
||||
* devices.
|
||||
* Do not fetch the feature report if the device has been explicitly
|
||||
* marked as non-capable.
|
||||
*/
|
||||
if (!(hdev->quirks & HID_QUIRK_NO_INIT_REPORTS))
|
||||
return;
|
||||
if (td->mtclass.name != MT_CLS_WIN_8)
|
||||
if (td->initial_quirks & HID_QUIRK_NO_INIT_REPORTS)
|
||||
return;
|
||||
|
||||
buf = hid_alloc_report_buf(report, GFP_KERNEL);
|
||||
@@ -567,6 +565,14 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
|
||||
case HID_UP_BUTTON:
|
||||
code = BTN_MOUSE + ((usage->hid - 1) & HID_USAGE);
|
||||
/*
|
||||
* MS PTP spec says that external buttons left and right have
|
||||
* usages 2 and 3.
|
||||
*/
|
||||
if (cls->name == MT_CLS_WIN_8 &&
|
||||
field->application == HID_DG_TOUCHPAD &&
|
||||
(usage->hid & HID_USAGE) > 1)
|
||||
code--;
|
||||
hid_map_usage(hi, usage, bit, max, EV_KEY, code);
|
||||
input_set_capability(hi->input, EV_KEY, code);
|
||||
return 1;
|
||||
@@ -842,7 +848,9 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
if (!td->mtclass.export_all_inputs &&
|
||||
field->application != HID_DG_TOUCHSCREEN &&
|
||||
field->application != HID_DG_PEN &&
|
||||
field->application != HID_DG_TOUCHPAD)
|
||||
field->application != HID_DG_TOUCHPAD &&
|
||||
field->application != HID_GD_KEYBOARD &&
|
||||
field->application != HID_CP_CONSUMER_CONTROL)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
@@ -1083,36 +1091,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
}
|
||||
}
|
||||
|
||||
/* This allows the driver to correctly support devices
|
||||
* that emit events over several HID messages.
|
||||
*/
|
||||
hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
|
||||
|
||||
/*
|
||||
* This allows the driver to handle different input sensors
|
||||
* that emits events through different reports on the same HID
|
||||
* device.
|
||||
*/
|
||||
hdev->quirks |= HID_QUIRK_MULTI_INPUT;
|
||||
hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT;
|
||||
|
||||
/*
|
||||
* Handle special quirks for Windows 8 certified devices.
|
||||
*/
|
||||
if (id->group == HID_GROUP_MULTITOUCH_WIN_8)
|
||||
/*
|
||||
* Some multitouch screens do not like to be polled for input
|
||||
* reports. Fortunately, the Win8 spec says that all touches
|
||||
* should be sent during each report, making the initialization
|
||||
* of input reports unnecessary.
|
||||
*
|
||||
* In addition some touchpads do not behave well if we read
|
||||
* all feature reports from them. Instead we prevent
|
||||
* initial report fetching and then selectively fetch each
|
||||
* report we are interested in.
|
||||
*/
|
||||
hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
|
||||
|
||||
td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL);
|
||||
if (!td) {
|
||||
dev_err(&hdev->dev, "cannot allocate multitouch data\n");
|
||||
@@ -1136,6 +1114,39 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
|
||||
td->serial_maybe = true;
|
||||
|
||||
/*
|
||||
* Store the initial quirk state
|
||||
*/
|
||||
td->initial_quirks = hdev->quirks;
|
||||
|
||||
/* This allows the driver to correctly support devices
|
||||
* that emit events over several HID messages.
|
||||
*/
|
||||
hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
|
||||
|
||||
/*
|
||||
* This allows the driver to handle different input sensors
|
||||
* that emits events through different reports on the same HID
|
||||
* device.
|
||||
*/
|
||||
hdev->quirks |= HID_QUIRK_MULTI_INPUT;
|
||||
hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT;
|
||||
|
||||
/*
|
||||
* Some multitouch screens do not like to be polled for input
|
||||
* reports. Fortunately, the Win8 spec says that all touches
|
||||
* should be sent during each report, making the initialization
|
||||
* of input reports unnecessary. For Win7 devices, well, let's hope
|
||||
* they will still be happy (this is only be a problem if a touch
|
||||
* was already there while probing the device).
|
||||
*
|
||||
* In addition some touchpads do not behave well if we read
|
||||
* all feature reports from them. Instead we prevent
|
||||
* initial report fetching and then selectively fetch each
|
||||
* report we are interested in.
|
||||
*/
|
||||
hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
@@ -1204,8 +1215,11 @@ static int mt_resume(struct hid_device *hdev)
|
||||
|
||||
static void mt_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct mt_device *td = hid_get_drvdata(hdev);
|
||||
|
||||
sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
|
||||
hid_hw_stop(hdev);
|
||||
hdev->quirks = td->initial_quirks;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -795,6 +795,12 @@ static const struct hid_device_id sensor_hub_devices[] = {
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
|
||||
USB_DEVICE_ID_MS_TYPE_COVER_2),
|
||||
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
|
||||
0x07bd), /* Microsoft Surface 3 */
|
||||
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROCHIP,
|
||||
0x0f01), /* MM7150 */
|
||||
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0,
|
||||
USB_DEVICE_ID_STM_HID_SENSOR),
|
||||
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
|
||||
|
||||
+322
-125
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
* HID driver for THQ PS3 uDraw tablet
|
||||
*
|
||||
* Copyright (C) 2016 Red Hat Inc. All Rights Reserved
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include "hid-ids.h"
|
||||
|
||||
MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
|
||||
MODULE_DESCRIPTION("PS3 uDraw tablet driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Protocol information from:
|
||||
* http://brandonw.net/udraw/
|
||||
* and the source code of:
|
||||
* https://vvvv.org/contribution/udraw-hid
|
||||
*/
|
||||
|
||||
/*
|
||||
* The device is setup with multiple input devices:
|
||||
* - the touch area which works as a touchpad
|
||||
* - the tablet area which works as a touchpad/drawing tablet
|
||||
* - a joypad with a d-pad, and 7 buttons
|
||||
* - an accelerometer device
|
||||
*/
|
||||
|
||||
enum {
|
||||
TOUCH_NONE,
|
||||
TOUCH_PEN,
|
||||
TOUCH_FINGER,
|
||||
TOUCH_TWOFINGER
|
||||
};
|
||||
|
||||
enum {
|
||||
AXIS_X,
|
||||
AXIS_Y,
|
||||
AXIS_Z
|
||||
};
|
||||
|
||||
/*
|
||||
* Accelerometer min/max values
|
||||
* in order, X, Y and Z
|
||||
*/
|
||||
static struct {
|
||||
int min;
|
||||
int max;
|
||||
} accel_limits[] = {
|
||||
[AXIS_X] = { 490, 534 },
|
||||
[AXIS_Y] = { 490, 534 },
|
||||
[AXIS_Z] = { 492, 536 }
|
||||
};
|
||||
|
||||
#define DEVICE_NAME "THQ uDraw Game Tablet for PS3"
|
||||
/* resolution in pixels */
|
||||
#define RES_X 1920
|
||||
#define RES_Y 1080
|
||||
/* size in mm */
|
||||
#define WIDTH 160
|
||||
#define HEIGHT 90
|
||||
#define PRESSURE_OFFSET 113
|
||||
#define MAX_PRESSURE (255 - PRESSURE_OFFSET)
|
||||
|
||||
struct udraw {
|
||||
struct input_dev *joy_input_dev;
|
||||
struct input_dev *touch_input_dev;
|
||||
struct input_dev *pen_input_dev;
|
||||
struct input_dev *accel_input_dev;
|
||||
struct hid_device *hdev;
|
||||
|
||||
/*
|
||||
* The device's two-finger support is pretty unreliable, as
|
||||
* the device could report a single touch when the two fingers
|
||||
* are too close together, and the distance between fingers, even
|
||||
* though reported is not in the same unit as the touches.
|
||||
*
|
||||
* We'll make do without it, and try to report the first touch
|
||||
* as reliably as possible.
|
||||
*/
|
||||
int last_one_finger_x;
|
||||
int last_one_finger_y;
|
||||
int last_two_finger_x;
|
||||
int last_two_finger_y;
|
||||
};
|
||||
|
||||
static int clamp_accel(int axis, int offset)
|
||||
{
|
||||
axis = clamp(axis,
|
||||
accel_limits[offset].min,
|
||||
accel_limits[offset].max);
|
||||
axis = (axis - accel_limits[offset].min) /
|
||||
((accel_limits[offset].max -
|
||||
accel_limits[offset].min) * 0xFF);
|
||||
return axis;
|
||||
}
|
||||
|
||||
static int udraw_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
u8 *data, int len)
|
||||
{
|
||||
struct udraw *udraw = hid_get_drvdata(hdev);
|
||||
int touch;
|
||||
int x, y, z;
|
||||
|
||||
if (len != 27)
|
||||
return 0;
|
||||
|
||||
if (data[11] == 0x00)
|
||||
touch = TOUCH_NONE;
|
||||
else if (data[11] == 0x40)
|
||||
touch = TOUCH_PEN;
|
||||
else if (data[11] == 0x80)
|
||||
touch = TOUCH_FINGER;
|
||||
else
|
||||
touch = TOUCH_TWOFINGER;
|
||||
|
||||
/* joypad */
|
||||
input_report_key(udraw->joy_input_dev, BTN_WEST, data[0] & 1);
|
||||
input_report_key(udraw->joy_input_dev, BTN_SOUTH, !!(data[0] & 2));
|
||||
input_report_key(udraw->joy_input_dev, BTN_EAST, !!(data[0] & 4));
|
||||
input_report_key(udraw->joy_input_dev, BTN_NORTH, !!(data[0] & 8));
|
||||
|
||||
input_report_key(udraw->joy_input_dev, BTN_SELECT, !!(data[1] & 1));
|
||||
input_report_key(udraw->joy_input_dev, BTN_START, !!(data[1] & 2));
|
||||
input_report_key(udraw->joy_input_dev, BTN_MODE, !!(data[1] & 16));
|
||||
|
||||
x = y = 0;
|
||||
switch (data[2]) {
|
||||
case 0x0:
|
||||
y = -127;
|
||||
break;
|
||||
case 0x1:
|
||||
y = -127;
|
||||
x = 127;
|
||||
break;
|
||||
case 0x2:
|
||||
x = 127;
|
||||
break;
|
||||
case 0x3:
|
||||
y = 127;
|
||||
x = 127;
|
||||
break;
|
||||
case 0x4:
|
||||
y = 127;
|
||||
break;
|
||||
case 0x5:
|
||||
y = 127;
|
||||
x = -127;
|
||||
break;
|
||||
case 0x6:
|
||||
x = -127;
|
||||
break;
|
||||
case 0x7:
|
||||
y = -127;
|
||||
x = -127;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
input_report_abs(udraw->joy_input_dev, ABS_X, x);
|
||||
input_report_abs(udraw->joy_input_dev, ABS_Y, y);
|
||||
|
||||
input_sync(udraw->joy_input_dev);
|
||||
|
||||
/* For pen and touchpad */
|
||||
x = y = 0;
|
||||
if (touch != TOUCH_NONE) {
|
||||
if (data[15] != 0x0F)
|
||||
x = data[15] * 256 + data[17];
|
||||
if (data[16] != 0x0F)
|
||||
y = data[16] * 256 + data[18];
|
||||
}
|
||||
|
||||
if (touch == TOUCH_FINGER) {
|
||||
/* Save the last one-finger touch */
|
||||
udraw->last_one_finger_x = x;
|
||||
udraw->last_one_finger_y = y;
|
||||
udraw->last_two_finger_x = -1;
|
||||
udraw->last_two_finger_y = -1;
|
||||
} else if (touch == TOUCH_TWOFINGER) {
|
||||
/*
|
||||
* We have a problem because x/y is the one for the
|
||||
* second finger but we want the first finger given
|
||||
* to user-space otherwise it'll look as if it jumped.
|
||||
*
|
||||
* See the udraw struct definition for why this was
|
||||
* implemented this way.
|
||||
*/
|
||||
if (udraw->last_two_finger_x == -1) {
|
||||
/* Save the position of the 2nd finger */
|
||||
udraw->last_two_finger_x = x;
|
||||
udraw->last_two_finger_y = y;
|
||||
|
||||
x = udraw->last_one_finger_x;
|
||||
y = udraw->last_one_finger_y;
|
||||
} else {
|
||||
/*
|
||||
* Offset the 2-finger coords using the
|
||||
* saved data from the first finger
|
||||
*/
|
||||
x = x - (udraw->last_two_finger_x
|
||||
- udraw->last_one_finger_x);
|
||||
y = y - (udraw->last_two_finger_y
|
||||
- udraw->last_one_finger_y);
|
||||
}
|
||||
}
|
||||
|
||||
/* touchpad */
|
||||
if (touch == TOUCH_FINGER || touch == TOUCH_TWOFINGER) {
|
||||
input_report_key(udraw->touch_input_dev, BTN_TOUCH, 1);
|
||||
input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER,
|
||||
touch == TOUCH_FINGER);
|
||||
input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP,
|
||||
touch == TOUCH_TWOFINGER);
|
||||
|
||||
input_report_abs(udraw->touch_input_dev, ABS_X, x);
|
||||
input_report_abs(udraw->touch_input_dev, ABS_Y, y);
|
||||
} else {
|
||||
input_report_key(udraw->touch_input_dev, BTN_TOUCH, 0);
|
||||
input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER, 0);
|
||||
input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, 0);
|
||||
}
|
||||
input_sync(udraw->touch_input_dev);
|
||||
|
||||
/* pen */
|
||||
if (touch == TOUCH_PEN) {
|
||||
int level;
|
||||
|
||||
level = clamp(data[13] - PRESSURE_OFFSET,
|
||||
0, MAX_PRESSURE);
|
||||
|
||||
input_report_key(udraw->pen_input_dev, BTN_TOUCH, (level != 0));
|
||||
input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 1);
|
||||
input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, level);
|
||||
input_report_abs(udraw->pen_input_dev, ABS_X, x);
|
||||
input_report_abs(udraw->pen_input_dev, ABS_Y, y);
|
||||
} else {
|
||||
input_report_key(udraw->pen_input_dev, BTN_TOUCH, 0);
|
||||
input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 0);
|
||||
input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, 0);
|
||||
}
|
||||
input_sync(udraw->pen_input_dev);
|
||||
|
||||
/* accel */
|
||||
x = (data[19] + (data[20] << 8));
|
||||
x = clamp_accel(x, AXIS_X);
|
||||
y = (data[21] + (data[22] << 8));
|
||||
y = clamp_accel(y, AXIS_Y);
|
||||
z = (data[23] + (data[24] << 8));
|
||||
z = clamp_accel(z, AXIS_Z);
|
||||
input_report_abs(udraw->accel_input_dev, ABS_X, x);
|
||||
input_report_abs(udraw->accel_input_dev, ABS_Y, y);
|
||||
input_report_abs(udraw->accel_input_dev, ABS_Z, z);
|
||||
input_sync(udraw->accel_input_dev);
|
||||
|
||||
/* let hidraw and hiddev handle the report */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udraw_open(struct input_dev *dev)
|
||||
{
|
||||
struct udraw *udraw = input_get_drvdata(dev);
|
||||
|
||||
return hid_hw_open(udraw->hdev);
|
||||
}
|
||||
|
||||
static void udraw_close(struct input_dev *dev)
|
||||
{
|
||||
struct udraw *udraw = input_get_drvdata(dev);
|
||||
|
||||
hid_hw_close(udraw->hdev);
|
||||
}
|
||||
|
||||
static struct input_dev *allocate_and_setup(struct hid_device *hdev,
|
||||
const char *name)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
|
||||
input_dev = devm_input_allocate_device(&hdev->dev);
|
||||
if (!input_dev)
|
||||
return NULL;
|
||||
|
||||
input_dev->name = name;
|
||||
input_dev->phys = hdev->phys;
|
||||
input_dev->dev.parent = &hdev->dev;
|
||||
input_dev->open = udraw_open;
|
||||
input_dev->close = udraw_close;
|
||||
input_dev->uniq = hdev->uniq;
|
||||
input_dev->id.bustype = hdev->bus;
|
||||
input_dev->id.vendor = hdev->vendor;
|
||||
input_dev->id.product = hdev->product;
|
||||
input_dev->id.version = hdev->version;
|
||||
input_set_drvdata(input_dev, hid_get_drvdata(hdev));
|
||||
|
||||
return input_dev;
|
||||
}
|
||||
|
||||
static bool udraw_setup_touch(struct udraw *udraw,
|
||||
struct hid_device *hdev)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
|
||||
input_dev = allocate_and_setup(hdev, DEVICE_NAME " Touchpad");
|
||||
if (!input_dev)
|
||||
return false;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0);
|
||||
input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0);
|
||||
input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT);
|
||||
|
||||
set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
||||
|
||||
set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
||||
|
||||
udraw->touch_input_dev = input_dev;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool udraw_setup_pen(struct udraw *udraw,
|
||||
struct hid_device *hdev)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
|
||||
input_dev = allocate_and_setup(hdev, DEVICE_NAME " Pen");
|
||||
if (!input_dev)
|
||||
return false;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0);
|
||||
input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0);
|
||||
input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE,
|
||||
0, MAX_PRESSURE, 0, 0);
|
||||
|
||||
set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
||||
|
||||
set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
||||
|
||||
udraw->pen_input_dev = input_dev;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool udraw_setup_accel(struct udraw *udraw,
|
||||
struct hid_device *hdev)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
|
||||
input_dev = allocate_and_setup(hdev, DEVICE_NAME " Accelerometer");
|
||||
if (!input_dev)
|
||||
return false;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_ABS);
|
||||
|
||||
/* 1G accel is reported as ~256, so clamp to 2G */
|
||||
input_set_abs_params(input_dev, ABS_X, -512, 512, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, -512, 512, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Z, -512, 512, 0, 0);
|
||||
|
||||
set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit);
|
||||
|
||||
udraw->accel_input_dev = input_dev;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool udraw_setup_joypad(struct udraw *udraw,
|
||||
struct hid_device *hdev)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
|
||||
input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad");
|
||||
if (!input_dev)
|
||||
return false;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
|
||||
set_bit(BTN_SOUTH, input_dev->keybit);
|
||||
set_bit(BTN_NORTH, input_dev->keybit);
|
||||
set_bit(BTN_EAST, input_dev->keybit);
|
||||
set_bit(BTN_WEST, input_dev->keybit);
|
||||
set_bit(BTN_SELECT, input_dev->keybit);
|
||||
set_bit(BTN_START, input_dev->keybit);
|
||||
set_bit(BTN_MODE, input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X, -127, 127, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, -127, 127, 0, 0);
|
||||
|
||||
udraw->joy_input_dev = input_dev;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int udraw_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
struct udraw *udraw;
|
||||
int ret;
|
||||
|
||||
udraw = devm_kzalloc(&hdev->dev, sizeof(struct udraw), GFP_KERNEL);
|
||||
if (!udraw)
|
||||
return -ENOMEM;
|
||||
|
||||
udraw->hdev = hdev;
|
||||
udraw->last_two_finger_x = -1;
|
||||
udraw->last_two_finger_y = -1;
|
||||
|
||||
hid_set_drvdata(hdev, udraw);
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "parse failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!udraw_setup_joypad(udraw, hdev) ||
|
||||
!udraw_setup_touch(udraw, hdev) ||
|
||||
!udraw_setup_pen(udraw, hdev) ||
|
||||
!udraw_setup_accel(udraw, hdev)) {
|
||||
hid_err(hdev, "could not allocate interfaces\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = input_register_device(udraw->joy_input_dev) ||
|
||||
input_register_device(udraw->touch_input_dev) ||
|
||||
input_register_device(udraw->pen_input_dev) ||
|
||||
input_register_device(udraw->accel_input_dev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "failed to register interfaces\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_device_id udraw_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, udraw_devices);
|
||||
|
||||
static struct hid_driver udraw_driver = {
|
||||
.name = "hid-udraw",
|
||||
.id_table = udraw_devices,
|
||||
.raw_event = udraw_raw_event,
|
||||
.probe = udraw_probe,
|
||||
};
|
||||
module_hid_driver(udraw_driver);
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm.h>
|
||||
@@ -37,10 +38,15 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/i2c/i2c-hid.h>
|
||||
|
||||
#include "../hid-ids.h"
|
||||
|
||||
/* quirks to control the device */
|
||||
#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0)
|
||||
|
||||
/* flags */
|
||||
#define I2C_HID_STARTED 0
|
||||
#define I2C_HID_RESET_PENDING 1
|
||||
@@ -143,10 +149,9 @@ struct i2c_hid {
|
||||
char *argsbuf; /* Command arguments buffer */
|
||||
|
||||
unsigned long flags; /* device flags */
|
||||
unsigned long quirks; /* Various quirks */
|
||||
|
||||
wait_queue_head_t wait; /* For waiting the interrupt */
|
||||
struct gpio_desc *desc;
|
||||
int irq;
|
||||
|
||||
struct i2c_hid_platform_data pdata;
|
||||
|
||||
@@ -154,6 +159,39 @@ struct i2c_hid {
|
||||
struct mutex reset_lock;
|
||||
};
|
||||
|
||||
static const struct i2c_hid_quirks {
|
||||
__u16 idVendor;
|
||||
__u16 idProduct;
|
||||
__u32 quirks;
|
||||
} i2c_hid_quirks[] = {
|
||||
{ USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8752,
|
||||
I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
|
||||
{ USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8755,
|
||||
I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* i2c_hid_lookup_quirk: return any quirks associated with a I2C HID device
|
||||
* @idVendor: the 16-bit vendor ID
|
||||
* @idProduct: the 16-bit product ID
|
||||
*
|
||||
* Returns: a u32 quirks value.
|
||||
*/
|
||||
static u32 i2c_hid_lookup_quirk(const u16 idVendor, const u16 idProduct)
|
||||
{
|
||||
u32 quirks = 0;
|
||||
int n;
|
||||
|
||||
for (n = 0; i2c_hid_quirks[n].idVendor; n++)
|
||||
if (i2c_hid_quirks[n].idVendor == idVendor &&
|
||||
(i2c_hid_quirks[n].idProduct == (__u16)HID_ANY_ID ||
|
||||
i2c_hid_quirks[n].idProduct == idProduct))
|
||||
quirks = i2c_hid_quirks[n].quirks;
|
||||
|
||||
return quirks;
|
||||
}
|
||||
|
||||
static int __i2c_hid_command(struct i2c_client *client,
|
||||
const struct i2c_hid_cmd *command, u8 reportID,
|
||||
u8 reportType, u8 *args, int args_len,
|
||||
@@ -346,11 +384,27 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)
|
||||
|
||||
i2c_hid_dbg(ihid, "%s\n", __func__);
|
||||
|
||||
/*
|
||||
* Some devices require to send a command to wakeup before power on.
|
||||
* The call will get a return value (EREMOTEIO) but device will be
|
||||
* triggered and activated. After that, it goes like a normal device.
|
||||
*/
|
||||
if (power_state == I2C_HID_PWR_ON &&
|
||||
ihid->quirks & I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV) {
|
||||
ret = i2c_hid_command(client, &hid_set_power_cmd, NULL, 0);
|
||||
|
||||
/* Device was already activated */
|
||||
if (!ret)
|
||||
goto set_pwr_exit;
|
||||
}
|
||||
|
||||
ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state,
|
||||
0, NULL, 0, NULL, 0);
|
||||
|
||||
if (ret)
|
||||
dev_err(&client->dev, "failed to change power setting.\n");
|
||||
|
||||
set_pwr_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -716,9 +770,11 @@ static int i2c_hid_start(struct hid_device *hid)
|
||||
i2c_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize);
|
||||
|
||||
if (bufsize > ihid->bufsize) {
|
||||
disable_irq(client->irq);
|
||||
i2c_hid_free_buffers(ihid);
|
||||
|
||||
ret = i2c_hid_alloc_buffers(ihid, bufsize);
|
||||
enable_irq(client->irq);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -806,18 +862,21 @@ static struct hid_ll_driver i2c_hid_ll_driver = {
|
||||
static int i2c_hid_init_irq(struct i2c_client *client)
|
||||
{
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
unsigned long irqflags = 0;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&client->dev, "Requesting IRQ: %d\n", ihid->irq);
|
||||
dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq);
|
||||
|
||||
ret = request_threaded_irq(ihid->irq, NULL, i2c_hid_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
client->name, ihid);
|
||||
if (!irq_get_trigger_type(client->irq))
|
||||
irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq,
|
||||
irqflags | IRQF_ONESHOT, client->name, ihid);
|
||||
if (ret < 0) {
|
||||
dev_warn(&client->dev,
|
||||
"Could not register for %s interrupt, irq = %d,"
|
||||
" ret = %d\n",
|
||||
client->name, ihid->irq, ret);
|
||||
client->name, client->irq, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -864,14 +923,6 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
/* Default GPIO mapping */
|
||||
static const struct acpi_gpio_params i2c_hid_irq_gpio = { 0, 0, true };
|
||||
static const struct acpi_gpio_mapping i2c_hid_acpi_gpios[] = {
|
||||
{ "gpios", &i2c_hid_irq_gpio, 1 },
|
||||
{ },
|
||||
};
|
||||
|
||||
static int i2c_hid_acpi_pdata(struct i2c_client *client,
|
||||
struct i2c_hid_platform_data *pdata)
|
||||
{
|
||||
@@ -882,7 +933,6 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,
|
||||
union acpi_object *obj;
|
||||
struct acpi_device *adev;
|
||||
acpi_handle handle;
|
||||
int ret;
|
||||
|
||||
handle = ACPI_HANDLE(&client->dev);
|
||||
if (!handle || acpi_bus_get_device(handle, &adev))
|
||||
@@ -898,9 +948,7 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,
|
||||
pdata->hid_descriptor_address = obj->integer.value;
|
||||
ACPI_FREE(obj);
|
||||
|
||||
/* GPIOs are optional */
|
||||
ret = acpi_dev_add_driver_gpios(adev, i2c_hid_acpi_gpios);
|
||||
return ret < 0 && ret != -ENXIO ? ret : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id i2c_hid_acpi_match[] = {
|
||||
@@ -964,6 +1012,19 @@ static int i2c_hid_probe(struct i2c_client *client,
|
||||
|
||||
dbg_hid("HID probe called for i2c 0x%02x\n", client->addr);
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(&client->dev,
|
||||
"HID over i2c has not been provided an Int IRQ\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (client->irq < 0) {
|
||||
if (client->irq != -EPROBE_DEFER)
|
||||
dev_err(&client->dev,
|
||||
"HID over i2c doesn't have a valid IRQ\n");
|
||||
return client->irq;
|
||||
}
|
||||
|
||||
ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL);
|
||||
if (!ihid)
|
||||
return -ENOMEM;
|
||||
@@ -983,23 +1044,6 @@ static int i2c_hid_probe(struct i2c_client *client,
|
||||
ihid->pdata = *platform_data;
|
||||
}
|
||||
|
||||
if (client->irq > 0) {
|
||||
ihid->irq = client->irq;
|
||||
} else if (ACPI_COMPANION(&client->dev)) {
|
||||
ihid->desc = gpiod_get(&client->dev, NULL, GPIOD_IN);
|
||||
if (IS_ERR(ihid->desc)) {
|
||||
dev_err(&client->dev, "Failed to get GPIO interrupt\n");
|
||||
return PTR_ERR(ihid->desc);
|
||||
}
|
||||
|
||||
ihid->irq = gpiod_to_irq(ihid->desc);
|
||||
if (ihid->irq < 0) {
|
||||
gpiod_put(ihid->desc);
|
||||
dev_err(&client->dev, "Failed to convert GPIO to IRQ\n");
|
||||
return ihid->irq;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, ihid);
|
||||
|
||||
ihid->client = client;
|
||||
@@ -1050,6 +1094,8 @@ static int i2c_hid_probe(struct i2c_client *client,
|
||||
client->name, hid->vendor, hid->product);
|
||||
strlcpy(hid->phys, dev_name(&client->dev), sizeof(hid->phys));
|
||||
|
||||
ihid->quirks = i2c_hid_lookup_quirk(hid->vendor, hid->product);
|
||||
|
||||
ret = hid_add_device(hid);
|
||||
if (ret) {
|
||||
if (ret != -ENODEV)
|
||||
@@ -1064,16 +1110,13 @@ err_mem_free:
|
||||
hid_destroy_device(hid);
|
||||
|
||||
err_irq:
|
||||
free_irq(ihid->irq, ihid);
|
||||
free_irq(client->irq, ihid);
|
||||
|
||||
err_pm:
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
pm_runtime_disable(&client->dev);
|
||||
|
||||
err:
|
||||
if (ihid->desc)
|
||||
gpiod_put(ihid->desc);
|
||||
|
||||
i2c_hid_free_buffers(ihid);
|
||||
kfree(ihid);
|
||||
return ret;
|
||||
@@ -1092,18 +1135,13 @@ static int i2c_hid_remove(struct i2c_client *client)
|
||||
hid = ihid->hid;
|
||||
hid_destroy_device(hid);
|
||||
|
||||
free_irq(ihid->irq, ihid);
|
||||
free_irq(client->irq, ihid);
|
||||
|
||||
if (ihid->bufsize)
|
||||
i2c_hid_free_buffers(ihid);
|
||||
|
||||
if (ihid->desc)
|
||||
gpiod_put(ihid->desc);
|
||||
|
||||
kfree(ihid);
|
||||
|
||||
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&client->dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1142,11 +1180,11 @@ static int i2c_hid_suspend(struct device *dev)
|
||||
/* Save some power */
|
||||
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
|
||||
|
||||
disable_irq(ihid->irq);
|
||||
disable_irq(client->irq);
|
||||
}
|
||||
|
||||
if (device_may_wakeup(&client->dev)) {
|
||||
wake_status = enable_irq_wake(ihid->irq);
|
||||
wake_status = enable_irq_wake(client->irq);
|
||||
if (!wake_status)
|
||||
ihid->irq_wake_enabled = true;
|
||||
else
|
||||
@@ -1166,7 +1204,7 @@ static int i2c_hid_resume(struct device *dev)
|
||||
int wake_status;
|
||||
|
||||
if (device_may_wakeup(&client->dev) && ihid->irq_wake_enabled) {
|
||||
wake_status = disable_irq_wake(ihid->irq);
|
||||
wake_status = disable_irq_wake(client->irq);
|
||||
if (!wake_status)
|
||||
ihid->irq_wake_enabled = false;
|
||||
else
|
||||
@@ -1179,7 +1217,7 @@ static int i2c_hid_resume(struct device *dev)
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
enable_irq(ihid->irq);
|
||||
enable_irq(client->irq);
|
||||
ret = i2c_hid_hwreset(client);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1197,19 +1235,17 @@ static int i2c_hid_resume(struct device *dev)
|
||||
static int i2c_hid_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
|
||||
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
|
||||
disable_irq(ihid->irq);
|
||||
disable_irq(client->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_hid_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
|
||||
enable_irq(ihid->irq);
|
||||
enable_irq(client->irq);
|
||||
i2c_hid_set_power(client, I2C_HID_PWR_ON);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include "client.h"
|
||||
#include "hw-ish.h"
|
||||
#include "utils.h"
|
||||
#include "hbm.h"
|
||||
|
||||
/* For FW reset flow */
|
||||
@@ -310,6 +309,7 @@ static int write_ipc_from_queue(struct ishtp_device *dev)
|
||||
((uint32_t)tv_utc.tv_usec);
|
||||
ts_format.ts1_source = HOST_SYSTEM_TIME_USEC;
|
||||
ts_format.ts2_source = HOST_UTC_TIME_USEC;
|
||||
ts_format.reserved = 0;
|
||||
|
||||
time_update.primary_host_time = usec_system;
|
||||
time_update.secondary_host_time = usec_utc;
|
||||
@@ -427,6 +427,59 @@ static int ipc_send_mng_msg(struct ishtp_device *dev, uint32_t msg_code,
|
||||
sizeof(uint32_t) + size);
|
||||
}
|
||||
|
||||
#define WAIT_FOR_FW_RDY 0x1
|
||||
#define WAIT_FOR_INPUT_RDY 0x2
|
||||
|
||||
/**
|
||||
* timed_wait_for_timeout() - wait special event with timeout
|
||||
* @dev: ISHTP device pointer
|
||||
* @condition: indicate the condition for waiting
|
||||
* @timeinc: time slice for every wait cycle, in ms
|
||||
* @timeout: time in ms for timeout
|
||||
*
|
||||
* This function will check special event to be ready in a loop, the loop
|
||||
* period is specificd in timeinc. Wait timeout will causes failure.
|
||||
*
|
||||
* Return: 0 for success else failure code
|
||||
*/
|
||||
static int timed_wait_for_timeout(struct ishtp_device *dev, int condition,
|
||||
unsigned int timeinc, unsigned int timeout)
|
||||
{
|
||||
bool complete = false;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
if (condition == WAIT_FOR_FW_RDY) {
|
||||
complete = ishtp_fw_is_ready(dev);
|
||||
} else if (condition == WAIT_FOR_INPUT_RDY) {
|
||||
complete = ish_is_input_ready(dev);
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!complete) {
|
||||
unsigned long left_time;
|
||||
|
||||
left_time = msleep_interruptible(timeinc);
|
||||
timeout -= (timeinc - left_time);
|
||||
}
|
||||
} while (!complete && timeout > 0);
|
||||
|
||||
if (complete)
|
||||
ret = 0;
|
||||
else
|
||||
ret = -EBUSY;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define TIME_SLICE_FOR_FW_RDY_MS 100
|
||||
#define TIME_SLICE_FOR_INPUT_RDY_MS 100
|
||||
#define TIMEOUT_FOR_FW_RDY_MS 2000
|
||||
#define TIMEOUT_FOR_INPUT_RDY_MS 2000
|
||||
|
||||
/**
|
||||
* ish_fw_reset_handler() - FW reset handler
|
||||
* @dev: ishtp device pointer
|
||||
@@ -456,8 +509,8 @@ static int ish_fw_reset_handler(struct ishtp_device *dev)
|
||||
ishtp_reset_handler(dev);
|
||||
|
||||
if (!ish_is_input_ready(dev))
|
||||
timed_wait_for_timeout(WAIT_FOR_SEND_SLICE,
|
||||
ish_is_input_ready(dev), (2 * HZ));
|
||||
timed_wait_for_timeout(dev, WAIT_FOR_INPUT_RDY,
|
||||
TIME_SLICE_FOR_INPUT_RDY_MS, TIMEOUT_FOR_INPUT_RDY_MS);
|
||||
|
||||
/* ISH FW is dead */
|
||||
if (!ish_is_input_ready(dev))
|
||||
@@ -472,8 +525,8 @@ static int ish_fw_reset_handler(struct ishtp_device *dev)
|
||||
sizeof(uint32_t));
|
||||
|
||||
/* Wait for ISH FW'es ILUP and ISHTP_READY */
|
||||
timed_wait_for_timeout(WAIT_FOR_SEND_SLICE, ishtp_fw_is_ready(dev),
|
||||
(2 * HZ));
|
||||
timed_wait_for_timeout(dev, WAIT_FOR_FW_RDY,
|
||||
TIME_SLICE_FOR_FW_RDY_MS, TIMEOUT_FOR_FW_RDY_MS);
|
||||
if (!ishtp_fw_is_ready(dev)) {
|
||||
/* ISH FW is dead */
|
||||
uint32_t ish_status;
|
||||
@@ -487,6 +540,8 @@ static int ish_fw_reset_handler(struct ishtp_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TIMEOUT_FOR_HW_RDY_MS 300
|
||||
|
||||
/**
|
||||
* ish_fw_reset_work_fn() - FW reset worker function
|
||||
* @unused: not used
|
||||
@@ -500,7 +555,7 @@ static void fw_reset_work_fn(struct work_struct *unused)
|
||||
rv = ish_fw_reset_handler(ishtp_dev);
|
||||
if (!rv) {
|
||||
/* ISH is ILUP & ISHTP-ready. Restart ISHTP */
|
||||
schedule_timeout(HZ / 3);
|
||||
msleep_interruptible(TIMEOUT_FOR_HW_RDY_MS);
|
||||
ishtp_dev->recvd_hw_ready = 1;
|
||||
wake_up_interruptible(&ishtp_dev->wait_hw_ready);
|
||||
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Utility macros of ISH
|
||||
*
|
||||
* Copyright (c) 2014-2016, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
#ifndef UTILS__H
|
||||
#define UTILS__H
|
||||
|
||||
#define WAIT_FOR_SEND_SLICE (HZ / 10)
|
||||
#define WAIT_FOR_CONNECT_SLICE (HZ / 10)
|
||||
|
||||
/*
|
||||
* Waits for specified event when a thread that triggers event can't signal
|
||||
* Also, waits *at_least* `timeinc` after condition is satisfied
|
||||
*/
|
||||
#define timed_wait_for(timeinc, condition) \
|
||||
do { \
|
||||
int completed = 0; \
|
||||
do { \
|
||||
unsigned long j; \
|
||||
int done = 0; \
|
||||
\
|
||||
completed = (condition); \
|
||||
for (j = jiffies, done = 0; !done; ) { \
|
||||
schedule_timeout(timeinc); \
|
||||
if (time_is_before_eq_jiffies(j + timeinc)) \
|
||||
done = 1; \
|
||||
} \
|
||||
} while (!(completed)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* Waits for specified event when a thread that triggers event
|
||||
* can't signal with timeout (use whenever we may hang)
|
||||
*/
|
||||
#define timed_wait_for_timeout(timeinc, condition, timeout) \
|
||||
do { \
|
||||
int t = timeout; \
|
||||
do { \
|
||||
unsigned long j; \
|
||||
int done = 0; \
|
||||
\
|
||||
for (j = jiffies, done = 0; !done; ) { \
|
||||
schedule_timeout(timeinc); \
|
||||
if (time_is_before_eq_jiffies(j + timeinc)) \
|
||||
done = 1; \
|
||||
} \
|
||||
t -= timeinc; \
|
||||
if (t <= 0) \
|
||||
break; \
|
||||
} while (!(condition)); \
|
||||
} while (0)
|
||||
|
||||
#endif /* UTILS__H */
|
||||
@@ -585,14 +585,7 @@ int ishtp_bus_new_client(struct ishtp_device *dev)
|
||||
*/
|
||||
i = dev->fw_client_presentation_num - 1;
|
||||
device_uuid = dev->fw_clients[i].props.protocol_name;
|
||||
dev_name = kasprintf(GFP_KERNEL,
|
||||
"{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
|
||||
device_uuid.b[3], device_uuid.b[2], device_uuid.b[1],
|
||||
device_uuid.b[0], device_uuid.b[5], device_uuid.b[4],
|
||||
device_uuid.b[7], device_uuid.b[6], device_uuid.b[8],
|
||||
device_uuid.b[9], device_uuid.b[10], device_uuid.b[11],
|
||||
device_uuid.b[12], device_uuid.b[13], device_uuid.b[14],
|
||||
device_uuid.b[15]);
|
||||
dev_name = kasprintf(GFP_KERNEL, "{%pUL}", device_uuid.b);
|
||||
if (!dev_name)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -378,11 +378,10 @@ static void ishtp_hbm_cl_disconnect_res(struct ishtp_device *dev,
|
||||
list_for_each_entry(cl, &dev->cl_list, link) {
|
||||
if (!rs->status && ishtp_hbm_cl_addr_equal(cl, rs)) {
|
||||
cl->state = ISHTP_CL_DISCONNECTED;
|
||||
wake_up_interruptible(&cl->wait_ctrl_res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cl)
|
||||
wake_up_interruptible(&cl->wait_ctrl_res);
|
||||
spin_unlock_irqrestore(&dev->cl_list_lock, flags);
|
||||
}
|
||||
|
||||
@@ -431,11 +430,10 @@ static void ishtp_hbm_cl_connect_res(struct ishtp_device *dev,
|
||||
cl->state = ISHTP_CL_DISCONNECTED;
|
||||
cl->status = -ENODEV;
|
||||
}
|
||||
wake_up_interruptible(&cl->wait_ctrl_res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cl)
|
||||
wake_up_interruptible(&cl->wait_ctrl_res);
|
||||
spin_unlock_irqrestore(&dev->cl_list_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
@@ -1459,7 +1459,7 @@ static int hid_post_reset(struct usb_interface *intf)
|
||||
rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL);
|
||||
if (!rdesc) {
|
||||
dbg_hid("couldn't allocate rdesc memory (post_reset)\n");
|
||||
return 1;
|
||||
return -ENOMEM;
|
||||
}
|
||||
status = hid_get_class_descriptor(dev,
|
||||
interface->desc.bInterfaceNumber,
|
||||
@@ -1467,13 +1467,13 @@ static int hid_post_reset(struct usb_interface *intf)
|
||||
if (status < 0) {
|
||||
dbg_hid("reading report descriptor failed (post_reset)\n");
|
||||
kfree(rdesc);
|
||||
return 1;
|
||||
return status;
|
||||
}
|
||||
status = memcmp(rdesc, hid->dev_rdesc, hid->dev_rsize);
|
||||
kfree(rdesc);
|
||||
if (status != 0) {
|
||||
dbg_hid("report descriptor changed\n");
|
||||
return 1;
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/* No need to do another reset or clear a halted endpoint */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user