mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 19:21:31 -04:00
Linux 4.2-rc3
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJVrBp0AAoJEHm+PkMAQRiGADsH/iCVjA1x+a19xpeuHNJcjcQR Lin8td8zdUxereBNQOFjnkhCmKtzLODXGkQdYFaWZx8mbEQ93wC+nPV8O+LsqPbI T/2MjJ78IjlNsoTyE4b1AhqpASsxrLZJNB+f9dd251f1BSAzKfRER0G46WxbOPN7 9HdzCMbtx03ZJkr9baE6alljxA43O5mpDzpotKIagfTMQr5h0ra16uXlmVXFjHRs roNJsugwKxJPnVmLWWOM1JsAjRSMBAXQUQxhJJCMDOhgZZ5KR1z6L30ESeFTUhvh WG/2MEfyVYXn91kV0N+AUCuOFHTv6uiaz3RaRVJFDSv3sPOh1WQ9tQHxyrBsqyY= =MA/P -----END PGP SIGNATURE----- Merge tag 'v4.2-rc3' into next Sync up with Linux 4.2-rc3 to bring in infrastructure (OF) pieces.
This commit is contained in:
commit
c57d5621d2
16312 changed files with 1451919 additions and 462660 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,10 +632,14 @@ 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.
|
||||
Provides HID support for Plantronics USB audio devices.
|
||||
Correctly maps vendor unique volume up/down HID usages to
|
||||
KEY_VOLUMEUP and KEY_VOLUMEDOWN events and prevents core mapping
|
||||
of other vendor unique HID usages to random mouse events.
|
||||
|
||||
Say M here if you may ever plug in a Plantronics USB audio device.
|
||||
|
||||
config HID_PRIMAX
|
||||
tristate "Primax non-fully HID-compliant devices"
|
||||
|
|
@ -885,6 +883,21 @@ config HID_SENSOR_HUB
|
|||
for events and handle data streams. Each sensor driver can format
|
||||
data and present to user mode using input or IIO interface.
|
||||
|
||||
config HID_SENSOR_CUSTOM_SENSOR
|
||||
tristate "HID Sensors hub custom sensor support"
|
||||
depends on HID_SENSOR_HUB
|
||||
default n
|
||||
---help---
|
||||
HID Sensor hub specification allows definition of some custom and
|
||||
generic sensors. Unlike other HID sensors, they can't be exported
|
||||
via Linux IIO because of custom fields. This is up to the manufacturer
|
||||
to decide how to interpret these special sensor ids and process in
|
||||
the user space. Currently some manufacturers are using these ids for
|
||||
sensor calibration and debugging other sensors. Manufacturers
|
||||
should't use these special custom sensor ids to export any of the
|
||||
standard sensors.
|
||||
Select this config option for custom/generic sensor support.
|
||||
|
||||
endmenu
|
||||
|
||||
endif # HID
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
|
|||
obj-$(CONFIG_HID_ACRUX) += hid-axff.o
|
||||
obj-$(CONFIG_HID_APPLE) += hid-apple.o
|
||||
obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o
|
||||
obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
|
||||
obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
|
||||
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
|
||||
obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o
|
||||
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
|
||||
|
|
@ -41,18 +41,17 @@ 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
|
||||
obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
|
||||
obj-$(CONFIG_HID_KYE) += hid-kye.o
|
||||
obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
|
||||
obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
|
||||
obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o
|
||||
obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
|
||||
obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
|
||||
obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o
|
||||
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
|
||||
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
|
||||
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
|
||||
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
|
||||
obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
|
||||
|
|
@ -101,6 +100,7 @@ obj-$(CONFIG_HID_WACOM) += wacom.o
|
|||
obj-$(CONFIG_HID_WALTOP) += hid-waltop.o
|
||||
obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o
|
||||
obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o
|
||||
obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR) += hid-sensor-custom.o
|
||||
|
||||
obj-$(CONFIG_USB_HID) += usbhid/
|
||||
obj-$(CONFIG_USB_MOUSE) += usbhid/
|
||||
|
|
|
|||
|
|
@ -706,7 +706,8 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
|
|||
|
||||
if (hid->vendor == USB_VENDOR_ID_MICROSOFT &&
|
||||
(hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 ||
|
||||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3_JP) &&
|
||||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3_JP ||
|
||||
hid->product == USB_DEVICE_ID_MS_POWER_COVER) &&
|
||||
hid->group == HID_GROUP_MULTITOUCH)
|
||||
hid->group = HID_GROUP_GENERIC;
|
||||
|
||||
|
|
@ -1061,13 +1062,13 @@ static u32 s32ton(__s32 value, unsigned n)
|
|||
* Search linux-kernel and linux-usb-devel archives for "hid-core extract".
|
||||
*/
|
||||
|
||||
static __u32 extract(const struct hid_device *hid, __u8 *report,
|
||||
__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
|
||||
unsigned offset, unsigned n)
|
||||
{
|
||||
u64 x;
|
||||
|
||||
if (n > 32)
|
||||
hid_warn(hid, "extract() called with n (%d) > 32! (%s)\n",
|
||||
hid_warn(hid, "hid_field_extract() called with n (%d) > 32! (%s)\n",
|
||||
n, current->comm);
|
||||
|
||||
report += offset >> 3; /* adjust byte index */
|
||||
|
|
@ -1076,6 +1077,7 @@ static __u32 extract(const struct hid_device *hid, __u8 *report,
|
|||
x = (x >> offset) & ((1ULL << n) - 1); /* extract bit field */
|
||||
return (u32) x;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_field_extract);
|
||||
|
||||
/*
|
||||
* "implement" : set bits in a little endian bit stream.
|
||||
|
|
@ -1221,9 +1223,9 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
|
|||
for (n = 0; n < count; n++) {
|
||||
|
||||
value[n] = min < 0 ?
|
||||
snto32(extract(hid, data, offset + n * size, size),
|
||||
size) :
|
||||
extract(hid, data, offset + n * size, size);
|
||||
snto32(hid_field_extract(hid, data, offset + n * size,
|
||||
size), size) :
|
||||
hid_field_extract(hid, data, offset + n * size, size);
|
||||
|
||||
/* Ignore report if ErrorRollOver */
|
||||
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
|
||||
|
|
@ -1562,12 +1564,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 +1662,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 +1682,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,12 +1846,14 @@ 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)
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
|
||||
#endif
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
|
||||
|
|
@ -1880,6 +1904,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
|
||||
|
|
@ -1938,9 +1963,12 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
|
||||
|
|
@ -1976,6 +2004,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_Q_PAD) },
|
||||
|
|
@ -2245,14 +2274,6 @@ static const struct hid_device_id hid_ignore_list[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) },
|
||||
|
|
@ -2379,14 +2400,6 @@ static const struct hid_device_id hid_ignore_list[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WTP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) },
|
||||
#endif
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
|
||||
{ }
|
||||
|
|
|
|||
|
|
@ -41,13 +41,9 @@ static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|||
|
||||
for (i = 0; i < *rsize - 4; i++)
|
||||
if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
|
||||
__u8 tmp;
|
||||
|
||||
rdesc[i] = 0x19;
|
||||
rdesc[i + 2] = 0x29;
|
||||
tmp = rdesc[i + 3];
|
||||
rdesc[i + 3] = rdesc[i + 1];
|
||||
rdesc[i + 1] = tmp;
|
||||
swap(rdesc[i + 3], rdesc[i + 1]);
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
@ -164,6 +164,7 @@
|
|||
#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
|
||||
#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
|
||||
#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
|
||||
#define USB_DEVICE_ID_ATEN_CS682 0x2213
|
||||
|
||||
#define USB_VENDOR_ID_ATMEL 0x03eb
|
||||
#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
|
||||
|
|
@ -226,6 +227,7 @@
|
|||
#define USB_DEVICE_ID_CHICONY_TACTICAL_PAD 0x0418
|
||||
#define USB_DEVICE_ID_CHICONY_MULTI_TOUCH 0xb19d
|
||||
#define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618
|
||||
#define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE 0x1053
|
||||
#define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123
|
||||
#define USB_DEVICE_ID_CHICONY_AK1D 0x1125
|
||||
|
||||
|
|
@ -362,16 +364,6 @@
|
|||
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a
|
||||
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100
|
||||
|
||||
#define USB_VENDOR_ID_GLAB 0x06c2
|
||||
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
|
||||
#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
|
||||
#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040
|
||||
#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044
|
||||
#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
|
||||
#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051
|
||||
#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053
|
||||
#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058
|
||||
|
||||
#define USB_VENDOR_ID_GOODTOUCH 0x1aad
|
||||
#define USB_DEVICE_ID_GOODTOUCH_000f 0x000f
|
||||
|
||||
|
|
@ -459,6 +451,11 @@
|
|||
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
|
||||
#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
|
||||
|
||||
#define USB_VENDOR_ID_HP 0x03f0
|
||||
#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A 0x0a4a
|
||||
#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A 0x0b4a
|
||||
#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE 0x134a
|
||||
|
||||
#define USB_VENDOR_ID_HUION 0x256c
|
||||
#define USB_DEVICE_ID_HUION_TABLET 0x006e
|
||||
|
||||
|
|
@ -533,6 +530,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
|
||||
|
|
@ -579,6 +577,7 @@
|
|||
#define USB_DEVICE_ID_LENOVO_TPKBD 0x6009
|
||||
#define USB_DEVICE_ID_LENOVO_CUSBKBD 0x6047
|
||||
#define USB_DEVICE_ID_LENOVO_CBTKBD 0x6048
|
||||
#define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067
|
||||
|
||||
#define USB_VENDOR_ID_LG 0x1fd2
|
||||
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
|
||||
|
|
@ -591,6 +590,9 @@
|
|||
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
|
||||
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
|
||||
#define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306
|
||||
#define USB_DEVICE_ID_LOGITECH_MOUSE_C01A 0xc01a
|
||||
#define USB_DEVICE_ID_LOGITECH_MOUSE_C05A 0xc05a
|
||||
#define USB_DEVICE_ID_LOGITECH_MOUSE_C06A 0xc06a
|
||||
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a
|
||||
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211
|
||||
#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215
|
||||
|
|
@ -663,6 +665,7 @@
|
|||
#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9
|
||||
#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07dc
|
||||
#define USB_DEVICE_ID_MS_TYPE_COVER_3_JP 0x07dd
|
||||
#define USB_DEVICE_ID_MS_POWER_COVER 0x07da
|
||||
|
||||
#define USB_VENDOR_ID_MOJO 0x8282
|
||||
#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201
|
||||
|
|
@ -842,6 +845,7 @@
|
|||
#define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306
|
||||
#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268
|
||||
#define USB_DEVICE_ID_SONY_PS4_CONTROLLER 0x05c4
|
||||
#define USB_DEVICE_ID_SONY_MOTION_CONTROLLER 0x03d5
|
||||
#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f
|
||||
#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002
|
||||
#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000
|
||||
|
|
@ -948,13 +952,6 @@
|
|||
#define USB_DEVICE_ID_VELLEMAN_K8061_FIRST 0x8061
|
||||
#define USB_DEVICE_ID_VELLEMAN_K8061_LAST 0x8068
|
||||
|
||||
#define USB_VENDOR_ID_VERNIER 0x08f7
|
||||
#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
|
||||
#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
|
||||
#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
|
||||
#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
|
||||
#define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006
|
||||
|
||||
#define USB_VENDOR_ID_VTL 0x0306
|
||||
#define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F 0xff3f
|
||||
|
||||
|
|
@ -973,9 +970,6 @@
|
|||
|
||||
#define USB_VENDOR_ID_WISEGROUP 0x0925
|
||||
#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
|
||||
#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
|
||||
#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
|
||||
#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201
|
||||
#define USB_DEVICE_ID_SUPER_JOY_BOX_3 0x8888
|
||||
#define USB_DEVICE_ID_QUAD_USB_JOYPAD 0x8800
|
||||
#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866
|
||||
|
|
@ -1022,10 +1016,18 @@
|
|||
#define USB_DEVICE_ID_ZYTRONIC_ZXY100 0x0005
|
||||
|
||||
#define USB_VENDOR_ID_PRIMAX 0x0461
|
||||
#define USB_DEVICE_ID_PRIMAX_MOUSE_4D22 0x4d22
|
||||
#define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05
|
||||
|
||||
|
||||
#define USB_VENDOR_ID_RISO_KAGAKU 0x1294 /* Riso Kagaku Corp. */
|
||||
#define USB_DEVICE_ID_RI_KA_WEBMAIL 0x1320 /* Webmail Notifier */
|
||||
|
||||
#define USB_VENDOR_ID_MULTIPLE_1781 0x1781
|
||||
#define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD 0x0a8d
|
||||
|
||||
#define USB_VENDOR_ID_DRACAL_RAPHNET 0x289b
|
||||
#define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002
|
||||
#define USB_DEVICE_ID_RAPHNET_4NES4SNES 0x0003
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -339,7 +339,7 @@ static int hidinput_get_battery_property(struct power_supply *psy,
|
|||
enum power_supply_property prop,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct hid_device *dev = container_of(psy, struct hid_device, battery);
|
||||
struct hid_device *dev = power_supply_get_drvdata(psy);
|
||||
int ret = 0;
|
||||
__u8 *buf;
|
||||
|
||||
|
|
@ -397,26 +397,32 @@ static int hidinput_get_battery_property(struct power_supply *psy,
|
|||
|
||||
static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field)
|
||||
{
|
||||
struct power_supply *battery = &dev->battery;
|
||||
int ret;
|
||||
struct power_supply_desc *psy_desc = NULL;
|
||||
struct power_supply_config psy_cfg = { .drv_data = dev, };
|
||||
unsigned quirks;
|
||||
s32 min, max;
|
||||
|
||||
if (field->usage->hid != HID_DC_BATTERYSTRENGTH)
|
||||
return false; /* no match */
|
||||
|
||||
if (battery->name != NULL)
|
||||
if (dev->battery != NULL)
|
||||
goto out; /* already initialized? */
|
||||
|
||||
battery->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq);
|
||||
if (battery->name == NULL)
|
||||
psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL);
|
||||
if (psy_desc == NULL)
|
||||
goto out;
|
||||
|
||||
battery->type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
battery->properties = hidinput_battery_props;
|
||||
battery->num_properties = ARRAY_SIZE(hidinput_battery_props);
|
||||
battery->use_for_apm = 0;
|
||||
battery->get_property = hidinput_get_battery_property;
|
||||
psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq);
|
||||
if (psy_desc->name == NULL) {
|
||||
kfree(psy_desc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
psy_desc->properties = hidinput_battery_props;
|
||||
psy_desc->num_properties = ARRAY_SIZE(hidinput_battery_props);
|
||||
psy_desc->use_for_apm = 0;
|
||||
psy_desc->get_property = hidinput_get_battery_property;
|
||||
|
||||
quirks = find_battery_quirk(dev);
|
||||
|
||||
|
|
@ -439,27 +445,30 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
|
|||
dev->battery_report_type = report_type;
|
||||
dev->battery_report_id = field->report->id;
|
||||
|
||||
ret = power_supply_register(&dev->dev, battery);
|
||||
if (ret != 0) {
|
||||
hid_warn(dev, "can't register power supply: %d\n", ret);
|
||||
kfree(battery->name);
|
||||
battery->name = NULL;
|
||||
dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
|
||||
if (IS_ERR(dev->battery)) {
|
||||
hid_warn(dev, "can't register power supply: %ld\n",
|
||||
PTR_ERR(dev->battery));
|
||||
kfree(psy_desc->name);
|
||||
kfree(psy_desc);
|
||||
dev->battery = NULL;
|
||||
} else {
|
||||
power_supply_powers(dev->battery, &dev->dev);
|
||||
}
|
||||
|
||||
power_supply_powers(battery, &dev->dev);
|
||||
|
||||
out:
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hidinput_cleanup_battery(struct hid_device *dev)
|
||||
{
|
||||
if (!dev->battery.name)
|
||||
if (!dev->battery)
|
||||
return;
|
||||
|
||||
power_supply_unregister(&dev->battery);
|
||||
kfree(dev->battery.name);
|
||||
dev->battery.name = NULL;
|
||||
power_supply_unregister(dev->battery);
|
||||
kfree(dev->battery->desc->name);
|
||||
kfree(dev->battery->desc);
|
||||
dev->battery = NULL;
|
||||
}
|
||||
#else /* !CONFIG_HID_BATTERY_STRENGTH */
|
||||
static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
|
||||
|
|
@ -711,6 +720,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;
|
||||
|
|
@ -1125,7 +1157,8 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
|||
return;
|
||||
|
||||
/* report the usage code as scancode if the key status has changed */
|
||||
if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)
|
||||
if (usage->type == EV_KEY &&
|
||||
(!test_bit(usage->code, input->key)) == value)
|
||||
input_event(input, EV_MSC, MSC_SCAN, usage->hid);
|
||||
|
||||
input_event(input, usage->type, usage->code, value);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,35 @@ struct lenovo_drvdata_cptkbd {
|
|||
|
||||
#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
|
||||
|
||||
static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
|
||||
0x05, 0x88, /* Usage Page (Vendor Usage Page 0x88) */
|
||||
0x09, 0x01, /* Usage (Vendor Usage 0x01) */
|
||||
0xa1, 0x01, /* Collection (Application) */
|
||||
0x85, 0x04, /* Report ID (4) */
|
||||
0x19, 0x00, /* Usage Minimum (0) */
|
||||
0x2a, 0xff, 0xff, /* Usage Maximum (65535) */
|
||||
};
|
||||
|
||||
static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_LENOVO_TPPRODOCK:
|
||||
/* the fixups that need to be done:
|
||||
* - get a reasonable usage max for the vendor collection
|
||||
* 0x8801 from the report ID 4
|
||||
*/
|
||||
if (*rsize >= 153 &&
|
||||
memcmp(&rdesc[140], lenovo_pro_dock_need_fixup_collection,
|
||||
sizeof(lenovo_pro_dock_need_fixup_collection)) == 0) {
|
||||
rdesc[151] = 0x01;
|
||||
rdesc[152] = 0x00;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,
|
||||
struct hid_input *hi, struct hid_field *field,
|
||||
struct hid_usage *usage, unsigned long **bit, int *max)
|
||||
|
|
@ -599,7 +628,8 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
|
|||
GFP_KERNEL);
|
||||
if (data_pointer == NULL) {
|
||||
hid_err(hdev, "Could not allocate memory for driver data\n");
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
// set same default values as windows driver
|
||||
|
|
@ -610,7 +640,8 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
|
|||
name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
|
||||
if (name_mute == NULL || name_micmute == NULL) {
|
||||
hid_err(hdev, "Could not allocate memory for led data\n");
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
|
||||
snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
|
||||
|
|
@ -634,6 +665,9 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
|
|||
lenovo_features_set_tpkbd(hdev);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lenovo_probe_cptkbd(struct hid_device *hdev)
|
||||
|
|
@ -762,10 +796,29 @@ static void lenovo_remove(struct hid_device *hdev)
|
|||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static void lenovo_input_configured(struct hid_device *hdev,
|
||||
struct hid_input *hi)
|
||||
{
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_LENOVO_TPKBD:
|
||||
case USB_DEVICE_ID_LENOVO_CUSBKBD:
|
||||
case USB_DEVICE_ID_LENOVO_CBTKBD:
|
||||
if (test_bit(EV_REL, hi->input->evbit)) {
|
||||
/* set only for trackpoint device */
|
||||
__set_bit(INPUT_PROP_POINTER, hi->input->propbit);
|
||||
__set_bit(INPUT_PROP_POINTING_STICK,
|
||||
hi->input->propbit);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const struct hid_device_id lenovo_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -774,10 +827,12 @@ MODULE_DEVICE_TABLE(hid, lenovo_devices);
|
|||
static struct hid_driver lenovo_driver = {
|
||||
.name = "lenovo",
|
||||
.id_table = lenovo_devices,
|
||||
.input_configured = lenovo_input_configured,
|
||||
.input_mapping = lenovo_input_mapping,
|
||||
.probe = lenovo_probe,
|
||||
.remove = lenovo_remove,
|
||||
.raw_event = lenovo_raw_event,
|
||||
.report_fixup = lenovo_report_fixup,
|
||||
};
|
||||
module_hid_driver(lenovo_driver);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -699,7 +700,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
/* insert a little delay of 10 jiffies ~ 40ms */
|
||||
wait_queue_head_t wait;
|
||||
init_waitqueue_head (&wait);
|
||||
wait_event_interruptible_timeout(wait, 0, 10);
|
||||
wait_event_interruptible_timeout(wait, 0,
|
||||
msecs_to_jiffies(40));
|
||||
|
||||
/* Select random Address */
|
||||
buf[1] = 0xB2;
|
||||
|
|
@ -711,13 +713,16 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
}
|
||||
|
||||
if (drv_data->quirks & LG_FF)
|
||||
lgff_init(hdev);
|
||||
if (drv_data->quirks & LG_FF2)
|
||||
lg2ff_init(hdev);
|
||||
if (drv_data->quirks & LG_FF3)
|
||||
lg3ff_init(hdev);
|
||||
if (drv_data->quirks & LG_FF4)
|
||||
lg4ff_init(hdev);
|
||||
ret = lgff_init(hdev);
|
||||
else if (drv_data->quirks & LG_FF2)
|
||||
ret = lg2ff_init(hdev);
|
||||
else if (drv_data->quirks & LG_FF3)
|
||||
ret = lg3ff_init(hdev);
|
||||
else if (drv_data->quirks & LG_FF4)
|
||||
ret = lg4ff_init(hdev);
|
||||
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
return 0;
|
||||
err_free:
|
||||
|
|
@ -730,8 +735,8 @@ static void lg_remove(struct hid_device *hdev)
|
|||
struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
|
||||
if (drv_data->quirks & LG_FF4)
|
||||
lg4ff_deinit(hdev);
|
||||
|
||||
hid_hw_stop(hdev);
|
||||
else
|
||||
hid_hw_stop(hdev);
|
||||
kfree(drv_data);
|
||||
}
|
||||
|
||||
|
|
@ -818,4 +823,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
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
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
|
||||
|
||||
|
|
@ -35,11 +40,11 @@ MODULE_AUTHOR("Nestor Lopez Casado <[email protected]>");
|
|||
#define HIDPP_REPORT_LONG_LENGTH 20
|
||||
|
||||
#define HIDPP_QUIRK_CLASS_WTP BIT(0)
|
||||
#define HIDPP_QUIRK_CLASS_M560 BIT(1)
|
||||
|
||||
/* bits 1..20 are reserved for classes */
|
||||
/* bits 2..20 are reserved for classes */
|
||||
#define HIDPP_QUIRK_DELAYED_INIT BIT(21)
|
||||
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
|
||||
#define HIDPP_QUIRK_MULTI_INPUT BIT(23)
|
||||
|
||||
/*
|
||||
* There are two hidpp protocols in use, the first version hidpp10 is known
|
||||
|
|
@ -701,12 +706,6 @@ static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
||||
|
||||
if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) &&
|
||||
(field->application == HID_GD_KEYBOARD))
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -715,10 +714,6 @@ static void wtp_populate_input(struct hidpp_device *hidpp,
|
|||
{
|
||||
struct wtp_data *wd = hidpp->private_data;
|
||||
|
||||
if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) && origin_is_hid_core)
|
||||
/* this is the generic hid-input call */
|
||||
return;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__clear_bit(EV_REL, input_dev->evbit);
|
||||
|
|
@ -936,6 +931,207 @@ static int wtp_connect(struct hid_device *hdev, bool connected)
|
|||
true, true);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Logitech M560 devices */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Logitech M560 protocol overview
|
||||
*
|
||||
* The Logitech M560 mouse, is designed for windows 8. When the middle and/or
|
||||
* the sides buttons are pressed, it sends some keyboard keys events
|
||||
* instead of buttons ones.
|
||||
* To complicate things further, the middle button keys sequence
|
||||
* is different from the odd press and the even press.
|
||||
*
|
||||
* forward button -> Super_R
|
||||
* backward button -> Super_L+'d' (press only)
|
||||
* middle button -> 1st time: Alt_L+SuperL+XF86TouchpadOff (press only)
|
||||
* 2nd time: left-click (press only)
|
||||
* NB: press-only means that when the button is pressed, the
|
||||
* KeyPress/ButtonPress and KeyRelease/ButtonRelease events are generated
|
||||
* together sequentially; instead when the button is released, no event is
|
||||
* generated !
|
||||
*
|
||||
* With the command
|
||||
* 10<xx>0a 3500af03 (where <xx> is the mouse id),
|
||||
* the mouse reacts differently:
|
||||
* - it never sends a keyboard key event
|
||||
* - for the three mouse button it sends:
|
||||
* middle button press 11<xx>0a 3500af00...
|
||||
* side 1 button (forward) press 11<xx>0a 3500b000...
|
||||
* side 2 button (backward) press 11<xx>0a 3500ae00...
|
||||
* middle/side1/side2 button release 11<xx>0a 35000000...
|
||||
*/
|
||||
|
||||
static const u8 m560_config_parameter[] = {0x00, 0xaf, 0x03};
|
||||
|
||||
struct m560_private_data {
|
||||
struct input_dev *input;
|
||||
};
|
||||
|
||||
/* how buttons are mapped in the report */
|
||||
#define M560_MOUSE_BTN_LEFT 0x01
|
||||
#define M560_MOUSE_BTN_RIGHT 0x02
|
||||
#define M560_MOUSE_BTN_WHEEL_LEFT 0x08
|
||||
#define M560_MOUSE_BTN_WHEEL_RIGHT 0x10
|
||||
|
||||
#define M560_SUB_ID 0x0a
|
||||
#define M560_BUTTON_MODE_REGISTER 0x35
|
||||
|
||||
static int m560_send_config_command(struct hid_device *hdev, bool connected)
|
||||
{
|
||||
struct hidpp_report response;
|
||||
struct hidpp_device *hidpp_dev;
|
||||
|
||||
hidpp_dev = hid_get_drvdata(hdev);
|
||||
|
||||
if (!connected)
|
||||
return -ENODEV;
|
||||
|
||||
return hidpp_send_rap_command_sync(
|
||||
hidpp_dev,
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
M560_SUB_ID,
|
||||
M560_BUTTON_MODE_REGISTER,
|
||||
(u8 *)m560_config_parameter,
|
||||
sizeof(m560_config_parameter),
|
||||
&response
|
||||
);
|
||||
}
|
||||
|
||||
static int m560_allocate(struct hid_device *hdev)
|
||||
{
|
||||
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
||||
struct m560_private_data *d;
|
||||
|
||||
d = devm_kzalloc(&hdev->dev, sizeof(struct m560_private_data),
|
||||
GFP_KERNEL);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
hidpp->private_data = d;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
|
||||
{
|
||||
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
||||
struct m560_private_data *mydata = hidpp->private_data;
|
||||
|
||||
/* sanity check */
|
||||
if (!mydata || !mydata->input) {
|
||||
hid_err(hdev, "error in parameter\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (size < 7) {
|
||||
hid_err(hdev, "error in report\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data[0] == REPORT_ID_HIDPP_LONG &&
|
||||
data[2] == M560_SUB_ID && data[6] == 0x00) {
|
||||
/*
|
||||
* m560 mouse report for middle, forward and backward button
|
||||
*
|
||||
* data[0] = 0x11
|
||||
* data[1] = device-id
|
||||
* data[2] = 0x0a
|
||||
* data[5] = 0xaf -> middle
|
||||
* 0xb0 -> forward
|
||||
* 0xae -> backward
|
||||
* 0x00 -> release all
|
||||
* data[6] = 0x00
|
||||
*/
|
||||
|
||||
switch (data[5]) {
|
||||
case 0xaf:
|
||||
input_report_key(mydata->input, BTN_MIDDLE, 1);
|
||||
break;
|
||||
case 0xb0:
|
||||
input_report_key(mydata->input, BTN_FORWARD, 1);
|
||||
break;
|
||||
case 0xae:
|
||||
input_report_key(mydata->input, BTN_BACK, 1);
|
||||
break;
|
||||
case 0x00:
|
||||
input_report_key(mydata->input, BTN_BACK, 0);
|
||||
input_report_key(mydata->input, BTN_FORWARD, 0);
|
||||
input_report_key(mydata->input, BTN_MIDDLE, 0);
|
||||
break;
|
||||
default:
|
||||
hid_err(hdev, "error in report\n");
|
||||
return 0;
|
||||
}
|
||||
input_sync(mydata->input);
|
||||
|
||||
} else if (data[0] == 0x02) {
|
||||
/*
|
||||
* Logitech M560 mouse report
|
||||
*
|
||||
* data[0] = type (0x02)
|
||||
* data[1..2] = buttons
|
||||
* data[3..5] = xy
|
||||
* data[6] = wheel
|
||||
*/
|
||||
|
||||
int v;
|
||||
|
||||
input_report_key(mydata->input, BTN_LEFT,
|
||||
!!(data[1] & M560_MOUSE_BTN_LEFT));
|
||||
input_report_key(mydata->input, BTN_RIGHT,
|
||||
!!(data[1] & M560_MOUSE_BTN_RIGHT));
|
||||
|
||||
if (data[1] & M560_MOUSE_BTN_WHEEL_LEFT)
|
||||
input_report_rel(mydata->input, REL_HWHEEL, -1);
|
||||
else if (data[1] & M560_MOUSE_BTN_WHEEL_RIGHT)
|
||||
input_report_rel(mydata->input, REL_HWHEEL, 1);
|
||||
|
||||
v = hid_snto32(hid_field_extract(hdev, data+3, 0, 12), 12);
|
||||
input_report_rel(mydata->input, REL_X, v);
|
||||
|
||||
v = hid_snto32(hid_field_extract(hdev, data+3, 12, 12), 12);
|
||||
input_report_rel(mydata->input, REL_Y, v);
|
||||
|
||||
v = hid_snto32(data[6], 8);
|
||||
input_report_rel(mydata->input, REL_WHEEL, v);
|
||||
|
||||
input_sync(mydata->input);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void m560_populate_input(struct hidpp_device *hidpp,
|
||||
struct input_dev *input_dev, bool origin_is_hid_core)
|
||||
{
|
||||
struct m560_private_data *mydata = hidpp->private_data;
|
||||
|
||||
mydata->input = input_dev;
|
||||
|
||||
__set_bit(EV_KEY, mydata->input->evbit);
|
||||
__set_bit(BTN_MIDDLE, mydata->input->keybit);
|
||||
__set_bit(BTN_RIGHT, mydata->input->keybit);
|
||||
__set_bit(BTN_LEFT, mydata->input->keybit);
|
||||
__set_bit(BTN_BACK, mydata->input->keybit);
|
||||
__set_bit(BTN_FORWARD, mydata->input->keybit);
|
||||
|
||||
__set_bit(EV_REL, mydata->input->evbit);
|
||||
__set_bit(REL_X, mydata->input->relbit);
|
||||
__set_bit(REL_Y, mydata->input->relbit);
|
||||
__set_bit(REL_WHEEL, mydata->input->relbit);
|
||||
__set_bit(REL_HWHEEL, mydata->input->relbit);
|
||||
}
|
||||
|
||||
static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Generic HID++ devices */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
|
@ -948,6 +1144,9 @@ static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
|
||||
return wtp_input_mapping(hdev, hi, field, usage, bit, max);
|
||||
else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560 &&
|
||||
field->application != HID_GD_MOUSE)
|
||||
return m560_input_mapping(hdev, hi, field, usage, bit, max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -957,6 +1156,8 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,
|
|||
{
|
||||
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
|
||||
wtp_populate_input(hidpp, input, origin_is_hid_core);
|
||||
else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
|
||||
m560_populate_input(hidpp, input, origin_is_hid_core);
|
||||
}
|
||||
|
||||
static void hidpp_input_configured(struct hid_device *hdev,
|
||||
|
|
@ -1044,6 +1245,8 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
|
|||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
|
||||
return wtp_raw_event(hdev, data, size);
|
||||
else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
|
||||
return m560_raw_event(hdev, data, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1121,6 +1324,10 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
|
|||
ret = wtp_connect(hdev, connected);
|
||||
if (ret)
|
||||
return;
|
||||
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
|
||||
ret = m560_send_config_command(hdev, connected);
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!connected || hidpp->delayed_input)
|
||||
|
|
@ -1188,10 +1395,19 @@ 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)
|
||||
goto wtp_allocate_fail;
|
||||
goto allocate_fail;
|
||||
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
|
||||
ret = m560_allocate(hdev);
|
||||
if (ret)
|
||||
goto allocate_fail;
|
||||
}
|
||||
|
||||
INIT_WORK(&hidpp->work, delayed_work_cb);
|
||||
|
|
@ -1210,6 +1426,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;
|
||||
|
|
@ -1234,10 +1451,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
|
||||
connect_mask &= ~HID_CONNECT_HIDINPUT;
|
||||
|
||||
/* Re-enable hidinput for multi-input devices */
|
||||
if (hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT)
|
||||
connect_mask |= HID_CONNECT_HIDINPUT;
|
||||
|
||||
ret = hid_hw_start(hdev, connect_mask);
|
||||
if (ret) {
|
||||
hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
|
||||
|
|
@ -1257,7 +1470,7 @@ hid_hw_start_fail:
|
|||
hid_parse_fail:
|
||||
cancel_work_sync(&hidpp->work);
|
||||
mutex_destroy(&hidpp->send_mutex);
|
||||
wtp_allocate_fail:
|
||||
allocate_fail:
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1285,11 +1498,10 @@ static const struct hid_device_id hidpp_devices[] = {
|
|||
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
|
||||
USB_DEVICE_ID_LOGITECH_T651),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_WTP },
|
||||
{ /* Keyboard TK820 */
|
||||
{ /* Mouse logitech M560 */
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, 0x4102),
|
||||
.driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_MULTI_INPUT |
|
||||
HIDPP_QUIRK_CLASS_WTP },
|
||||
USB_VENDOR_ID_LOGITECH, 0x402d),
|
||||
.driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
|
||||
|
||||
{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
|
||||
|
|
|
|||
|
|
@ -280,6 +280,8 @@ static const struct hid_device_id ms_devices[] = {
|
|||
.driver_data = MS_HIDINPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP),
|
||||
.driver_data = MS_HIDINPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
|
||||
.driver_data = MS_HIDINPUT },
|
||||
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
|
||||
.driver_data = MS_PRESENTER },
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Plantronics USB HID Driver
|
||||
*
|
||||
* Copyright (c) 2014 JD Cole <jd.cole@plantronics.com>
|
||||
* Copyright (c) 2014 Terry Junge <terry.junge@plantronics.com>
|
||||
* Copyright (c) 2015 Terry Junge <terry.junge@plantronics.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
@ -17,23 +17,138 @@
|
|||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define PLT_HID_1_0_PAGE 0xffa00000
|
||||
#define PLT_HID_2_0_PAGE 0xffa20000
|
||||
|
||||
#define PLT_BASIC_TELEPHONY 0x0003
|
||||
#define PLT_BASIC_EXCEPTION 0x0005
|
||||
|
||||
#define PLT_VOL_UP 0x00b1
|
||||
#define PLT_VOL_DOWN 0x00b2
|
||||
|
||||
#define PLT1_VOL_UP (PLT_HID_1_0_PAGE | PLT_VOL_UP)
|
||||
#define PLT1_VOL_DOWN (PLT_HID_1_0_PAGE | PLT_VOL_DOWN)
|
||||
#define PLT2_VOL_UP (PLT_HID_2_0_PAGE | PLT_VOL_UP)
|
||||
#define PLT2_VOL_DOWN (PLT_HID_2_0_PAGE | PLT_VOL_DOWN)
|
||||
|
||||
#define PLT_DA60 0xda60
|
||||
#define PLT_BT300_MIN 0x0413
|
||||
#define PLT_BT300_MAX 0x0418
|
||||
|
||||
|
||||
#define PLT_ALLOW_CONSUMER (field->application == HID_CP_CONSUMERCONTROL && \
|
||||
(usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER)
|
||||
|
||||
static int plantronics_input_mapping(struct hid_device *hdev,
|
||||
struct hid_input *hi,
|
||||
struct hid_field *field,
|
||||
struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
if (field->application == HID_CP_CONSUMERCONTROL
|
||||
&& (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
|
||||
hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n",
|
||||
usage->hid, field->application);
|
||||
return 0;
|
||||
unsigned short mapped_key;
|
||||
unsigned long plt_type = (unsigned long)hid_get_drvdata(hdev);
|
||||
|
||||
/* handle volume up/down mapping */
|
||||
/* non-standard types or multi-HID interfaces - plt_type is PID */
|
||||
if (!(plt_type & HID_USAGE_PAGE)) {
|
||||
switch (plt_type) {
|
||||
case PLT_DA60:
|
||||
if (PLT_ALLOW_CONSUMER)
|
||||
goto defaulted;
|
||||
goto ignored;
|
||||
default:
|
||||
if (PLT_ALLOW_CONSUMER)
|
||||
goto defaulted;
|
||||
}
|
||||
}
|
||||
/* handle standard types - plt_type is 0xffa0uuuu or 0xffa2uuuu */
|
||||
/* 'basic telephony compliant' - allow default consumer page map */
|
||||
else if ((plt_type & HID_USAGE) >= PLT_BASIC_TELEPHONY &&
|
||||
(plt_type & HID_USAGE) != PLT_BASIC_EXCEPTION) {
|
||||
if (PLT_ALLOW_CONSUMER)
|
||||
goto defaulted;
|
||||
}
|
||||
/* not 'basic telephony' - apply legacy mapping */
|
||||
/* only map if the field is in the device's primary vendor page */
|
||||
else if (!((field->application ^ plt_type) & HID_USAGE_PAGE)) {
|
||||
switch (usage->hid) {
|
||||
case PLT1_VOL_UP:
|
||||
case PLT2_VOL_UP:
|
||||
mapped_key = KEY_VOLUMEUP;
|
||||
goto mapped;
|
||||
case PLT1_VOL_DOWN:
|
||||
case PLT2_VOL_DOWN:
|
||||
mapped_key = KEY_VOLUMEDOWN;
|
||||
goto mapped;
|
||||
}
|
||||
}
|
||||
|
||||
hid_dbg(hdev, "usage: %08x (appl: %08x) - ignored\n",
|
||||
usage->hid, field->application);
|
||||
/*
|
||||
* Future mapping of call control or other usages,
|
||||
* if and when keys are defined would go here
|
||||
* otherwise, ignore everything else that was not mapped
|
||||
*/
|
||||
|
||||
ignored:
|
||||
return -1;
|
||||
|
||||
defaulted:
|
||||
hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n",
|
||||
usage->hid, field->application);
|
||||
return 0;
|
||||
|
||||
mapped:
|
||||
hid_map_usage_clear(hi, usage, bit, max, EV_KEY, mapped_key);
|
||||
hid_dbg(hdev, "usage: %08x (appl: %08x) - mapped to key %d\n",
|
||||
usage->hid, field->application, mapped_key);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned long plantronics_device_type(struct hid_device *hdev)
|
||||
{
|
||||
unsigned i, col_page;
|
||||
unsigned long plt_type = hdev->product;
|
||||
|
||||
/* multi-HID interfaces? - plt_type is PID */
|
||||
if (plt_type >= PLT_BT300_MIN && plt_type <= PLT_BT300_MAX)
|
||||
goto exit;
|
||||
|
||||
/* determine primary vendor page */
|
||||
for (i = 0; i < hdev->maxcollection; i++) {
|
||||
col_page = hdev->collection[i].usage & HID_USAGE_PAGE;
|
||||
if (col_page == PLT_HID_2_0_PAGE) {
|
||||
plt_type = hdev->collection[i].usage;
|
||||
break;
|
||||
}
|
||||
if (col_page == PLT_HID_1_0_PAGE)
|
||||
plt_type = hdev->collection[i].usage;
|
||||
}
|
||||
|
||||
exit:
|
||||
hid_dbg(hdev, "plt_type decoded as: %08lx\n", plt_type);
|
||||
return plt_type;
|
||||
}
|
||||
|
||||
static int plantronics_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "parse failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
hid_set_drvdata(hdev, (void *)plantronics_device_type(hdev));
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
|
||||
HID_CONNECT_HIDINPUT_FORCE | HID_CONNECT_HIDDEV_FORCE);
|
||||
if (ret)
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct hid_device_id plantronics_devices[] = {
|
||||
|
|
@ -46,6 +161,7 @@ static struct hid_driver plantronics_driver = {
|
|||
.name = "plantronics",
|
||||
.id_table = plantronics_devices,
|
||||
.input_mapping = plantronics_input_mapping,
|
||||
.probe = plantronics_probe,
|
||||
};
|
||||
module_hid_driver(plantronics_driver);
|
||||
|
||||
|
|
|
|||
|
|
@ -395,11 +395,10 @@ static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data)
|
|||
|
||||
/* break keys */
|
||||
for (bit_index = 0; bit_index < 24; bit_index++) {
|
||||
key = pm->last_key[bit_index];
|
||||
if (!((0x01 << bit_index) & bit_mask)) {
|
||||
input_event(pm->input_ep82, EV_KEY,
|
||||
pm->last_key[bit_index], 0);
|
||||
pm->last_key[bit_index] = 0;
|
||||
pm->last_key[bit_index] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@
|
|||
#define RMI_SET_RMI_MODE_REPORT_ID 0x0f /* Feature Report */
|
||||
|
||||
/* flags */
|
||||
#define RMI_READ_REQUEST_PENDING BIT(0)
|
||||
#define RMI_READ_DATA_PENDING BIT(1)
|
||||
#define RMI_STARTED BIT(2)
|
||||
#define RMI_READ_REQUEST_PENDING 0
|
||||
#define RMI_READ_DATA_PENDING 1
|
||||
#define RMI_STARTED 2
|
||||
|
||||
/* device flags */
|
||||
#define RMI_DEVICE BIT(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;
|
||||
}
|
||||
|
||||
|
|
@ -850,6 +1013,7 @@ static int rmi_populate_f30(struct hid_device *hdev)
|
|||
|
||||
static int rmi_populate(struct hid_device *hdev)
|
||||
{
|
||||
struct rmi_data *data = hid_get_drvdata(hdev);
|
||||
int ret;
|
||||
|
||||
ret = rmi_scan_pdt(hdev);
|
||||
|
|
@ -858,15 +1022,23 @@ 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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rmi_populate_f30(hdev);
|
||||
if (ret)
|
||||
hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
|
||||
if (!(data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS)) {
|
||||
ret = rmi_populate_f30(hdev);
|
||||
if (ret)
|
||||
hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -907,6 +1079,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);
|
||||
|
|
|
|||
849
drivers/hid/hid-sensor-custom.c
Normal file
849
drivers/hid/hid-sensor-custom.c
Normal file
|
|
@ -0,0 +1,849 @@
|
|||
/*
|
||||
* hid-sensor-custom.c
|
||||
* Copyright (c) 2015, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
|
||||
#define HID_CUSTOM_NAME_LENGTH 64
|
||||
#define HID_CUSTOM_MAX_CORE_ATTRS 10
|
||||
#define HID_CUSTOM_TOTAL_ATTRS (HID_CUSTOM_MAX_CORE_ATTRS + 1)
|
||||
#define HID_CUSTOM_FIFO_SIZE 4096
|
||||
#define HID_CUSTOM_MAX_FEATURE_BYTES 64
|
||||
|
||||
struct hid_sensor_custom_field {
|
||||
int report_id;
|
||||
char group_name[HID_CUSTOM_NAME_LENGTH];
|
||||
struct hid_sensor_hub_attribute_info attribute;
|
||||
struct device_attribute sd_attrs[HID_CUSTOM_MAX_CORE_ATTRS];
|
||||
char attr_name[HID_CUSTOM_TOTAL_ATTRS][HID_CUSTOM_NAME_LENGTH];
|
||||
struct attribute *attrs[HID_CUSTOM_TOTAL_ATTRS];
|
||||
struct attribute_group hid_custom_attribute_group;
|
||||
};
|
||||
|
||||
struct hid_sensor_custom {
|
||||
struct mutex mutex;
|
||||
struct platform_device *pdev;
|
||||
struct hid_sensor_hub_device *hsdev;
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
int sensor_field_count;
|
||||
struct hid_sensor_custom_field *fields;
|
||||
int input_field_count;
|
||||
int input_report_size;
|
||||
int input_report_recd_size;
|
||||
bool input_skip_sample;
|
||||
bool enable;
|
||||
struct hid_sensor_custom_field *power_state;
|
||||
struct hid_sensor_custom_field *report_state;
|
||||
struct miscdevice custom_dev;
|
||||
struct kfifo data_fifo;
|
||||
unsigned long misc_opened;
|
||||
wait_queue_head_t wait;
|
||||
};
|
||||
|
||||
/* Header for each sample to user space via dev interface */
|
||||
struct hid_sensor_sample {
|
||||
u32 usage_id;
|
||||
u64 timestamp;
|
||||
u32 raw_len;
|
||||
} __packed;
|
||||
|
||||
static struct attribute hid_custom_attrs[] = {
|
||||
{.name = "name", .mode = S_IRUGO},
|
||||
{.name = "units", .mode = S_IRUGO},
|
||||
{.name = "unit-expo", .mode = S_IRUGO},
|
||||
{.name = "minimum", .mode = S_IRUGO},
|
||||
{.name = "maximum", .mode = S_IRUGO},
|
||||
{.name = "size", .mode = S_IRUGO},
|
||||
{.name = "value", .mode = S_IWUSR | S_IRUGO},
|
||||
{.name = NULL}
|
||||
};
|
||||
|
||||
static const struct hid_custom_usage_desc {
|
||||
int usage_id;
|
||||
char *desc;
|
||||
} hid_custom_usage_desc_table[] = {
|
||||
{0x200201, "event-sensor-state"},
|
||||
{0x200202, "event-sensor-event"},
|
||||
{0x200301, "property-friendly-name"},
|
||||
{0x200302, "property-persistent-unique-id"},
|
||||
{0x200303, "property-sensor-status"},
|
||||
{0x200304, "property-min-report-interval"},
|
||||
{0x200305, "property-sensor-manufacturer"},
|
||||
{0x200306, "property-sensor-model"},
|
||||
{0x200307, "property-sensor-serial-number"},
|
||||
{0x200308, "property-sensor-description"},
|
||||
{0x200309, "property-sensor-connection-type"},
|
||||
{0x20030A, "property-sensor-device-path"},
|
||||
{0x20030B, "property-hardware-revision"},
|
||||
{0x20030C, "property-firmware-version"},
|
||||
{0x20030D, "property-release-date"},
|
||||
{0x20030E, "property-report-interval"},
|
||||
{0x20030F, "property-change-sensitivity-absolute"},
|
||||
{0x200310, "property-change-sensitivity-percent-range"},
|
||||
{0x200311, "property-change-sensitivity-percent-relative"},
|
||||
{0x200312, "property-accuracy"},
|
||||
{0x200313, "property-resolution"},
|
||||
{0x200314, "property-maximum"},
|
||||
{0x200315, "property-minimum"},
|
||||
{0x200316, "property-reporting-state"},
|
||||
{0x200317, "property-sampling-rate"},
|
||||
{0x200318, "property-response-curve"},
|
||||
{0x200319, "property-power-state"},
|
||||
{0x200540, "data-field-custom"},
|
||||
{0x200541, "data-field-custom-usage"},
|
||||
{0x200542, "data-field-custom-boolean-array"},
|
||||
{0x200543, "data-field-custom-value"},
|
||||
{0x200544, "data-field-custom-value_1"},
|
||||
{0x200545, "data-field-custom-value_2"},
|
||||
{0x200546, "data-field-custom-value_3"},
|
||||
{0x200547, "data-field-custom-value_4"},
|
||||
{0x200548, "data-field-custom-value_5"},
|
||||
{0x200549, "data-field-custom-value_6"},
|
||||
{0x20054A, "data-field-custom-value_7"},
|
||||
{0x20054B, "data-field-custom-value_8"},
|
||||
{0x20054C, "data-field-custom-value_9"},
|
||||
{0x20054D, "data-field-custom-value_10"},
|
||||
{0x20054E, "data-field-custom-value_11"},
|
||||
{0x20054F, "data-field-custom-value_12"},
|
||||
{0x200550, "data-field-custom-value_13"},
|
||||
{0x200551, "data-field-custom-value_14"},
|
||||
{0x200552, "data-field-custom-value_15"},
|
||||
{0x200553, "data-field-custom-value_16"},
|
||||
{0x200554, "data-field-custom-value_17"},
|
||||
{0x200555, "data-field-custom-value_18"},
|
||||
{0x200556, "data-field-custom-value_19"},
|
||||
{0x200557, "data-field-custom-value_20"},
|
||||
{0x200558, "data-field-custom-value_21"},
|
||||
{0x200559, "data-field-custom-value_22"},
|
||||
{0x20055A, "data-field-custom-value_23"},
|
||||
{0x20055B, "data-field-custom-value_24"},
|
||||
{0x20055C, "data-field-custom-value_25"},
|
||||
{0x20055D, "data-field-custom-value_26"},
|
||||
{0x20055E, "data-field-custom-value_27"},
|
||||
{0x20055F, "data-field-custom-value_28"},
|
||||
};
|
||||
|
||||
static int usage_id_cmp(const void *p1, const void *p2)
|
||||
{
|
||||
if (*(int *)p1 < *(int *)p2)
|
||||
return -1;
|
||||
|
||||
if (*(int *)p1 > *(int *)p2)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t enable_sensor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
|
||||
|
||||
return sprintf(buf, "%d\n", sensor_inst->enable);
|
||||
}
|
||||
|
||||
static int set_power_report_state(struct hid_sensor_custom *sensor_inst,
|
||||
bool state)
|
||||
{
|
||||
int power_val = -1;
|
||||
int report_val = -1;
|
||||
u32 power_state_usage_id;
|
||||
u32 report_state_usage_id;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* It is possible that the power/report state ids are not present.
|
||||
* In this case this function will return success. But if the
|
||||
* ids are present, then it will return error if set fails.
|
||||
*/
|
||||
if (state) {
|
||||
power_state_usage_id =
|
||||
HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM;
|
||||
report_state_usage_id =
|
||||
HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM;
|
||||
} else {
|
||||
power_state_usage_id =
|
||||
HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM;
|
||||
report_state_usage_id =
|
||||
HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM;
|
||||
}
|
||||
|
||||
if (sensor_inst->power_state)
|
||||
power_val = hid_sensor_get_usage_index(sensor_inst->hsdev,
|
||||
sensor_inst->power_state->attribute.report_id,
|
||||
sensor_inst->power_state->attribute.index,
|
||||
power_state_usage_id);
|
||||
if (sensor_inst->report_state)
|
||||
report_val = hid_sensor_get_usage_index(sensor_inst->hsdev,
|
||||
sensor_inst->report_state->attribute.report_id,
|
||||
sensor_inst->report_state->attribute.index,
|
||||
report_state_usage_id);
|
||||
|
||||
if (power_val >= 0) {
|
||||
power_val +=
|
||||
sensor_inst->power_state->attribute.logical_minimum;
|
||||
ret = sensor_hub_set_feature(sensor_inst->hsdev,
|
||||
sensor_inst->power_state->attribute.report_id,
|
||||
sensor_inst->power_state->attribute.index,
|
||||
sizeof(power_val),
|
||||
&power_val);
|
||||
if (ret) {
|
||||
hid_err(sensor_inst->hsdev->hdev,
|
||||
"Set power state failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (report_val >= 0) {
|
||||
report_val +=
|
||||
sensor_inst->report_state->attribute.logical_minimum;
|
||||
ret = sensor_hub_set_feature(sensor_inst->hsdev,
|
||||
sensor_inst->report_state->attribute.report_id,
|
||||
sensor_inst->report_state->attribute.index,
|
||||
sizeof(report_val),
|
||||
&report_val);
|
||||
if (ret) {
|
||||
hid_err(sensor_inst->hsdev->hdev,
|
||||
"Set report state failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t enable_sensor_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
|
||||
int value;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (kstrtoint(buf, 0, &value) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&sensor_inst->mutex);
|
||||
if (value && !sensor_inst->enable) {
|
||||
ret = sensor_hub_device_open(sensor_inst->hsdev);
|
||||
if (ret)
|
||||
goto unlock_state;
|
||||
|
||||
ret = set_power_report_state(sensor_inst, true);
|
||||
if (ret) {
|
||||
sensor_hub_device_close(sensor_inst->hsdev);
|
||||
goto unlock_state;
|
||||
}
|
||||
sensor_inst->enable = true;
|
||||
} else if (!value && sensor_inst->enable) {
|
||||
ret = set_power_report_state(sensor_inst, false);
|
||||
sensor_hub_device_close(sensor_inst->hsdev);
|
||||
sensor_inst->enable = false;
|
||||
}
|
||||
unlock_state:
|
||||
mutex_unlock(&sensor_inst->mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(enable_sensor);
|
||||
|
||||
static struct attribute *enable_sensor_attrs[] = {
|
||||
&dev_attr_enable_sensor.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group enable_sensor_attr_group = {
|
||||
.attrs = enable_sensor_attrs,
|
||||
};
|
||||
|
||||
static ssize_t show_value(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
|
||||
struct hid_sensor_hub_attribute_info *attribute;
|
||||
int index, usage, field_index;
|
||||
char name[HID_CUSTOM_NAME_LENGTH];
|
||||
bool feature = false;
|
||||
bool input = false;
|
||||
int value = 0;
|
||||
|
||||
if (sscanf(attr->attr.name, "feature-%d-%x-%s", &index, &usage,
|
||||
name) == 3) {
|
||||
feature = true;
|
||||
field_index = index + sensor_inst->input_field_count;
|
||||
} else if (sscanf(attr->attr.name, "input-%d-%x-%s", &index, &usage,
|
||||
name) == 3) {
|
||||
input = true;
|
||||
field_index = index;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
if (!strncmp(name, "value", strlen("value"))) {
|
||||
u32 report_id;
|
||||
int ret;
|
||||
|
||||
attribute = &sensor_inst->fields[field_index].attribute;
|
||||
report_id = attribute->report_id;
|
||||
if (feature) {
|
||||
u8 values[HID_CUSTOM_MAX_FEATURE_BYTES];
|
||||
int len = 0;
|
||||
u64 value = 0;
|
||||
int i = 0;
|
||||
|
||||
ret = sensor_hub_get_feature(sensor_inst->hsdev,
|
||||
report_id,
|
||||
index,
|
||||
sizeof(values), values);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
while (i < ret) {
|
||||
if (i + attribute->size > ret) {
|
||||
len += snprintf(&buf[len],
|
||||
PAGE_SIZE - len,
|
||||
"%d ", values[i]);
|
||||
break;
|
||||
}
|
||||
switch (attribute->size) {
|
||||
case 2:
|
||||
value = (u64) *(u16 *)&values[i];
|
||||
i += attribute->size;
|
||||
break;
|
||||
case 4:
|
||||
value = (u64) *(u32 *)&values[i];
|
||||
i += attribute->size;
|
||||
break;
|
||||
case 8:
|
||||
value = *(u64 *)&values[i];
|
||||
i += attribute->size;
|
||||
break;
|
||||
default:
|
||||
value = (u64) values[i];
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
len += snprintf(&buf[len], PAGE_SIZE - len,
|
||||
"%lld ", value);
|
||||
}
|
||||
len += snprintf(&buf[len], PAGE_SIZE - len, "\n");
|
||||
|
||||
return len;
|
||||
} else if (input)
|
||||
value = sensor_hub_input_attr_get_raw_value(
|
||||
sensor_inst->hsdev,
|
||||
sensor_inst->hsdev->usage,
|
||||
usage, report_id,
|
||||
SENSOR_HUB_SYNC);
|
||||
} else if (!strncmp(name, "units", strlen("units")))
|
||||
value = sensor_inst->fields[field_index].attribute.units;
|
||||
else if (!strncmp(name, "unit-expo", strlen("unit-expo")))
|
||||
value = sensor_inst->fields[field_index].attribute.unit_expo;
|
||||
else if (!strncmp(name, "size", strlen("size")))
|
||||
value = sensor_inst->fields[field_index].attribute.size;
|
||||
else if (!strncmp(name, "minimum", strlen("minimum")))
|
||||
value = sensor_inst->fields[field_index].attribute.
|
||||
logical_minimum;
|
||||
else if (!strncmp(name, "maximum", strlen("maximum")))
|
||||
value = sensor_inst->fields[field_index].attribute.
|
||||
logical_maximum;
|
||||
else if (!strncmp(name, "name", strlen("name"))) {
|
||||
struct hid_custom_usage_desc *usage_desc;
|
||||
|
||||
usage_desc = bsearch(&usage, hid_custom_usage_desc_table,
|
||||
ARRAY_SIZE(hid_custom_usage_desc_table),
|
||||
sizeof(struct hid_custom_usage_desc),
|
||||
usage_id_cmp);
|
||||
if (usage_desc)
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
usage_desc->desc);
|
||||
else
|
||||
return sprintf(buf, "not-specified\n");
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
static ssize_t store_value(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
|
||||
int index, field_index, usage;
|
||||
char name[HID_CUSTOM_NAME_LENGTH];
|
||||
int value;
|
||||
|
||||
if (sscanf(attr->attr.name, "feature-%d-%x-%s", &index, &usage,
|
||||
name) == 3) {
|
||||
field_index = index + sensor_inst->input_field_count;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
if (!strncmp(name, "value", strlen("value"))) {
|
||||
u32 report_id;
|
||||
int ret;
|
||||
|
||||
if (kstrtoint(buf, 0, &value) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
report_id = sensor_inst->fields[field_index].attribute.
|
||||
report_id;
|
||||
ret = sensor_hub_set_feature(sensor_inst->hsdev, report_id,
|
||||
index, sizeof(value), &value);
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int hid_sensor_capture_sample(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id, size_t raw_len,
|
||||
char *raw_data, void *priv)
|
||||
{
|
||||
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(priv);
|
||||
struct hid_sensor_sample header;
|
||||
|
||||
/* If any error occurs in a sample, rest of the fields are ignored */
|
||||
if (sensor_inst->input_skip_sample) {
|
||||
hid_err(sensor_inst->hsdev->hdev, "Skipped remaining data\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
hid_dbg(sensor_inst->hsdev->hdev, "%s received %d of %d\n", __func__,
|
||||
(int) (sensor_inst->input_report_recd_size + raw_len),
|
||||
sensor_inst->input_report_size);
|
||||
|
||||
if (!test_bit(0, &sensor_inst->misc_opened))
|
||||
return 0;
|
||||
|
||||
if (!sensor_inst->input_report_recd_size) {
|
||||
int required_size = sizeof(struct hid_sensor_sample) +
|
||||
sensor_inst->input_report_size;
|
||||
header.usage_id = hsdev->usage;
|
||||
header.raw_len = sensor_inst->input_report_size;
|
||||
header.timestamp = ktime_get_real_ns();
|
||||
if (kfifo_avail(&sensor_inst->data_fifo) >= required_size) {
|
||||
kfifo_in(&sensor_inst->data_fifo,
|
||||
(unsigned char *)&header,
|
||||
sizeof(header));
|
||||
} else
|
||||
sensor_inst->input_skip_sample = true;
|
||||
}
|
||||
if (kfifo_avail(&sensor_inst->data_fifo) >= raw_len)
|
||||
kfifo_in(&sensor_inst->data_fifo, (unsigned char *)raw_data,
|
||||
raw_len);
|
||||
|
||||
sensor_inst->input_report_recd_size += raw_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_sensor_send_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id, void *priv)
|
||||
{
|
||||
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(priv);
|
||||
|
||||
if (!test_bit(0, &sensor_inst->misc_opened))
|
||||
return 0;
|
||||
|
||||
sensor_inst->input_report_recd_size = 0;
|
||||
sensor_inst->input_skip_sample = false;
|
||||
|
||||
wake_up(&sensor_inst->wait);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_sensor_custom_add_field(struct hid_sensor_custom *sensor_inst,
|
||||
int index, int report_type,
|
||||
struct hid_report *report,
|
||||
struct hid_field *field)
|
||||
{
|
||||
struct hid_sensor_custom_field *sensor_field;
|
||||
void *fields;
|
||||
|
||||
fields = krealloc(sensor_inst->fields,
|
||||
(sensor_inst->sensor_field_count + 1) *
|
||||
sizeof(struct hid_sensor_custom_field), GFP_KERNEL);
|
||||
if (!fields) {
|
||||
kfree(sensor_inst->fields);
|
||||
return -ENOMEM;
|
||||
}
|
||||
sensor_inst->fields = fields;
|
||||
sensor_field = &sensor_inst->fields[sensor_inst->sensor_field_count];
|
||||
sensor_field->attribute.usage_id = sensor_inst->hsdev->usage;
|
||||
if (field->logical)
|
||||
sensor_field->attribute.attrib_id = field->logical;
|
||||
else
|
||||
sensor_field->attribute.attrib_id = field->usage[0].hid;
|
||||
|
||||
sensor_field->attribute.index = index;
|
||||
sensor_field->attribute.report_id = report->id;
|
||||
sensor_field->attribute.units = field->unit;
|
||||
sensor_field->attribute.unit_expo = field->unit_exponent;
|
||||
sensor_field->attribute.size = (field->report_size / 8);
|
||||
sensor_field->attribute.logical_minimum = field->logical_minimum;
|
||||
sensor_field->attribute.logical_maximum = field->logical_maximum;
|
||||
|
||||
if (report_type == HID_FEATURE_REPORT)
|
||||
snprintf(sensor_field->group_name,
|
||||
sizeof(sensor_field->group_name), "feature-%x-%x",
|
||||
sensor_field->attribute.index,
|
||||
sensor_field->attribute.attrib_id);
|
||||
else if (report_type == HID_INPUT_REPORT) {
|
||||
snprintf(sensor_field->group_name,
|
||||
sizeof(sensor_field->group_name),
|
||||
"input-%x-%x", sensor_field->attribute.index,
|
||||
sensor_field->attribute.attrib_id);
|
||||
sensor_inst->input_field_count++;
|
||||
sensor_inst->input_report_size += (field->report_size *
|
||||
field->report_count) / 8;
|
||||
}
|
||||
|
||||
memset(&sensor_field->hid_custom_attribute_group, 0,
|
||||
sizeof(struct attribute_group));
|
||||
sensor_inst->sensor_field_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_sensor_custom_add_fields(struct hid_sensor_custom *sensor_inst,
|
||||
struct hid_report_enum *report_enum,
|
||||
int report_type)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
struct hid_report *report;
|
||||
struct hid_field *field;
|
||||
struct hid_sensor_hub_device *hsdev = sensor_inst->hsdev;
|
||||
|
||||
list_for_each_entry(report, &report_enum->report_list, list) {
|
||||
for (i = 0; i < report->maxfield; ++i) {
|
||||
field = report->field[i];
|
||||
if (field->maxusage &&
|
||||
((field->usage[0].collection_index >=
|
||||
hsdev->start_collection_index) &&
|
||||
(field->usage[0].collection_index <
|
||||
hsdev->end_collection_index))) {
|
||||
|
||||
ret = hid_sensor_custom_add_field(sensor_inst,
|
||||
i,
|
||||
report_type,
|
||||
report,
|
||||
field);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_sensor_custom_add_attributes(struct hid_sensor_custom
|
||||
*sensor_inst)
|
||||
{
|
||||
struct hid_sensor_hub_device *hsdev = sensor_inst->hsdev;
|
||||
struct hid_device *hdev = hsdev->hdev;
|
||||
int ret = -1;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < HID_REPORT_TYPES; ++j) {
|
||||
if (j == HID_OUTPUT_REPORT)
|
||||
continue;
|
||||
|
||||
ret = hid_sensor_custom_add_fields(sensor_inst,
|
||||
&hdev->report_enum[j], j);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* Create sysfs attributes */
|
||||
for (i = 0; i < sensor_inst->sensor_field_count; ++i) {
|
||||
j = 0;
|
||||
while (j < HID_CUSTOM_TOTAL_ATTRS &&
|
||||
hid_custom_attrs[j].name) {
|
||||
struct device_attribute *device_attr;
|
||||
|
||||
device_attr = &sensor_inst->fields[i].sd_attrs[j];
|
||||
|
||||
snprintf((char *)&sensor_inst->fields[i].attr_name[j],
|
||||
HID_CUSTOM_NAME_LENGTH, "%s-%s",
|
||||
sensor_inst->fields[i].group_name,
|
||||
hid_custom_attrs[j].name);
|
||||
sysfs_attr_init(&device_attr->attr);
|
||||
device_attr->attr.name =
|
||||
(char *)&sensor_inst->fields[i].attr_name[j];
|
||||
device_attr->attr.mode = hid_custom_attrs[j].mode;
|
||||
device_attr->show = show_value;
|
||||
if (hid_custom_attrs[j].mode & S_IWUSR)
|
||||
device_attr->store = store_value;
|
||||
sensor_inst->fields[i].attrs[j] = &device_attr->attr;
|
||||
++j;
|
||||
}
|
||||
sensor_inst->fields[i].attrs[j] = NULL;
|
||||
sensor_inst->fields[i].hid_custom_attribute_group.attrs =
|
||||
sensor_inst->fields[i].attrs;
|
||||
sensor_inst->fields[i].hid_custom_attribute_group.name =
|
||||
sensor_inst->fields[i].group_name;
|
||||
ret = sysfs_create_group(&sensor_inst->pdev->dev.kobj,
|
||||
&sensor_inst->fields[i].
|
||||
hid_custom_attribute_group);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
/* For power or report field store indexes */
|
||||
if (sensor_inst->fields[i].attribute.attrib_id ==
|
||||
HID_USAGE_SENSOR_PROY_POWER_STATE)
|
||||
sensor_inst->power_state = &sensor_inst->fields[i];
|
||||
else if (sensor_inst->fields[i].attribute.attrib_id ==
|
||||
HID_USAGE_SENSOR_PROP_REPORT_STATE)
|
||||
sensor_inst->report_state = &sensor_inst->fields[i];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hid_sensor_custom_remove_attributes(struct hid_sensor_custom *
|
||||
sensor_inst)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sensor_inst->sensor_field_count; ++i)
|
||||
sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
|
||||
&sensor_inst->fields[i].
|
||||
hid_custom_attribute_group);
|
||||
|
||||
kfree(sensor_inst->fields);
|
||||
}
|
||||
|
||||
static ssize_t hid_sensor_custom_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *f_ps)
|
||||
{
|
||||
struct hid_sensor_custom *sensor_inst;
|
||||
unsigned int copied;
|
||||
int ret;
|
||||
|
||||
sensor_inst = container_of(file->private_data,
|
||||
struct hid_sensor_custom, custom_dev);
|
||||
|
||||
if (count < sizeof(struct hid_sensor_sample))
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
if (kfifo_is_empty(&sensor_inst->data_fifo)) {
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = wait_event_interruptible(sensor_inst->wait,
|
||||
!kfifo_is_empty(&sensor_inst->data_fifo));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = kfifo_to_user(&sensor_inst->data_fifo, buf, count,
|
||||
&copied);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
} while (copied == 0);
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
static int hid_sensor_custom_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hid_sensor_custom *sensor_inst;
|
||||
|
||||
sensor_inst = container_of(file->private_data,
|
||||
struct hid_sensor_custom, custom_dev);
|
||||
|
||||
clear_bit(0, &sensor_inst->misc_opened);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_sensor_custom_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hid_sensor_custom *sensor_inst;
|
||||
|
||||
sensor_inst = container_of(file->private_data,
|
||||
struct hid_sensor_custom, custom_dev);
|
||||
/* We essentially have single reader and writer */
|
||||
if (test_and_set_bit(0, &sensor_inst->misc_opened))
|
||||
return -EBUSY;
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
static unsigned int hid_sensor_custom_poll(struct file *file,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
struct hid_sensor_custom *sensor_inst;
|
||||
unsigned int mask = 0;
|
||||
|
||||
sensor_inst = container_of(file->private_data,
|
||||
struct hid_sensor_custom, custom_dev);
|
||||
|
||||
poll_wait(file, &sensor_inst->wait, wait);
|
||||
|
||||
if (!kfifo_is_empty(&sensor_inst->data_fifo))
|
||||
mask = POLLIN | POLLRDNORM;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static const struct file_operations hid_sensor_custom_fops = {
|
||||
.open = hid_sensor_custom_open,
|
||||
.read = hid_sensor_custom_read,
|
||||
.release = hid_sensor_custom_release,
|
||||
.poll = hid_sensor_custom_poll,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static int hid_sensor_custom_dev_if_add(struct hid_sensor_custom *sensor_inst)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = kfifo_alloc(&sensor_inst->data_fifo, HID_CUSTOM_FIFO_SIZE,
|
||||
GFP_KERNEL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
init_waitqueue_head(&sensor_inst->wait);
|
||||
|
||||
sensor_inst->custom_dev.minor = MISC_DYNAMIC_MINOR;
|
||||
sensor_inst->custom_dev.name = dev_name(&sensor_inst->pdev->dev);
|
||||
sensor_inst->custom_dev.fops = &hid_sensor_custom_fops,
|
||||
ret = misc_register(&sensor_inst->custom_dev);
|
||||
if (ret) {
|
||||
kfifo_free(&sensor_inst->data_fifo);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom
|
||||
*sensor_inst)
|
||||
{
|
||||
wake_up(&sensor_inst->wait);
|
||||
misc_deregister(&sensor_inst->custom_dev);
|
||||
kfifo_free(&sensor_inst->data_fifo);
|
||||
|
||||
}
|
||||
|
||||
static int hid_sensor_custom_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hid_sensor_custom *sensor_inst;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
int ret;
|
||||
|
||||
sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst),
|
||||
GFP_KERNEL);
|
||||
if (!sensor_inst)
|
||||
return -ENOMEM;
|
||||
|
||||
sensor_inst->callbacks.capture_sample = hid_sensor_capture_sample;
|
||||
sensor_inst->callbacks.send_event = hid_sensor_send_event;
|
||||
sensor_inst->callbacks.pdev = pdev;
|
||||
sensor_inst->hsdev = hsdev;
|
||||
sensor_inst->pdev = pdev;
|
||||
mutex_init(&sensor_inst->mutex);
|
||||
platform_set_drvdata(pdev, sensor_inst);
|
||||
ret = sensor_hub_register_callback(hsdev, hsdev->usage,
|
||||
&sensor_inst->callbacks);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "callback reg failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(&sensor_inst->pdev->dev.kobj,
|
||||
&enable_sensor_attr_group);
|
||||
if (ret)
|
||||
goto err_remove_callback;
|
||||
|
||||
ret = hid_sensor_custom_add_attributes(sensor_inst);
|
||||
if (ret)
|
||||
goto err_remove_group;
|
||||
|
||||
ret = hid_sensor_custom_dev_if_add(sensor_inst);
|
||||
if (ret)
|
||||
goto err_remove_attributes;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_attributes:
|
||||
hid_sensor_custom_remove_attributes(sensor_inst);
|
||||
err_remove_group:
|
||||
sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
|
||||
&enable_sensor_attr_group);
|
||||
err_remove_callback:
|
||||
sensor_hub_remove_callback(hsdev, hsdev->usage);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hid_sensor_custom_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
|
||||
hid_sensor_custom_dev_if_remove(sensor_inst);
|
||||
hid_sensor_custom_remove_attributes(sensor_inst);
|
||||
sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
|
||||
&enable_sensor_attr_group);
|
||||
sensor_hub_remove_callback(hsdev, hsdev->usage);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_sensor_custom_ids[] = {
|
||||
{
|
||||
.name = "HID-SENSOR-2000e1",
|
||||
},
|
||||
{
|
||||
.name = "HID-SENSOR-2000e2",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, hid_sensor_custom_ids);
|
||||
|
||||
static struct platform_driver hid_sensor_custom_platform_driver = {
|
||||
.id_table = hid_sensor_custom_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = hid_sensor_custom_probe,
|
||||
.remove = hid_sensor_custom_remove,
|
||||
};
|
||||
module_platform_driver(hid_sensor_custom_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HID Sensor Custom and Generic sensor Driver");
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <[email protected]>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -28,30 +28,11 @@
|
|||
|
||||
#define HID_SENSOR_HUB_ENUM_QUIRK 0x01
|
||||
|
||||
/**
|
||||
* struct sensor_hub_pending - Synchronous read pending information
|
||||
* @status: Pending status true/false.
|
||||
* @ready: Completion synchronization data.
|
||||
* @usage_id: Usage id for physical device, E.g. Gyro usage id.
|
||||
* @attr_usage_id: Usage Id of a field, E.g. X-AXIS for a gyro.
|
||||
* @raw_size: Response size for a read request.
|
||||
* @raw_data: Place holder for received response.
|
||||
*/
|
||||
struct sensor_hub_pending {
|
||||
bool status;
|
||||
struct completion ready;
|
||||
u32 usage_id;
|
||||
u32 attr_usage_id;
|
||||
int raw_size;
|
||||
u8 *raw_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sensor_hub_data - Hold a instance data for a HID hub device
|
||||
* @hsdev: Stored hid instance for current hub device.
|
||||
* @mutex: Mutex to serialize synchronous request.
|
||||
* @lock: Spin lock to protect pending request structure.
|
||||
* @pending: Holds information of pending sync read request.
|
||||
* @dyn_callback_list: Holds callback function
|
||||
* @dyn_callback_lock: spin lock to protect callback list
|
||||
* @hid_sensor_hub_client_devs: Stores all MFD cells for a hub instance.
|
||||
|
|
@ -61,7 +42,6 @@ struct sensor_hub_pending {
|
|||
struct sensor_hub_data {
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
struct sensor_hub_pending pending;
|
||||
struct list_head dyn_callback_list;
|
||||
spinlock_t dyn_callback_lock;
|
||||
struct mfd_cell *hid_sensor_hub_client_devs;
|
||||
|
|
@ -106,7 +86,8 @@ static int sensor_hub_get_physical_device_count(struct hid_device *hdev)
|
|||
|
||||
for (i = 0; i < hdev->maxcollection; ++i) {
|
||||
struct hid_collection *collection = &hdev->collection[i];
|
||||
if (collection->type == HID_COLLECTION_PHYSICAL)
|
||||
if (collection->type == HID_COLLECTION_PHYSICAL ||
|
||||
collection->type == HID_COLLECTION_APPLICATION)
|
||||
++count;
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +120,8 @@ static struct hid_sensor_hub_callbacks *sensor_hub_get_callback(
|
|||
|
||||
spin_lock_irqsave(&pdata->dyn_callback_lock, flags);
|
||||
list_for_each_entry(callback, &pdata->dyn_callback_list, list)
|
||||
if (callback->usage_id == usage_id &&
|
||||
if ((callback->usage_id == usage_id ||
|
||||
callback->usage_id == HID_USAGE_SENSOR_COLLECTION) &&
|
||||
(collection_index >=
|
||||
callback->hsdev->start_collection_index) &&
|
||||
(collection_index <
|
||||
|
|
@ -179,7 +161,18 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
|
|||
callback->usage_callback = usage_callback;
|
||||
callback->usage_id = usage_id;
|
||||
callback->priv = NULL;
|
||||
list_add_tail(&callback->list, &pdata->dyn_callback_list);
|
||||
/*
|
||||
* If there is a handler registered for the collection type, then
|
||||
* it will handle all reports for sensors in this collection. If
|
||||
* there is also an individual sensor handler registration, then
|
||||
* we want to make sure that the reports are directed to collection
|
||||
* handler, as this may be a fusion sensor. So add collection handlers
|
||||
* to the beginning of the list, so that they are matched first.
|
||||
*/
|
||||
if (usage_id == HID_USAGE_SENSOR_COLLECTION)
|
||||
list_add(&callback->list, &pdata->dyn_callback_list);
|
||||
else
|
||||
list_add_tail(&callback->list, &pdata->dyn_callback_list);
|
||||
spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);
|
||||
|
||||
return 0;
|
||||
|
|
@ -208,10 +201,14 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
|
|||
EXPORT_SYMBOL_GPL(sensor_hub_remove_callback);
|
||||
|
||||
int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
|
||||
u32 field_index, s32 value)
|
||||
u32 field_index, int buffer_size, void *buffer)
|
||||
{
|
||||
struct hid_report *report;
|
||||
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
|
||||
__s32 *buf32 = buffer;
|
||||
int i = 0;
|
||||
int remaining_bytes;
|
||||
__s32 value;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
|
@ -220,7 +217,21 @@ int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
|
|||
ret = -EINVAL;
|
||||
goto done_proc;
|
||||
}
|
||||
hid_set_field(report->field[field_index], 0, value);
|
||||
|
||||
remaining_bytes = do_div(buffer_size, sizeof(__s32));
|
||||
if (buffer_size) {
|
||||
for (i = 0; i < buffer_size; ++i) {
|
||||
hid_set_field(report->field[field_index], i,
|
||||
(__force __s32)cpu_to_le32(*buf32));
|
||||
++buf32;
|
||||
}
|
||||
}
|
||||
if (remaining_bytes) {
|
||||
value = 0;
|
||||
memcpy(&value, (u8 *)buf32, remaining_bytes);
|
||||
hid_set_field(report->field[field_index], i,
|
||||
(__force __s32)cpu_to_le32(value));
|
||||
}
|
||||
hid_hw_request(hsdev->hdev, report, HID_REQ_SET_REPORT);
|
||||
hid_hw_wait(hsdev->hdev);
|
||||
|
||||
|
|
@ -232,10 +243,11 @@ done_proc:
|
|||
EXPORT_SYMBOL_GPL(sensor_hub_set_feature);
|
||||
|
||||
int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
|
||||
u32 field_index, s32 *value)
|
||||
u32 field_index, int buffer_size, void *buffer)
|
||||
{
|
||||
struct hid_report *report;
|
||||
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
|
||||
int report_size;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
|
@ -247,7 +259,17 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
|
|||
}
|
||||
hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
|
||||
hid_hw_wait(hsdev->hdev);
|
||||
*value = report->field[field_index]->value[0];
|
||||
|
||||
/* calculate number of bytes required to read this field */
|
||||
report_size = DIV_ROUND_UP(report->field[field_index]->report_size,
|
||||
8) *
|
||||
report->field[field_index]->report_count;
|
||||
if (!report_size) {
|
||||
ret = -EINVAL;
|
||||
goto done_proc;
|
||||
}
|
||||
ret = min(report_size, buffer_size);
|
||||
memcpy(buffer, report->field[field_index]->value, ret);
|
||||
|
||||
done_proc:
|
||||
mutex_unlock(&data->mutex);
|
||||
|
|
@ -259,47 +281,54 @@ EXPORT_SYMBOL_GPL(sensor_hub_get_feature);
|
|||
|
||||
int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id,
|
||||
u32 attr_usage_id, u32 report_id)
|
||||
u32 attr_usage_id, u32 report_id,
|
||||
enum sensor_hub_read_flags flag)
|
||||
{
|
||||
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
|
||||
unsigned long flags;
|
||||
struct hid_report *report;
|
||||
int ret_val = 0;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
memset(&data->pending, 0, sizeof(data->pending));
|
||||
init_completion(&data->pending.ready);
|
||||
data->pending.usage_id = usage_id;
|
||||
data->pending.attr_usage_id = attr_usage_id;
|
||||
data->pending.raw_size = 0;
|
||||
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
data->pending.status = true;
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT);
|
||||
report = sensor_hub_report(report_id, hsdev->hdev,
|
||||
HID_INPUT_REPORT);
|
||||
if (!report)
|
||||
goto err_free;
|
||||
return -EINVAL;
|
||||
|
||||
hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
|
||||
wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5);
|
||||
switch (data->pending.raw_size) {
|
||||
case 1:
|
||||
ret_val = *(u8 *)data->pending.raw_data;
|
||||
break;
|
||||
case 2:
|
||||
ret_val = *(u16 *)data->pending.raw_data;
|
||||
break;
|
||||
case 4:
|
||||
ret_val = *(u32 *)data->pending.raw_data;
|
||||
break;
|
||||
default:
|
||||
ret_val = 0;
|
||||
mutex_lock(hsdev->mutex_ptr);
|
||||
if (flag == SENSOR_HUB_SYNC) {
|
||||
memset(&hsdev->pending, 0, sizeof(hsdev->pending));
|
||||
init_completion(&hsdev->pending.ready);
|
||||
hsdev->pending.usage_id = usage_id;
|
||||
hsdev->pending.attr_usage_id = attr_usage_id;
|
||||
hsdev->pending.raw_size = 0;
|
||||
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
hsdev->pending.status = true;
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
}
|
||||
kfree(data->pending.raw_data);
|
||||
|
||||
err_free:
|
||||
data->pending.status = false;
|
||||
mutex_lock(&data->mutex);
|
||||
hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (flag == SENSOR_HUB_SYNC) {
|
||||
wait_for_completion_interruptible_timeout(
|
||||
&hsdev->pending.ready, HZ*5);
|
||||
switch (hsdev->pending.raw_size) {
|
||||
case 1:
|
||||
ret_val = *(u8 *)hsdev->pending.raw_data;
|
||||
break;
|
||||
case 2:
|
||||
ret_val = *(u16 *)hsdev->pending.raw_data;
|
||||
break;
|
||||
case 4:
|
||||
ret_val = *(u32 *)hsdev->pending.raw_data;
|
||||
break;
|
||||
default:
|
||||
ret_val = 0;
|
||||
}
|
||||
kfree(hsdev->pending.raw_data);
|
||||
hsdev->pending.status = false;
|
||||
}
|
||||
mutex_unlock(hsdev->mutex_ptr);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
|
@ -455,16 +484,6 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
|
|||
report->field[i]->report_count)/8);
|
||||
sz = (report->field[i]->report_size *
|
||||
report->field[i]->report_count)/8;
|
||||
if (pdata->pending.status && pdata->pending.attr_usage_id ==
|
||||
report->field[i]->usage->hid) {
|
||||
hid_dbg(hdev, "data was pending ...\n");
|
||||
pdata->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC);
|
||||
if (pdata->pending.raw_data)
|
||||
pdata->pending.raw_size = sz;
|
||||
else
|
||||
pdata->pending.raw_size = 0;
|
||||
complete(&pdata->pending.ready);
|
||||
}
|
||||
collection = &hdev->collection[
|
||||
report->field[i]->usage->collection_index];
|
||||
hid_dbg(hdev, "collection->usage %x\n",
|
||||
|
|
@ -474,8 +493,23 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
|
|||
report->field[i]->physical,
|
||||
report->field[i]->usage[0].collection_index,
|
||||
&hsdev, &priv);
|
||||
|
||||
if (callback && callback->capture_sample) {
|
||||
if (!callback) {
|
||||
ptr += sz;
|
||||
continue;
|
||||
}
|
||||
if (hsdev->pending.status && (hsdev->pending.attr_usage_id ==
|
||||
report->field[i]->usage->hid ||
|
||||
hsdev->pending.attr_usage_id ==
|
||||
report->field[i]->logical)) {
|
||||
hid_dbg(hdev, "data was pending ...\n");
|
||||
hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC);
|
||||
if (hsdev->pending.raw_data)
|
||||
hsdev->pending.raw_size = sz;
|
||||
else
|
||||
hsdev->pending.raw_size = 0;
|
||||
complete(&hsdev->pending.ready);
|
||||
}
|
||||
if (callback->capture_sample) {
|
||||
if (report->field[i]->logical)
|
||||
callback->capture_sample(hsdev,
|
||||
report->field[i]->logical, sz, ptr,
|
||||
|
|
@ -572,6 +606,7 @@ static int sensor_hub_probe(struct hid_device *hdev,
|
|||
int dev_cnt;
|
||||
struct hid_sensor_hub_device *hsdev;
|
||||
struct hid_sensor_hub_device *last_hsdev = NULL;
|
||||
struct hid_sensor_hub_device *collection_hsdev = NULL;
|
||||
|
||||
sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL);
|
||||
if (!sd) {
|
||||
|
|
@ -618,7 +653,8 @@ static int sensor_hub_probe(struct hid_device *hdev,
|
|||
for (i = 0; i < hdev->maxcollection; ++i) {
|
||||
struct hid_collection *collection = &hdev->collection[i];
|
||||
|
||||
if (collection->type == HID_COLLECTION_PHYSICAL) {
|
||||
if (collection->type == HID_COLLECTION_PHYSICAL ||
|
||||
collection->type == HID_COLLECTION_APPLICATION) {
|
||||
|
||||
hsdev = devm_kzalloc(&hdev->dev, sizeof(*hsdev),
|
||||
GFP_KERNEL);
|
||||
|
|
@ -630,6 +666,15 @@ static int sensor_hub_probe(struct hid_device *hdev,
|
|||
hsdev->hdev = hdev;
|
||||
hsdev->vendor_id = hdev->vendor;
|
||||
hsdev->product_id = hdev->product;
|
||||
hsdev->usage = collection->usage;
|
||||
hsdev->mutex_ptr = devm_kzalloc(&hdev->dev,
|
||||
sizeof(struct mutex),
|
||||
GFP_KERNEL);
|
||||
if (!hsdev->mutex_ptr) {
|
||||
ret = -ENOMEM;
|
||||
goto err_stop_hw;
|
||||
}
|
||||
mutex_init(hsdev->mutex_ptr);
|
||||
hsdev->start_collection_index = i;
|
||||
if (last_hsdev)
|
||||
last_hsdev->end_collection_index = i;
|
||||
|
|
@ -653,10 +698,17 @@ static int sensor_hub_probe(struct hid_device *hdev,
|
|||
hid_dbg(hdev, "Adding %s:%d\n", name,
|
||||
hsdev->start_collection_index);
|
||||
sd->hid_sensor_client_cnt++;
|
||||
if (collection_hsdev)
|
||||
collection_hsdev->end_collection_index = i;
|
||||
if (collection->type == HID_COLLECTION_APPLICATION &&
|
||||
collection->usage == HID_USAGE_SENSOR_COLLECTION)
|
||||
collection_hsdev = hsdev;
|
||||
}
|
||||
}
|
||||
if (last_hsdev)
|
||||
last_hsdev->end_collection_index = i;
|
||||
if (collection_hsdev)
|
||||
collection_hsdev->end_collection_index = i;
|
||||
|
||||
ret = mfd_add_hotplug_devices(&hdev->dev,
|
||||
sd->hid_sensor_hub_client_devs,
|
||||
|
|
@ -676,13 +728,18 @@ static void sensor_hub_remove(struct hid_device *hdev)
|
|||
{
|
||||
struct sensor_hub_data *data = hid_get_drvdata(hdev);
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
hid_dbg(hdev, " hardware removed\n");
|
||||
hid_hw_close(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
if (data->pending.status)
|
||||
complete(&data->pending.ready);
|
||||
for (i = 0; i < data->hid_sensor_client_cnt; ++i) {
|
||||
struct hid_sensor_hub_device *hsdev =
|
||||
data->hid_sensor_hub_client_devs[i].platform_data;
|
||||
if (hsdev->pending.status)
|
||||
complete(&hsdev->pending.ready);
|
||||
}
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
mfd_remove_devices(&hdev->dev);
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
|
|
|
|||
|
|
@ -166,6 +166,9 @@ static const struct hid_device_id sjoy_devices[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD),
|
||||
.driver_data = HID_QUIRK_MULTI_INPUT |
|
||||
HID_QUIRK_SKIP_OUTPUT_REPORTS },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII),
|
||||
.driver_data = HID_QUIRK_MULTI_INPUT |
|
||||
HID_QUIRK_SKIP_OUTPUT_REPORTS },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, sjoy_devices);
|
||||
|
|
|
|||
|
|
@ -46,20 +46,37 @@
|
|||
#define PS3REMOTE BIT(4)
|
||||
#define DUALSHOCK4_CONTROLLER_USB BIT(5)
|
||||
#define DUALSHOCK4_CONTROLLER_BT BIT(6)
|
||||
#define MOTION_CONTROLLER_USB BIT(7)
|
||||
#define MOTION_CONTROLLER_BT BIT(8)
|
||||
#define NAVIGATION_CONTROLLER_USB BIT(9)
|
||||
#define NAVIGATION_CONTROLLER_BT BIT(10)
|
||||
|
||||
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
|
||||
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
|
||||
#define NAVIGATION_CONTROLLER (NAVIGATION_CONTROLLER_USB |\
|
||||
NAVIGATION_CONTROLLER_BT)
|
||||
#define DUALSHOCK4_CONTROLLER (DUALSHOCK4_CONTROLLER_USB |\
|
||||
DUALSHOCK4_CONTROLLER_BT)
|
||||
#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\
|
||||
DUALSHOCK4_CONTROLLER)
|
||||
#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
|
||||
#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
|
||||
DUALSHOCK4_CONTROLLER | MOTION_CONTROLLER |\
|
||||
NAVIGATION_CONTROLLER)
|
||||
#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\
|
||||
MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER)
|
||||
#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\
|
||||
MOTION_CONTROLLER)
|
||||
|
||||
#define MAX_LEDS 4
|
||||
|
||||
/*
|
||||
* The Sixaxis reports both digital and analog values for each button on the
|
||||
* controller except for Start, Select and the PS button. The controller ends
|
||||
* up reporting 27 axes which causes them to spill over into the multi-touch
|
||||
* axis values. Additionally, the controller only has 20 actual, physical axes
|
||||
* so there are several unused axes in between the used ones.
|
||||
*/
|
||||
static __u8 sixaxis_rdesc[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystik), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x85, 0x01, /* Report ID (1), */
|
||||
|
|
@ -134,6 +151,186 @@ static __u8 sixaxis_rdesc[] = {
|
|||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
/* PS/3 Motion controller */
|
||||
static __u8 motion_rdesc[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x85, 0x01, /* Report ID (1), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x15, /* Report Count (21), */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x35, 0x00, /* Physical Minimum (0), */
|
||||
0x45, 0x01, /* Physical Maximum (1), */
|
||||
0x05, 0x09, /* Usage Page (Button), */
|
||||
0x19, 0x01, /* Usage Minimum (01h), */
|
||||
0x29, 0x15, /* Usage Maximum (15h), */
|
||||
0x81, 0x02, /* Input (Variable), * Buttons */
|
||||
0x95, 0x0B, /* Report Count (11), */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), * Padding */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0xA1, 0x00, /* Collection (Physical), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x35, 0x00, /* Physical Minimum (0), */
|
||||
0x46, 0xFF, 0x00, /* Physical Maximum (255), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x81, 0x02, /* Input (Variable), * Trigger */
|
||||
0xC0, /* End Collection, */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x07, /* Report Count (7), * skip 7 bytes */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x46, 0xFF, 0xFF, /* Physical Maximum (65535), */
|
||||
0x27, 0xFF, 0xFF, 0x00, 0x00, /* Logical Maximum (65535), */
|
||||
0x95, 0x03, /* Report Count (3), * 3x Accels */
|
||||
0x09, 0x33, /* Usage (rX), */
|
||||
0x09, 0x34, /* Usage (rY), */
|
||||
0x09, 0x35, /* Usage (rZ), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
||||
0x95, 0x03, /* Report Count (3), * Skip Accels 2nd frame */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0x95, 0x03, /* Report Count (3), * 3x Gyros */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
||||
0x95, 0x03, /* Report Count (3), * Skip Gyros 2nd frame */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x75, 0x0C, /* Report Size (12), */
|
||||
0x46, 0xFF, 0x0F, /* Physical Maximum (4095), */
|
||||
0x26, 0xFF, 0x0F, /* Logical Maximum (4095), */
|
||||
0x95, 0x04, /* Report Count (4), * Skip Temp and Magnetometers */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x46, 0xFF, 0x00, /* Physical Maximum (255), */
|
||||
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
|
||||
0x95, 0x06, /* Report Count (6), * Skip Timestamp and Extension Bytes */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x30, /* Report Count (48), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0x91, 0x02, /* Output (Variable), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x30, /* Report Count (48), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x85, 0x02, /* Report ID (2), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x30, /* Report Count (48), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x85, 0xEE, /* Report ID (238), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x30, /* Report Count (48), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x85, 0xEF, /* Report ID (239), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x30, /* Report Count (48), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
/* PS/3 Navigation controller */
|
||||
static __u8 navigation_rdesc[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystik), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x85, 0x01, /* Report ID (1), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x13, /* Report Count (19), */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x35, 0x00, /* Physical Minimum (0), */
|
||||
0x45, 0x01, /* Physical Maximum (1), */
|
||||
0x05, 0x09, /* Usage Page (Button), */
|
||||
0x19, 0x01, /* Usage Minimum (01h), */
|
||||
0x29, 0x13, /* Usage Maximum (13h), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x0D, /* Report Count (13), */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xA1, 0x00, /* Collection (Physical), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x35, 0x00, /* Physical Minimum (0), */
|
||||
0x46, 0xFF, 0x00, /* Physical Maximum (255), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
||||
0x95, 0x06, /* Report Count (6), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x05, /* Report Count (5), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
||||
0x95, 0x20, /* Report Count (26), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x30, /* Report Count (48), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0x91, 0x02, /* Output (Variable), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x30, /* Report Count (48), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x85, 0x02, /* Report ID (2), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x30, /* Report Count (48), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x85, 0xEE, /* Report ID (238), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x30, /* Report Count (48), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x85, 0xEF, /* Report ID (239), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x30, /* Report Count (48), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
/*
|
||||
* The default descriptor doesn't provide mapping for the accelerometers
|
||||
* or orientation sensors. This fixed descriptor maps the accelerometers
|
||||
|
|
@ -798,11 +995,20 @@ union sixaxis_output_report_01 {
|
|||
__u8 buf[36];
|
||||
};
|
||||
|
||||
struct motion_output_report_02 {
|
||||
u8 type, zero;
|
||||
u8 r, g, b;
|
||||
u8 zero2;
|
||||
u8 rumble;
|
||||
};
|
||||
|
||||
#define DS4_REPORT_0x02_SIZE 37
|
||||
#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
|
||||
#define MOTION_REPORT_0x02_SIZE 49
|
||||
|
||||
static DEFINE_SPINLOCK(sony_dev_list_lock);
|
||||
static LIST_HEAD(sony_device_list);
|
||||
|
|
@ -815,7 +1021,8 @@ struct sony_sc {
|
|||
struct led_classdev *leds[MAX_LEDS];
|
||||
unsigned long quirks;
|
||||
struct work_struct state_worker;
|
||||
struct power_supply battery;
|
||||
struct power_supply *battery;
|
||||
struct power_supply_desc battery_desc;
|
||||
int device_id;
|
||||
__u8 *output_report_dmabuf;
|
||||
|
||||
|
|
@ -842,6 +1049,20 @@ static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|||
return sixaxis_rdesc;
|
||||
}
|
||||
|
||||
static u8 *motion_fixup(struct hid_device *hdev, u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
*rsize = sizeof(motion_rdesc);
|
||||
return motion_rdesc;
|
||||
}
|
||||
|
||||
static u8 *navigation_fixup(struct hid_device *hdev, u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
*rsize = sizeof(navigation_rdesc);
|
||||
return navigation_rdesc;
|
||||
}
|
||||
|
||||
static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
|
|
@ -922,6 +1143,12 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|||
if (sc->quirks & SIXAXIS_CONTROLLER)
|
||||
return sixaxis_fixup(hdev, rdesc, rsize);
|
||||
|
||||
if (sc->quirks & MOTION_CONTROLLER)
|
||||
return motion_fixup(hdev, rdesc, rsize);
|
||||
|
||||
if (sc->quirks & NAVIGATION_CONTROLLER)
|
||||
return navigation_fixup(hdev, rdesc, rsize);
|
||||
|
||||
if (sc->quirks & PS3REMOTE)
|
||||
return ps3remote_fixup(hdev, rdesc, rsize);
|
||||
|
||||
|
|
@ -932,6 +1159,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
|
|||
{
|
||||
static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
|
||||
unsigned long flags;
|
||||
int offset;
|
||||
__u8 cable_state, battery_capacity, battery_charging;
|
||||
|
||||
/*
|
||||
|
|
@ -940,12 +1168,14 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
|
|||
* It does not report the actual level while charging so it
|
||||
* is set to 100% while charging is in progress.
|
||||
*/
|
||||
if (rd[30] >= 0xee) {
|
||||
offset = (sc->quirks & MOTION_CONTROLLER) ? 12 : 30;
|
||||
|
||||
if (rd[offset] >= 0xee) {
|
||||
battery_capacity = 100;
|
||||
battery_charging = !(rd[30] & 0x01);
|
||||
battery_charging = !(rd[offset] & 0x01);
|
||||
cable_state = 1;
|
||||
} else {
|
||||
__u8 index = rd[30] <= 5 ? rd[30] : 5;
|
||||
__u8 index = rd[offset] <= 5 ? rd[offset] : 5;
|
||||
battery_capacity = sixaxis_battery_capacity[index];
|
||||
battery_charging = 0;
|
||||
cable_state = 0;
|
||||
|
|
@ -1046,6 +1276,11 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
|
|||
swap(rd[47], rd[48]);
|
||||
|
||||
sixaxis_parse_report(sc, rd, size);
|
||||
} else if ((sc->quirks & MOTION_CONTROLLER_BT) && rd[0] == 0x01 && size == 49) {
|
||||
sixaxis_parse_report(sc, rd, size);
|
||||
} else if ((sc->quirks & NAVIGATION_CONTROLLER) && rd[0] == 0x01 &&
|
||||
size == 49) {
|
||||
sixaxis_parse_report(sc, rd, size);
|
||||
} else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
|
||||
size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
|
||||
&& rd[0] == 0x11 && size == 78)) {
|
||||
|
|
@ -1130,18 +1365,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;
|
||||
|
|
@ -1186,7 +1441,7 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
|
||||
static void sixaxis_set_leds_from_id(struct sony_sc *sc)
|
||||
{
|
||||
static const __u8 sixaxis_leds[10][4] = {
|
||||
{ 0x01, 0x00, 0x00, 0x00 },
|
||||
|
|
@ -1201,16 +1456,18 @@ static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
|
|||
{ 0x01, 0x01, 0x01, 0x01 }
|
||||
};
|
||||
|
||||
BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
|
||||
int id = sc->device_id;
|
||||
|
||||
BUILD_BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
|
||||
|
||||
if (id < 0)
|
||||
return;
|
||||
|
||||
id %= 10;
|
||||
memcpy(values, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
|
||||
memcpy(sc->led_state, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
|
||||
}
|
||||
|
||||
static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
|
||||
static void dualshock4_set_leds_from_id(struct sony_sc *sc)
|
||||
{
|
||||
/* The first 4 color/index entries match what the PS4 assigns */
|
||||
static const __u8 color_code[7][3] = {
|
||||
|
|
@ -1223,46 +1480,44 @@ static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
|
|||
/* White */ { 0x01, 0x01, 0x01 }
|
||||
};
|
||||
|
||||
BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
|
||||
int id = sc->device_id;
|
||||
|
||||
BUILD_BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
|
||||
|
||||
if (id < 0)
|
||||
return;
|
||||
|
||||
id %= 7;
|
||||
memcpy(values, color_code[id], sizeof(color_code[id]));
|
||||
memcpy(sc->led_state, color_code[id], sizeof(color_code[id]));
|
||||
}
|
||||
|
||||
static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
|
||||
static void buzz_set_leds(struct sony_sc *sc)
|
||||
{
|
||||
struct hid_device *hdev = sc->hdev;
|
||||
struct list_head *report_list =
|
||||
&hdev->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct hid_report *report = list_entry(report_list->next,
|
||||
struct hid_report, list);
|
||||
__s32 *value = report->field[0]->value;
|
||||
|
||||
BUILD_BUG_ON(MAX_LEDS < 4);
|
||||
|
||||
value[0] = 0x00;
|
||||
value[1] = leds[0] ? 0xff : 0x00;
|
||||
value[2] = leds[1] ? 0xff : 0x00;
|
||||
value[3] = leds[2] ? 0xff : 0x00;
|
||||
value[4] = leds[3] ? 0xff : 0x00;
|
||||
value[1] = sc->led_state[0] ? 0xff : 0x00;
|
||||
value[2] = sc->led_state[1] ? 0xff : 0x00;
|
||||
value[3] = sc->led_state[2] ? 0xff : 0x00;
|
||||
value[4] = sc->led_state[3] ? 0xff : 0x00;
|
||||
value[5] = 0x00;
|
||||
value[6] = 0x00;
|
||||
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
||||
static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count)
|
||||
static void sony_set_leds(struct sony_sc *sc)
|
||||
{
|
||||
int n;
|
||||
|
||||
BUG_ON(count > MAX_LEDS);
|
||||
|
||||
if (sc->quirks & BUZZ_CONTROLLER && count == 4) {
|
||||
buzz_set_leds(sc->hdev, leds);
|
||||
} else {
|
||||
for (n = 0; n < count; n++)
|
||||
sc->led_state[n] = leds[n];
|
||||
if (!(sc->quirks & BUZZ_CONTROLLER))
|
||||
schedule_work(&sc->state_worker);
|
||||
}
|
||||
else
|
||||
buzz_set_leds(sc);
|
||||
}
|
||||
|
||||
static void sony_led_set_brightness(struct led_classdev *led,
|
||||
|
|
@ -1302,8 +1557,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
|
|||
drv_data->led_delay_on[n] = 0;
|
||||
drv_data->led_delay_off[n] = 0;
|
||||
|
||||
sony_set_leds(drv_data, drv_data->led_state,
|
||||
drv_data->led_count);
|
||||
sony_set_leds(drv_data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1409,7 +1663,6 @@ static int sony_leds_init(struct sony_sc *sc)
|
|||
const char *name_fmt;
|
||||
static const char * const ds4_name_str[] = { "red", "green", "blue",
|
||||
"global" };
|
||||
__u8 initial_values[MAX_LEDS] = { 0 };
|
||||
__u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 };
|
||||
__u8 use_hw_blink[MAX_LEDS] = { 0 };
|
||||
|
||||
|
|
@ -1424,16 +1677,31 @@ static int sony_leds_init(struct sony_sc *sc)
|
|||
if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
|
||||
return -ENODEV;
|
||||
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
|
||||
dualshock4_set_leds_from_id(sc->device_id, initial_values);
|
||||
initial_values[3] = 1;
|
||||
dualshock4_set_leds_from_id(sc);
|
||||
sc->led_state[3] = 1;
|
||||
sc->led_count = 4;
|
||||
memset(max_brightness, 255, 3);
|
||||
use_hw_blink[3] = 1;
|
||||
use_ds4_names = 1;
|
||||
name_len = 0;
|
||||
name_fmt = "%s:%s";
|
||||
} else if (sc->quirks & MOTION_CONTROLLER) {
|
||||
sc->led_count = 3;
|
||||
memset(max_brightness, 255, 3);
|
||||
use_ds4_names = 1;
|
||||
name_len = 0;
|
||||
name_fmt = "%s:%s";
|
||||
} else if (sc->quirks & NAVIGATION_CONTROLLER) {
|
||||
static const __u8 navigation_leds[4] = {0x01, 0x00, 0x00, 0x00};
|
||||
|
||||
memcpy(sc->led_state, navigation_leds, sizeof(navigation_leds));
|
||||
sc->led_count = 1;
|
||||
memset(use_hw_blink, 1, 4);
|
||||
use_ds4_names = 0;
|
||||
name_len = strlen("::sony#");
|
||||
name_fmt = "%s::sony%d";
|
||||
} else {
|
||||
sixaxis_set_leds_from_id(sc->device_id, initial_values);
|
||||
sixaxis_set_leds_from_id(sc);
|
||||
sc->led_count = 4;
|
||||
memset(use_hw_blink, 1, 4);
|
||||
use_ds4_names = 0;
|
||||
|
|
@ -1446,7 +1714,7 @@ static int sony_leds_init(struct sony_sc *sc)
|
|||
* only relevant if the driver is loaded after somebody actively set the
|
||||
* LEDs to on
|
||||
*/
|
||||
sony_set_leds(sc, initial_values, sc->led_count);
|
||||
sony_set_leds(sc);
|
||||
|
||||
name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
|
||||
|
||||
|
|
@ -1469,7 +1737,7 @@ static int sony_leds_init(struct sony_sc *sc)
|
|||
else
|
||||
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
|
||||
led->name = name;
|
||||
led->brightness = initial_values[n];
|
||||
led->brightness = sc->led_state[n];
|
||||
led->max_brightness = max_brightness[n];
|
||||
led->brightness_get = sony_led_get_brightness;
|
||||
led->brightness_set = sony_led_set_brightness;
|
||||
|
|
@ -1600,9 +1868,31 @@ static void dualshock4_state_worker(struct work_struct *work)
|
|||
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
||||
static void motion_state_worker(struct work_struct *work)
|
||||
{
|
||||
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
|
||||
struct hid_device *hdev = sc->hdev;
|
||||
struct motion_output_report_02 *report =
|
||||
(struct motion_output_report_02 *)sc->output_report_dmabuf;
|
||||
|
||||
memset(report, 0, MOTION_REPORT_0x02_SIZE);
|
||||
|
||||
report->type = 0x02; /* set leds */
|
||||
report->r = sc->led_state[0];
|
||||
report->g = sc->led_state[1];
|
||||
report->b = sc->led_state[2];
|
||||
|
||||
#ifdef CONFIG_SONY_FF
|
||||
report->rumble = max(sc->right, sc->left);
|
||||
#endif
|
||||
|
||||
hid_hw_output_report(hdev, (__u8 *)report, MOTION_REPORT_0x02_SIZE);
|
||||
}
|
||||
|
||||
static int sony_allocate_output_report(struct sony_sc *sc)
|
||||
{
|
||||
if (sc->quirks & SIXAXIS_CONTROLLER)
|
||||
if ((sc->quirks & SIXAXIS_CONTROLLER) ||
|
||||
(sc->quirks & NAVIGATION_CONTROLLER))
|
||||
sc->output_report_dmabuf =
|
||||
kmalloc(sizeof(union sixaxis_output_report_01),
|
||||
GFP_KERNEL);
|
||||
|
|
@ -1612,6 +1902,9 @@ static int sony_allocate_output_report(struct sony_sc *sc)
|
|||
else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
|
||||
sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x05_SIZE,
|
||||
GFP_KERNEL);
|
||||
else if (sc->quirks & MOTION_CONTROLLER)
|
||||
sc->output_report_dmabuf = kmalloc(MOTION_REPORT_0x02_SIZE,
|
||||
GFP_KERNEL);
|
||||
else
|
||||
return 0;
|
||||
|
||||
|
|
@ -1660,7 +1953,7 @@ static int sony_battery_get_property(struct power_supply *psy,
|
|||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct sony_sc *sc = container_of(psy, struct sony_sc, battery);
|
||||
struct sony_sc *sc = power_supply_get_drvdata(psy);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
u8 battery_charging, battery_capacity, cable_state;
|
||||
|
|
@ -1699,6 +1992,7 @@ static int sony_battery_get_property(struct power_supply *psy,
|
|||
|
||||
static int sony_battery_probe(struct sony_sc *sc)
|
||||
{
|
||||
struct power_supply_config psy_cfg = { .drv_data = sc, };
|
||||
struct hid_device *hdev = sc->hdev;
|
||||
int ret;
|
||||
|
||||
|
|
@ -1708,39 +2002,42 @@ static int sony_battery_probe(struct sony_sc *sc)
|
|||
*/
|
||||
sc->battery_capacity = 100;
|
||||
|
||||
sc->battery.properties = sony_battery_props;
|
||||
sc->battery.num_properties = ARRAY_SIZE(sony_battery_props);
|
||||
sc->battery.get_property = sony_battery_get_property;
|
||||
sc->battery.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
sc->battery.use_for_apm = 0;
|
||||
sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%pMR",
|
||||
sc->mac_address);
|
||||
if (!sc->battery.name)
|
||||
sc->battery_desc.properties = sony_battery_props;
|
||||
sc->battery_desc.num_properties = ARRAY_SIZE(sony_battery_props);
|
||||
sc->battery_desc.get_property = sony_battery_get_property;
|
||||
sc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
sc->battery_desc.use_for_apm = 0;
|
||||
sc->battery_desc.name = kasprintf(GFP_KERNEL,
|
||||
"sony_controller_battery_%pMR",
|
||||
sc->mac_address);
|
||||
if (!sc->battery_desc.name)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = power_supply_register(&hdev->dev, &sc->battery);
|
||||
if (ret) {
|
||||
sc->battery = power_supply_register(&hdev->dev, &sc->battery_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(sc->battery)) {
|
||||
ret = PTR_ERR(sc->battery);
|
||||
hid_err(hdev, "Unable to register battery device\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
power_supply_powers(&sc->battery, &hdev->dev);
|
||||
power_supply_powers(sc->battery, &hdev->dev);
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kfree(sc->battery.name);
|
||||
sc->battery.name = NULL;
|
||||
kfree(sc->battery_desc.name);
|
||||
sc->battery_desc.name = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sony_battery_remove(struct sony_sc *sc)
|
||||
{
|
||||
if (!sc->battery.name)
|
||||
if (!sc->battery_desc.name)
|
||||
return;
|
||||
|
||||
power_supply_unregister(&sc->battery);
|
||||
kfree(sc->battery.name);
|
||||
sc->battery.name = NULL;
|
||||
power_supply_unregister(sc->battery);
|
||||
kfree(sc->battery_desc.name);
|
||||
sc->battery_desc.name = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1813,6 +2110,8 @@ static int sony_check_add(struct sony_sc *sc)
|
|||
int n, ret;
|
||||
|
||||
if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
|
||||
(sc->quirks & MOTION_CONTROLLER_BT) ||
|
||||
(sc->quirks & NAVIGATION_CONTROLLER_BT) ||
|
||||
(sc->quirks & SIXAXIS_CONTROLLER_BT)) {
|
||||
/*
|
||||
* sony_get_bt_devaddr() attempts to parse the Bluetooth MAC
|
||||
|
|
@ -1845,7 +2144,8 @@ static int sony_check_add(struct sony_sc *sc)
|
|||
}
|
||||
|
||||
memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
|
||||
} else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
|
||||
} else if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
|
||||
(sc->quirks & NAVIGATION_CONTROLLER_USB)) {
|
||||
buf = kmalloc(SIXAXIS_REPORT_0xF2_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
|
@ -1967,19 +2267,20 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = sony_allocate_output_report(sc);
|
||||
if (ret < 0) {
|
||||
hid_err(hdev, "failed to allocate the output report buffer\n");
|
||||
goto err_stop;
|
||||
}
|
||||
|
||||
ret = sony_set_device_id(sc);
|
||||
if (ret < 0) {
|
||||
hid_err(hdev, "failed to allocate the device id\n");
|
||||
goto err_stop;
|
||||
}
|
||||
|
||||
if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
|
||||
ret = sony_allocate_output_report(sc);
|
||||
if (ret < 0) {
|
||||
hid_err(hdev, "failed to allocate the output report buffer\n");
|
||||
goto err_stop;
|
||||
}
|
||||
|
||||
if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
|
||||
(sc->quirks & NAVIGATION_CONTROLLER_USB)) {
|
||||
/*
|
||||
* The Sony Sixaxis does not handle HID Output Reports on the
|
||||
* Interrupt EP like it could, so we need to force HID Output
|
||||
|
|
@ -1994,7 +2295,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
|
||||
ret = sixaxis_set_operational_usb(hdev);
|
||||
sony_init_work(sc, sixaxis_state_worker);
|
||||
} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
|
||||
} else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) ||
|
||||
(sc->quirks & NAVIGATION_CONTROLLER_BT)) {
|
||||
/*
|
||||
* The Sixaxis wants output reports sent on the ctrl endpoint
|
||||
* when connected via Bluetooth.
|
||||
|
|
@ -2017,6 +2319,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
}
|
||||
|
||||
sony_init_work(sc, dualshock4_state_worker);
|
||||
} else if (sc->quirks & MOTION_CONTROLLER) {
|
||||
sony_init_work(sc, motion_state_worker);
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
|
@ -2096,7 +2400,13 @@ static const struct hid_device_id sony_devices[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
|
||||
.driver_data = SIXAXIS_CONTROLLER_USB },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER),
|
||||
.driver_data = SIXAXIS_CONTROLLER_USB },
|
||||
.driver_data = NAVIGATION_CONTROLLER_USB },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER),
|
||||
.driver_data = NAVIGATION_CONTROLLER_BT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER),
|
||||
.driver_data = MOTION_CONTROLLER_USB },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER),
|
||||
.driver_data = MOTION_CONTROLLER_BT },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
|
||||
.driver_data = SIXAXIS_CONTROLLER_BT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE),
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -203,8 +203,7 @@ static int wiimod_battery_get_property(struct power_supply *psy,
|
|||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct wiimote_data *wdata = container_of(psy, struct wiimote_data,
|
||||
battery);
|
||||
struct wiimote_data *wdata = power_supply_get_drvdata(psy);
|
||||
int ret = 0, state;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -238,42 +237,46 @@ static int wiimod_battery_get_property(struct power_supply *psy,
|
|||
static int wiimod_battery_probe(const struct wiimod_ops *ops,
|
||||
struct wiimote_data *wdata)
|
||||
{
|
||||
struct power_supply_config psy_cfg = { .drv_data = wdata, };
|
||||
int ret;
|
||||
|
||||
wdata->battery.properties = wiimod_battery_props;
|
||||
wdata->battery.num_properties = ARRAY_SIZE(wiimod_battery_props);
|
||||
wdata->battery.get_property = wiimod_battery_get_property;
|
||||
wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
wdata->battery.use_for_apm = 0;
|
||||
wdata->battery.name = kasprintf(GFP_KERNEL, "wiimote_battery_%s",
|
||||
wdata->hdev->uniq);
|
||||
if (!wdata->battery.name)
|
||||
wdata->battery_desc.properties = wiimod_battery_props;
|
||||
wdata->battery_desc.num_properties = ARRAY_SIZE(wiimod_battery_props);
|
||||
wdata->battery_desc.get_property = wiimod_battery_get_property;
|
||||
wdata->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
wdata->battery_desc.use_for_apm = 0;
|
||||
wdata->battery_desc.name = kasprintf(GFP_KERNEL, "wiimote_battery_%s",
|
||||
wdata->hdev->uniq);
|
||||
if (!wdata->battery_desc.name)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = power_supply_register(&wdata->hdev->dev, &wdata->battery);
|
||||
if (ret) {
|
||||
wdata->battery = power_supply_register(&wdata->hdev->dev,
|
||||
&wdata->battery_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(wdata->battery)) {
|
||||
hid_err(wdata->hdev, "cannot register battery device\n");
|
||||
ret = PTR_ERR(wdata->battery);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
power_supply_powers(&wdata->battery, &wdata->hdev->dev);
|
||||
power_supply_powers(wdata->battery, &wdata->hdev->dev);
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kfree(wdata->battery.name);
|
||||
wdata->battery.name = NULL;
|
||||
kfree(wdata->battery_desc.name);
|
||||
wdata->battery_desc.name = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wiimod_battery_remove(const struct wiimod_ops *ops,
|
||||
struct wiimote_data *wdata)
|
||||
{
|
||||
if (!wdata->battery.name)
|
||||
if (!wdata->battery_desc.name)
|
||||
return;
|
||||
|
||||
power_supply_unregister(&wdata->battery);
|
||||
kfree(wdata->battery.name);
|
||||
wdata->battery.name = NULL;
|
||||
power_supply_unregister(wdata->battery);
|
||||
kfree(wdata->battery_desc.name);
|
||||
wdata->battery_desc.name = NULL;
|
||||
}
|
||||
|
||||
static const struct wiimod_ops wiimod_battery = {
|
||||
|
|
|
|||
|
|
@ -147,7 +147,8 @@ struct wiimote_data {
|
|||
struct led_classdev *leds[4];
|
||||
struct input_dev *accel;
|
||||
struct input_dev *ir;
|
||||
struct power_supply battery;
|
||||
struct power_supply *battery;
|
||||
struct power_supply_desc battery_desc;
|
||||
struct input_dev *mp;
|
||||
struct timer_list timer;
|
||||
struct wiimote_debug *debug;
|
||||
|
|
|
|||
|
|
@ -37,13 +37,14 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <linux/i2c/i2c-hid.h>
|
||||
|
||||
/* flags */
|
||||
#define I2C_HID_STARTED (1 << 0)
|
||||
#define I2C_HID_RESET_PENDING (1 << 1)
|
||||
#define I2C_HID_READ_PENDING (1 << 2)
|
||||
#define I2C_HID_STARTED 0
|
||||
#define I2C_HID_RESET_PENDING 1
|
||||
#define I2C_HID_READ_PENDING 2
|
||||
|
||||
#define I2C_HID_PWR_ON 0x00
|
||||
#define I2C_HID_PWR_SLEEP 0x01
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
@ -851,6 +862,7 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,
|
|||
union acpi_object *obj;
|
||||
struct acpi_device *adev;
|
||||
acpi_handle handle;
|
||||
int ret;
|
||||
|
||||
handle = ACPI_HANDLE(&client->dev);
|
||||
if (!handle || acpi_bus_get_device(handle, &adev))
|
||||
|
|
@ -866,7 +878,9 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,
|
|||
pdata->hid_descriptor_address = obj->integer.value;
|
||||
ACPI_FREE(obj);
|
||||
|
||||
return 0;
|
||||
/* GPIOs are optional */
|
||||
ret = acpi_dev_add_driver_gpios(adev, i2c_hid_acpi_gpios);
|
||||
return ret < 0 && ret != -ENXIO ? ret : 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id i2c_hid_acpi_match[] = {
|
||||
|
|
@ -930,12 +944,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 +963,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;
|
||||
|
|
@ -994,7 +1019,6 @@ static int i2c_hid_probe(struct i2c_client *client,
|
|||
hid->driver_data = client;
|
||||
hid->ll_driver = &i2c_hid_ll_driver;
|
||||
hid->dev.parent = &client->dev;
|
||||
ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
|
||||
hid->bus = BUS_I2C;
|
||||
hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
|
||||
hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
|
||||
|
|
@ -1017,13 +1041,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 +1069,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 +1092,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 +1112,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 +1133,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) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH_2968, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
|
||||
{ USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS, HID_QUIRK_MULTI_INPUT },
|
||||
|
||||
{ USB_VENDOR_ID_AIREN, USB_DEVICE_ID_AIREN_SLIMPLUS, HID_QUIRK_NOGET },
|
||||
|
|
@ -61,6 +60,7 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS682, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FIGHTERSTICK, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE, HID_QUIRK_NOGET },
|
||||
|
|
@ -69,6 +69,7 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B, HID_QUIRK_ALWAYS_POLL },
|
||||
|
|
@ -78,10 +79,17 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C01A, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C05A, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C06A, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
|
||||
|
|
@ -92,6 +100,7 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_MOUSE_4D22, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET },
|
||||
|
|
@ -107,12 +116,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 },
|
||||
|
|
@ -128,6 +133,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 },
|
||||
|
|
@ -136,6 +142,9 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096, HID_QUIRK_NO_INIT_INPUT_REPORTS },
|
||||
{ USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_2NES2SNES, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_4NES4SNES, HID_QUIRK_MULTI_INPUT },
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -119,8 +119,10 @@ struct wacom {
|
|||
u8 img_lum; /* OLED matrix display brightness */
|
||||
} led;
|
||||
bool led_initialized;
|
||||
struct power_supply battery;
|
||||
struct power_supply ac;
|
||||
struct power_supply *battery;
|
||||
struct power_supply *ac;
|
||||
struct power_supply_desc battery_desc;
|
||||
struct power_supply_desc ac_desc;
|
||||
};
|
||||
|
||||
static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
|
||||
|
|
@ -129,18 +131,13 @@ 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);
|
||||
void wacom_setup_device_quirks(struct wacom_features *features);
|
||||
int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
|
||||
void wacom_setup_device_quirks(struct wacom *wacom);
|
||||
int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac);
|
||||
int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac);
|
||||
int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac);
|
||||
|
|
@ -149,4 +146,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
|
||||
|
|
|
|||
|
|
@ -35,7 +35,11 @@ static int wacom_get_report(struct hid_device *hdev, u8 type, u8 *buf,
|
|||
do {
|
||||
retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
|
||||
HID_REQ_GET_REPORT);
|
||||
} while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
|
||||
} while ((retval == -ETIMEDOUT || retval == -EAGAIN) && --retries);
|
||||
|
||||
if (retval < 0)
|
||||
hid_err(hdev, "wacom_get_report: ran out of retries "
|
||||
"(last error = %d)\n", retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -48,7 +52,11 @@ static int wacom_set_report(struct hid_device *hdev, u8 type, u8 *buf,
|
|||
do {
|
||||
retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
|
||||
HID_REQ_SET_REPORT);
|
||||
} while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
|
||||
} while ((retval == -ETIMEDOUT || retval == -EAGAIN) && --retries);
|
||||
|
||||
if (retval < 0)
|
||||
hid_err(hdev, "wacom_set_report: ran out of retries "
|
||||
"(last error = %d)\n", retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -117,9 +125,16 @@ static void wacom_feature_mapping(struct hid_device *hdev,
|
|||
break;
|
||||
data[0] = field->report->id;
|
||||
ret = wacom_get_report(hdev, HID_FEATURE_REPORT,
|
||||
data, 2, 0);
|
||||
if (ret == 2)
|
||||
data, 2, WAC_CMD_RETRIES);
|
||||
if (ret == 2) {
|
||||
features->touch_max = data[1];
|
||||
} else {
|
||||
features->touch_max = 16;
|
||||
hid_warn(hdev, "wacom_feature_mapping: "
|
||||
"could not get HID_DG_CONTACTMAX, "
|
||||
"defaulting to %d\n",
|
||||
features->touch_max);
|
||||
}
|
||||
kfree(data);
|
||||
}
|
||||
break;
|
||||
|
|
@ -181,7 +196,11 @@ static void wacom_usage_mapping(struct hid_device *hdev,
|
|||
* X/Y values and some cases of invalid Digitizer X/Y
|
||||
* values commonly reported.
|
||||
*/
|
||||
if (!pen && !finger)
|
||||
if (pen)
|
||||
features->device_type |= WACOM_DEVICETYPE_PEN;
|
||||
else if (finger)
|
||||
features->device_type |= WACOM_DEVICETYPE_TOUCH;
|
||||
else
|
||||
return;
|
||||
|
||||
/*
|
||||
|
|
@ -198,14 +217,11 @@ static void wacom_usage_mapping(struct hid_device *hdev,
|
|||
case HID_GD_X:
|
||||
features->x_max = field->logical_maximum;
|
||||
if (finger) {
|
||||
features->device_type = BTN_TOOL_FINGER;
|
||||
features->x_phy = field->physical_maximum;
|
||||
if (features->type != BAMBOO_PT) {
|
||||
features->unit = field->unit;
|
||||
features->unitExpo = field->unit_exponent;
|
||||
}
|
||||
} else {
|
||||
features->device_type = BTN_TOOL_PEN;
|
||||
}
|
||||
break;
|
||||
case HID_GD_Y:
|
||||
|
|
@ -237,7 +253,7 @@ static void wacom_post_parse_hid(struct hid_device *hdev,
|
|||
if (features->type == HID_GENERIC) {
|
||||
/* Any last-minute generic device setup */
|
||||
if (features->touch_max > 1) {
|
||||
input_mt_init_slots(wacom_wac->input, wacom_wac->features.touch_max,
|
||||
input_mt_init_slots(wacom_wac->touch_input, wacom_wac->features.touch_max,
|
||||
INPUT_MT_DIRECT);
|
||||
}
|
||||
}
|
||||
|
|
@ -395,7 +411,7 @@ static int wacom_query_tablet_data(struct hid_device *hdev,
|
|||
if (features->type == HID_GENERIC)
|
||||
return wacom_hid_set_device_mode(hdev);
|
||||
|
||||
if (features->device_type == BTN_TOOL_FINGER) {
|
||||
if (features->device_type & WACOM_DEVICETYPE_TOUCH) {
|
||||
if (features->type > TABLETPC) {
|
||||
/* MT Tablet PC touch */
|
||||
return wacom_set_device_mode(hdev, 3, 4, 4);
|
||||
|
|
@ -406,7 +422,10 @@ 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->device_type == BTN_TOOL_PEN) {
|
||||
else if (features->type == BAMBOO_PAD) {
|
||||
return wacom_set_device_mode(hdev, 2, 2, 2);
|
||||
}
|
||||
} else if (features->device_type & WACOM_DEVICETYPE_PEN) {
|
||||
if (features->type <= BAMBOO_PT && features->type != WIRELESS) {
|
||||
return wacom_set_device_mode(hdev, 2, 2, 2);
|
||||
}
|
||||
|
|
@ -422,7 +441,6 @@ static void wacom_retrieve_hid_descriptor(struct hid_device *hdev,
|
|||
struct usb_interface *intf = wacom->intf;
|
||||
|
||||
/* default features */
|
||||
features->device_type = BTN_TOOL_PEN;
|
||||
features->x_fuzz = 4;
|
||||
features->y_fuzz = 4;
|
||||
features->pressure_fuzz = 0;
|
||||
|
|
@ -436,17 +454,13 @@ static void wacom_retrieve_hid_descriptor(struct hid_device *hdev,
|
|||
*/
|
||||
if (features->type == WIRELESS) {
|
||||
if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
|
||||
features->device_type = 0;
|
||||
features->device_type = WACOM_DEVICETYPE_NONE;
|
||||
} else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) {
|
||||
features->device_type = BTN_TOOL_FINGER;
|
||||
features->device_type |= WACOM_DEVICETYPE_TOUCH;
|
||||
features->pktlen = WACOM_PKGLEN_BBTOUCH3;
|
||||
}
|
||||
}
|
||||
|
||||
/* only devices that support touch need to retrieve the info */
|
||||
if (features->type < BAMBOO_PT)
|
||||
return;
|
||||
|
||||
wacom_parse_hid(hdev, features);
|
||||
}
|
||||
|
||||
|
|
@ -524,6 +538,11 @@ static int wacom_add_shared_data(struct hid_device *hdev)
|
|||
|
||||
wacom_wac->shared = &data->shared;
|
||||
|
||||
if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)
|
||||
wacom_wac->shared->touch = hdev;
|
||||
else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN)
|
||||
wacom_wac->shared->pen = hdev;
|
||||
|
||||
out:
|
||||
mutex_unlock(&wacom_udev_list_lock);
|
||||
return retval;
|
||||
|
|
@ -541,14 +560,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -832,6 +859,9 @@ static int wacom_initialize_leds(struct wacom *wacom)
|
|||
{
|
||||
int error;
|
||||
|
||||
if (!(wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD))
|
||||
return 0;
|
||||
|
||||
/* Initialize default values */
|
||||
switch (wacom->wacom_wac.features.type) {
|
||||
case INTUOS4S:
|
||||
|
|
@ -865,17 +895,14 @@ static int wacom_initialize_leds(struct wacom *wacom)
|
|||
case INTUOSPS:
|
||||
case INTUOSPM:
|
||||
case INTUOSPL:
|
||||
if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) {
|
||||
wacom->led.select[0] = 0;
|
||||
wacom->led.select[1] = 0;
|
||||
wacom->led.llv = 32;
|
||||
wacom->led.hlv = 0;
|
||||
wacom->led.img_lum = 0;
|
||||
wacom->led.select[0] = 0;
|
||||
wacom->led.select[1] = 0;
|
||||
wacom->led.llv = 32;
|
||||
wacom->led.hlv = 0;
|
||||
wacom->led.img_lum = 0;
|
||||
|
||||
error = sysfs_create_group(&wacom->hdev->dev.kobj,
|
||||
&intuos5_led_attr_group);
|
||||
} else
|
||||
return 0;
|
||||
error = sysfs_create_group(&wacom->hdev->dev.kobj,
|
||||
&intuos5_led_attr_group);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -898,6 +925,9 @@ static void wacom_destroy_leds(struct wacom *wacom)
|
|||
if (!wacom->led_initialized)
|
||||
return;
|
||||
|
||||
if (!(wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD))
|
||||
return;
|
||||
|
||||
wacom->led_initialized = false;
|
||||
|
||||
switch (wacom->wacom_wac.features.type) {
|
||||
|
|
@ -921,14 +951,14 @@ static void wacom_destroy_leds(struct wacom *wacom)
|
|||
case INTUOSPS:
|
||||
case INTUOSPM:
|
||||
case INTUOSPL:
|
||||
if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN)
|
||||
sysfs_remove_group(&wacom->hdev->dev.kobj,
|
||||
&intuos5_led_attr_group);
|
||||
sysfs_remove_group(&wacom->hdev->dev.kobj,
|
||||
&intuos5_led_attr_group);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static enum power_supply_property wacom_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_SCOPE,
|
||||
POWER_SUPPLY_PROP_CAPACITY
|
||||
|
|
@ -944,10 +974,13 @@ static int wacom_battery_get_property(struct power_supply *psy,
|
|||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct wacom *wacom = container_of(psy, struct wacom, battery);
|
||||
struct wacom *wacom = power_supply_get_drvdata(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 +994,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;
|
||||
|
|
@ -976,7 +1011,7 @@ static int wacom_ac_get_property(struct power_supply *psy,
|
|||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct wacom *wacom = container_of(psy, struct wacom, ac);
|
||||
struct wacom *wacom = power_supply_get_drvdata(psy);
|
||||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
|
|
@ -998,42 +1033,46 @@ static int wacom_ac_get_property(struct power_supply *psy,
|
|||
static int wacom_initialize_battery(struct wacom *wacom)
|
||||
{
|
||||
static atomic_t battery_no = ATOMIC_INIT(0);
|
||||
int error;
|
||||
struct power_supply_config psy_cfg = { .drv_data = wacom, };
|
||||
unsigned long n;
|
||||
|
||||
if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) {
|
||||
struct power_supply_desc *bat_desc = &wacom->battery_desc;
|
||||
struct power_supply_desc *ac_desc = &wacom->ac_desc;
|
||||
n = atomic_inc_return(&battery_no) - 1;
|
||||
|
||||
wacom->battery.properties = wacom_battery_props;
|
||||
wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
|
||||
wacom->battery.get_property = wacom_battery_get_property;
|
||||
bat_desc->properties = wacom_battery_props;
|
||||
bat_desc->num_properties = ARRAY_SIZE(wacom_battery_props);
|
||||
bat_desc->get_property = wacom_battery_get_property;
|
||||
sprintf(wacom->wacom_wac.bat_name, "wacom_battery_%ld", n);
|
||||
wacom->battery.name = wacom->wacom_wac.bat_name;
|
||||
wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
wacom->battery.use_for_apm = 0;
|
||||
bat_desc->name = wacom->wacom_wac.bat_name;
|
||||
bat_desc->type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
bat_desc->use_for_apm = 0;
|
||||
|
||||
wacom->ac.properties = wacom_ac_props;
|
||||
wacom->ac.num_properties = ARRAY_SIZE(wacom_ac_props);
|
||||
wacom->ac.get_property = wacom_ac_get_property;
|
||||
ac_desc->properties = wacom_ac_props;
|
||||
ac_desc->num_properties = ARRAY_SIZE(wacom_ac_props);
|
||||
ac_desc->get_property = wacom_ac_get_property;
|
||||
sprintf(wacom->wacom_wac.ac_name, "wacom_ac_%ld", n);
|
||||
wacom->ac.name = wacom->wacom_wac.ac_name;
|
||||
wacom->ac.type = POWER_SUPPLY_TYPE_MAINS;
|
||||
wacom->ac.use_for_apm = 0;
|
||||
ac_desc->name = wacom->wacom_wac.ac_name;
|
||||
ac_desc->type = POWER_SUPPLY_TYPE_MAINS;
|
||||
ac_desc->use_for_apm = 0;
|
||||
|
||||
error = power_supply_register(&wacom->hdev->dev,
|
||||
&wacom->battery);
|
||||
if (error)
|
||||
return error;
|
||||
wacom->battery = power_supply_register(&wacom->hdev->dev,
|
||||
&wacom->battery_desc, &psy_cfg);
|
||||
if (IS_ERR(wacom->battery))
|
||||
return PTR_ERR(wacom->battery);
|
||||
|
||||
power_supply_powers(&wacom->battery, &wacom->hdev->dev);
|
||||
power_supply_powers(wacom->battery, &wacom->hdev->dev);
|
||||
|
||||
error = power_supply_register(&wacom->hdev->dev, &wacom->ac);
|
||||
if (error) {
|
||||
power_supply_unregister(&wacom->battery);
|
||||
return error;
|
||||
wacom->ac = power_supply_register(&wacom->hdev->dev,
|
||||
&wacom->ac_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(wacom->ac)) {
|
||||
power_supply_unregister(wacom->battery);
|
||||
return PTR_ERR(wacom->ac);
|
||||
}
|
||||
|
||||
power_supply_powers(&wacom->ac, &wacom->hdev->dev);
|
||||
power_supply_powers(wacom->ac, &wacom->hdev->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -1041,12 +1080,11 @@ 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) {
|
||||
power_supply_unregister(&wacom->battery);
|
||||
wacom->battery.dev = NULL;
|
||||
power_supply_unregister(&wacom->ac);
|
||||
wacom->ac.dev = NULL;
|
||||
if (wacom->battery) {
|
||||
power_supply_unregister(wacom->battery);
|
||||
wacom->battery = NULL;
|
||||
power_supply_unregister(wacom->ac);
|
||||
wacom->ac = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1092,7 +1130,7 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom)
|
|||
if (!input_dev)
|
||||
return NULL;
|
||||
|
||||
input_dev->name = wacom_wac->name;
|
||||
input_dev->name = wacom_wac->pen_name;
|
||||
input_dev->phys = hdev->phys;
|
||||
input_dev->dev.parent = &hdev->dev;
|
||||
input_dev->open = wacom_open;
|
||||
|
|
@ -1111,27 +1149,33 @@ static void wacom_free_inputs(struct wacom *wacom)
|
|||
{
|
||||
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
|
||||
|
||||
if (wacom_wac->input)
|
||||
input_free_device(wacom_wac->input);
|
||||
if (wacom_wac->pen_input)
|
||||
input_free_device(wacom_wac->pen_input);
|
||||
if (wacom_wac->touch_input)
|
||||
input_free_device(wacom_wac->touch_input);
|
||||
if (wacom_wac->pad_input)
|
||||
input_free_device(wacom_wac->pad_input);
|
||||
wacom_wac->input = NULL;
|
||||
wacom_wac->pen_input = NULL;
|
||||
wacom_wac->touch_input = NULL;
|
||||
wacom_wac->pad_input = NULL;
|
||||
}
|
||||
|
||||
static int wacom_allocate_inputs(struct wacom *wacom)
|
||||
{
|
||||
struct input_dev *input_dev, *pad_input_dev;
|
||||
struct input_dev *pen_input_dev, *touch_input_dev, *pad_input_dev;
|
||||
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
|
||||
|
||||
input_dev = wacom_allocate_input(wacom);
|
||||
pen_input_dev = wacom_allocate_input(wacom);
|
||||
touch_input_dev = wacom_allocate_input(wacom);
|
||||
pad_input_dev = wacom_allocate_input(wacom);
|
||||
if (!input_dev || !pad_input_dev) {
|
||||
if (!pen_input_dev || !touch_input_dev || !pad_input_dev) {
|
||||
wacom_free_inputs(wacom);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
wacom_wac->input = input_dev;
|
||||
wacom_wac->pen_input = pen_input_dev;
|
||||
wacom_wac->touch_input = touch_input_dev;
|
||||
wacom_wac->touch_input->name = wacom_wac->touch_name;
|
||||
wacom_wac->pad_input = pad_input_dev;
|
||||
wacom_wac->pad_input->name = wacom_wac->pad_name;
|
||||
|
||||
|
|
@ -1140,11 +1184,17 @@ static int wacom_allocate_inputs(struct wacom *wacom)
|
|||
|
||||
static void wacom_clean_inputs(struct wacom *wacom)
|
||||
{
|
||||
if (wacom->wacom_wac.input) {
|
||||
if (wacom->wacom_wac.input_registered)
|
||||
input_unregister_device(wacom->wacom_wac.input);
|
||||
if (wacom->wacom_wac.pen_input) {
|
||||
if (wacom->wacom_wac.pen_registered)
|
||||
input_unregister_device(wacom->wacom_wac.pen_input);
|
||||
else
|
||||
input_free_device(wacom->wacom_wac.input);
|
||||
input_free_device(wacom->wacom_wac.pen_input);
|
||||
}
|
||||
if (wacom->wacom_wac.touch_input) {
|
||||
if (wacom->wacom_wac.touch_registered)
|
||||
input_unregister_device(wacom->wacom_wac.touch_input);
|
||||
else
|
||||
input_free_device(wacom->wacom_wac.touch_input);
|
||||
}
|
||||
if (wacom->wacom_wac.pad_input) {
|
||||
if (wacom->wacom_wac.pad_registered)
|
||||
|
|
@ -1152,29 +1202,49 @@ static void wacom_clean_inputs(struct wacom *wacom)
|
|||
else
|
||||
input_free_device(wacom->wacom_wac.pad_input);
|
||||
}
|
||||
wacom->wacom_wac.input = NULL;
|
||||
wacom->wacom_wac.pen_input = NULL;
|
||||
wacom->wacom_wac.touch_input = NULL;
|
||||
wacom->wacom_wac.pad_input = NULL;
|
||||
wacom_destroy_leds(wacom);
|
||||
}
|
||||
|
||||
static int wacom_register_inputs(struct wacom *wacom)
|
||||
{
|
||||
struct input_dev *input_dev, *pad_input_dev;
|
||||
struct input_dev *pen_input_dev, *touch_input_dev, *pad_input_dev;
|
||||
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
|
||||
int error;
|
||||
int error = 0;
|
||||
|
||||
input_dev = wacom_wac->input;
|
||||
pen_input_dev = wacom_wac->pen_input;
|
||||
touch_input_dev = wacom_wac->touch_input;
|
||||
pad_input_dev = wacom_wac->pad_input;
|
||||
|
||||
if (!input_dev || !pad_input_dev)
|
||||
if (!pen_input_dev || !touch_input_dev || !pad_input_dev)
|
||||
return -EINVAL;
|
||||
|
||||
error = wacom_setup_pentouch_input_capabilities(input_dev, wacom_wac);
|
||||
if (!error) {
|
||||
error = input_register_device(input_dev);
|
||||
error = wacom_setup_pen_input_capabilities(pen_input_dev, wacom_wac);
|
||||
if (error) {
|
||||
/* no pen in use on this interface */
|
||||
input_free_device(pen_input_dev);
|
||||
wacom_wac->pen_input = NULL;
|
||||
pen_input_dev = NULL;
|
||||
} else {
|
||||
error = input_register_device(pen_input_dev);
|
||||
if (error)
|
||||
return error;
|
||||
wacom_wac->input_registered = true;
|
||||
goto fail_register_pen_input;
|
||||
wacom_wac->pen_registered = true;
|
||||
}
|
||||
|
||||
error = wacom_setup_touch_input_capabilities(touch_input_dev, wacom_wac);
|
||||
if (error) {
|
||||
/* no touch in use on this interface */
|
||||
input_free_device(touch_input_dev);
|
||||
wacom_wac->touch_input = NULL;
|
||||
touch_input_dev = NULL;
|
||||
} else {
|
||||
error = input_register_device(touch_input_dev);
|
||||
if (error)
|
||||
goto fail_register_touch_input;
|
||||
wacom_wac->touch_registered = true;
|
||||
}
|
||||
|
||||
error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac);
|
||||
|
|
@ -1201,9 +1271,14 @@ fail_leds:
|
|||
pad_input_dev = NULL;
|
||||
wacom_wac->pad_registered = false;
|
||||
fail_register_pad_input:
|
||||
input_unregister_device(input_dev);
|
||||
wacom_wac->input = NULL;
|
||||
wacom_wac->input_registered = false;
|
||||
input_unregister_device(touch_input_dev);
|
||||
wacom_wac->touch_input = NULL;
|
||||
wacom_wac->touch_registered = false;
|
||||
fail_register_touch_input:
|
||||
input_unregister_device(pen_input_dev);
|
||||
wacom_wac->pen_input = NULL;
|
||||
wacom_wac->pen_registered = false;
|
||||
fail_register_pen_input:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
@ -1260,8 +1335,11 @@ static void wacom_wireless_work(struct work_struct *work)
|
|||
/* Stylus interface */
|
||||
wacom_wac1->features =
|
||||
*((struct wacom_features *)id->driver_data);
|
||||
wacom_wac1->features.device_type = BTN_TOOL_PEN;
|
||||
snprintf(wacom_wac1->name, WACOM_NAME_MAX, "%s (WL) Pen",
|
||||
wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PEN;
|
||||
if (wacom_wac1->features.type != INTUOSHT &&
|
||||
wacom_wac1->features.type != BAMBOO_PT)
|
||||
wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PAD;
|
||||
snprintf(wacom_wac1->pen_name, WACOM_NAME_MAX, "%s (WL) Pen",
|
||||
wacom_wac1->features.name);
|
||||
snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad",
|
||||
wacom_wac1->features.name);
|
||||
|
|
@ -1279,16 +1357,16 @@ static void wacom_wireless_work(struct work_struct *work)
|
|||
wacom_wac2->features =
|
||||
*((struct wacom_features *)id->driver_data);
|
||||
wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
|
||||
wacom_wac2->features.device_type = BTN_TOOL_FINGER;
|
||||
wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
|
||||
if (wacom_wac2->features.touch_max)
|
||||
snprintf(wacom_wac2->name, WACOM_NAME_MAX,
|
||||
"%s (WL) Finger",wacom_wac2->features.name);
|
||||
else
|
||||
snprintf(wacom_wac2->name, WACOM_NAME_MAX,
|
||||
"%s (WL) Pad",wacom_wac2->features.name);
|
||||
snprintf(wacom_wac2->touch_name, WACOM_NAME_MAX,
|
||||
"%s (WL) Finger",wacom_wac2->features.name);
|
||||
snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
|
||||
"%s (WL) Pad", wacom_wac2->features.name);
|
||||
"%s (WL) Pad",wacom_wac2->features.name);
|
||||
if (wacom_wac1->features.touch_max)
|
||||
wacom_wac2->features.device_type |= WACOM_DEVICETYPE_TOUCH;
|
||||
if (wacom_wac1->features.type == INTUOSHT ||
|
||||
wacom_wac1->features.type == BAMBOO_PT)
|
||||
wacom_wac2->features.device_type |= WACOM_DEVICETYPE_PAD;
|
||||
wacom_wac2->pid = wacom_wac->pid;
|
||||
error = wacom_allocate_inputs(wacom2) ||
|
||||
wacom_register_inputs(wacom2);
|
||||
|
|
@ -1297,7 +1375,7 @@ static void wacom_wireless_work(struct work_struct *work)
|
|||
|
||||
if (wacom_wac1->features.type == INTUOSHT &&
|
||||
wacom_wac1->features.touch_max)
|
||||
wacom_wac->shared->touch_input = wacom_wac2->input;
|
||||
wacom_wac->shared->touch_input = wacom_wac2->touch_input;
|
||||
}
|
||||
|
||||
error = wacom_initialize_battery(wacom);
|
||||
|
|
@ -1313,6 +1391,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) {
|
||||
wacom_initialize_battery(wacom);
|
||||
}
|
||||
else if (!(wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) &&
|
||||
wacom->battery) {
|
||||
wacom_destroy_battery(wacom);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Not all devices report physical dimensions from HID.
|
||||
* Compute the default from hardcoded logical dimension
|
||||
|
|
@ -1330,6 +1422,12 @@ static void wacom_set_default_phy(struct wacom_features *features)
|
|||
|
||||
static void wacom_calculate_res(struct wacom_features *features)
|
||||
{
|
||||
/* set unit to "100th of a mm" for devices not reported by HID */
|
||||
if (!features->unit) {
|
||||
features->unit = 0x11;
|
||||
features->unitExpo = -3;
|
||||
}
|
||||
|
||||
features->x_resolution = wacom_calc_hid_res(features->x_max,
|
||||
features->x_phy,
|
||||
features->unit,
|
||||
|
|
@ -1357,6 +1455,49 @@ static size_t wacom_compute_pktlen(struct hid_device *hdev)
|
|||
return size;
|
||||
}
|
||||
|
||||
static void wacom_update_name(struct wacom *wacom)
|
||||
{
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct wacom_features *features = &wacom_wac->features;
|
||||
char name[WACOM_NAME_MAX];
|
||||
|
||||
/* Generic devices name unspecified */
|
||||
if ((features->type == HID_GENERIC) && !strcmp("Wacom HID", features->name)) {
|
||||
if (strstr(wacom->hdev->name, "Wacom") ||
|
||||
strstr(wacom->hdev->name, "wacom") ||
|
||||
strstr(wacom->hdev->name, "WACOM")) {
|
||||
/* name is in HID descriptor, use it */
|
||||
strlcpy(name, wacom->hdev->name, sizeof(name));
|
||||
|
||||
/* strip out excess whitespaces */
|
||||
while (1) {
|
||||
char *gap = strstr(name, " ");
|
||||
if (gap == NULL)
|
||||
break;
|
||||
/* shift everything including the terminator */
|
||||
memmove(gap, gap+1, strlen(gap));
|
||||
}
|
||||
/* get rid of trailing whitespace */
|
||||
if (name[strlen(name)-1] == ' ')
|
||||
name[strlen(name)-1] = '\0';
|
||||
} else {
|
||||
/* no meaningful name retrieved. use product ID */
|
||||
snprintf(name, sizeof(name),
|
||||
"%s %X", features->name, wacom->hdev->product);
|
||||
}
|
||||
} else {
|
||||
strlcpy(name, features->name, sizeof(name));
|
||||
}
|
||||
|
||||
/* Append the device type to the name */
|
||||
snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name),
|
||||
"%s Pen", name);
|
||||
snprintf(wacom_wac->touch_name, sizeof(wacom_wac->touch_name),
|
||||
"%s Finger", name);
|
||||
snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
|
||||
"%s Pad", name);
|
||||
}
|
||||
|
||||
static int wacom_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
|
|
@ -1373,6 +1514,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,70 +1556,50 @@ 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);
|
||||
|
||||
/* Retrieve the physical and logical size for touch devices */
|
||||
wacom_retrieve_hid_descriptor(hdev, features);
|
||||
wacom_setup_device_quirks(wacom);
|
||||
|
||||
/*
|
||||
* Intuos5 has no useful data about its touch interface in its
|
||||
* HID descriptor. If this is the touch interface (PacketSize
|
||||
* of WACOM_PKGLEN_BBTOUCH3), override the table values.
|
||||
*/
|
||||
if (features->type >= INTUOS5S && features->type <= INTUOSHT) {
|
||||
if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
|
||||
features->device_type = BTN_TOOL_FINGER;
|
||||
if (features->device_type == WACOM_DEVICETYPE_NONE &&
|
||||
features->type != WIRELESS) {
|
||||
error = features->type == HID_GENERIC ? -ENODEV : 0;
|
||||
|
||||
features->x_max = 4096;
|
||||
features->y_max = 4096;
|
||||
} else {
|
||||
features->device_type = BTN_TOOL_PEN;
|
||||
}
|
||||
}
|
||||
dev_warn(&hdev->dev, "Unknown device_type for '%s'. %s.",
|
||||
hdev->name,
|
||||
error ? "Ignoring" : "Assuming pen");
|
||||
|
||||
/*
|
||||
* Same thing for Bamboo 3rd gen.
|
||||
*/
|
||||
if ((features->type == BAMBOO_PT) &&
|
||||
(features->pktlen == WACOM_PKGLEN_BBTOUCH3) &&
|
||||
(features->device_type == BTN_TOOL_PEN)) {
|
||||
features->device_type = BTN_TOOL_FINGER;
|
||||
|
||||
features->x_max = 4096;
|
||||
features->y_max = 4096;
|
||||
}
|
||||
|
||||
if (hdev->bus == BUS_BLUETOOTH)
|
||||
features->quirks |= WACOM_QUIRK_BATTERY;
|
||||
|
||||
wacom_setup_device_quirks(features);
|
||||
|
||||
/* set unit to "100th of a mm" for devices not reported by HID */
|
||||
if (!features->unit) {
|
||||
features->unit = 0x11;
|
||||
features->unitExpo = -3;
|
||||
}
|
||||
wacom_calculate_res(features);
|
||||
|
||||
strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
|
||||
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);
|
||||
|
||||
error = wacom_add_shared_data(hdev);
|
||||
if (error)
|
||||
goto fail_shared_data;
|
||||
|
||||
features->device_type |= WACOM_DEVICETYPE_PEN;
|
||||
}
|
||||
|
||||
wacom_calculate_res(features);
|
||||
|
||||
wacom_update_name(wacom);
|
||||
|
||||
error = wacom_add_shared_data(hdev);
|
||||
if (error)
|
||||
goto fail_shared_data;
|
||||
|
||||
if (!(features->quirks & WACOM_QUIRK_MONITOR) &&
|
||||
(features->quirks & WACOM_QUIRK_BATTERY)) {
|
||||
error = wacom_initialize_battery(wacom);
|
||||
|
|
@ -1513,9 +1637,9 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
if (features->quirks & WACOM_QUIRK_MONITOR)
|
||||
error = hid_hw_open(hdev);
|
||||
|
||||
if (wacom_wac->features.type == INTUOSHT && wacom_wac->features.touch_max) {
|
||||
if (wacom_wac->features.device_type == BTN_TOOL_FINGER)
|
||||
wacom_wac->shared->touch_input = wacom_wac->input;
|
||||
if (wacom_wac->features.type == INTUOSHT &&
|
||||
wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) {
|
||||
wacom_wac->shared->touch_input = wacom_wac->touch_input;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -1527,7 +1651,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 +1674,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);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -18,10 +18,7 @@
|
|||
#define WACOM_NAME_MAX 64
|
||||
|
||||
/* packet length for individual models */
|
||||
#define WACOM_PKGLEN_PENPRTN 7
|
||||
#define WACOM_PKGLEN_GRAPHIRE 8
|
||||
#define WACOM_PKGLEN_BBFUN 9
|
||||
#define WACOM_PKGLEN_INTUOS 10
|
||||
#define WACOM_PKGLEN_TPC1FG 5
|
||||
#define WACOM_PKGLEN_TPC1FG_B 10
|
||||
#define WACOM_PKGLEN_TPC2FG 14
|
||||
|
|
@ -29,10 +26,9 @@
|
|||
#define WACOM_PKGLEN_BBTOUCH3 64
|
||||
#define WACOM_PKGLEN_BBPEN 10
|
||||
#define WACOM_PKGLEN_WIRELESS 32
|
||||
#define WACOM_PKGLEN_MTOUCH 62
|
||||
#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,18 +63,29 @@
|
|||
#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
|
||||
|
||||
/* device types */
|
||||
#define WACOM_DEVICETYPE_NONE 0x0000
|
||||
#define WACOM_DEVICETYPE_PEN 0x0001
|
||||
#define WACOM_DEVICETYPE_TOUCH 0x0002
|
||||
#define WACOM_DEVICETYPE_PAD 0x0004
|
||||
|
||||
#define WACOM_VENDORDEFINED_PEN 0xff0d0001
|
||||
|
||||
#define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \
|
||||
((f)->physical == HID_DG_STYLUS) || \
|
||||
((f)->physical == HID_DG_PEN) || \
|
||||
((f)->application == HID_DG_PEN))
|
||||
((f)->application == HID_DG_PEN) || \
|
||||
((f)->application == HID_DG_DIGITIZER) || \
|
||||
((f)->application == WACOM_VENDORDEFINED_PEN))
|
||||
#define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \
|
||||
((f)->physical == HID_DG_FINGER) || \
|
||||
((f)->application == HID_DG_TOUCHSCREEN))
|
||||
|
|
@ -122,6 +129,7 @@ enum {
|
|||
BAMBOO_PT,
|
||||
WACOM_24HDT,
|
||||
WACOM_27QHDT,
|
||||
BAMBOO_PAD,
|
||||
TABLETPC, /* add new TPC below */
|
||||
TABLETPCE,
|
||||
TABLETPC2FG,
|
||||
|
|
@ -169,6 +177,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 {
|
||||
|
|
@ -186,7 +196,8 @@ struct hid_data {
|
|||
};
|
||||
|
||||
struct wacom_wac {
|
||||
char name[WACOM_NAME_MAX];
|
||||
char pen_name[WACOM_NAME_MAX];
|
||||
char touch_name[WACOM_NAME_MAX];
|
||||
char pad_name[WACOM_NAME_MAX];
|
||||
char bat_name[WACOM_NAME_MAX];
|
||||
char ac_name[WACOM_NAME_MAX];
|
||||
|
|
@ -197,14 +208,17 @@ struct wacom_wac {
|
|||
bool reporting_data;
|
||||
struct wacom_features features;
|
||||
struct wacom_shared *shared;
|
||||
struct input_dev *input;
|
||||
struct input_dev *pen_input;
|
||||
struct input_dev *touch_input;
|
||||
struct input_dev *pad_input;
|
||||
bool input_registered;
|
||||
bool pen_registered;
|
||||
bool touch_registered;
|
||||
bool pad_registered;
|
||||
int pid;
|
||||
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