mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 03:27:44 -04:00
Merge branches 'for-4.0/upstream-fixes', 'for-4.1/genius', 'for-4.1/huion-uclogic-merge', 'for-4.1/i2c-hid', 'for-4.1/kconfig-drop-expert-dependency', 'for-4.1/logitech', 'for-4.1/multitouch', 'for-4.1/rmi', 'for-4.1/sony', 'for-4.1/upstream' and 'for-4.1/wacom' into for-linus
This commit is contained in:
parent
43faadfe96
feb6faf1e5
ee20fe2386
a485923efb
7af05e73cd
b832da5602
2c6e0277e1
f097deef59
2e701a359a
8fec02a73e
b4bf2120d4
commit
05f6d02521
7668 changed files with 355131 additions and 185271 deletions
|
|
@ -92,7 +92,7 @@ menu "Special HID drivers"
|
|||
depends on HID
|
||||
|
||||
config HID_A4TECH
|
||||
tristate "A4 tech mice" if EXPERT
|
||||
tristate "A4 tech mice"
|
||||
depends on HID
|
||||
default !EXPERT
|
||||
---help---
|
||||
|
|
@ -113,7 +113,7 @@ config HID_ACRUX_FF
|
|||
game controllers.
|
||||
|
||||
config HID_APPLE
|
||||
tristate "Apple {i,Power,Mac}Books" if EXPERT
|
||||
tristate "Apple {i,Power,Mac}Books"
|
||||
depends on HID
|
||||
default !EXPERT
|
||||
---help---
|
||||
|
|
@ -141,7 +141,7 @@ config HID_AUREAL
|
|||
Support for Aureal Cy se W-01RN Remote Controller and other Aureal derived remotes.
|
||||
|
||||
config HID_BELKIN
|
||||
tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT
|
||||
tristate "Belkin Flip KVM and Wireless keyboard"
|
||||
depends on HID
|
||||
default !EXPERT
|
||||
---help---
|
||||
|
|
@ -158,14 +158,14 @@ config HID_BETOP_FF
|
|||
- BETOP 2185 PC & BFM MODE
|
||||
|
||||
config HID_CHERRY
|
||||
tristate "Cherry Cymotion keyboard" if EXPERT
|
||||
tristate "Cherry Cymotion keyboard"
|
||||
depends on HID
|
||||
default !EXPERT
|
||||
---help---
|
||||
Support for Cherry Cymotion keyboard.
|
||||
|
||||
config HID_CHICONY
|
||||
tristate "Chicony Tactical pad" if EXPERT
|
||||
tristate "Chicony Tactical pad"
|
||||
depends on HID
|
||||
default !EXPERT
|
||||
---help---
|
||||
|
|
@ -196,7 +196,7 @@ config HID_CP2112
|
|||
customizable USB descriptor fields are exposed as sysfs attributes.
|
||||
|
||||
config HID_CYPRESS
|
||||
tristate "Cypress mouse and barcode readers" if EXPERT
|
||||
tristate "Cypress mouse and barcode readers"
|
||||
depends on HID
|
||||
default !EXPERT
|
||||
---help---
|
||||
|
|
@ -245,7 +245,7 @@ config HID_ELO
|
|||
different devices than those handled by CONFIG_TOUCHSCREEN_USB_ELO.
|
||||
|
||||
config HID_EZKEY
|
||||
tristate "Ezkey BTC 8193 keyboard" if EXPERT
|
||||
tristate "Ezkey BTC 8193 keyboard"
|
||||
depends on HID
|
||||
default !EXPERT
|
||||
---help---
|
||||
|
|
@ -286,12 +286,6 @@ config HID_GT683R
|
|||
Currently the following devices are know to be supported:
|
||||
- MSI GT683R
|
||||
|
||||
config HID_HUION
|
||||
tristate "Huion tablets"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Huion 580 tablet.
|
||||
|
||||
config HID_KEYTOUCH
|
||||
tristate "Keytouch HID devices"
|
||||
depends on HID
|
||||
|
|
@ -312,9 +306,9 @@ config HID_KYE
|
|||
|
||||
config HID_UCLOGIC
|
||||
tristate "UC-Logic"
|
||||
depends on HID
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for UC-Logic tablets.
|
||||
Support for UC-Logic and Huion tablets.
|
||||
|
||||
config HID_WALTOP
|
||||
tristate "Waltop"
|
||||
|
|
@ -344,7 +338,7 @@ config HID_TWINHAN
|
|||
Support for Twinhan IR remote control.
|
||||
|
||||
config HID_KENSINGTON
|
||||
tristate "Kensington Slimblade Trackball" if EXPERT
|
||||
tristate "Kensington Slimblade Trackball"
|
||||
depends on HID
|
||||
default !EXPERT
|
||||
---help---
|
||||
|
|
@ -372,7 +366,7 @@ config HID_LENOVO
|
|||
- ThinkPad Compact USB Keyboard with TrackPoint (supports Fn keys)
|
||||
|
||||
config HID_LOGITECH
|
||||
tristate "Logitech devices" if EXPERT
|
||||
tristate "Logitech devices"
|
||||
depends on HID
|
||||
default !EXPERT
|
||||
---help---
|
||||
|
|
@ -461,14 +455,14 @@ config HID_MAGICMOUSE
|
|||
Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad.
|
||||
|
||||
config HID_MICROSOFT
|
||||
tristate "Microsoft non-fully HID-compliant devices" if EXPERT
|
||||
tristate "Microsoft non-fully HID-compliant devices"
|
||||
depends on HID
|
||||
default !EXPERT
|
||||
---help---
|
||||
Support for Microsoft devices that are not fully compliant with HID standard.
|
||||
|
||||
config HID_MONTEREY
|
||||
tristate "Monterey Genius KB29E keyboard" if EXPERT
|
||||
tristate "Monterey Genius KB29E keyboard"
|
||||
depends on HID
|
||||
default !EXPERT
|
||||
---help---
|
||||
|
|
@ -638,7 +632,6 @@ config HID_PICOLCD_CIR
|
|||
|
||||
config HID_PLANTRONICS
|
||||
tristate "Plantronics USB HID Driver"
|
||||
default !EXPERT
|
||||
depends on HID
|
||||
---help---
|
||||
Provides HID support for Plantronics telephony devices.
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
|
|||
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
|
||||
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
|
||||
obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o
|
||||
obj-$(CONFIG_HID_HUION) += hid-huion.o
|
||||
obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o
|
||||
obj-$(CONFIG_HID_ICADE) += hid-icade.o
|
||||
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
|
||||
|
|
|
|||
|
|
@ -1562,12 +1562,26 @@ read_report_descriptor(struct file *filp, struct kobject *kobj,
|
|||
return count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
show_country(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
||||
|
||||
return sprintf(buf, "%02x\n", hdev->country & 0xff);
|
||||
}
|
||||
|
||||
static struct bin_attribute dev_bin_attr_report_desc = {
|
||||
.attr = { .name = "report_descriptor", .mode = 0444 },
|
||||
.read = read_report_descriptor,
|
||||
.size = HID_MAX_DESCRIPTOR_SIZE,
|
||||
};
|
||||
|
||||
static struct device_attribute dev_attr_country = {
|
||||
.attr = { .name = "country", .mode = 0444 },
|
||||
.show = show_country,
|
||||
};
|
||||
|
||||
int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
|
||||
{
|
||||
static const char *types[] = { "Device", "Pointer", "Mouse", "Device",
|
||||
|
|
@ -1646,6 +1660,11 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
|
|||
bus = "<UNKNOWN>";
|
||||
}
|
||||
|
||||
ret = device_create_file(&hdev->dev, &dev_attr_country);
|
||||
if (ret)
|
||||
hid_warn(hdev,
|
||||
"can't create sysfs country code attribute err: %d\n", ret);
|
||||
|
||||
ret = device_create_bin_file(&hdev->dev, &dev_bin_attr_report_desc);
|
||||
if (ret)
|
||||
hid_warn(hdev,
|
||||
|
|
@ -1661,6 +1680,7 @@ EXPORT_SYMBOL_GPL(hid_connect);
|
|||
|
||||
void hid_disconnect(struct hid_device *hdev)
|
||||
{
|
||||
device_remove_file(&hdev->dev, &dev_attr_country);
|
||||
device_remove_bin_file(&hdev->dev, &dev_bin_attr_report_desc);
|
||||
if (hdev->claimed & HID_CLAIMED_INPUT)
|
||||
hidinput_disconnect(hdev);
|
||||
|
|
@ -1824,6 +1844,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) },
|
||||
#if IS_ENABLED(CONFIG_HID_LENOVO)
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ static const struct hid_usage_entry hid_usage_table[] = {
|
|||
{0, 0x53, "DeviceIndex"},
|
||||
{0, 0x54, "ContactCount"},
|
||||
{0, 0x55, "ContactMaximumNumber"},
|
||||
{0, 0x59, "ButtonType"},
|
||||
{0, 0x5A, "SecondaryBarrelSwitch"},
|
||||
{0, 0x5B, "TransducerSerialNumber"},
|
||||
{ 15, 0, "PhysicalInterfaceDevice" },
|
||||
|
|
@ -1127,7 +1128,8 @@ static ssize_t hid_debug_events_read(struct file *file, char __user *buffer,
|
|||
|
||||
if (!list->hdev || !list->hdev->debug) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
set_current_state(TASK_RUNNING);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* allow O_NONBLOCK from other threads */
|
||||
|
|
|
|||
|
|
@ -1,290 +0,0 @@
|
|||
/*
|
||||
* HID driver for Huion devices not fully compliant with HID standard
|
||||
*
|
||||
* Copyright (c) 2013 Martin Rusko
|
||||
* Copyright (c) 2014 Nikolai Kondrashov
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "usbhid/usbhid.h"
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
/* Report descriptor template placeholder head */
|
||||
#define HUION_PH_HEAD 0xFE, 0xED, 0x1D
|
||||
|
||||
/* Report descriptor template placeholder IDs */
|
||||
enum huion_ph_id {
|
||||
HUION_PH_ID_X_LM,
|
||||
HUION_PH_ID_X_PM,
|
||||
HUION_PH_ID_Y_LM,
|
||||
HUION_PH_ID_Y_PM,
|
||||
HUION_PH_ID_PRESSURE_LM,
|
||||
HUION_PH_ID_NUM
|
||||
};
|
||||
|
||||
/* Report descriptor template placeholder */
|
||||
#define HUION_PH(_ID) HUION_PH_HEAD, HUION_PH_ID_##_ID
|
||||
|
||||
/* Fixed report descriptor template */
|
||||
static const __u8 huion_tablet_rdesc_template[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x07, /* Report ID (7), */
|
||||
0x09, 0x20, /* Usage (Stylus), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x09, 0x44, /* Usage (Barrel Switch), */
|
||||
0x09, 0x46, /* Usage (Tablet Pick), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x09, 0x32, /* Usage (In Range), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0xA4, /* Push, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x65, 0x13, /* Unit (Inch), */
|
||||
0x55, 0xFD, /* Unit Exponent (-3), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x27, HUION_PH(X_LM), /* Logical Maximum (PLACEHOLDER), */
|
||||
0x47, HUION_PH(X_PM), /* Physical Maximum (PLACEHOLDER), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x27, HUION_PH(Y_LM), /* Logical Maximum (PLACEHOLDER), */
|
||||
0x47, HUION_PH(Y_PM), /* Physical Maximum (PLACEHOLDER), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xB4, /* Pop, */
|
||||
0x09, 0x30, /* Usage (Tip Pressure), */
|
||||
0x27,
|
||||
HUION_PH(PRESSURE_LM), /* Logical Maximum (PLACEHOLDER), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
/* Parameter indices */
|
||||
enum huion_prm {
|
||||
HUION_PRM_X_LM = 1,
|
||||
HUION_PRM_Y_LM = 2,
|
||||
HUION_PRM_PRESSURE_LM = 4,
|
||||
HUION_PRM_RESOLUTION = 5,
|
||||
HUION_PRM_NUM
|
||||
};
|
||||
|
||||
/* Driver data */
|
||||
struct huion_drvdata {
|
||||
__u8 *rdesc;
|
||||
unsigned int rsize;
|
||||
};
|
||||
|
||||
static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct huion_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_HUION_TABLET:
|
||||
if (drvdata->rdesc != NULL) {
|
||||
rdesc = drvdata->rdesc;
|
||||
*rsize = drvdata->rsize;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable fully-functional tablet mode and determine device parameters.
|
||||
*
|
||||
* @hdev: HID device
|
||||
*/
|
||||
static int huion_tablet_enable(struct hid_device *hdev)
|
||||
{
|
||||
int rc;
|
||||
struct usb_device *usb_dev = hid_to_usb_dev(hdev);
|
||||
struct huion_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
__le16 *buf = NULL;
|
||||
size_t len;
|
||||
s32 params[HUION_PH_ID_NUM];
|
||||
s32 resolution;
|
||||
__u8 *p;
|
||||
s32 v;
|
||||
|
||||
/*
|
||||
* Read string descriptor containing tablet parameters. The specific
|
||||
* string descriptor and data were discovered by sniffing the Windows
|
||||
* driver traffic.
|
||||
* NOTE: This enables fully-functional tablet mode.
|
||||
*/
|
||||
len = HUION_PRM_NUM * sizeof(*buf);
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
hid_err(hdev, "failed to allocate parameter buffer\n");
|
||||
rc = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
|
||||
(USB_DT_STRING << 8) + 0x64,
|
||||
0x0409, buf, len,
|
||||
USB_CTRL_GET_TIMEOUT);
|
||||
if (rc == -EPIPE) {
|
||||
hid_err(hdev, "device parameters not found\n");
|
||||
rc = -ENODEV;
|
||||
goto cleanup;
|
||||
} else if (rc < 0) {
|
||||
hid_err(hdev, "failed to get device parameters: %d\n", rc);
|
||||
rc = -ENODEV;
|
||||
goto cleanup;
|
||||
} else if (rc != len) {
|
||||
hid_err(hdev, "invalid device parameters\n");
|
||||
rc = -ENODEV;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Extract device parameters */
|
||||
params[HUION_PH_ID_X_LM] = le16_to_cpu(buf[HUION_PRM_X_LM]);
|
||||
params[HUION_PH_ID_Y_LM] = le16_to_cpu(buf[HUION_PRM_Y_LM]);
|
||||
params[HUION_PH_ID_PRESSURE_LM] =
|
||||
le16_to_cpu(buf[HUION_PRM_PRESSURE_LM]);
|
||||
resolution = le16_to_cpu(buf[HUION_PRM_RESOLUTION]);
|
||||
if (resolution == 0) {
|
||||
params[HUION_PH_ID_X_PM] = 0;
|
||||
params[HUION_PH_ID_Y_PM] = 0;
|
||||
} else {
|
||||
params[HUION_PH_ID_X_PM] = params[HUION_PH_ID_X_LM] *
|
||||
1000 / resolution;
|
||||
params[HUION_PH_ID_Y_PM] = params[HUION_PH_ID_Y_LM] *
|
||||
1000 / resolution;
|
||||
}
|
||||
|
||||
/* Allocate fixed report descriptor */
|
||||
drvdata->rdesc = devm_kmalloc(&hdev->dev,
|
||||
sizeof(huion_tablet_rdesc_template),
|
||||
GFP_KERNEL);
|
||||
if (drvdata->rdesc == NULL) {
|
||||
hid_err(hdev, "failed to allocate fixed rdesc\n");
|
||||
rc = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
drvdata->rsize = sizeof(huion_tablet_rdesc_template);
|
||||
|
||||
/* Format fixed report descriptor */
|
||||
memcpy(drvdata->rdesc, huion_tablet_rdesc_template,
|
||||
drvdata->rsize);
|
||||
for (p = drvdata->rdesc;
|
||||
p <= drvdata->rdesc + drvdata->rsize - 4;) {
|
||||
if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D &&
|
||||
p[3] < sizeof(params)) {
|
||||
v = params[p[3]];
|
||||
put_unaligned(cpu_to_le32(v), (s32 *)p);
|
||||
p += 4;
|
||||
} else {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
cleanup:
|
||||
kfree(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
int rc;
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
struct huion_drvdata *drvdata;
|
||||
|
||||
/* Allocate and assign driver data */
|
||||
drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (drvdata == NULL) {
|
||||
hid_err(hdev, "failed to allocate driver data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
hid_set_drvdata(hdev, drvdata);
|
||||
|
||||
switch (id->product) {
|
||||
case USB_DEVICE_ID_HUION_TABLET:
|
||||
/* If this is the pen interface */
|
||||
if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
|
||||
rc = huion_tablet_enable(hdev);
|
||||
if (rc) {
|
||||
hid_err(hdev, "tablet enabling failed\n");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
rc = hid_parse(hdev);
|
||||
if (rc) {
|
||||
hid_err(hdev, "parse failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (rc) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int huion_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
u8 *data, int size)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
|
||||
/* If this is a pen input report */
|
||||
if (intf->cur_altsetting->desc.bInterfaceNumber == 0 &&
|
||||
report->type == HID_INPUT_REPORT &&
|
||||
report->id == 0x07 && size >= 2)
|
||||
/* Invert the in-range bit */
|
||||
data[1] ^= 0x40;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_device_id huion_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, huion_devices);
|
||||
|
||||
static struct hid_driver huion_driver = {
|
||||
.name = "huion",
|
||||
.id_table = huion_devices,
|
||||
.probe = huion_probe,
|
||||
.report_fixup = huion_report_fixup,
|
||||
.raw_event = huion_raw_event,
|
||||
};
|
||||
module_hid_driver(huion_driver);
|
||||
|
||||
MODULE_AUTHOR("Martin Rusko");
|
||||
MODULE_DESCRIPTION("Huion HID driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -538,6 +538,7 @@
|
|||
#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011
|
||||
#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2 0x501a
|
||||
#define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013
|
||||
#define USB_DEVICE_ID_KYE_PENSKETCH_M912 0x5015
|
||||
|
||||
#define USB_VENDOR_ID_LABTEC 0x1020
|
||||
#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006
|
||||
|
|
|
|||
|
|
@ -711,6 +711,29 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
}
|
||||
break;
|
||||
|
||||
case HID_UP_TELEPHONY:
|
||||
switch (usage->hid & HID_USAGE) {
|
||||
case 0x2f: map_key_clear(KEY_MICMUTE); break;
|
||||
case 0xb0: map_key_clear(KEY_NUMERIC_0); break;
|
||||
case 0xb1: map_key_clear(KEY_NUMERIC_1); break;
|
||||
case 0xb2: map_key_clear(KEY_NUMERIC_2); break;
|
||||
case 0xb3: map_key_clear(KEY_NUMERIC_3); break;
|
||||
case 0xb4: map_key_clear(KEY_NUMERIC_4); break;
|
||||
case 0xb5: map_key_clear(KEY_NUMERIC_5); break;
|
||||
case 0xb6: map_key_clear(KEY_NUMERIC_6); break;
|
||||
case 0xb7: map_key_clear(KEY_NUMERIC_7); break;
|
||||
case 0xb8: map_key_clear(KEY_NUMERIC_8); break;
|
||||
case 0xb9: map_key_clear(KEY_NUMERIC_9); break;
|
||||
case 0xba: map_key_clear(KEY_NUMERIC_STAR); break;
|
||||
case 0xbb: map_key_clear(KEY_NUMERIC_POUND); break;
|
||||
case 0xbc: map_key_clear(KEY_NUMERIC_A); break;
|
||||
case 0xbd: map_key_clear(KEY_NUMERIC_B); break;
|
||||
case 0xbe: map_key_clear(KEY_NUMERIC_C); break;
|
||||
case 0xbf: map_key_clear(KEY_NUMERIC_D); break;
|
||||
default: goto ignore;
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_UP_CONSUMER: /* USB HUT v1.12, pages 75-84 */
|
||||
switch (usage->hid & HID_USAGE) {
|
||||
case 0x000: goto ignore;
|
||||
|
|
|
|||
|
|
@ -268,6 +268,137 @@ static __u8 easypen_m610x_rdesc_fixed[] = {
|
|||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
|
||||
/* Original PenSketch M912 report descriptor size */
|
||||
#define PENSKETCH_M912_RDESC_ORIG_SIZE 482
|
||||
|
||||
/* Fixed PenSketch M912 report descriptor */
|
||||
static __u8 pensketch_m912_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x08, /* Usage (00h), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x05, /* Report ID (5), */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
||||
0x09, 0x01, /* Usage (01h), */
|
||||
0x15, 0x81, /* Logical Minimum (-127), */
|
||||
0x25, 0x7F, /* Logical Maximum (127), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x07, /* Report Count (7), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x10, /* Report ID (16), */
|
||||
0x09, 0x20, /* Usage (Stylus), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x09, 0x44, /* Usage (Barrel Switch), */
|
||||
0x09, 0x46, /* Usage (Tablet Pick), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x04, /* Report Count (4), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x09, 0x32, /* Usage (In Range), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0xA4, /* Push, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x55, 0xFD, /* Unit Exponent (-3), */
|
||||
0x65, 0x13, /* Unit (Inch), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x27, 0x00, 0xF0, 0x00, 0x00, /* Logical Maximum (61440), */
|
||||
0x46, 0xE0, 0x2E, /* Physical Maximum (12000), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x27, 0x00, 0xB4, 0x00, 0x00, /* Logical Maximum (46080), */
|
||||
0x46, 0x28, 0x23, /* Physical Maximum (9000), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xB4, /* Pop, */
|
||||
0x09, 0x30, /* Usage (Tip Pressure), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x26, 0xFF, 0x07, /* Logical Maximum (2047), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x21, /* Usage (Puck), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x11, /* Report ID (17), */
|
||||
0x09, 0x21, /* Usage (Puck), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x05, 0x09, /* Usage Page (Button), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x19, 0x01, /* Usage Minimum (01h), */
|
||||
0x29, 0x03, /* Usage Maximum (03h), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x04, /* Report Count (4), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x0B, 0x32, 0x00, 0x0D, 0x00, /* Usage (Digitizer In Range), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xA4, /* Push, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x55, 0xFD, /* Unit Exponent (-3), */
|
||||
0x65, 0x13, /* Unit (Inch), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x27, 0x00, 0xF0, 0x00, 0x00, /* Logical Maximum (61440), */
|
||||
0x46, 0xE0, 0x2E, /* Physical Maximum (12000), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x27, 0x00, 0xB4, 0x00, 0x00, /* Logical Maximum (46080), */
|
||||
0x46, 0x28, 0x23, /* Physical Maximum (9000), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x38, /* Usage (Wheel), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x15, 0xFF, /* Logical Minimum (-1), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x44, /* Physical Maximum (0), */
|
||||
0x81, 0x06, /* Input (Variable, Relative), */
|
||||
0xB4, /* Pop, */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x0C, /* Usage Page (Consumer), */
|
||||
0x09, 0x01, /* Usage (Consumer Control), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x12, /* Report ID (18), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x08, /* Report Count (8), */
|
||||
0x05, 0x0C, /* Usage Page (Consumer), */
|
||||
0x0A, 0x6A, 0x02, /* Usage (AC Delete), */
|
||||
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
|
||||
0x0A, 0x01, 0x02, /* Usage (AC New), */
|
||||
0x0A, 0x2F, 0x02, /* Usage (AC Zoom), */
|
||||
0x0A, 0x25, 0x02, /* Usage (AC Forward), */
|
||||
0x0A, 0x24, 0x02, /* Usage (AC Back), */
|
||||
0x0A, 0x2D, 0x02, /* Usage (AC Zoom In), */
|
||||
0x0A, 0x2E, 0x02, /* Usage (AC Zoom Out), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x30, /* Report Count (48), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 *kye_consumer_control_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize, int offset, const char *device_name) {
|
||||
/*
|
||||
|
|
@ -335,6 +466,12 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|||
*rsize = sizeof(easypen_m610x_rdesc_fixed);
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_KYE_PENSKETCH_M912:
|
||||
if (*rsize == PENSKETCH_M912_RDESC_ORIG_SIZE) {
|
||||
rdesc = pensketch_m912_rdesc_fixed;
|
||||
*rsize = sizeof(pensketch_m912_rdesc_fixed);
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE:
|
||||
rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 104,
|
||||
"Genius Gila Gaming Mouse");
|
||||
|
|
@ -418,6 +555,7 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X:
|
||||
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2:
|
||||
case USB_DEVICE_ID_KYE_EASYPEN_M610X:
|
||||
case USB_DEVICE_ID_KYE_PENSKETCH_M912:
|
||||
ret = kye_tablet_enable(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "tablet enabling failed\n");
|
||||
|
|
@ -457,6 +595,8 @@ static const struct hid_device_id kye_devices[] = {
|
|||
USB_DEVICE_ID_GENIUS_GX_IMPERATOR) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
|
||||
USB_DEVICE_ID_GENIUS_MANTICORE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
|
||||
USB_DEVICE_ID_KYE_PENSKETCH_M912) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, kye_devices);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "usbhid/usbhid.h"
|
||||
#include "hid-ids.h"
|
||||
#include "hid-lg.h"
|
||||
#include "hid-lg4ff.h"
|
||||
|
||||
#define LG_RDESC 0x001
|
||||
#define LG_BAD_RELATIVE_KEYS 0x002
|
||||
|
|
@ -818,4 +819,10 @@ static struct hid_driver lg_driver = {
|
|||
};
|
||||
module_hid_driver(lg_driver);
|
||||
|
||||
#ifdef CONFIG_LOGIWHEELS_FF
|
||||
int lg4ff_no_autoswitch = 0;
|
||||
module_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically");
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -24,16 +24,4 @@ int lg3ff_init(struct hid_device *hdev);
|
|||
static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LOGIWHEELS_FF
|
||||
int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data);
|
||||
int lg4ff_init(struct hid_device *hdev);
|
||||
int lg4ff_deinit(struct hid_device *hdev);
|
||||
#else
|
||||
static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; }
|
||||
static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
|
||||
static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -30,23 +30,44 @@
|
|||
|
||||
#include "usbhid/usbhid.h"
|
||||
#include "hid-lg.h"
|
||||
#include "hid-lg4ff.h"
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define DFGT_REV_MAJ 0x13
|
||||
#define DFGT_REV_MIN 0x22
|
||||
#define DFGT2_REV_MIN 0x26
|
||||
#define DFP_REV_MAJ 0x11
|
||||
#define DFP_REV_MIN 0x06
|
||||
#define FFEX_REV_MAJ 0x21
|
||||
#define FFEX_REV_MIN 0x00
|
||||
#define G25_REV_MAJ 0x12
|
||||
#define G25_REV_MIN 0x22
|
||||
#define G27_REV_MAJ 0x12
|
||||
#define G27_REV_MIN 0x38
|
||||
#define G27_2_REV_MIN 0x39
|
||||
|
||||
#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
|
||||
|
||||
#define LG4FF_MMODE_IS_MULTIMODE 0
|
||||
#define LG4FF_MMODE_SWITCHED 1
|
||||
#define LG4FF_MMODE_NOT_MULTIMODE 2
|
||||
|
||||
#define LG4FF_MODE_NATIVE_IDX 0
|
||||
#define LG4FF_MODE_DFEX_IDX 1
|
||||
#define LG4FF_MODE_DFP_IDX 2
|
||||
#define LG4FF_MODE_G25_IDX 3
|
||||
#define LG4FF_MODE_DFGT_IDX 4
|
||||
#define LG4FF_MODE_G27_IDX 5
|
||||
#define LG4FF_MODE_MAX_IDX 6
|
||||
|
||||
#define LG4FF_MODE_NATIVE BIT(LG4FF_MODE_NATIVE_IDX)
|
||||
#define LG4FF_MODE_DFEX BIT(LG4FF_MODE_DFEX_IDX)
|
||||
#define LG4FF_MODE_DFP BIT(LG4FF_MODE_DFP_IDX)
|
||||
#define LG4FF_MODE_G25 BIT(LG4FF_MODE_G25_IDX)
|
||||
#define LG4FF_MODE_DFGT BIT(LG4FF_MODE_DFGT_IDX)
|
||||
#define LG4FF_MODE_G27 BIT(LG4FF_MODE_G27_IDX)
|
||||
|
||||
#define LG4FF_DFEX_TAG "DF-EX"
|
||||
#define LG4FF_DFEX_NAME "Driving Force / Formula EX"
|
||||
#define LG4FF_DFP_TAG "DFP"
|
||||
#define LG4FF_DFP_NAME "Driving Force Pro"
|
||||
#define LG4FF_G25_TAG "G25"
|
||||
#define LG4FF_G25_NAME "G25 Racing Wheel"
|
||||
#define LG4FF_G27_TAG "G27"
|
||||
#define LG4FF_G27_NAME "G27 Racing Wheel"
|
||||
#define LG4FF_DFGT_TAG "DFGT"
|
||||
#define LG4FF_DFGT_NAME "Driving Force GT"
|
||||
|
||||
#define LG4FF_FFEX_REV_MAJ 0x21
|
||||
#define LG4FF_FFEX_REV_MIN 0x00
|
||||
|
||||
static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
|
||||
static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
|
||||
|
||||
|
|
@ -59,6 +80,10 @@ struct lg4ff_device_entry {
|
|||
__u8 led_state;
|
||||
struct led_classdev *led[5];
|
||||
#endif
|
||||
u32 alternate_modes;
|
||||
const char *real_tag;
|
||||
const char *real_name;
|
||||
u16 real_product_id;
|
||||
struct list_head list;
|
||||
void (*set_range)(struct hid_device *hid, u16 range);
|
||||
};
|
||||
|
|
@ -77,6 +102,35 @@ struct lg4ff_wheel {
|
|||
void (*set_range)(struct hid_device *hid, u16 range);
|
||||
};
|
||||
|
||||
struct lg4ff_compat_mode_switch {
|
||||
const __u8 cmd_count; /* Number of commands to send */
|
||||
const __u8 cmd[];
|
||||
};
|
||||
|
||||
struct lg4ff_wheel_ident_info {
|
||||
const u16 mask;
|
||||
const u16 result;
|
||||
const u16 real_product_id;
|
||||
};
|
||||
|
||||
struct lg4ff_wheel_ident_checklist {
|
||||
const u32 count;
|
||||
const struct lg4ff_wheel_ident_info *models[];
|
||||
};
|
||||
|
||||
struct lg4ff_multimode_wheel {
|
||||
const u16 product_id;
|
||||
const u32 alternate_modes;
|
||||
const char *real_tag;
|
||||
const char *real_name;
|
||||
};
|
||||
|
||||
struct lg4ff_alternate_mode {
|
||||
const u16 product_id;
|
||||
const char *tag;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static const struct lg4ff_wheel lg4ff_devices[] = {
|
||||
{USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
|
||||
{USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
|
||||
|
|
@ -88,48 +142,108 @@ static const struct lg4ff_wheel lg4ff_devices[] = {
|
|||
{USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}
|
||||
};
|
||||
|
||||
struct lg4ff_native_cmd {
|
||||
const __u8 cmd_num; /* Number of commands to send */
|
||||
const __u8 cmd[];
|
||||
static const struct lg4ff_multimode_wheel lg4ff_multimode_wheels[] = {
|
||||
{USB_DEVICE_ID_LOGITECH_DFP_WHEEL,
|
||||
LG4FF_MODE_NATIVE | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
|
||||
LG4FF_DFP_TAG, LG4FF_DFP_NAME},
|
||||
{USB_DEVICE_ID_LOGITECH_G25_WHEEL,
|
||||
LG4FF_MODE_NATIVE | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
|
||||
LG4FF_G25_TAG, LG4FF_G25_NAME},
|
||||
{USB_DEVICE_ID_LOGITECH_DFGT_WHEEL,
|
||||
LG4FF_MODE_NATIVE | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
|
||||
LG4FF_DFGT_TAG, LG4FF_DFGT_NAME},
|
||||
{USB_DEVICE_ID_LOGITECH_G27_WHEEL,
|
||||
LG4FF_MODE_NATIVE | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
|
||||
LG4FF_G27_TAG, LG4FF_G27_NAME},
|
||||
};
|
||||
|
||||
struct lg4ff_usb_revision {
|
||||
const __u16 rev_maj;
|
||||
const __u16 rev_min;
|
||||
const struct lg4ff_native_cmd *command;
|
||||
static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = {
|
||||
[LG4FF_MODE_NATIVE_IDX] = {0, "native", ""},
|
||||
[LG4FF_MODE_DFEX_IDX] = {USB_DEVICE_ID_LOGITECH_WHEEL, LG4FF_DFEX_TAG, LG4FF_DFEX_NAME},
|
||||
[LG4FF_MODE_DFP_IDX] = {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, LG4FF_DFP_TAG, LG4FF_DFP_NAME},
|
||||
[LG4FF_MODE_G25_IDX] = {USB_DEVICE_ID_LOGITECH_G25_WHEEL, LG4FF_G25_TAG, LG4FF_G25_NAME},
|
||||
[LG4FF_MODE_DFGT_IDX] = {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, LG4FF_DFGT_TAG, LG4FF_DFGT_NAME},
|
||||
[LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME}
|
||||
};
|
||||
|
||||
static const struct lg4ff_native_cmd native_dfp = {
|
||||
/* Multimode wheel identificators */
|
||||
static const struct lg4ff_wheel_ident_info lg4ff_dfp_ident_info = {
|
||||
0xf000,
|
||||
0x1000,
|
||||
USB_DEVICE_ID_LOGITECH_DFP_WHEEL
|
||||
};
|
||||
|
||||
static const struct lg4ff_wheel_ident_info lg4ff_g25_ident_info = {
|
||||
0xff00,
|
||||
0x1200,
|
||||
USB_DEVICE_ID_LOGITECH_G25_WHEEL
|
||||
};
|
||||
|
||||
static const struct lg4ff_wheel_ident_info lg4ff_g27_ident_info = {
|
||||
0xfff0,
|
||||
0x1230,
|
||||
USB_DEVICE_ID_LOGITECH_G27_WHEEL
|
||||
};
|
||||
|
||||
static const struct lg4ff_wheel_ident_info lg4ff_dfgt_ident_info = {
|
||||
0xff00,
|
||||
0x1300,
|
||||
USB_DEVICE_ID_LOGITECH_DFGT_WHEEL
|
||||
};
|
||||
|
||||
/* Multimode wheel identification checklists */
|
||||
static const struct lg4ff_wheel_ident_checklist lg4ff_main_checklist = {
|
||||
4,
|
||||
{&lg4ff_dfgt_ident_info,
|
||||
&lg4ff_g27_ident_info,
|
||||
&lg4ff_g25_ident_info,
|
||||
&lg4ff_dfp_ident_info}
|
||||
};
|
||||
|
||||
/* Compatibility mode switching commands */
|
||||
/* EXT_CMD9 - Understood by G27 and DFGT */
|
||||
static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfex = {
|
||||
2,
|
||||
{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
|
||||
0xf8, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DF-EX with detach */
|
||||
};
|
||||
|
||||
static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfp = {
|
||||
2,
|
||||
{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
|
||||
0xf8, 0x09, 0x01, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DFP with detach */
|
||||
};
|
||||
|
||||
static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g25 = {
|
||||
2,
|
||||
{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
|
||||
0xf8, 0x09, 0x02, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G25 with detach */
|
||||
};
|
||||
|
||||
static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfgt = {
|
||||
2,
|
||||
{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
|
||||
0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DFGT with detach */
|
||||
};
|
||||
|
||||
static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g27 = {
|
||||
2,
|
||||
{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
|
||||
0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G27 with detach */
|
||||
};
|
||||
|
||||
/* EXT_CMD1 - Understood by DFP, G25, G27 and DFGT */
|
||||
static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext01_dfp = {
|
||||
1,
|
||||
{0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||
};
|
||||
|
||||
static const struct lg4ff_native_cmd native_dfgt = {
|
||||
2,
|
||||
{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */
|
||||
0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00} /* 2nd command */
|
||||
};
|
||||
|
||||
static const struct lg4ff_native_cmd native_g25 = {
|
||||
/* EXT_CMD16 - Understood by G25 and G27 */
|
||||
static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext16_g25 = {
|
||||
1,
|
||||
{0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||
};
|
||||
|
||||
static const struct lg4ff_native_cmd native_g27 = {
|
||||
2,
|
||||
{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */
|
||||
0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* 2nd command */
|
||||
};
|
||||
|
||||
static const struct lg4ff_usb_revision lg4ff_revs[] = {
|
||||
{DFGT_REV_MAJ, DFGT_REV_MIN, &native_dfgt}, /* Driving Force GT */
|
||||
{DFGT_REV_MAJ, DFGT2_REV_MIN, &native_dfgt}, /* Driving Force GT v2 */
|
||||
{DFP_REV_MAJ, DFP_REV_MIN, &native_dfp}, /* Driving Force Pro */
|
||||
{G25_REV_MAJ, G25_REV_MIN, &native_g25}, /* G25 */
|
||||
{G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */
|
||||
{G27_REV_MAJ, G27_2_REV_MIN, &native_g27}, /* G27 v2 */
|
||||
};
|
||||
|
||||
/* Recalculates X axis value accordingly to currently selected range */
|
||||
static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
|
||||
{
|
||||
|
|
@ -396,21 +510,217 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
|
|||
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
||||
static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_native_cmd *cmd)
|
||||
static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(const u16 real_product_id, const u16 target_product_id)
|
||||
{
|
||||
switch (real_product_id) {
|
||||
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
|
||||
switch (target_product_id) {
|
||||
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
|
||||
return &lg4ff_mode_switch_ext01_dfp;
|
||||
/* DFP can only be switched to its native mode */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
|
||||
switch (target_product_id) {
|
||||
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
|
||||
return &lg4ff_mode_switch_ext01_dfp;
|
||||
case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
|
||||
return &lg4ff_mode_switch_ext16_g25;
|
||||
/* G25 can only be switched to DFP mode or its native mode */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
|
||||
switch (target_product_id) {
|
||||
case USB_DEVICE_ID_LOGITECH_WHEEL:
|
||||
return &lg4ff_mode_switch_ext09_dfex;
|
||||
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
|
||||
return &lg4ff_mode_switch_ext09_dfp;
|
||||
case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
|
||||
return &lg4ff_mode_switch_ext09_g25;
|
||||
case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
|
||||
return &lg4ff_mode_switch_ext09_g27;
|
||||
/* G27 can only be switched to DF-EX, DFP, G25 or its native mode */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
|
||||
switch (target_product_id) {
|
||||
case USB_DEVICE_ID_LOGITECH_WHEEL:
|
||||
return &lg4ff_mode_switch_ext09_dfex;
|
||||
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
|
||||
return &lg4ff_mode_switch_ext09_dfp;
|
||||
case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
|
||||
return &lg4ff_mode_switch_ext09_dfgt;
|
||||
/* DFGT can only be switched to DF-EX, DFP or its native mode */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
/* No other wheels have multiple modes */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct lg4ff_compat_mode_switch *s)
|
||||
{
|
||||
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
|
||||
__u8 i, j;
|
||||
__s32 *value = report->field[0]->value;
|
||||
u8 i;
|
||||
|
||||
j = 0;
|
||||
while (j < 7*cmd->cmd_num) {
|
||||
for (i = 0; i < 7; i++)
|
||||
report->field[0]->value[i] = cmd->cmd[j++];
|
||||
for (i = 0; i < s->cmd_count; i++) {
|
||||
u8 j;
|
||||
|
||||
for (j = 0; j < 7; j++)
|
||||
value[j] = s->cmd[j + (7*i)];
|
||||
|
||||
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
|
||||
}
|
||||
hid_hw_wait(hid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t lg4ff_alternate_modes_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct hid_device *hid = to_hid_device(dev);
|
||||
struct lg4ff_device_entry *entry;
|
||||
struct lg_drv_data *drv_data;
|
||||
ssize_t count = 0;
|
||||
int i;
|
||||
|
||||
drv_data = hid_get_drvdata(hid);
|
||||
if (!drv_data) {
|
||||
hid_err(hid, "Private driver data not found!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entry = drv_data->device_props;
|
||||
if (!entry) {
|
||||
hid_err(hid, "Device properties not found!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!entry->real_name) {
|
||||
hid_err(hid, "NULL pointer to string\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < LG4FF_MODE_MAX_IDX; i++) {
|
||||
if (entry->alternate_modes & BIT(i)) {
|
||||
/* Print tag and full name */
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count, "%s: %s",
|
||||
lg4ff_alternate_modes[i].tag,
|
||||
!lg4ff_alternate_modes[i].product_id ? entry->real_name : lg4ff_alternate_modes[i].name);
|
||||
if (count >= PAGE_SIZE - 1)
|
||||
return count;
|
||||
|
||||
/* Mark the currently active mode with an asterisk */
|
||||
if (lg4ff_alternate_modes[i].product_id == entry->product_id ||
|
||||
(lg4ff_alternate_modes[i].product_id == 0 && entry->product_id == entry->real_product_id))
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count, " *\n");
|
||||
else
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
|
||||
|
||||
if (count >= PAGE_SIZE - 1)
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct hid_device *hid = to_hid_device(dev);
|
||||
struct lg4ff_device_entry *entry;
|
||||
struct lg_drv_data *drv_data;
|
||||
const struct lg4ff_compat_mode_switch *s;
|
||||
u16 target_product_id = 0;
|
||||
int i, ret;
|
||||
char *lbuf;
|
||||
|
||||
drv_data = hid_get_drvdata(hid);
|
||||
if (!drv_data) {
|
||||
hid_err(hid, "Private driver data not found!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
entry = drv_data->device_props;
|
||||
if (!entry) {
|
||||
hid_err(hid, "Device properties not found!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Allow \n at the end of the input parameter */
|
||||
lbuf = kasprintf(GFP_KERNEL, "%s", buf);
|
||||
if (!lbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
i = strlen(lbuf);
|
||||
if (lbuf[i-1] == '\n') {
|
||||
if (i == 1) {
|
||||
kfree(lbuf);
|
||||
return -EINVAL;
|
||||
}
|
||||
lbuf[i-1] = '\0';
|
||||
}
|
||||
|
||||
for (i = 0; i < LG4FF_MODE_MAX_IDX; i++) {
|
||||
const u16 mode_product_id = lg4ff_alternate_modes[i].product_id;
|
||||
const char *tag = lg4ff_alternate_modes[i].tag;
|
||||
|
||||
if (entry->alternate_modes & BIT(i)) {
|
||||
if (!strcmp(tag, lbuf)) {
|
||||
if (!mode_product_id)
|
||||
target_product_id = entry->real_product_id;
|
||||
else
|
||||
target_product_id = mode_product_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == LG4FF_MODE_MAX_IDX) {
|
||||
hid_info(hid, "Requested mode \"%s\" is not supported by the device\n", lbuf);
|
||||
kfree(lbuf);
|
||||
return -EINVAL;
|
||||
}
|
||||
kfree(lbuf); /* Not needed anymore */
|
||||
|
||||
if (target_product_id == entry->product_id) /* Nothing to do */
|
||||
return count;
|
||||
|
||||
/* Automatic switching has to be disabled for the switch to DF-EX mode to work correctly */
|
||||
if (target_product_id == USB_DEVICE_ID_LOGITECH_WHEEL && !lg4ff_no_autoswitch) {
|
||||
hid_info(hid, "\"%s\" cannot be switched to \"DF-EX\" mode. Load the \"hid_logitech\" module with \"lg4ff_no_autoswitch=1\" parameter set and try again\n",
|
||||
entry->real_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Take care of hardware limitations */
|
||||
if ((entry->real_product_id == USB_DEVICE_ID_LOGITECH_DFP_WHEEL || entry->real_product_id == USB_DEVICE_ID_LOGITECH_G25_WHEEL) &&
|
||||
entry->product_id > target_product_id) {
|
||||
hid_info(hid, "\"%s\" cannot be switched back into \"%s\" mode\n", entry->real_name, lg4ff_alternate_modes[i].name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s = lg4ff_get_mode_switch_command(entry->real_product_id, target_product_id);
|
||||
if (!s) {
|
||||
hid_err(hid, "Invalid target product ID %X\n", target_product_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = lg4ff_switch_compatibility_mode(hid, s);
|
||||
return (ret == 0 ? count : ret);
|
||||
}
|
||||
static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store);
|
||||
|
||||
/* Read current range and display it in terminal */
|
||||
static ssize_t range_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -472,6 +782,41 @@ static ssize_t range_store(struct device *dev, struct device_attribute *attr,
|
|||
}
|
||||
static DEVICE_ATTR_RW(range);
|
||||
|
||||
static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct hid_device *hid = to_hid_device(dev);
|
||||
struct lg4ff_device_entry *entry;
|
||||
struct lg_drv_data *drv_data;
|
||||
size_t count;
|
||||
|
||||
drv_data = hid_get_drvdata(hid);
|
||||
if (!drv_data) {
|
||||
hid_err(hid, "Private driver data not found!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entry = drv_data->device_props;
|
||||
if (!entry) {
|
||||
hid_err(hid, "Device properties not found!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!entry->real_tag || !entry->real_name) {
|
||||
hid_err(hid, "NULL pointer to string\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->real_tag, entry->real_name);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t lg4ff_real_id_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
/* Real ID is a read-only value */
|
||||
return -EPERM;
|
||||
}
|
||||
static DEVICE_ATTR(real_id, S_IRUGO, lg4ff_real_id_show, lg4ff_real_id_store);
|
||||
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
|
||||
{
|
||||
|
|
@ -555,20 +900,119 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
|
|||
}
|
||||
#endif
|
||||
|
||||
static u16 lg4ff_identify_multimode_wheel(struct hid_device *hid, const u16 reported_product_id, const u16 bcdDevice)
|
||||
{
|
||||
const struct lg4ff_wheel_ident_checklist *checklist;
|
||||
int i, from_idx, to_idx;
|
||||
|
||||
switch (reported_product_id) {
|
||||
case USB_DEVICE_ID_LOGITECH_WHEEL:
|
||||
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
|
||||
checklist = &lg4ff_main_checklist;
|
||||
from_idx = 0;
|
||||
to_idx = checklist->count - 1;
|
||||
break;
|
||||
case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
|
||||
checklist = &lg4ff_main_checklist;
|
||||
from_idx = 0;
|
||||
to_idx = checklist->count - 2; /* End identity check at G25 */
|
||||
break;
|
||||
case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
|
||||
checklist = &lg4ff_main_checklist;
|
||||
from_idx = 1; /* Start identity check at G27 */
|
||||
to_idx = checklist->count - 3; /* End identity check at G27 */
|
||||
break;
|
||||
case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
|
||||
checklist = &lg4ff_main_checklist;
|
||||
from_idx = 0;
|
||||
to_idx = checklist->count - 4; /* End identity check at DFGT */
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = from_idx; i <= to_idx; i++) {
|
||||
const u16 mask = checklist->models[i]->mask;
|
||||
const u16 result = checklist->models[i]->result;
|
||||
const u16 real_product_id = checklist->models[i]->real_product_id;
|
||||
|
||||
if ((bcdDevice & mask) == result) {
|
||||
dbg_hid("Found wheel with real PID %X whose reported PID is %X\n", real_product_id, reported_product_id);
|
||||
return real_product_id;
|
||||
}
|
||||
}
|
||||
|
||||
/* No match found. This is either Driving Force or an unknown
|
||||
* wheel model, do not touch it */
|
||||
dbg_hid("Wheel with bcdDevice %X was not recognized as multimode wheel, leaving in its current mode\n", bcdDevice);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lg4ff_handle_multimode_wheel(struct hid_device *hid, u16 *real_product_id, const u16 bcdDevice)
|
||||
{
|
||||
const u16 reported_product_id = hid->product;
|
||||
int ret;
|
||||
|
||||
*real_product_id = lg4ff_identify_multimode_wheel(hid, reported_product_id, bcdDevice);
|
||||
/* Probed wheel is not a multimode wheel */
|
||||
if (!*real_product_id) {
|
||||
*real_product_id = reported_product_id;
|
||||
dbg_hid("Wheel is not a multimode wheel\n");
|
||||
return LG4FF_MMODE_NOT_MULTIMODE;
|
||||
}
|
||||
|
||||
/* Switch from "Driving Force" mode to native mode automatically.
|
||||
* Otherwise keep the wheel in its current mode */
|
||||
if (reported_product_id == USB_DEVICE_ID_LOGITECH_WHEEL &&
|
||||
reported_product_id != *real_product_id &&
|
||||
!lg4ff_no_autoswitch) {
|
||||
const struct lg4ff_compat_mode_switch *s = lg4ff_get_mode_switch_command(*real_product_id, *real_product_id);
|
||||
|
||||
if (!s) {
|
||||
hid_err(hid, "Invalid product id %X\n", *real_product_id);
|
||||
return LG4FF_MMODE_NOT_MULTIMODE;
|
||||
}
|
||||
|
||||
ret = lg4ff_switch_compatibility_mode(hid, s);
|
||||
if (ret) {
|
||||
/* Wheel could not have been switched to native mode,
|
||||
* leave it in "Driving Force" mode and continue */
|
||||
hid_err(hid, "Unable to switch wheel mode, errno %d\n", ret);
|
||||
return LG4FF_MMODE_IS_MULTIMODE;
|
||||
}
|
||||
return LG4FF_MMODE_SWITCHED;
|
||||
}
|
||||
|
||||
return LG4FF_MMODE_IS_MULTIMODE;
|
||||
}
|
||||
|
||||
|
||||
int lg4ff_init(struct hid_device *hid)
|
||||
{
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
struct input_dev *dev = hidinput->input;
|
||||
const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
|
||||
const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);
|
||||
struct lg4ff_device_entry *entry;
|
||||
struct lg_drv_data *drv_data;
|
||||
struct usb_device_descriptor *udesc;
|
||||
int error, i, j;
|
||||
__u16 bcdDevice, rev_maj, rev_min;
|
||||
int mmode_ret, mmode_idx = -1;
|
||||
u16 real_product_id;
|
||||
|
||||
/* Check that the report looks ok */
|
||||
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
|
||||
return -1;
|
||||
|
||||
/* Check if a multimode wheel has been connected and
|
||||
* handle it appropriately */
|
||||
mmode_ret = lg4ff_handle_multimode_wheel(hid, &real_product_id, bcdDevice);
|
||||
|
||||
/* Wheel has been told to switch to native mode. There is no point in going on
|
||||
* with the initialization as the wheel will do a USB reset when it switches mode
|
||||
*/
|
||||
if (mmode_ret == LG4FF_MMODE_SWITCHED)
|
||||
return 0;
|
||||
|
||||
/* Check what wheel has been connected */
|
||||
for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
|
||||
if (hid->product == lg4ff_devices[i].product_id) {
|
||||
|
|
@ -583,25 +1027,15 @@ int lg4ff_init(struct hid_device *hid)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Attempt to switch wheel to native mode when applicable */
|
||||
udesc = &(hid_to_usb_dev(hid)->descriptor);
|
||||
if (!udesc) {
|
||||
hid_err(hid, "NULL USB device descriptor\n");
|
||||
return -1;
|
||||
}
|
||||
bcdDevice = le16_to_cpu(udesc->bcdDevice);
|
||||
rev_maj = bcdDevice >> 8;
|
||||
rev_min = bcdDevice & 0xff;
|
||||
if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
|
||||
for (mmode_idx = 0; mmode_idx < ARRAY_SIZE(lg4ff_multimode_wheels); mmode_idx++) {
|
||||
if (real_product_id == lg4ff_multimode_wheels[mmode_idx].product_id)
|
||||
break;
|
||||
}
|
||||
|
||||
if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_WHEEL) {
|
||||
dbg_hid("Generic wheel detected, can it do native?\n");
|
||||
dbg_hid("USB revision: %2x.%02x\n", rev_maj, rev_min);
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(lg4ff_revs); j++) {
|
||||
if (lg4ff_revs[j].rev_maj == rev_maj && lg4ff_revs[j].rev_min == rev_min) {
|
||||
hid_lg4ff_switch_native(hid, lg4ff_revs[j].command);
|
||||
hid_info(hid, "Switched to native mode\n");
|
||||
}
|
||||
if (mmode_idx == ARRAY_SIZE(lg4ff_multimode_wheels)) {
|
||||
hid_err(hid, "Device product ID %X is not listed as a multimode wheel", real_product_id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -630,14 +1064,23 @@ int lg4ff_init(struct hid_device *hid)
|
|||
drv_data->device_props = entry;
|
||||
|
||||
entry->product_id = lg4ff_devices[i].product_id;
|
||||
entry->real_product_id = real_product_id;
|
||||
entry->min_range = lg4ff_devices[i].min_range;
|
||||
entry->max_range = lg4ff_devices[i].max_range;
|
||||
entry->set_range = lg4ff_devices[i].set_range;
|
||||
if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
|
||||
BUG_ON(mmode_idx == -1);
|
||||
entry->alternate_modes = lg4ff_multimode_wheels[mmode_idx].alternate_modes;
|
||||
entry->real_tag = lg4ff_multimode_wheels[mmode_idx].real_tag;
|
||||
entry->real_name = lg4ff_multimode_wheels[mmode_idx].real_name;
|
||||
}
|
||||
|
||||
/* Check if autocentering is available and
|
||||
* set the centering force to zero by default */
|
||||
if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
|
||||
if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
|
||||
/* Formula Force EX expects different autocentering command */
|
||||
if ((bcdDevice >> 8) == LG4FF_FFEX_REV_MAJ &&
|
||||
(bcdDevice & 0xff) == LG4FF_FFEX_REV_MIN)
|
||||
dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
|
||||
else
|
||||
dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
|
||||
|
|
@ -649,6 +1092,14 @@ int lg4ff_init(struct hid_device *hid)
|
|||
error = device_create_file(&hid->dev, &dev_attr_range);
|
||||
if (error)
|
||||
return error;
|
||||
if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
|
||||
error = device_create_file(&hid->dev, &dev_attr_real_id);
|
||||
if (error)
|
||||
return error;
|
||||
error = device_create_file(&hid->dev, &dev_attr_alternate_modes);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
dbg_hid("sysfs interface created\n");
|
||||
|
||||
/* Set the maximum range to start with */
|
||||
|
|
@ -711,24 +1162,26 @@ out:
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int lg4ff_deinit(struct hid_device *hid)
|
||||
{
|
||||
struct lg4ff_device_entry *entry;
|
||||
struct lg_drv_data *drv_data;
|
||||
|
||||
device_remove_file(&hid->dev, &dev_attr_range);
|
||||
|
||||
drv_data = hid_get_drvdata(hid);
|
||||
if (!drv_data) {
|
||||
hid_err(hid, "Error while deinitializing device, no private driver data.\n");
|
||||
return -1;
|
||||
}
|
||||
entry = drv_data->device_props;
|
||||
if (!entry) {
|
||||
hid_err(hid, "Error while deinitializing device, no device properties data.\n");
|
||||
return -1;
|
||||
if (!entry)
|
||||
goto out; /* Nothing more to do */
|
||||
|
||||
device_remove_file(&hid->dev, &dev_attr_range);
|
||||
|
||||
/* Multimode devices will have at least the "MODE_NATIVE" bit set */
|
||||
if (entry->alternate_modes) {
|
||||
device_remove_file(&hid->dev, &dev_attr_real_id);
|
||||
device_remove_file(&hid->dev, &dev_attr_alternate_modes);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
|
|
@ -752,6 +1205,7 @@ int lg4ff_deinit(struct hid_device *hid)
|
|||
/* Deallocate memory */
|
||||
kfree(entry);
|
||||
|
||||
out:
|
||||
dbg_hid("Device successfully unregistered\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
18
drivers/hid/hid-lg4ff.h
Normal file
18
drivers/hid/hid-lg4ff.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef __HID_LG4FF_H
|
||||
#define __HID_LG4FF_H
|
||||
|
||||
#ifdef CONFIG_LOGIWHEELS_FF
|
||||
extern int lg4ff_no_autoswitch; /* From hid-lg.c */
|
||||
|
||||
int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data);
|
||||
int lg4ff_init(struct hid_device *hdev);
|
||||
int lg4ff_deinit(struct hid_device *hdev);
|
||||
#else
|
||||
static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; }
|
||||
static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
|
||||
static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -28,6 +28,11 @@ MODULE_LICENSE("GPL");
|
|||
MODULE_AUTHOR("Benjamin Tissoires <[email protected]>");
|
||||
MODULE_AUTHOR("Nestor Lopez Casado <[email protected]>");
|
||||
|
||||
static bool disable_raw_mode;
|
||||
module_param(disable_raw_mode, bool, 0644);
|
||||
MODULE_PARM_DESC(disable_raw_mode,
|
||||
"Disable Raw mode reporting for touchpads and keep firmware gestures.");
|
||||
|
||||
#define REPORT_ID_HIDPP_SHORT 0x10
|
||||
#define REPORT_ID_HIDPP_LONG 0x11
|
||||
|
||||
|
|
@ -1188,6 +1193,11 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
|
||||
hidpp->quirks = id->driver_data;
|
||||
|
||||
if (disable_raw_mode) {
|
||||
hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP;
|
||||
hidpp->quirks &= ~HIDPP_QUIRK_DELAYED_INIT;
|
||||
}
|
||||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
|
||||
ret = wtp_allocate(hdev, id);
|
||||
if (ret)
|
||||
|
|
@ -1210,6 +1220,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
connected = hidpp_is_connected(hidpp);
|
||||
if (id->group != HID_GROUP_LOGITECH_DJ_DEVICE) {
|
||||
if (!connected) {
|
||||
ret = -ENODEV;
|
||||
hid_err(hdev, "Device not connected");
|
||||
hid_device_io_stop(hdev);
|
||||
goto hid_parse_fail;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@
|
|||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
|
|
@ -72,6 +71,8 @@ MODULE_LICENSE("GPL");
|
|||
#define MT_INPUTMODE_TOUCHSCREEN 0x02
|
||||
#define MT_INPUTMODE_TOUCHPAD 0x03
|
||||
|
||||
#define MT_BUTTONTYPE_CLICKPAD 0
|
||||
|
||||
struct mt_slot {
|
||||
__s32 x, y, cx, cy, p, w, h;
|
||||
__s32 contactid; /* the device ContactID assigned to this slot */
|
||||
|
|
@ -116,6 +117,8 @@ struct mt_device {
|
|||
__u8 touches_by_report; /* how many touches are present in one report:
|
||||
* 1 means we should use a serial protocol
|
||||
* > 1 means hybrid (multitouch) protocol */
|
||||
__u8 buttons_count; /* number of physical buttons per touchpad */
|
||||
bool is_buttonpad; /* is this device a button pad? */
|
||||
bool serial_maybe; /* need to check for serial protocol */
|
||||
bool curvalid; /* is the current contact valid? */
|
||||
unsigned mt_flags; /* flags to pass to input-mt */
|
||||
|
|
@ -333,6 +336,16 @@ static void mt_feature_mapping(struct hid_device *hdev,
|
|||
/* check if the maxcontacts is given by the class */
|
||||
td->maxcontacts = td->mtclass.maxcontacts;
|
||||
|
||||
break;
|
||||
case HID_DG_BUTTONTYPE:
|
||||
if (usage->usage_index >= field->report_count) {
|
||||
dev_err(&hdev->dev, "HID_DG_BUTTONTYPE out of range\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (field->value[usage->usage_index] == MT_BUTTONTYPE_CLICKPAD)
|
||||
td->is_buttonpad = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -379,6 +392,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
|
||||
}
|
||||
|
||||
/* count the buttons on touchpads */
|
||||
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
|
||||
td->buttons_count++;
|
||||
|
||||
if (usage->usage_index)
|
||||
prev_usage = &field->usage[usage->usage_index - 1];
|
||||
|
||||
|
|
@ -728,6 +745,13 @@ static void mt_touch_input_configured(struct hid_device *hdev,
|
|||
if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
|
||||
td->mt_flags |= INPUT_MT_DROP_UNUSED;
|
||||
|
||||
/* check for clickpads */
|
||||
if ((td->mt_flags & INPUT_MT_POINTER) && (td->buttons_count == 1))
|
||||
td->is_buttonpad = true;
|
||||
|
||||
if (td->is_buttonpad)
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
||||
|
||||
input_mt_init_slots(input, td->maxcontacts, td->mt_flags);
|
||||
|
||||
td->mt_flags = 0;
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ struct rmi_data {
|
|||
|
||||
unsigned long flags;
|
||||
|
||||
struct rmi_function f01;
|
||||
struct rmi_function f11;
|
||||
struct rmi_function f30;
|
||||
|
||||
|
|
@ -124,6 +125,7 @@ struct rmi_data {
|
|||
struct hid_device *hdev;
|
||||
|
||||
unsigned long device_flags;
|
||||
unsigned long firmware_id;
|
||||
};
|
||||
|
||||
#define RMI_PAGE(addr) (((addr) >> 8) & 0xff)
|
||||
|
|
@ -272,6 +274,46 @@ static inline int rmi_read(struct hid_device *hdev, u16 addr, void *buf)
|
|||
return rmi_read_block(hdev, addr, buf, 1);
|
||||
}
|
||||
|
||||
static int rmi_write_block(struct hid_device *hdev, u16 addr, void *buf,
|
||||
const int len)
|
||||
{
|
||||
struct rmi_data *data = hid_get_drvdata(hdev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->page_mutex);
|
||||
|
||||
if (RMI_PAGE(addr) != data->page) {
|
||||
ret = rmi_set_page(hdev, RMI_PAGE(addr));
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
data->writeReport[0] = RMI_WRITE_REPORT_ID;
|
||||
data->writeReport[1] = len;
|
||||
data->writeReport[2] = addr & 0xFF;
|
||||
data->writeReport[3] = (addr >> 8) & 0xFF;
|
||||
memcpy(&data->writeReport[4], buf, len);
|
||||
|
||||
ret = rmi_write_report(hdev, data->writeReport,
|
||||
data->output_report_size);
|
||||
if (ret < 0) {
|
||||
dev_err(&hdev->dev,
|
||||
"failed to write request output report (%d)\n",
|
||||
ret);
|
||||
goto exit;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->page_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rmi_write(struct hid_device *hdev, u16 addr, void *buf)
|
||||
{
|
||||
return rmi_write_block(hdev, addr, buf, 1);
|
||||
}
|
||||
|
||||
static void rmi_f11_process_touch(struct rmi_data *hdata, int slot,
|
||||
u8 finger_state, u8 *touch_data)
|
||||
{
|
||||
|
|
@ -532,6 +574,9 @@ static void rmi_register_function(struct rmi_data *data,
|
|||
u16 page_base = page << 8;
|
||||
|
||||
switch (pdt_entry->function_number) {
|
||||
case 0x01:
|
||||
f = &data->f01;
|
||||
break;
|
||||
case 0x11:
|
||||
f = &data->f11;
|
||||
break;
|
||||
|
|
@ -604,6 +649,92 @@ error_exit:
|
|||
return retval;
|
||||
}
|
||||
|
||||
#define RMI_DEVICE_F01_BASIC_QUERY_LEN 11
|
||||
|
||||
static int rmi_populate_f01(struct hid_device *hdev)
|
||||
{
|
||||
struct rmi_data *data = hid_get_drvdata(hdev);
|
||||
u8 basic_queries[RMI_DEVICE_F01_BASIC_QUERY_LEN];
|
||||
u8 info[3];
|
||||
int ret;
|
||||
bool has_query42;
|
||||
bool has_lts;
|
||||
bool has_sensor_id;
|
||||
bool has_ds4_queries = false;
|
||||
bool has_build_id_query = false;
|
||||
bool has_package_id_query = false;
|
||||
u16 query_offset = data->f01.query_base_addr;
|
||||
u16 prod_info_addr;
|
||||
u8 ds4_query_len;
|
||||
|
||||
ret = rmi_read_block(hdev, query_offset, basic_queries,
|
||||
RMI_DEVICE_F01_BASIC_QUERY_LEN);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Can not read basic queries from Function 0x1.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
has_lts = !!(basic_queries[0] & BIT(2));
|
||||
has_sensor_id = !!(basic_queries[1] & BIT(3));
|
||||
has_query42 = !!(basic_queries[1] & BIT(7));
|
||||
|
||||
query_offset += 11;
|
||||
prod_info_addr = query_offset + 6;
|
||||
query_offset += 10;
|
||||
|
||||
if (has_lts)
|
||||
query_offset += 20;
|
||||
|
||||
if (has_sensor_id)
|
||||
query_offset++;
|
||||
|
||||
if (has_query42) {
|
||||
ret = rmi_read(hdev, query_offset, info);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Can not read query42.\n");
|
||||
return ret;
|
||||
}
|
||||
has_ds4_queries = !!(info[0] & BIT(0));
|
||||
query_offset++;
|
||||
}
|
||||
|
||||
if (has_ds4_queries) {
|
||||
ret = rmi_read(hdev, query_offset, &ds4_query_len);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Can not read DS4 Query length.\n");
|
||||
return ret;
|
||||
}
|
||||
query_offset++;
|
||||
|
||||
if (ds4_query_len > 0) {
|
||||
ret = rmi_read(hdev, query_offset, info);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Can not read DS4 query.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
has_package_id_query = !!(info[0] & BIT(0));
|
||||
has_build_id_query = !!(info[0] & BIT(1));
|
||||
}
|
||||
}
|
||||
|
||||
if (has_package_id_query)
|
||||
prod_info_addr++;
|
||||
|
||||
if (has_build_id_query) {
|
||||
ret = rmi_read_block(hdev, prod_info_addr, info, 3);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Can not read product info.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->firmware_id = info[1] << 8 | info[0];
|
||||
data->firmware_id += info[2] * 65536;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_populate_f11(struct hid_device *hdev)
|
||||
{
|
||||
struct rmi_data *data = hid_get_drvdata(hdev);
|
||||
|
|
@ -620,6 +751,8 @@ static int rmi_populate_f11(struct hid_device *hdev)
|
|||
bool has_gestures;
|
||||
bool has_rel;
|
||||
bool has_data40 = false;
|
||||
bool has_dribble = false;
|
||||
bool has_palm_detect = false;
|
||||
unsigned x_size, y_size;
|
||||
u16 query_offset;
|
||||
|
||||
|
|
@ -661,6 +794,14 @@ static int rmi_populate_f11(struct hid_device *hdev)
|
|||
has_rel = !!(buf[0] & BIT(3));
|
||||
has_gestures = !!(buf[0] & BIT(5));
|
||||
|
||||
ret = rmi_read(hdev, data->f11.query_base_addr + 5, buf);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can not get absolute data sources: %d.\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
has_dribble = !!(buf[0] & BIT(4));
|
||||
|
||||
/*
|
||||
* At least 4 queries are guaranteed to be present in F11
|
||||
* +1 for query 5 which is present since absolute events are
|
||||
|
|
@ -680,6 +821,7 @@ static int rmi_populate_f11(struct hid_device *hdev)
|
|||
ret);
|
||||
return ret;
|
||||
}
|
||||
has_palm_detect = !!(buf[0] & BIT(0));
|
||||
has_query10 = !!(buf[0] & BIT(2));
|
||||
|
||||
query_offset += 2; /* query 7 and 8 are present */
|
||||
|
|
@ -766,17 +908,38 @@ static int rmi_populate_f11(struct hid_device *hdev)
|
|||
* retrieve the ctrl registers
|
||||
* the ctrl register has a size of 20 but a fw bug split it into 16 + 4,
|
||||
* and there is no way to know if the first 20 bytes are here or not.
|
||||
* We use only the first 10 bytes, so get only them.
|
||||
* We use only the first 12 bytes, so get only them.
|
||||
*/
|
||||
ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 10);
|
||||
ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 12);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can not read ctrl block of size 10: %d.\n", ret);
|
||||
hid_err(hdev, "can not read ctrl block of size 11: %d.\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->max_x = buf[6] | (buf[7] << 8);
|
||||
data->max_y = buf[8] | (buf[9] << 8);
|
||||
|
||||
if (has_dribble) {
|
||||
buf[0] = buf[0] & ~BIT(6);
|
||||
ret = rmi_write(hdev, data->f11.control_base_addr, buf);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can not write to control reg 0: %d.\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_palm_detect) {
|
||||
buf[11] = buf[11] & ~BIT(0);
|
||||
ret = rmi_write(hdev, data->f11.control_base_addr + 11,
|
||||
&buf[11]);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can not write to control reg 11: %d.\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -858,6 +1021,12 @@ static int rmi_populate(struct hid_device *hdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = rmi_populate_f01(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Error while initializing F01 (%d).\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rmi_populate_f11(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Error while initializing F11 (%d).\n", ret);
|
||||
|
|
@ -907,6 +1076,8 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi)
|
|||
if (ret)
|
||||
goto exit;
|
||||
|
||||
hid_info(hdev, "firmware id: %ld\n", data->firmware_id);
|
||||
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 1, data->max_x, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 1, data->max_y, 0, 0);
|
||||
|
|
|
|||
|
|
@ -802,7 +802,8 @@ union sixaxis_output_report_01 {
|
|||
#define DS4_REPORT_0x05_SIZE 32
|
||||
#define DS4_REPORT_0x11_SIZE 78
|
||||
#define DS4_REPORT_0x81_SIZE 7
|
||||
#define SIXAXIS_REPORT_0xF2_SIZE 18
|
||||
#define SIXAXIS_REPORT_0xF2_SIZE 17
|
||||
#define SIXAXIS_REPORT_0xF5_SIZE 8
|
||||
|
||||
static DEFINE_SPINLOCK(sony_dev_list_lock);
|
||||
static LIST_HEAD(sony_device_list);
|
||||
|
|
@ -1130,18 +1131,38 @@ static void sony_input_configured(struct hid_device *hdev,
|
|||
*/
|
||||
static int sixaxis_set_operational_usb(struct hid_device *hdev)
|
||||
{
|
||||
const int buf_size =
|
||||
max(SIXAXIS_REPORT_0xF2_SIZE, SIXAXIS_REPORT_0xF5_SIZE);
|
||||
__u8 *buf;
|
||||
int ret;
|
||||
char *buf = kmalloc(18, GFP_KERNEL);
|
||||
|
||||
buf = kmalloc(buf_size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hid_hw_raw_request(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT,
|
||||
HID_REQ_GET_REPORT);
|
||||
ret = hid_hw_raw_request(hdev, 0xf2, buf, SIXAXIS_REPORT_0xF2_SIZE,
|
||||
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
|
||||
if (ret < 0) {
|
||||
hid_err(hdev, "can't set operational mode: step 1\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some compatible controllers like the Speedlink Strike FX and
|
||||
* Gasia need another query plus an USB interrupt to get operational.
|
||||
*/
|
||||
ret = hid_hw_raw_request(hdev, 0xf5, buf, SIXAXIS_REPORT_0xF5_SIZE,
|
||||
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
|
||||
if (ret < 0) {
|
||||
hid_err(hdev, "can't set operational mode: step 2\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = hid_hw_output_report(hdev, buf, 1);
|
||||
if (ret < 0)
|
||||
hid_err(hdev, "can't set operational mode\n");
|
||||
hid_err(hdev, "can't set operational mode: step 3\n");
|
||||
|
||||
out:
|
||||
kfree(buf);
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
/*
|
||||
* HID driver for UC-Logic devices not fully compliant with HID standard
|
||||
*
|
||||
* Copyright (c) 2010 Nikolai Kondrashov
|
||||
* Copyright (c) 2010-2014 Nikolai Kondrashov
|
||||
* Copyright (c) 2013 Martin Rusko
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
@ -15,6 +16,8 @@
|
|||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "usbhid/usbhid.h"
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
|
|
@ -546,11 +549,93 @@ static __u8 twha60_rdesc_fixed1[] = {
|
|||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
/* Report descriptor template placeholder head */
|
||||
#define UCLOGIC_PH_HEAD 0xFE, 0xED, 0x1D
|
||||
|
||||
/* Report descriptor template placeholder IDs */
|
||||
enum uclogic_ph_id {
|
||||
UCLOGIC_PH_ID_X_LM,
|
||||
UCLOGIC_PH_ID_X_PM,
|
||||
UCLOGIC_PH_ID_Y_LM,
|
||||
UCLOGIC_PH_ID_Y_PM,
|
||||
UCLOGIC_PH_ID_PRESSURE_LM,
|
||||
UCLOGIC_PH_ID_NUM
|
||||
};
|
||||
|
||||
/* Report descriptor template placeholder */
|
||||
#define UCLOGIC_PH(_ID) UCLOGIC_PH_HEAD, UCLOGIC_PH_ID_##_ID
|
||||
#define UCLOGIC_PEN_REPORT_ID 0x07
|
||||
|
||||
/* Fixed report descriptor template */
|
||||
static const __u8 uclogic_tablet_rdesc_template[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x07, /* Report ID (7), */
|
||||
0x09, 0x20, /* Usage (Stylus), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x09, 0x44, /* Usage (Barrel Switch), */
|
||||
0x09, 0x46, /* Usage (Tablet Pick), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x09, 0x32, /* Usage (In Range), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0xA4, /* Push, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x65, 0x13, /* Unit (Inch), */
|
||||
0x55, 0xFD, /* Unit Exponent (-3), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x27, UCLOGIC_PH(X_LM), /* Logical Maximum (PLACEHOLDER), */
|
||||
0x47, UCLOGIC_PH(X_PM), /* Physical Maximum (PLACEHOLDER), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x27, UCLOGIC_PH(Y_LM), /* Logical Maximum (PLACEHOLDER), */
|
||||
0x47, UCLOGIC_PH(Y_PM), /* Physical Maximum (PLACEHOLDER), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xB4, /* Pop, */
|
||||
0x09, 0x30, /* Usage (Tip Pressure), */
|
||||
0x27,
|
||||
UCLOGIC_PH(PRESSURE_LM),/* Logical Maximum (PLACEHOLDER), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
/* Parameter indices */
|
||||
enum uclogic_prm {
|
||||
UCLOGIC_PRM_X_LM = 1,
|
||||
UCLOGIC_PRM_Y_LM = 2,
|
||||
UCLOGIC_PRM_PRESSURE_LM = 4,
|
||||
UCLOGIC_PRM_RESOLUTION = 5,
|
||||
UCLOGIC_PRM_NUM
|
||||
};
|
||||
|
||||
/* Driver data */
|
||||
struct uclogic_drvdata {
|
||||
__u8 *rdesc;
|
||||
unsigned int rsize;
|
||||
bool invert_pen_inrange;
|
||||
bool ignore_pen_usage;
|
||||
};
|
||||
|
||||
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
|
||||
__u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
|
||||
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209:
|
||||
|
|
@ -621,11 +706,241 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (drvdata->rdesc != NULL) {
|
||||
rdesc = drvdata->rdesc;
|
||||
*rsize = drvdata->rsize;
|
||||
}
|
||||
}
|
||||
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
||||
/* discard the unused pen interface */
|
||||
if ((drvdata->ignore_pen_usage) &&
|
||||
(field->application == HID_DG_PEN))
|
||||
return -1;
|
||||
|
||||
/* let hid-core decide what to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void uclogic_input_configured(struct hid_device *hdev,
|
||||
struct hid_input *hi)
|
||||
{
|
||||
char *name;
|
||||
const char *suffix = NULL;
|
||||
struct hid_field *field;
|
||||
size_t len;
|
||||
|
||||
/* no report associated (HID_QUIRK_MULTI_INPUT not set) */
|
||||
if (!hi->report)
|
||||
return;
|
||||
|
||||
field = hi->report->field[0];
|
||||
|
||||
switch (field->application) {
|
||||
case HID_GD_KEYBOARD:
|
||||
suffix = "Keyboard";
|
||||
break;
|
||||
case HID_GD_MOUSE:
|
||||
suffix = "Mouse";
|
||||
break;
|
||||
case HID_GD_KEYPAD:
|
||||
suffix = "Pad";
|
||||
break;
|
||||
case HID_DG_PEN:
|
||||
suffix = "Pen";
|
||||
break;
|
||||
case HID_CP_CONSUMER_CONTROL:
|
||||
suffix = "Consumer Control";
|
||||
break;
|
||||
case HID_GD_SYSTEM_CONTROL:
|
||||
suffix = "System Control";
|
||||
break;
|
||||
}
|
||||
|
||||
if (suffix) {
|
||||
len = strlen(hdev->name) + 2 + strlen(suffix);
|
||||
name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL);
|
||||
if (name) {
|
||||
snprintf(name, len, "%s %s", hdev->name, suffix);
|
||||
hi->input->name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable fully-functional tablet mode and determine device parameters.
|
||||
*
|
||||
* @hdev: HID device
|
||||
*/
|
||||
static int uclogic_tablet_enable(struct hid_device *hdev)
|
||||
{
|
||||
int rc;
|
||||
struct usb_device *usb_dev = hid_to_usb_dev(hdev);
|
||||
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
__le16 *buf = NULL;
|
||||
size_t len;
|
||||
s32 params[UCLOGIC_PH_ID_NUM];
|
||||
s32 resolution;
|
||||
__u8 *p;
|
||||
s32 v;
|
||||
|
||||
/*
|
||||
* Read string descriptor containing tablet parameters. The specific
|
||||
* string descriptor and data were discovered by sniffing the Windows
|
||||
* driver traffic.
|
||||
* NOTE: This enables fully-functional tablet mode.
|
||||
*/
|
||||
len = UCLOGIC_PRM_NUM * sizeof(*buf);
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
hid_err(hdev, "failed to allocate parameter buffer\n");
|
||||
rc = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
|
||||
(USB_DT_STRING << 8) + 0x64,
|
||||
0x0409, buf, len,
|
||||
USB_CTRL_GET_TIMEOUT);
|
||||
if (rc == -EPIPE) {
|
||||
hid_err(hdev, "device parameters not found\n");
|
||||
rc = -ENODEV;
|
||||
goto cleanup;
|
||||
} else if (rc < 0) {
|
||||
hid_err(hdev, "failed to get device parameters: %d\n", rc);
|
||||
rc = -ENODEV;
|
||||
goto cleanup;
|
||||
} else if (rc != len) {
|
||||
hid_err(hdev, "invalid device parameters\n");
|
||||
rc = -ENODEV;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Extract device parameters */
|
||||
params[UCLOGIC_PH_ID_X_LM] = le16_to_cpu(buf[UCLOGIC_PRM_X_LM]);
|
||||
params[UCLOGIC_PH_ID_Y_LM] = le16_to_cpu(buf[UCLOGIC_PRM_Y_LM]);
|
||||
params[UCLOGIC_PH_ID_PRESSURE_LM] =
|
||||
le16_to_cpu(buf[UCLOGIC_PRM_PRESSURE_LM]);
|
||||
resolution = le16_to_cpu(buf[UCLOGIC_PRM_RESOLUTION]);
|
||||
if (resolution == 0) {
|
||||
params[UCLOGIC_PH_ID_X_PM] = 0;
|
||||
params[UCLOGIC_PH_ID_Y_PM] = 0;
|
||||
} else {
|
||||
params[UCLOGIC_PH_ID_X_PM] = params[UCLOGIC_PH_ID_X_LM] *
|
||||
1000 / resolution;
|
||||
params[UCLOGIC_PH_ID_Y_PM] = params[UCLOGIC_PH_ID_Y_LM] *
|
||||
1000 / resolution;
|
||||
}
|
||||
|
||||
/* Allocate fixed report descriptor */
|
||||
drvdata->rdesc = devm_kzalloc(&hdev->dev,
|
||||
sizeof(uclogic_tablet_rdesc_template),
|
||||
GFP_KERNEL);
|
||||
if (drvdata->rdesc == NULL) {
|
||||
hid_err(hdev, "failed to allocate fixed rdesc\n");
|
||||
rc = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
drvdata->rsize = sizeof(uclogic_tablet_rdesc_template);
|
||||
|
||||
/* Format fixed report descriptor */
|
||||
memcpy(drvdata->rdesc, uclogic_tablet_rdesc_template,
|
||||
drvdata->rsize);
|
||||
for (p = drvdata->rdesc;
|
||||
p <= drvdata->rdesc + drvdata->rsize - 4;) {
|
||||
if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D &&
|
||||
p[3] < sizeof(params)) {
|
||||
v = params[p[3]];
|
||||
put_unaligned(cpu_to_le32(v), (s32 *)p);
|
||||
p += 4;
|
||||
} else {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
cleanup:
|
||||
kfree(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int uclogic_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
int rc;
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
struct uclogic_drvdata *drvdata;
|
||||
|
||||
/*
|
||||
* libinput requires the pad interface to be on a different node
|
||||
* than the pen, so use QUIRK_MULTI_INPUT for all tablets.
|
||||
*/
|
||||
hdev->quirks |= HID_QUIRK_MULTI_INPUT;
|
||||
hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT;
|
||||
|
||||
/* Allocate and assign driver data */
|
||||
drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (drvdata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
hid_set_drvdata(hdev, drvdata);
|
||||
|
||||
switch (id->product) {
|
||||
case USB_DEVICE_ID_HUION_TABLET:
|
||||
/* If this is the pen interface */
|
||||
if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
|
||||
rc = uclogic_tablet_enable(hdev);
|
||||
if (rc) {
|
||||
hid_err(hdev, "tablet enabling failed\n");
|
||||
return rc;
|
||||
}
|
||||
drvdata->invert_pen_inrange = true;
|
||||
} else {
|
||||
drvdata->ignore_pen_usage = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
rc = hid_parse(hdev);
|
||||
if (rc) {
|
||||
hid_err(hdev, "parse failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (rc) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
u8 *data, int size)
|
||||
{
|
||||
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
||||
if ((drvdata->invert_pen_inrange) &&
|
||||
(report->type == HID_INPUT_REPORT) &&
|
||||
(report->id == UCLOGIC_PEN_REPORT_ID) &&
|
||||
(size >= 2))
|
||||
/* Invert the in-range bit */
|
||||
data[1] ^= 0x40;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_device_id uclogic_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
|
||||
USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },
|
||||
|
|
@ -641,6 +956,8 @@ static const struct hid_device_id uclogic_devices[] = {
|
|||
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_HUION, USB_DEVICE_ID_HUION_TABLET) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, uclogic_devices);
|
||||
|
|
@ -648,8 +965,14 @@ MODULE_DEVICE_TABLE(hid, uclogic_devices);
|
|||
static struct hid_driver uclogic_driver = {
|
||||
.name = "uclogic",
|
||||
.id_table = uclogic_devices,
|
||||
.probe = uclogic_probe,
|
||||
.report_fixup = uclogic_report_fixup,
|
||||
.raw_event = uclogic_raw_event,
|
||||
.input_mapping = uclogic_input_mapping,
|
||||
.input_configured = uclogic_input_configured,
|
||||
};
|
||||
module_hid_driver(uclogic_driver);
|
||||
|
||||
MODULE_AUTHOR("Martin Rusko");
|
||||
MODULE_AUTHOR("Nikolai Kondrashov");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <linux/i2c/i2c-hid.h>
|
||||
|
||||
|
|
@ -144,6 +145,8 @@ struct i2c_hid {
|
|||
unsigned long flags; /* device flags */
|
||||
|
||||
wait_queue_head_t wait; /* For waiting the interrupt */
|
||||
struct gpio_desc *desc;
|
||||
int irq;
|
||||
|
||||
struct i2c_hid_platform_data pdata;
|
||||
};
|
||||
|
|
@ -785,16 +788,16 @@ static int i2c_hid_init_irq(struct i2c_client *client)
|
|||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq);
|
||||
dev_dbg(&client->dev, "Requesting IRQ: %d\n", ihid->irq);
|
||||
|
||||
ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq,
|
||||
ret = request_threaded_irq(ihid->irq, NULL, i2c_hid_irq,
|
||||
IRQF_TRIGGER_LOW | 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, client->irq, ret);
|
||||
client->name, ihid->irq, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -841,6 +844,14 @@ 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)
|
||||
{
|
||||
|
|
@ -866,7 +877,7 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,
|
|||
pdata->hid_descriptor_address = obj->integer.value;
|
||||
ACPI_FREE(obj);
|
||||
|
||||
return 0;
|
||||
return acpi_dev_add_driver_gpios(adev, i2c_hid_acpi_gpios);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id i2c_hid_acpi_match[] = {
|
||||
|
|
@ -930,12 +941,6 @@ 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;
|
||||
}
|
||||
|
||||
ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL);
|
||||
if (!ihid)
|
||||
return -ENOMEM;
|
||||
|
|
@ -955,6 +960,23 @@ 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;
|
||||
|
|
@ -1017,13 +1039,16 @@ err_mem_free:
|
|||
hid_destroy_device(hid);
|
||||
|
||||
err_irq:
|
||||
free_irq(client->irq, ihid);
|
||||
free_irq(ihid->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;
|
||||
|
|
@ -1042,13 +1067,18 @@ static int i2c_hid_remove(struct i2c_client *client)
|
|||
hid = ihid->hid;
|
||||
hid_destroy_device(hid);
|
||||
|
||||
free_irq(client->irq, ihid);
|
||||
free_irq(ihid->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;
|
||||
}
|
||||
|
||||
|
|
@ -1060,9 +1090,9 @@ static int i2c_hid_suspend(struct device *dev)
|
|||
struct hid_device *hid = ihid->hid;
|
||||
int ret = 0;
|
||||
|
||||
disable_irq(client->irq);
|
||||
disable_irq(ihid->irq);
|
||||
if (device_may_wakeup(&client->dev))
|
||||
enable_irq_wake(client->irq);
|
||||
enable_irq_wake(ihid->irq);
|
||||
|
||||
if (hid->driver && hid->driver->suspend)
|
||||
ret = hid->driver->suspend(hid, PMSG_SUSPEND);
|
||||
|
|
@ -1080,13 +1110,13 @@ static int i2c_hid_resume(struct device *dev)
|
|||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
struct hid_device *hid = ihid->hid;
|
||||
|
||||
enable_irq(client->irq);
|
||||
enable_irq(ihid->irq);
|
||||
ret = i2c_hid_hwreset(client);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
disable_irq_wake(client->irq);
|
||||
disable_irq_wake(ihid->irq);
|
||||
|
||||
if (hid->driver && hid->driver->reset_resume) {
|
||||
ret = hid->driver->reset_resume(hid);
|
||||
|
|
@ -1101,17 +1131,19 @@ 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(client->irq);
|
||||
disable_irq(ihid->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(client->irq);
|
||||
enable_irq(ihid->irq);
|
||||
i2c_hid_set_power(client, I2C_HID_PWR_ON);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -568,6 +568,12 @@ static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
|
|||
int type_id;
|
||||
int error;
|
||||
|
||||
pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0;
|
||||
if (old) {
|
||||
pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] =
|
||||
pidff->pid_id[effect->id];
|
||||
}
|
||||
|
||||
switch (effect->type) {
|
||||
case FF_CONSTANT:
|
||||
if (!old) {
|
||||
|
|
|
|||
|
|
@ -114,12 +114,8 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_2, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_KNA5, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWA60, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET, HID_QUIRK_MULTI_INPUT },
|
||||
|
|
@ -135,6 +131,7 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS },
|
||||
|
|
|
|||
|
|
@ -129,13 +129,6 @@ static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
|
|||
schedule_work(&wacom->work);
|
||||
}
|
||||
|
||||
static inline void wacom_notify_battery(struct wacom_wac *wacom_wac)
|
||||
{
|
||||
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
|
||||
|
||||
power_supply_changed(&wacom->battery);
|
||||
}
|
||||
|
||||
extern const struct hid_device_id wacom_ids[];
|
||||
|
||||
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
|
||||
|
|
@ -149,4 +142,5 @@ void wacom_wac_usage_mapping(struct hid_device *hdev,
|
|||
int wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value);
|
||||
void wacom_wac_report(struct hid_device *hdev, struct hid_report *report);
|
||||
void wacom_battery_work(struct work_struct *work);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -406,6 +406,9 @@ static int wacom_query_tablet_data(struct hid_device *hdev,
|
|||
else if (features->type == WACOM_27QHDT) {
|
||||
return wacom_set_device_mode(hdev, 131, 3, 2);
|
||||
}
|
||||
else if (features->type == BAMBOO_PAD) {
|
||||
return wacom_set_device_mode(hdev, 2, 2, 2);
|
||||
}
|
||||
} else if (features->device_type == BTN_TOOL_PEN) {
|
||||
if (features->type <= BAMBOO_PT && features->type != WIRELESS) {
|
||||
return wacom_set_device_mode(hdev, 2, 2, 2);
|
||||
|
|
@ -524,6 +527,11 @@ static int wacom_add_shared_data(struct hid_device *hdev)
|
|||
|
||||
wacom_wac->shared = &data->shared;
|
||||
|
||||
if (wacom_wac->features.device_type == BTN_TOOL_FINGER)
|
||||
wacom_wac->shared->touch = hdev;
|
||||
else if (wacom_wac->features.device_type == BTN_TOOL_PEN)
|
||||
wacom_wac->shared->pen = hdev;
|
||||
|
||||
out:
|
||||
mutex_unlock(&wacom_udev_list_lock);
|
||||
return retval;
|
||||
|
|
@ -541,14 +549,22 @@ static void wacom_release_shared_data(struct kref *kref)
|
|||
kfree(data);
|
||||
}
|
||||
|
||||
static void wacom_remove_shared_data(struct wacom_wac *wacom)
|
||||
static void wacom_remove_shared_data(struct wacom *wacom)
|
||||
{
|
||||
struct wacom_hdev_data *data;
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
|
||||
if (wacom_wac->shared) {
|
||||
data = container_of(wacom_wac->shared, struct wacom_hdev_data,
|
||||
shared);
|
||||
|
||||
if (wacom_wac->shared->touch == wacom->hdev)
|
||||
wacom_wac->shared->touch = NULL;
|
||||
else if (wacom_wac->shared->pen == wacom->hdev)
|
||||
wacom_wac->shared->pen = NULL;
|
||||
|
||||
if (wacom->shared) {
|
||||
data = container_of(wacom->shared, struct wacom_hdev_data, shared);
|
||||
kref_put(&data->kref, wacom_release_shared_data);
|
||||
wacom->shared = NULL;
|
||||
wacom_wac->shared = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -929,6 +945,7 @@ static void wacom_destroy_leds(struct wacom *wacom)
|
|||
}
|
||||
|
||||
static enum power_supply_property wacom_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_SCOPE,
|
||||
POWER_SUPPLY_PROP_CAPACITY
|
||||
|
|
@ -948,6 +965,9 @@ static int wacom_battery_get_property(struct power_supply *psy,
|
|||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = wacom->wacom_wac.bat_connected;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_SCOPE:
|
||||
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
|
||||
break;
|
||||
|
|
@ -961,6 +981,8 @@ static int wacom_battery_get_property(struct power_supply *psy,
|
|||
else if (wacom->wacom_wac.battery_capacity == 100 &&
|
||||
wacom->wacom_wac.ps_connected)
|
||||
val->intval = POWER_SUPPLY_STATUS_FULL;
|
||||
else if (wacom->wacom_wac.ps_connected)
|
||||
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
else
|
||||
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
break;
|
||||
|
|
@ -1041,8 +1063,7 @@ static int wacom_initialize_battery(struct wacom *wacom)
|
|||
|
||||
static void wacom_destroy_battery(struct wacom *wacom)
|
||||
{
|
||||
if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) &&
|
||||
wacom->battery.dev) {
|
||||
if (wacom->battery.dev) {
|
||||
power_supply_unregister(&wacom->battery);
|
||||
wacom->battery.dev = NULL;
|
||||
power_supply_unregister(&wacom->ac);
|
||||
|
|
@ -1313,6 +1334,20 @@ fail:
|
|||
return;
|
||||
}
|
||||
|
||||
void wacom_battery_work(struct work_struct *work)
|
||||
{
|
||||
struct wacom *wacom = container_of(work, struct wacom, work);
|
||||
|
||||
if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) &&
|
||||
!wacom->battery.dev) {
|
||||
wacom_initialize_battery(wacom);
|
||||
}
|
||||
else if (!(wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) &&
|
||||
wacom->battery.dev) {
|
||||
wacom_destroy_battery(wacom);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Not all devices report physical dimensions from HID.
|
||||
* Compute the default from hardcoded logical dimension
|
||||
|
|
@ -1373,6 +1408,9 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
|
||||
hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
|
||||
|
||||
/* hid-core sets this quirk for the boot interface */
|
||||
hdev->quirks &= ~HID_QUIRK_NOGET;
|
||||
|
||||
wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
|
||||
if (!wacom)
|
||||
return -ENOMEM;
|
||||
|
|
@ -1412,6 +1450,21 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
goto fail_allocate_inputs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bamboo Pad has a generic hid handling for the Pen, and we switch it
|
||||
* into debug mode for the touch part.
|
||||
* We ignore the other interfaces.
|
||||
*/
|
||||
if (features->type == BAMBOO_PAD) {
|
||||
if (features->pktlen == WACOM_PKGLEN_PENABLED) {
|
||||
features->type = HID_GENERIC;
|
||||
} else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) &&
|
||||
(features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) {
|
||||
error = -ENODEV;
|
||||
goto fail_shared_data;
|
||||
}
|
||||
}
|
||||
|
||||
/* set the default size in case we do not get them from hid */
|
||||
wacom_set_default_phy(features);
|
||||
|
||||
|
|
@ -1446,6 +1499,12 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
features->y_max = 4096;
|
||||
}
|
||||
|
||||
/*
|
||||
* Same thing for Bamboo PAD
|
||||
*/
|
||||
if (features->type == BAMBOO_PAD)
|
||||
features->device_type = BTN_TOOL_FINGER;
|
||||
|
||||
if (hdev->bus == BUS_BLUETOOTH)
|
||||
features->quirks |= WACOM_QUIRK_BATTERY;
|
||||
|
||||
|
|
@ -1462,19 +1521,17 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
|
||||
"%s Pad", features->name);
|
||||
|
||||
if (features->quirks & WACOM_QUIRK_MULTI_INPUT) {
|
||||
/* Append the device type to the name */
|
||||
if (features->device_type != BTN_TOOL_FINGER)
|
||||
strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX);
|
||||
else if (features->touch_max)
|
||||
strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX);
|
||||
else
|
||||
strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX);
|
||||
/* Append the device type to the name */
|
||||
if (features->device_type != BTN_TOOL_FINGER)
|
||||
strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX);
|
||||
else if (features->touch_max)
|
||||
strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX);
|
||||
else
|
||||
strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX);
|
||||
|
||||
error = wacom_add_shared_data(hdev);
|
||||
if (error)
|
||||
goto fail_shared_data;
|
||||
}
|
||||
error = wacom_add_shared_data(hdev);
|
||||
if (error)
|
||||
goto fail_shared_data;
|
||||
|
||||
if (!(features->quirks & WACOM_QUIRK_MONITOR) &&
|
||||
(features->quirks & WACOM_QUIRK_BATTERY)) {
|
||||
|
|
@ -1527,7 +1584,7 @@ fail_register_inputs:
|
|||
wacom_clean_inputs(wacom);
|
||||
wacom_destroy_battery(wacom);
|
||||
fail_battery:
|
||||
wacom_remove_shared_data(wacom_wac);
|
||||
wacom_remove_shared_data(wacom);
|
||||
fail_shared_data:
|
||||
wacom_clean_inputs(wacom);
|
||||
fail_allocate_inputs:
|
||||
|
|
@ -1550,7 +1607,7 @@ static void wacom_remove(struct hid_device *hdev)
|
|||
if (hdev->bus == BUS_BLUETOOTH)
|
||||
device_remove_file(&hdev->dev, &dev_attr_speed);
|
||||
wacom_destroy_battery(wacom);
|
||||
wacom_remove_shared_data(&wacom->wacom_wac);
|
||||
wacom_remove_shared_data(wacom);
|
||||
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
kfree(wacom);
|
||||
|
|
|
|||
|
|
@ -45,6 +45,27 @@ static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 };
|
|||
*/
|
||||
static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 };
|
||||
|
||||
static void wacom_notify_battery(struct wacom_wac *wacom_wac,
|
||||
int bat_capacity, bool bat_charging, bool bat_connected,
|
||||
bool ps_connected)
|
||||
{
|
||||
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
|
||||
bool changed = wacom_wac->battery_capacity != bat_capacity ||
|
||||
wacom_wac->bat_charging != bat_charging ||
|
||||
wacom_wac->bat_connected != bat_connected ||
|
||||
wacom_wac->ps_connected != ps_connected;
|
||||
|
||||
if (changed) {
|
||||
wacom_wac->battery_capacity = bat_capacity;
|
||||
wacom_wac->bat_charging = bat_charging;
|
||||
wacom_wac->bat_connected = bat_connected;
|
||||
wacom_wac->ps_connected = ps_connected;
|
||||
|
||||
if (wacom->battery.dev)
|
||||
power_supply_changed(&wacom->battery);
|
||||
}
|
||||
}
|
||||
|
||||
static int wacom_penpartner_irq(struct wacom_wac *wacom)
|
||||
{
|
||||
unsigned char *data = wacom->data;
|
||||
|
|
@ -419,17 +440,26 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
|
|||
rw = (data[7] >> 2 & 0x07);
|
||||
battery_capacity = batcap_gr[rw];
|
||||
ps_connected = rw == 7;
|
||||
if ((wacom->battery_capacity != battery_capacity) ||
|
||||
(wacom->ps_connected != ps_connected)) {
|
||||
wacom->battery_capacity = battery_capacity;
|
||||
wacom->ps_connected = ps_connected;
|
||||
wacom_notify_battery(wacom);
|
||||
}
|
||||
wacom_notify_battery(wacom, battery_capacity, ps_connected,
|
||||
1, ps_connected);
|
||||
}
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void wacom_intuos_schedule_prox_event(struct wacom_wac *wacom_wac)
|
||||
{
|
||||
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
|
||||
struct hid_report *r;
|
||||
struct hid_report_enum *re;
|
||||
|
||||
re = &(wacom->hdev->report_enum[HID_FEATURE_REPORT]);
|
||||
r = re->report_id_hash[WACOM_REPORT_INTUOSREAD];
|
||||
if (r) {
|
||||
hid_hw_request(wacom->hdev, r, HID_REQ_GET_REPORT);
|
||||
}
|
||||
}
|
||||
|
||||
static int wacom_intuos_inout(struct wacom_wac *wacom)
|
||||
{
|
||||
struct wacom_features *features = &wacom->features;
|
||||
|
|
@ -551,12 +581,9 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
|
|||
(features->type == CINTIQ && !(data[1] & 0x40)))
|
||||
return 1;
|
||||
|
||||
if (wacom->shared) {
|
||||
wacom->shared->stylus_in_proximity = true;
|
||||
|
||||
if (wacom->shared->touch_down)
|
||||
return 1;
|
||||
}
|
||||
wacom->shared->stylus_in_proximity = true;
|
||||
if (wacom->shared->touch_down)
|
||||
return 1;
|
||||
|
||||
/* in Range while exiting */
|
||||
if (((data[1] & 0xfe) == 0x20) && wacom->reporting_data) {
|
||||
|
|
@ -568,8 +595,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
|
|||
|
||||
/* Exit report */
|
||||
if ((data[1] & 0xfe) == 0x80) {
|
||||
if (features->quirks & WACOM_QUIRK_MULTI_INPUT)
|
||||
wacom->shared->stylus_in_proximity = false;
|
||||
wacom->shared->stylus_in_proximity = false;
|
||||
wacom->reporting_data = false;
|
||||
|
||||
/* don't report exit if we don't know the ID */
|
||||
|
|
@ -610,8 +636,11 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
|
|||
}
|
||||
|
||||
/* don't report other events if we don't know the ID */
|
||||
if (!wacom->id[idx])
|
||||
if (!wacom->id[idx]) {
|
||||
/* but reschedule a read of the current tool */
|
||||
wacom_intuos_schedule_prox_event(wacom);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1023,15 +1052,9 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
|
|||
bat_charging = (power_raw & 0x08) ? 1 : 0;
|
||||
ps_connected = (power_raw & 0x10) ? 1 : 0;
|
||||
battery_capacity = batcap_i4[power_raw & 0x07];
|
||||
if ((wacom->battery_capacity != battery_capacity) ||
|
||||
(wacom->bat_charging != bat_charging) ||
|
||||
(wacom->ps_connected != ps_connected)) {
|
||||
wacom->battery_capacity = battery_capacity;
|
||||
wacom->bat_charging = bat_charging;
|
||||
wacom->ps_connected = ps_connected;
|
||||
wacom_notify_battery(wacom);
|
||||
}
|
||||
|
||||
wacom_notify_battery(wacom, battery_capacity, bat_charging,
|
||||
battery_capacity || bat_charging,
|
||||
ps_connected);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(wacom->input->dev.parent,
|
||||
|
|
@ -1042,6 +1065,28 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
|
||||
{
|
||||
struct input_dev *input = wacom->input;
|
||||
unsigned touch_max = wacom->features.touch_max;
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
/* non-HID_GENERIC single touch input doesn't call this routine */
|
||||
if ((touch_max == 1) && (wacom->features.type == HID_GENERIC))
|
||||
return wacom->hid_data.tipswitch &&
|
||||
!wacom->shared->stylus_in_proximity;
|
||||
|
||||
for (i = 0; i < input->mt->num_slots; i++) {
|
||||
struct input_mt_slot *ps = &input->mt->slots[i];
|
||||
int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
|
||||
if (id >= 0)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int wacom_24hdt_irq(struct wacom_wac *wacom)
|
||||
{
|
||||
struct input_dev *input = wacom->input;
|
||||
|
|
@ -1052,7 +1097,6 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
|
|||
int num_contacts_left = 4; /* maximum contacts per packet */
|
||||
int byte_per_packet = WACOM_BYTES_PER_24HDT_PACKET;
|
||||
int y_offset = 2;
|
||||
static int contact_with_no_pen_down_count = 0;
|
||||
|
||||
if (wacom->features.type == WACOM_27QHDT) {
|
||||
current_num_contacts = data[63];
|
||||
|
|
@ -1065,10 +1109,8 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
|
|||
* First packet resets the counter since only the first
|
||||
* packet in series will have non-zero current_num_contacts.
|
||||
*/
|
||||
if (current_num_contacts) {
|
||||
if (current_num_contacts)
|
||||
wacom->num_contacts_left = current_num_contacts;
|
||||
contact_with_no_pen_down_count = 0;
|
||||
}
|
||||
|
||||
contacts_to_send = min(num_contacts_left, wacom->num_contacts_left);
|
||||
|
||||
|
|
@ -1101,15 +1143,14 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
|
|||
input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h));
|
||||
input_report_abs(input, ABS_MT_ORIENTATION, w > h);
|
||||
}
|
||||
contact_with_no_pen_down_count++;
|
||||
}
|
||||
}
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
input_mt_sync_frame(input);
|
||||
|
||||
wacom->num_contacts_left -= contacts_to_send;
|
||||
if (wacom->num_contacts_left <= 0) {
|
||||
wacom->num_contacts_left = 0;
|
||||
wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
|
||||
wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -1122,7 +1163,6 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
|
|||
int current_num_contacts = data[2];
|
||||
int contacts_to_send = 0;
|
||||
int x_offset = 0;
|
||||
static int contact_with_no_pen_down_count = 0;
|
||||
|
||||
/* MTTPC does not support Height and Width */
|
||||
if (wacom->features.type == MTTPC || wacom->features.type == MTTPC_B)
|
||||
|
|
@ -1132,10 +1172,8 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
|
|||
* First packet resets the counter since only the first
|
||||
* packet in series will have non-zero current_num_contacts.
|
||||
*/
|
||||
if (current_num_contacts) {
|
||||
if (current_num_contacts)
|
||||
wacom->num_contacts_left = current_num_contacts;
|
||||
contact_with_no_pen_down_count = 0;
|
||||
}
|
||||
|
||||
/* There are at most 5 contacts per packet */
|
||||
contacts_to_send = min(5, wacom->num_contacts_left);
|
||||
|
|
@ -1156,15 +1194,14 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
|
|||
int y = get_unaligned_le16(&data[offset + x_offset + 9]);
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
contact_with_no_pen_down_count++;
|
||||
}
|
||||
}
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
input_mt_sync_frame(input);
|
||||
|
||||
wacom->num_contacts_left -= contacts_to_send;
|
||||
if (wacom->num_contacts_left <= 0) {
|
||||
wacom->num_contacts_left = 0;
|
||||
wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
|
||||
wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -1173,7 +1210,6 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
|
|||
{
|
||||
struct input_dev *input = wacom->input;
|
||||
unsigned char *data = wacom->data;
|
||||
int contact_with_no_pen_down_count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
|
|
@ -1188,13 +1224,12 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
|
|||
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
contact_with_no_pen_down_count++;
|
||||
}
|
||||
}
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
input_mt_sync_frame(input);
|
||||
|
||||
/* keep touch state for pen event */
|
||||
wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
|
||||
wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -1522,29 +1557,6 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int wacom_wac_finger_count_touches(struct hid_device *hdev)
|
||||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct input_dev *input = wacom_wac->input;
|
||||
unsigned touch_max = wacom_wac->features.touch_max;
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
if (touch_max == 1)
|
||||
return wacom_wac->hid_data.tipswitch &&
|
||||
!wacom_wac->shared->stylus_in_proximity;
|
||||
|
||||
for (i = 0; i < input->mt->num_slots; i++) {
|
||||
struct input_mt_slot *ps = &input->mt->slots[i];
|
||||
int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
|
||||
if (id >= 0)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void wacom_wac_finger_report(struct hid_device *hdev,
|
||||
struct hid_report *report)
|
||||
{
|
||||
|
|
@ -1559,7 +1571,7 @@ static void wacom_wac_finger_report(struct hid_device *hdev,
|
|||
input_sync(input);
|
||||
|
||||
/* keep touch state for pen event */
|
||||
wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(hdev);
|
||||
wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(wacom_wac);
|
||||
}
|
||||
|
||||
void wacom_wac_usage_mapping(struct hid_device *hdev,
|
||||
|
|
@ -1619,7 +1631,6 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
|
|||
struct input_dev *pad_input = wacom->pad_input;
|
||||
unsigned char *data = wacom->data;
|
||||
int i;
|
||||
int contact_with_no_pen_down_count = 0;
|
||||
|
||||
if (data[0] != 0x02)
|
||||
return 0;
|
||||
|
|
@ -1647,22 +1658,21 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
|
|||
}
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
contact_with_no_pen_down_count++;
|
||||
}
|
||||
}
|
||||
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
input_mt_sync_frame(input);
|
||||
|
||||
input_report_key(pad_input, BTN_LEFT, (data[1] & 0x08) != 0);
|
||||
input_report_key(pad_input, BTN_FORWARD, (data[1] & 0x04) != 0);
|
||||
input_report_key(pad_input, BTN_BACK, (data[1] & 0x02) != 0);
|
||||
input_report_key(pad_input, BTN_RIGHT, (data[1] & 0x01) != 0);
|
||||
wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
|
||||
wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data, int last_touch_count)
|
||||
static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
|
||||
{
|
||||
struct wacom_features *features = &wacom->features;
|
||||
struct input_dev *input = wacom->input;
|
||||
|
|
@ -1670,7 +1680,7 @@ static int wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data, in
|
|||
int slot = input_mt_get_slot_by_key(input, data[0]);
|
||||
|
||||
if (slot < 0)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
touch = touch && !wacom->shared->stylus_in_proximity;
|
||||
|
||||
|
|
@ -1702,9 +1712,7 @@ static int wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data, in
|
|||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
input_report_abs(input, ABS_MT_TOUCH_MAJOR, width);
|
||||
input_report_abs(input, ABS_MT_TOUCH_MINOR, height);
|
||||
last_touch_count++;
|
||||
}
|
||||
return last_touch_count;
|
||||
}
|
||||
|
||||
static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data)
|
||||
|
|
@ -1729,7 +1737,6 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
|
|||
unsigned char *data = wacom->data;
|
||||
int count = data[1] & 0x07;
|
||||
int i;
|
||||
int contact_with_no_pen_down_count = 0;
|
||||
|
||||
if (data[0] != 0x02)
|
||||
return 0;
|
||||
|
|
@ -1740,15 +1747,13 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
|
|||
int msg_id = data[offset];
|
||||
|
||||
if (msg_id >= 2 && msg_id <= 17)
|
||||
contact_with_no_pen_down_count =
|
||||
wacom_bpt3_touch_msg(wacom, data + offset,
|
||||
contact_with_no_pen_down_count);
|
||||
wacom_bpt3_touch_msg(wacom, data + offset);
|
||||
else if (msg_id == 128)
|
||||
wacom_bpt3_button_msg(wacom, data + offset);
|
||||
|
||||
}
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
|
||||
input_mt_sync_frame(input);
|
||||
wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -1760,23 +1765,9 @@ static int wacom_bpt_pen(struct wacom_wac *wacom)
|
|||
unsigned char *data = wacom->data;
|
||||
int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0;
|
||||
|
||||
if (data[0] != WACOM_REPORT_PENABLED && data[0] != WACOM_REPORT_USB)
|
||||
if (data[0] != WACOM_REPORT_PENABLED)
|
||||
return 0;
|
||||
|
||||
if (data[0] == WACOM_REPORT_USB) {
|
||||
if (features->type == INTUOSHT &&
|
||||
wacom->shared->touch_input &&
|
||||
features->touch_max) {
|
||||
input_report_switch(wacom->shared->touch_input,
|
||||
SW_MUTE_DEVICE, data[8] & 0x40);
|
||||
input_sync(wacom->shared->touch_input);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (wacom->shared->touch_down)
|
||||
return 0;
|
||||
|
||||
prox = (data[1] & 0x20) == 0x20;
|
||||
|
||||
/*
|
||||
|
|
@ -1789,17 +1780,21 @@ static int wacom_bpt_pen(struct wacom_wac *wacom)
|
|||
*
|
||||
* Hardware does report zero in most out-of-prox cases but not all.
|
||||
*/
|
||||
if (prox) {
|
||||
if (!wacom->shared->stylus_in_proximity) {
|
||||
if (data[1] & 0x08) {
|
||||
wacom->tool[0] = BTN_TOOL_RUBBER;
|
||||
wacom->id[0] = ERASER_DEVICE_ID;
|
||||
} else {
|
||||
wacom->tool[0] = BTN_TOOL_PEN;
|
||||
wacom->id[0] = STYLUS_DEVICE_ID;
|
||||
}
|
||||
wacom->shared->stylus_in_proximity = true;
|
||||
if (!wacom->shared->stylus_in_proximity) {
|
||||
if (data[1] & 0x08) {
|
||||
wacom->tool[0] = BTN_TOOL_RUBBER;
|
||||
wacom->id[0] = ERASER_DEVICE_ID;
|
||||
} else {
|
||||
wacom->tool[0] = BTN_TOOL_PEN;
|
||||
wacom->id[0] = STYLUS_DEVICE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
wacom->shared->stylus_in_proximity = prox;
|
||||
if (wacom->shared->touch_down)
|
||||
return 0;
|
||||
|
||||
if (prox) {
|
||||
x = le16_to_cpup((__le16 *)&data[2]);
|
||||
y = le16_to_cpup((__le16 *)&data[4]);
|
||||
p = le16_to_cpup((__le16 *)&data[6]);
|
||||
|
|
@ -1815,6 +1810,8 @@ static int wacom_bpt_pen(struct wacom_wac *wacom)
|
|||
pen = data[1] & 0x01;
|
||||
btn1 = data[1] & 0x02;
|
||||
btn2 = data[1] & 0x04;
|
||||
} else {
|
||||
wacom->id[0] = 0;
|
||||
}
|
||||
|
||||
input_report_key(input, BTN_TOUCH, pen);
|
||||
|
|
@ -1826,11 +1823,6 @@ static int wacom_bpt_pen(struct wacom_wac *wacom)
|
|||
input_report_abs(input, ABS_PRESSURE, p);
|
||||
input_report_abs(input, ABS_DISTANCE, d);
|
||||
|
||||
if (!prox) {
|
||||
wacom->id[0] = 0;
|
||||
wacom->shared->stylus_in_proximity = false;
|
||||
}
|
||||
|
||||
input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */
|
||||
input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */
|
||||
|
||||
|
|
@ -1849,6 +1841,91 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void wacom_bamboo_pad_pen_event(struct wacom_wac *wacom,
|
||||
unsigned char *data)
|
||||
{
|
||||
unsigned char prefix;
|
||||
|
||||
/*
|
||||
* We need to reroute the event from the debug interface to the
|
||||
* pen interface.
|
||||
* We need to add the report ID to the actual pen report, so we
|
||||
* temporary overwrite the first byte to prevent having to kzalloc/kfree
|
||||
* and memcpy the report.
|
||||
*/
|
||||
prefix = data[0];
|
||||
data[0] = WACOM_REPORT_BPAD_PEN;
|
||||
|
||||
/*
|
||||
* actually reroute the event.
|
||||
* No need to check if wacom->shared->pen is valid, hid_input_report()
|
||||
* will check for us.
|
||||
*/
|
||||
hid_input_report(wacom->shared->pen, HID_INPUT_REPORT, data,
|
||||
WACOM_PKGLEN_PENABLED, 1);
|
||||
|
||||
data[0] = prefix;
|
||||
}
|
||||
|
||||
static int wacom_bamboo_pad_touch_event(struct wacom_wac *wacom,
|
||||
unsigned char *data)
|
||||
{
|
||||
struct input_dev *input = wacom->input;
|
||||
unsigned char *finger_data, prefix;
|
||||
unsigned id;
|
||||
int x, y;
|
||||
bool valid;
|
||||
|
||||
prefix = data[0];
|
||||
|
||||
for (id = 0; id < wacom->features.touch_max; id++) {
|
||||
valid = !!(prefix & BIT(id)) &&
|
||||
!wacom->shared->stylus_in_proximity;
|
||||
|
||||
input_mt_slot(input, id);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, valid);
|
||||
|
||||
if (!valid)
|
||||
continue;
|
||||
|
||||
finger_data = data + 1 + id * 3;
|
||||
x = finger_data[0] | ((finger_data[1] & 0x0f) << 8);
|
||||
y = (finger_data[2] << 4) | (finger_data[1] >> 4);
|
||||
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
}
|
||||
|
||||
input_mt_sync_frame(input);
|
||||
|
||||
input_report_key(input, BTN_LEFT, prefix & 0x40);
|
||||
input_report_key(input, BTN_RIGHT, prefix & 0x80);
|
||||
|
||||
/* keep touch state for pen event */
|
||||
wacom->shared->touch_down = !!prefix &&
|
||||
!wacom->shared->stylus_in_proximity;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wacom_bamboo_pad_irq(struct wacom_wac *wacom, size_t len)
|
||||
{
|
||||
unsigned char *data = wacom->data;
|
||||
|
||||
if (!((len == WACOM_PKGLEN_BPAD_TOUCH) ||
|
||||
(len == WACOM_PKGLEN_BPAD_TOUCH_USB)) ||
|
||||
(data[0] != WACOM_REPORT_BPAD_TOUCH))
|
||||
return 0;
|
||||
|
||||
if (data[1] & 0x01)
|
||||
wacom_bamboo_pad_pen_event(wacom, &data[1]);
|
||||
|
||||
if (data[1] & 0x02)
|
||||
return wacom_bamboo_pad_touch_event(wacom, &data[9]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
|
||||
{
|
||||
unsigned char *data = wacom->data;
|
||||
|
|
@ -1859,7 +1936,7 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
|
|||
|
||||
connected = data[1] & 0x01;
|
||||
if (connected) {
|
||||
int pid, battery, ps_connected;
|
||||
int pid, battery, charging;
|
||||
|
||||
if ((wacom->shared->type == INTUOSHT) &&
|
||||
wacom->shared->touch_input &&
|
||||
|
|
@ -1871,33 +1948,66 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
|
|||
|
||||
pid = get_unaligned_be16(&data[6]);
|
||||
battery = (data[5] & 0x3f) * 100 / 31;
|
||||
ps_connected = !!(data[5] & 0x80);
|
||||
charging = !!(data[5] & 0x80);
|
||||
if (wacom->pid != pid) {
|
||||
wacom->pid = pid;
|
||||
wacom_schedule_work(wacom);
|
||||
}
|
||||
|
||||
if (wacom->shared->type &&
|
||||
(battery != wacom->battery_capacity ||
|
||||
ps_connected != wacom->ps_connected)) {
|
||||
wacom->battery_capacity = battery;
|
||||
wacom->ps_connected = ps_connected;
|
||||
wacom->bat_charging = ps_connected &&
|
||||
wacom->battery_capacity < 100;
|
||||
wacom_notify_battery(wacom);
|
||||
}
|
||||
if (wacom->shared->type)
|
||||
wacom_notify_battery(wacom, battery, charging, 1, 0);
|
||||
|
||||
} else if (wacom->pid != 0) {
|
||||
/* disconnected while previously connected */
|
||||
wacom->pid = 0;
|
||||
wacom_schedule_work(wacom);
|
||||
wacom->battery_capacity = 0;
|
||||
wacom->bat_charging = 0;
|
||||
wacom->ps_connected = 0;
|
||||
wacom_notify_battery(wacom, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len)
|
||||
{
|
||||
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
|
||||
struct wacom_features *features = &wacom_wac->features;
|
||||
unsigned char *data = wacom_wac->data;
|
||||
|
||||
if (data[0] != WACOM_REPORT_USB)
|
||||
return 0;
|
||||
|
||||
if (features->type == INTUOSHT &&
|
||||
wacom_wac->shared->touch_input &&
|
||||
features->touch_max) {
|
||||
input_report_switch(wacom_wac->shared->touch_input,
|
||||
SW_MUTE_DEVICE, data[8] & 0x40);
|
||||
input_sync(wacom_wac->shared->touch_input);
|
||||
}
|
||||
|
||||
if (data[9] & 0x02) { /* wireless module is attached */
|
||||
int battery = (data[8] & 0x3f) * 100 / 31;
|
||||
bool charging = !!(data[8] & 0x80);
|
||||
|
||||
wacom_notify_battery(wacom_wac, battery, charging,
|
||||
battery || charging, 1);
|
||||
|
||||
if (!wacom->battery.dev &&
|
||||
!(features->quirks & WACOM_QUIRK_BATTERY)) {
|
||||
features->quirks |= WACOM_QUIRK_BATTERY;
|
||||
INIT_WORK(&wacom->work, wacom_battery_work);
|
||||
wacom_schedule_work(wacom_wac);
|
||||
}
|
||||
}
|
||||
else if ((features->quirks & WACOM_QUIRK_BATTERY) &&
|
||||
wacom->battery.dev) {
|
||||
features->quirks &= ~WACOM_QUIRK_BATTERY;
|
||||
INIT_WORK(&wacom->work, wacom_battery_work);
|
||||
wacom_schedule_work(wacom_wac);
|
||||
wacom_notify_battery(wacom_wac, 0, 0, 0, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
|
||||
{
|
||||
bool sync;
|
||||
|
|
@ -1967,6 +2077,8 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
|
|||
case INTUOSPL:
|
||||
if (len == WACOM_PKGLEN_BBTOUCH3)
|
||||
sync = wacom_bpt3_touch(wacom_wac);
|
||||
else if (wacom_wac->data[0] == WACOM_REPORT_USB)
|
||||
sync = wacom_status_irq(wacom_wac, len);
|
||||
else
|
||||
sync = wacom_intuos_irq(wacom_wac);
|
||||
break;
|
||||
|
|
@ -1982,7 +2094,14 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
|
|||
|
||||
case BAMBOO_PT:
|
||||
case INTUOSHT:
|
||||
sync = wacom_bpt_irq(wacom_wac, len);
|
||||
if (wacom_wac->data[0] == WACOM_REPORT_USB)
|
||||
sync = wacom_status_irq(wacom_wac, len);
|
||||
else
|
||||
sync = wacom_bpt_irq(wacom_wac, len);
|
||||
break;
|
||||
|
||||
case BAMBOO_PAD:
|
||||
sync = wacom_bamboo_pad_irq(wacom_wac, len);
|
||||
break;
|
||||
|
||||
case WIRELESS:
|
||||
|
|
@ -2054,12 +2173,6 @@ void wacom_setup_device_quirks(struct wacom_features *features)
|
|||
features->y_max = 1023;
|
||||
}
|
||||
|
||||
/* these device have multiple inputs */
|
||||
if (features->type >= WIRELESS ||
|
||||
(features->type >= INTUOS5S && features->type <= INTUOSHT) ||
|
||||
(features->oVid && features->oPid))
|
||||
features->quirks |= WACOM_QUIRK_MULTI_INPUT;
|
||||
|
||||
/* quirk for bamboo touch with 2 low res touches */
|
||||
if (features->type == BAMBOO_PT &&
|
||||
features->pktlen == WACOM_PKGLEN_BBTOUCH) {
|
||||
|
|
@ -2323,6 +2436,13 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
|
|||
0, 0);
|
||||
}
|
||||
break;
|
||||
case BAMBOO_PAD:
|
||||
__clear_bit(ABS_MISC, input_dev->absbit);
|
||||
input_mt_init_slots(input_dev, features->touch_max,
|
||||
INPUT_MT_POINTER);
|
||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||
__set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2772,6 +2892,15 @@ static const struct wacom_features wacom_features_0x304 =
|
|||
{ "Wacom Cintiq 13HD", 59152, 33448, 1023, 63,
|
||||
WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
|
||||
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
|
||||
static const struct wacom_features wacom_features_0x333 =
|
||||
{ "Wacom Cintiq 13HD touch", 59152, 33448, 2047, 63,
|
||||
WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
|
||||
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
|
||||
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x335 };
|
||||
static const struct wacom_features wacom_features_0x335 =
|
||||
{ "Wacom Cintiq 13HD touch", .type = WACOM_24HDT, /* Touch */
|
||||
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x333, .touch_max = 10,
|
||||
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
|
||||
static const struct wacom_features wacom_features_0xC7 =
|
||||
{ "Wacom DTU1931", 37832, 30305, 511, 0,
|
||||
PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
|
|
@ -2976,6 +3105,12 @@ static const struct wacom_features wacom_features_0x30C =
|
|||
{ "Wacom ISDv5 30C", .type = WACOM_24HDT, /* Touch */
|
||||
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30A, .touch_max = 10,
|
||||
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
|
||||
static const struct wacom_features wacom_features_0x318 =
|
||||
{ "Wacom USB Bamboo PAD", 4095, 4095, /* Touch */
|
||||
.type = BAMBOO_PAD, 35, 48, .touch_max = 4 };
|
||||
static const struct wacom_features wacom_features_0x319 =
|
||||
{ "Wacom Wireless Bamboo PAD", 4095, 4095, /* Touch */
|
||||
.type = BAMBOO_PAD, 35, 48, .touch_max = 4 };
|
||||
static const struct wacom_features wacom_features_0x323 =
|
||||
{ "Wacom Intuos P M", 21600, 13500, 1023, 31,
|
||||
INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
|
|
@ -2992,6 +3127,10 @@ static const struct wacom_features wacom_features_HID_ANY_ID =
|
|||
HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
|
||||
.driver_data = (kernel_ulong_t)&wacom_features_##prod
|
||||
|
||||
#define I2C_DEVICE_WACOM(prod) \
|
||||
HID_DEVICE(BUS_I2C, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
|
||||
.driver_data = (kernel_ulong_t)&wacom_features_##prod
|
||||
|
||||
#define USB_DEVICE_LENOVO(prod) \
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \
|
||||
.driver_data = (kernel_ulong_t)&wacom_features_##prod
|
||||
|
|
@ -3124,11 +3263,15 @@ const struct hid_device_id wacom_ids[] = {
|
|||
{ USB_DEVICE_WACOM(0x314) },
|
||||
{ USB_DEVICE_WACOM(0x315) },
|
||||
{ USB_DEVICE_WACOM(0x317) },
|
||||
{ USB_DEVICE_WACOM(0x318) },
|
||||
{ USB_DEVICE_WACOM(0x319) },
|
||||
{ USB_DEVICE_WACOM(0x323) },
|
||||
{ USB_DEVICE_WACOM(0x32A) },
|
||||
{ USB_DEVICE_WACOM(0x32B) },
|
||||
{ USB_DEVICE_WACOM(0x32C) },
|
||||
{ USB_DEVICE_WACOM(0x32F) },
|
||||
{ USB_DEVICE_WACOM(0x333) },
|
||||
{ USB_DEVICE_WACOM(0x335) },
|
||||
{ USB_DEVICE_WACOM(0x4001) },
|
||||
{ USB_DEVICE_WACOM(0x4004) },
|
||||
{ USB_DEVICE_WACOM(0x5000) },
|
||||
|
|
@ -3136,6 +3279,7 @@ const struct hid_device_id wacom_ids[] = {
|
|||
{ USB_DEVICE_LENOVO(0x6004) },
|
||||
|
||||
{ USB_DEVICE_WACOM(HID_ANY_ID) },
|
||||
{ I2C_DEVICE_WACOM(HID_ANY_ID) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, wacom_ids);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
#define WACOM_PKGLEN_MTTPC 40
|
||||
#define WACOM_PKGLEN_DTUS 68
|
||||
#define WACOM_PKGLEN_PENABLED 8
|
||||
#define WACOM_PKGLEN_BPAD_TOUCH 32
|
||||
#define WACOM_PKGLEN_BPAD_TOUCH_USB 64
|
||||
|
||||
/* wacom data size per MT contact */
|
||||
#define WACOM_BYTES_PER_MT_PACKET 11
|
||||
|
|
@ -67,13 +69,14 @@
|
|||
#define WACOM_REPORT_24HDT 1
|
||||
#define WACOM_REPORT_WL 128
|
||||
#define WACOM_REPORT_USB 192
|
||||
#define WACOM_REPORT_BPAD_PEN 3
|
||||
#define WACOM_REPORT_BPAD_TOUCH 16
|
||||
|
||||
/* device quirks */
|
||||
#define WACOM_QUIRK_MULTI_INPUT 0x0001
|
||||
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002
|
||||
#define WACOM_QUIRK_NO_INPUT 0x0004
|
||||
#define WACOM_QUIRK_MONITOR 0x0008
|
||||
#define WACOM_QUIRK_BATTERY 0x0010
|
||||
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0001
|
||||
#define WACOM_QUIRK_NO_INPUT 0x0002
|
||||
#define WACOM_QUIRK_MONITOR 0x0004
|
||||
#define WACOM_QUIRK_BATTERY 0x0008
|
||||
|
||||
#define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \
|
||||
((f)->physical == HID_DG_STYLUS) || \
|
||||
|
|
@ -122,6 +125,7 @@ enum {
|
|||
BAMBOO_PT,
|
||||
WACOM_24HDT,
|
||||
WACOM_27QHDT,
|
||||
BAMBOO_PAD,
|
||||
TABLETPC, /* add new TPC below */
|
||||
TABLETPCE,
|
||||
TABLETPC2FG,
|
||||
|
|
@ -169,6 +173,8 @@ struct wacom_shared {
|
|||
unsigned touch_max;
|
||||
int type;
|
||||
struct input_dev *touch_input;
|
||||
struct hid_device *pen;
|
||||
struct hid_device *touch;
|
||||
};
|
||||
|
||||
struct hid_data {
|
||||
|
|
@ -205,6 +211,7 @@ struct wacom_wac {
|
|||
int battery_capacity;
|
||||
int num_contacts_left;
|
||||
int bat_charging;
|
||||
int bat_connected;
|
||||
int ps_connected;
|
||||
u8 bt_features;
|
||||
u8 bt_high_speed;
|
||||
|
|
|
|||
Loading…
Reference in a new issue