Commit 7af4c727 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull HID subsystem updates from Jiri Kosina:

 - The need for HID_QUIRK_NO_INIT_REPORTS per-device quirk has been
   growing dramatically during past years, so the time has come to
   switch over the default, and perform the pro-active reading only in
   cases where it's really needed (multitouch, wacom).

   The only place where this behavior is (in some form) preserved is
   hiddev so that we don't introduce userspace-visible change of
   behavior.

   From Benjamin Tissoires

 - HID++ support for power_supply / baterry reporting.

   From Benjamin Tissoires and Bastien Nocera

 - Vast improvements / rework of DS3 and DS4 in Sony driver.

   From Roderick Colenbrander

 - Improvment (in terms of getting closer to the Microsoft's
   interpretation of slightly ambiguous specification) of logical range
   interpretation in case null-state is set in the rdesc.

   From Valtteri Heikkilä and Tomasz Kramkowski

 - A lot of newly supported device IDs and small assorted fixes

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (71 commits)
  HID: usbhid: Add HID_QUIRK_NOGET for Aten CS-1758 KVM switch
  HID: asus: support backlight on USB keyboards
  HID: wacom: Move wacom_remote_irq and wacom_remote_status_irq
  HID: wacom: generic: sync pad events only for actual packets
  HID: sony: remove redundant check for -ve err
  HID: sony: Make sure to unregister sensors on failure
  HID: sony: Make DS4 bt poll interval adjustable
  HID: sony: Set proper bit flags on DS4 output report
  HID: sony: DS4 use brighter LED colors
  HID: sony: Improve navigation controller axis/button mapping
  HID: sony: Use DS3 MAC address as unique identifier on USB
  HID: logitech-hidpp: add a sysfs file to tell we support power_supply
  HID: logitech-hidpp: enable HID++ 1.0 battery reporting
  HID: logitech-hidpp: add support for battery status for the K750
  HID: logitech-hidpp: battery: provide CAPACITY_LEVEL
  HID: logitech-hidpp: rename battery level into capacity
  HID: logitech-hidpp: battery: provide ONLINE property
  HID: logitech-hidpp: notify battery on connect
  HID: logitech-hidpp: return an error if the queried feature is not present
  HID: logitech-hidpp: create the battery for all types of HID++ devices
  ...
parents 68fed41e 4d6ca227
......@@ -4127,6 +4127,9 @@
usbhid.mousepoll=
[USBHID] The interval which mice are to be polled at.
usbhid.jspoll=
[USBHID] The interval which joysticks are to be polled at.
usb-storage.delay_use=
[UMS] The delay in seconds before a new device is
scanned for Logical Units (default 1).
......
......@@ -17,6 +17,22 @@ Required properties:
- interrupt-parent: the phandle for the interrupt controller
- interrupts: interrupt line
Additional optional properties:
Some devices may support additional optional properties to help with, e.g.,
power sequencing. The following properties can be supported by one or more
device-specific compatible properties, which should be used in addition to the
"hid-over-i2c" string.
- compatible:
* "wacom,w9013" (Wacom W9013 digitizer). Supports:
- vdd-supply
- post-power-on-delay-ms
- vdd-supply: phandle of the regulator that provides the supply voltage.
- post-power-on-delay-ms: time required by the device after enabling its regulators
before it is ready for communication. Must be used with 'vdd-supply'.
Example:
i2c-hid-dev@2c {
......
......@@ -301,7 +301,10 @@ them as any other INPUT_PROP_BUTTONPAD device.
INPUT_PROP_ACCELEROMETER
-------------------------
Directional axes on this device (absolute and/or relative x, y, z) represent
accelerometer data. All other axes retain their meaning. A device must not mix
accelerometer data. Some devices also report gyroscope data, which devices
can report through the rotational axes (absolute and/or relative rx, ry, rz).
All other axes retain their meaning. A device must not mix
regular directional axes and accelerometer axes on the same event node.
Guidelines:
......
......@@ -98,6 +98,18 @@ config HID_A4TECH
---help---
Support for A4 tech X5 and WOP-35 / Trust 450L mice.
config HID_ACCUTOUCH
tristate "Accutouch touch device"
depends on USB_HID
---help---
This selects a driver for the Accutouch 2216 touch controller.
The driver works around a problem in the reported device capabilities
which causes userspace to detect the device as a mouse rather than
a touchscreen.
Say Y here if you have a Accutouch 2216 touch controller.
config HID_ACRUX
tristate "ACRUX game controller support"
depends on HID
......@@ -136,13 +148,16 @@ config HID_APPLEIR
config HID_ASUS
tristate "Asus"
depends on I2C_HID
depends on LEDS_CLASS
---help---
Support for Asus notebook built-in keyboard and touchpad via i2c.
Support for Asus notebook built-in keyboard and touchpad via i2c, and
the Asus Republic of Gamers laptop keyboard special keys.
Supported devices:
- EeeBook X205TA
- VivoBook E200HA
- GL553V series
- GL753V series
config HID_AUREAL
tristate "Aureal"
......@@ -215,7 +230,8 @@ config HID_CMEDIA
config HID_CP2112
tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
depends on USB_HID && I2C && GPIOLIB && GPIOLIB_IRQCHIP
depends on USB_HID && I2C && GPIOLIB
select GPIOLIB_IRQCHIP
---help---
Support for Silicon Labs CP2112 HID USB to SMBus Master Bridge.
This is a HID device driver which registers as an i2c adapter
......@@ -441,6 +457,7 @@ config HID_LOGITECH_DJ
config HID_LOGITECH_HIDPP
tristate "Logitech HID++ devices support"
depends on HID_LOGITECH
select POWER_SUPPLY
---help---
Support for Logitech devices relyingon the HID++ Logitech specification
......@@ -581,6 +598,12 @@ config HID_MULTITOUCH
To compile this driver as a module, choose M here: the
module will be called hid-multitouch.
config HID_NTI
tristate "NTI keyboard adapters"
---help---
Support for the "extra" Sun keyboard keys on keyboards attached
through Network Technologies USB-SUN keyboard adapters.
config HID_NTRIG
tristate "N-Trig touch screen"
depends on USB_HID
......
......@@ -21,6 +21,7 @@ hid-wiimote-y := hid-wiimote-core.o hid-wiimote-modules.o
hid-wiimote-$(CONFIG_DEBUG_FS) += hid-wiimote-debug.o
obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
obj-$(CONFIG_HID_ACCUTOUCH) += hid-accutouch.o
obj-$(CONFIG_HID_ALPS) += hid-alps.o
obj-$(CONFIG_HID_ACRUX) += hid-axff.o
obj-$(CONFIG_HID_APPLE) += hid-apple.o
......@@ -62,6 +63,7 @@ obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
obj-$(CONFIG_HID_NTI) += hid-nti.o
obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o
obj-$(CONFIG_HID_ORTEK) += hid-ortek.o
obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
......
/*
* HID driver for Elo Accutouch touchscreens
*
* Copyright (c) 2016, Collabora Ltd.
* Copyright (c) 2016, General Electric Company
*
* based on hid-penmount.c
* Copyright (c) 2014 Christian Gmeiner <christian.gmeiner <at> gmail.com>
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
static int accutouch_input_mapping(struct hid_device *hdev,
struct hid_input *hi,
struct hid_field *field,
struct hid_usage *usage,
unsigned long **bit, int *max)
{
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
return 1;
}
return 0;
}
static const struct hid_device_id accutouch_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_ACCUTOUCH_2216) },
{ }
};
MODULE_DEVICE_TABLE(hid, accutouch_devices);
static struct hid_driver accutouch_driver = {
.name = "hid-accutouch",
.id_table = accutouch_devices,
.input_mapping = accutouch_input_mapping,
};
module_hid_driver(accutouch_driver);
MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.co.uk");
MODULE_DESCRIPTION("Elo Accutouch HID TouchScreen driver");
MODULE_LICENSE("GPL");
......@@ -40,8 +40,12 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define FEATURE_REPORT_ID 0x0d
#define INPUT_REPORT_ID 0x5d
#define FEATURE_KBD_REPORT_ID 0x5a
#define INPUT_REPORT_SIZE 28
#define FEATURE_KBD_REPORT_SIZE 16
#define SUPPORT_KBD_BACKLIGHT BIT(0)
#define MAX_CONTACTS 5
......@@ -63,18 +67,31 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define QUIRK_NO_INIT_REPORTS BIT(1)
#define QUIRK_SKIP_INPUT_MAPPING BIT(2)
#define QUIRK_IS_MULTITOUCH BIT(3)
#define QUIRK_NO_CONSUMER_USAGES BIT(4)
#define QUIRK_USE_KBD_BACKLIGHT BIT(5)
#define KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
QUIRK_NO_INIT_REPORTS)
#define TOUCHPAD_QUIRKS (QUIRK_NO_INIT_REPORTS | \
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
QUIRK_NO_INIT_REPORTS | \
QUIRK_NO_CONSUMER_USAGES)
#define I2C_TOUCHPAD_QUIRKS (QUIRK_NO_INIT_REPORTS | \
QUIRK_SKIP_INPUT_MAPPING | \
QUIRK_IS_MULTITOUCH)
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
struct asus_kbd_leds {
struct led_classdev cdev;
struct hid_device *hdev;
struct work_struct work;
unsigned int brightness;
bool removed;
};
struct asus_drvdata {
unsigned long quirks;
struct input_dev *input;
struct asus_kbd_leds *kbd_backlight;
bool enable_backlight;
};
static void asus_report_contact_down(struct input_dev *input,
......@@ -169,6 +186,148 @@ static int asus_raw_event(struct hid_device *hdev,
return 0;
}
static int asus_kbd_set_report(struct hid_device *hdev, u8 *buf, size_t buf_size)
{
unsigned char *dmabuf;
int ret;
dmabuf = kmemdup(buf, buf_size, GFP_KERNEL);
if (!dmabuf)
return -ENOMEM;
ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, dmabuf,
buf_size, HID_FEATURE_REPORT,
HID_REQ_SET_REPORT);
kfree(dmabuf);
return ret;
}
static int asus_kbd_init(struct hid_device *hdev)
{
u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
int ret;
ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
if (ret < 0)
hid_err(hdev, "Asus failed to send init command: %d\n", ret);
return ret;
}
static int asus_kbd_get_functions(struct hid_device *hdev,
unsigned char *kbd_func)
{
u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x05, 0x20, 0x31, 0x00, 0x08 };
u8 *readbuf;
int ret;
ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
if (ret < 0) {
hid_err(hdev, "Asus failed to send configuration command: %d\n", ret);
return ret;
}
readbuf = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL);
if (!readbuf)
return -ENOMEM;
ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, readbuf,
FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret < 0) {
hid_err(hdev, "Asus failed to request functions: %d\n", ret);
kfree(readbuf);
return ret;
}
*kbd_func = readbuf[6];
kfree(readbuf);
return ret;
}
static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
cdev);
if (led->brightness == brightness)
return;
led->brightness = brightness;
schedule_work(&led->work);
}
static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev)
{
struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
cdev);
return led->brightness;
}
static void asus_kbd_backlight_work(struct work_struct *work)
{
struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
int ret;
if (led->removed)
return;
buf[4] = led->brightness;
ret = asus_kbd_set_report(led->hdev, buf, sizeof(buf));
if (ret < 0)
hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret);
}
static int asus_kbd_register_leds(struct hid_device *hdev)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
unsigned char kbd_func;
int ret;
/* Initialize keyboard */
ret = asus_kbd_init(hdev);
if (ret < 0)
return ret;
/* Get keyboard functions */
ret = asus_kbd_get_functions(hdev, &kbd_func);
if (ret < 0)
return ret;
/* Check for backlight support */
if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
return -ENODEV;
drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
sizeof(struct asus_kbd_leds),
GFP_KERNEL);
if (!drvdata->kbd_backlight)
return -ENOMEM;
drvdata->kbd_backlight->removed = false;
drvdata->kbd_backlight->brightness = 0;
drvdata->kbd_backlight->hdev = hdev;
drvdata->kbd_backlight->cdev.name = "asus::kbd_backlight";
drvdata->kbd_backlight->cdev.max_brightness = 3;
drvdata->kbd_backlight->cdev.brightness_set = asus_kbd_backlight_set;
drvdata->kbd_backlight->cdev.brightness_get = asus_kbd_backlight_get;
INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work);
ret = devm_led_classdev_register(&hdev->dev, &drvdata->kbd_backlight->cdev);
if (ret < 0) {
/* No need to have this still around */
devm_kfree(&hdev->dev, drvdata->kbd_backlight);
}
return ret;
}
static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
struct input_dev *input = hi->input;
......@@ -196,9 +355,14 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
drvdata->input = input;
if (drvdata->enable_backlight && asus_kbd_register_leds(hdev))
hid_warn(hdev, "Failed to initialize backlight.\n");
return 0;
}
#define asus_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, \
max, EV_KEY, (c))
static int asus_input_mapping(struct hid_device *hdev,
struct hid_input *hi, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit,
......@@ -213,6 +377,65 @@ static int asus_input_mapping(struct hid_device *hdev,
return -1;
}
/* ASUS-specific keyboard hotkeys */
if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) {
set_bit(EV_REP, hi->input->evbit);
switch (usage->hid & HID_USAGE) {
case 0x10: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break;
case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break;
case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF); break;
case 0x6c: asus_map_key_clear(KEY_SLEEP); break;
case 0x82: asus_map_key_clear(KEY_CAMERA); break;
case 0x88: asus_map_key_clear(KEY_RFKILL); break;
case 0xb5: asus_map_key_clear(KEY_CALC); break;
case 0xc4: asus_map_key_clear(KEY_KBDILLUMUP); break;
case 0xc5: asus_map_key_clear(KEY_KBDILLUMDOWN); break;
/* ASUS touchpad toggle */
case 0x6b: asus_map_key_clear(KEY_F21); break;
/* ROG key */
case 0x38: asus_map_key_clear(KEY_PROG1); break;
/* Fn+C ASUS Splendid */
case 0xba: asus_map_key_clear(KEY_PROG2); break;
/* Fn+Space Power4Gear Hybrid */
case 0x5c: asus_map_key_clear(KEY_PROG3); break;
default:
/* ASUS lazily declares 256 usages, ignore the rest,
* as some make the keyboard appear as a pointer device. */
return -1;
}
/*
* Check and enable backlight only on devices with UsagePage ==
* 0xff31 to avoid initializing the keyboard firmware multiple
* times on devices with multiple HID descriptors but same
* PID/VID.
*/
if (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT)
drvdata->enable_backlight = true;
return 1;
}
if (drvdata->quirks & QUIRK_NO_CONSUMER_USAGES &&
(usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
switch (usage->hid & HID_USAGE) {
case 0xe2: /* Mute */
case 0xe9: /* Volume up */
case 0xea: /* Volume down */
return 0;
default:
/* Ignore dummy Consumer usages which make the
* keyboard incorrectly appear as a pointer device.
*/
return -1;
}
}
return 0;
}
......@@ -305,6 +528,16 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
}
static void asus_remove(struct hid_device *hdev)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
if (drvdata->kbd_backlight) {
drvdata->kbd_backlight->removed = true;
cancel_work_sync(&drvdata->kbd_backlight->work);
}
}
static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
......@@ -320,9 +553,13 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
static const struct hid_device_id asus_devices[] = {
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD), KEYBOARD_QUIRKS},
USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD), I2C_KEYBOARD_QUIRKS},
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_TOUCHPAD), TOUCHPAD_QUIRKS },
USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD), I2C_TOUCHPAD_QUIRKS },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2), QUIRK_USE_KBD_BACKLIGHT },
{ }
};
MODULE_DEVICE_TABLE(hid, asus_devices);
......@@ -332,6 +569,7 @@ static struct hid_driver asus_driver = {
.id_table = asus_devices,
.report_fixup = asus_report_fixup,
.probe = asus_probe,
.remove = asus_remove,
.input_mapping = asus_input_mapping,
.input_configured = asus_input_configured,
#ifdef CONFIG_PM
......
......@@ -1694,7 +1694,7 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
len += sprintf(buf + len, "input");
if (hdev->claimed & HID_CLAIMED_HIDDEV)
len += sprintf(buf + len, "%shiddev%d", len ? "," : "",
hdev->minor);
((struct hiddev *)hdev->hiddev)->minor);
if (hdev->claimed & HID_CLAIMED_HIDRAW)
len += sprintf(buf + len, "%shidraw%d", len ? "," : "",
((struct hidraw *)hdev->hidraw)->minor);
......@@ -1851,8 +1851,10 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD) },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_TOUCHPAD) },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD) },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185BFM, 0x2208) },
......@@ -1891,6 +1893,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_ACCUTOUCH_2216) },
{ HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) },
{ HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },
......@@ -1991,6 +1994,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ 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_NTI, USB_DEVICE_ID_USB_SUN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
......
......@@ -27,6 +27,7 @@
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/hid.h>
#include <linux/hidraw.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/nls.h>
......@@ -1297,7 +1298,8 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
dev->adap.algo_data = dev;
dev->adap.dev.parent = &hdev->dev;
snprintf(dev->adap.name, sizeof(dev->adap.name),
"CP2112 SMBus Bridge on hiddev%d", hdev->minor);
"CP2112 SMBus Bridge on hidraw%d",
((struct hidraw *)hdev->hidraw)->minor);
dev->hwversion = buf[2];
init_waitqueue_head(&dev->wait);
......
......@@ -140,9 +140,11 @@ static const struct hid_usage_entry hid_usage_table[] = {
{0, 0x03, "LightPen"},
{0, 0x04, "TouchScreen"},
{0, 0x05, "TouchPad"},
{0, 0x0e, "DeviceConfiguration"},
{0, 0x20, "Stylus"},
{0, 0x21, "Puck"},
{0, 0x22, "Finger"},
{0, 0x23, "DeviceSettings"},
{0, 0x30, "TipPressure"},
{0, 0x31, "BarrelPressure"},
{0, 0x32, "InRange"},
......
......@@ -173,8 +173,10 @@
#define USB_VENDOR_ID_ASUSTEK 0x0b05
#define USB_DEVICE_ID_ASUSTEK_LCM 0x1726
#define USB_DEVICE_ID_ASUSTEK_LCM2 0x175b
#define USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD 0x8585
#define USB_DEVICE_ID_ASUSTEK_TOUCHPAD 0x0101
#define USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD 0x8585
#define USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD 0x0101
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837
#define USB_VENDOR_ID_ATEN 0x0557
#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
......@@ -184,6 +186,7 @@
#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
#define USB_DEVICE_ID_ATEN_CS682 0x2213
#define USB_DEVICE_ID_ATEN_CS692 0x8021
#define USB_DEVICE_ID_ATEN_CS1758 0x2220
#define USB_VENDOR_ID_ATMEL 0x03eb
#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
......@@ -366,6 +369,7 @@
#define USB_VENDOR_ID_ELO 0x04E7
#define USB_DEVICE_ID_ELO_TS2515 0x0022
#define USB_DEVICE_ID_ELO_TS2700 0x0020
#define USB_DEVICE_ID_ELO_ACCUTOUCH_2216 0x0050
#define USB_VENDOR_ID_EMS 0x2006
#define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118
......@@ -548,6 +552,9 @@
#define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615
#define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070
#define USB_VENDOR_ID_INNOMEDIA 0x1292
#define USB_DEVICE_ID_INNEX_GENESIS_ATARI 0x4745
#define USB_VENDOR_ID_ITE 0x048d
#define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386
#define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350
......@@ -771,6 +778,9 @@
#define USB_DEVICE_ID_NOVATEK_PCT 0x0600
#define USB_DEVICE_ID_NOVATEK_MOUSE 0x1602
#define USB_VENDOR_ID_NTI 0x0757
#define USB_DEVICE_ID_USB_SUN 0x0a00
#define USB_VENDOR_ID_NTRIG 0x1b96
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1 0x0003
......
......@@ -1150,18 +1150,26 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
/*
* Ignore out-of-range values as per HID specification,
* section 5.10 and 6.2.25.
* section 5.10 and 6.2.25, when NULL state bit is present.
* When it's not, clamp the value to match Microsoft's input
* driver as mentioned in "Required HID usages for digitizers":
* https://msdn.microsoft.com/en-us/library/windows/hardware/dn672278(v=vs.85).asp
*
* The logical_minimum < logical_maximum check is done so that we
* don't unintentionally discard values sent by devices which
* don't specify logical min and max.
*/
if ((field->flags & HID_MAIN_ITEM_VARIABLE) &&
(field->logical_minimum < field->logical_maximum) &&
(value < field->logical_minimum ||
value > field->logical_maximum)) {
dbg_hid("Ignoring out-of-range value %x\n", value);
return;
(field->logical_minimum < field->logical_maximum)) {
if (field->flags & HID_MAIN_ITEM_NULL_STATE &&
(value < field->logical_minimum ||
value > field->logical_maximum)) {
dbg_hid("Ignoring out-of-range value %x\n", value);
return;
}
value = clamp(value,
field->logical_minimum,
field->logical_maximum);
}
/*
......
......@@ -692,8 +692,12 @@ static void logi_dj_ll_close(struct hid_device *hid)
dbg_hid("%s:%s\n", __func__, hid->phys);
}
static u8 unifying_name_query[] = {0x10, 0xff, 0x83, 0xb5, 0x40, 0x00, 0x00};
static u8 unifying_name_answer[] = {0x11, 0xff, 0x83, 0xb5};
/*
* Register 0xB5 is "pairing information". It is solely intended for the
* receiver, so do not overwrite the device index.
*/
static u8 unifying_pairing_query[] = {0x10, 0xff, 0x83, 0xb5};
static u8 unifying_pairing_answer[] = {0x11, 0xff, 0x83, 0xb5};
static int logi_dj_ll_raw_request(struct hid_device *hid,
unsigned char reportnum, __u8 *buf,
......@@ -712,9 +716,9 @@ static int logi_dj_ll_raw_request(struct hid_device *hid,
/* special case where we should not overwrite
* the device_index */
if (count == 7 && !memcmp(buf, unifying_name_query,
sizeof(unifying_name_query)))
buf[4] |= djdev->device_index - 1;
if (count == 7 && !memcmp(buf, unifying_pairing_query,
sizeof(unifying_pairing_query)))
buf[4] = (buf[4] & 0xf0) | (djdev->device_index - 1);
else
buf[1] = djdev->device_index;
return hid_hw_raw_request(djrcv_dev->hdev, reportnum, buf,
......@@ -911,9 +915,8 @@ static int logi_dj_hidpp_event(struct hid_device *hdev,
/* special case were the device wants to know its unifying
* name */
if (size == HIDPP_REPORT_LONG_LENGTH &&
!memcmp(data, unifying_name_answer,
sizeof(unifying_name_answer)) &&
((data[4] & 0xF0) == 0x40))
!memcmp(data, unifying_pairing_answer,
sizeof(unifying_pairing_answer)))
device_index = (data[4] & 0x0F) + 1;
else
return false;
......
......@@ -56,15 +56,21 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_QUIRK_CLASS_M560 BIT(1)
#define HIDPP_QUIRK_CLASS_K400 BIT(2)
#define HIDPP_QUIRK_CLASS_G920 BIT(3)
#define HIDPP_QUIRK_CLASS_K750 BIT(4)
/* bits 2..20 are reserved for classes */
#define HIDPP_QUIRK_CONNECT_EVENTS BIT(21)
/* #define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) disabled */
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
#define HIDPP_QUIRK_NO_HIDINPUT BIT(23)
#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
#define HIDPP_QUIRK_UNIFYING BIT(25)
#define HIDPP_QUIRK_DELAYED_INIT (HIDPP_QUIRK_NO_HIDINPUT | \
HIDPP_QUIRK_CONNECT_EVENTS)
#define HIDPP_QUIRK_DELAYED_INIT HIDPP_QUIRK_NO_HIDINPUT
#define HIDPP_CAPABILITY_HIDPP10_BATTERY BIT(0)
#define HIDPP_CAPABILITY_HIDPP20_BATTERY BIT(1)
#define HIDPP_CAPABILITY_BATTERY_MILEAGE BIT(2)
#define HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS BIT(3)
/*
* There are two hidpp protocols in use, the first version hidpp10 is known
......@@ -110,6 +116,18 @@ struct hidpp_report {
};
} __packed;
struct hidpp_battery {
u8 feature_index;
u8 solar_feature_index;
struct power_supply_desc desc;
struct power_supply *ps;
char name[64];
int status;
int capacity;
int level;
bool online;
};
struct hidpp_device {
struct hid_device *hid_dev;
struct mutex send_mutex;
......@@ -128,8 +146,10 @@ struct hidpp_device {
struct input_dev *delayed_input;
unsigned long quirks;
};
unsigned long capabilities;
struct hidpp_battery battery;
};
/* HID++ 1.0 error codes */
#define HIDPP_ERROR 0x8f
......@@ -380,15 +400,220 @@ static void hidpp_prefix_name(char **name, int name_length)
#define HIDPP_SET_LONG_REGISTER 0x82
#define HIDPP_GET_LONG_REGISTER 0x83
#define HIDPP_REG_GENERAL 0x00
static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
{
struct hidpp_report response;
int ret;
u8 params[3] = { 0 };
ret = hidpp_send_rap_command_sync(hidpp_dev,
REPORT_ID_HIDPP_SHORT,
HIDPP_GET_REGISTER,
HIDPP_REG_GENERAL,
NULL, 0, &response);
if (ret)
return ret;
memcpy(params, response.rap.params, 3);
/* Set the battery bit */
params[0] |= BIT(4);
return hidpp_send_rap_command_sync(hidpp_dev,
REPORT_ID_HIDPP_SHORT,
HIDPP_SET_REGISTER,
HIDPP_REG_GENERAL,
params, 3, &response);
}
#define HIDPP_REG_BATTERY_STATUS 0x07
static int hidpp10_battery_status_map_level(u8 param)
{
int level;
switch (param) {
case 1 ... 2:
level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
break;
case 3 ... 4:
level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
break;
case 5 ... 6:
level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
break;
case 7:
level = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
break;
default:
level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
}
return level;
}
static int hidpp10_battery_status_map_status(u8 param)
{
int status;
switch (param) {
case 0x00:
/* discharging (in use) */
status = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case 0x21: /* (standard) charging */
case 0x24: /* fast charging */
case 0x25: /* slow charging */
status = POWER_SUPPLY_STATUS_CHARGING;
break;
case 0x26: /* topping charge */
case 0x22: /* charge complete */
status = POWER_SUPPLY_STATUS_FULL;
break;
case 0x20: /* unknown */
status = POWER_SUPPLY_STATUS_UNKNOWN;
break;
/*
* 0x01...0x1F = reserved (not charging)
* 0x23 = charging error
* 0x27..0xff = reserved
*/
default:
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
}
return status;
}
static int hidpp10_query_battery_status(struct hidpp_device *hidpp)
{
struct hidpp_report response;
int ret, status;
ret = hidpp_send_rap_command_sync(hidpp,
REPORT_ID_HIDPP_SHORT,
HIDPP_GET_REGISTER,
HIDPP_REG_BATTERY_STATUS,
NULL, 0, &response);
if (ret)
return ret;
hidpp->battery.level =
hidpp10_battery_status_map_level(response.rap.params[0]);
status = hidpp10_battery_status_map_status(response.rap.params[1]);
hidpp->battery.status = status;
/* the capacity is only available when discharging or full */
hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
status == POWER_SUPPLY_STATUS_FULL;
return 0;
}
#define HIDPP_REG_BATTERY_MILEAGE 0x0D
static int hidpp10_battery_mileage_map_status(u8 param)
{
int status;
switch (param >> 6) {
case 0x00:
/* discharging (in use) */
status = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case 0x01: /* charging */
status = POWER_SUPPLY_STATUS_CHARGING;
break;
case 0x02: /* charge complete */
status = POWER_SUPPLY_STATUS_FULL;
break;
/*
* 0x03 = charging error
*/
default:
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
}
return status;
}
static int hidpp10_query_battery_mileage(struct hidpp_device *hidpp)
{
struct hidpp_report response;
int ret, status;
ret = hidpp_send_rap_command_sync(hidpp,
REPORT_ID_HIDPP_SHORT,
HIDPP_GET_REGISTER,
HIDPP_REG_BATTERY_MILEAGE,
NULL, 0, &response);
if (ret)
return ret;
hidpp->battery.capacity = response.rap.params[0];
status = hidpp10_battery_mileage_map_status(response.rap.params[2]);
hidpp->battery.status = status;
/* the capacity is only available when discharging or full */
hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
status == POWER_SUPPLY_STATUS_FULL;
return 0;
}
static int hidpp10_battery_event(struct hidpp_device *hidpp, u8 *data, int size)
{
struct hidpp_report *report = (struct hidpp_report *)data;
int status, capacity, level;
bool changed;
if (report->report_id != REPORT_ID_HIDPP_SHORT)
return 0;
switch (report->rap.sub_id) {
case HIDPP_REG_BATTERY_STATUS:
capacity = hidpp->battery.capacity;
level = hidpp10_battery_status_map_level(report->rawbytes[1]);
status = hidpp10_battery_status_map_status(report->rawbytes[2]);
break;
case HIDPP_REG_BATTERY_MILEAGE:
capacity = report->rap.params[0];
level = hidpp->battery.level;
status = hidpp10_battery_mileage_map_status(report->rawbytes[3]);
break;
default:
return 0;
}
changed = capacity != hidpp->battery.capacity ||
level != hidpp->battery.level ||
status != hidpp->battery.status;
/* the capacity is only available when discharging or full */
hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
status == POWER_SUPPLY_STATUS_FULL;
if (changed) {
hidpp->battery.level = level;
hidpp->battery.status = status;
if (hidpp->battery.ps)
power_supply_changed(hidpp->battery.ps);
}
return 0;
}
#define HIDPP_REG_PAIRING_INFORMATION 0xB5
#define DEVICE_NAME 0x40
#define HIDPP_EXTENDED_PAIRING 0x30
#define HIDPP_DEVICE_NAME 0x40
static char *hidpp_get_unifying_name(struct hidpp_device *hidpp_dev)
static char *hidpp_unifying_get_name(struct hidpp_device *hidpp_dev)
{
struct hidpp_report response;
int ret;
/* hid-logitech-dj is in charge of setting the right device index */
u8 params[1] = { DEVICE_NAME };
u8 params[1] = { HIDPP_DEVICE_NAME };
char *name;
int len;
......@@ -417,6 +642,54 @@ static char *hidpp_get_unifying_name(struct hidpp_device *hidpp_dev)
return name;
}
static int hidpp_unifying_get_serial(struct hidpp_device *hidpp, u32 *serial)
{
struct hidpp_report response;
int ret;
u8 params[1] = { HIDPP_EXTENDED_PAIRING };
ret = hidpp_send_rap_command_sync(hidpp,
REPORT_ID_HIDPP_SHORT,
HIDPP_GET_LONG_REGISTER,
HIDPP_REG_PAIRING_INFORMATION,
params, 1, &response);
if (ret)
return ret;
/*
* We don't care about LE or BE, we will output it as a string
* with %4phD, so we need to keep the order.
*/
*serial = *((u32 *)&response.rap.params[1]);
return 0;
}
static int hidpp_unifying_init(struct hidpp_device *hidpp)
{
struct hid_device *hdev = hidpp->hid_dev;
const char *name;
u32 serial;
int ret;
ret = hidpp_unifying_get_serial(hidpp, &serial);
if (ret)
return ret;
snprintf(hdev->uniq, sizeof(hdev->uniq), "%04x-%4phD",
hdev->product, &serial);
dbg_hid("HID++ Unifying: Got serial: %s\n", hdev->uniq);
name = hidpp_unifying_get_name(hidpp);
if (!name)
return -EIO;
snprintf(hdev->name, sizeof(hdev->name), "%s", name);
dbg_hid("HID++ Unifying: Got name: %s\n", name);
kfree(name);
return 0;
}
/* -------------------------------------------------------------------------- */
/* 0x0000: Root */
/* -------------------------------------------------------------------------- */
......@@ -441,6 +714,9 @@ static int hidpp_root_get_feature(struct hidpp_device *hidpp, u16 feature,
if (ret)
return ret;
if (response.fap.params[0] == 0)
return -ENOENT;
*feature_index = response.fap.params[0];
*feature_type = response.fap.params[1];
......@@ -606,6 +882,355 @@ static char *hidpp_get_device_name(struct hidpp_device *hidpp)
return name;
}
/* -------------------------------------------------------------------------- */
/* 0x1000: Battery level status */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_BATTERY_LEVEL_STATUS 0x1000
#define CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS 0x00
#define CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_CAPABILITY 0x10
#define EVENT_BATTERY_LEVEL_STATUS_BROADCAST 0x00
#define FLAG_BATTERY_LEVEL_DISABLE_OSD BIT(0)
#define FLAG_BATTERY_LEVEL_MILEAGE BIT(1)
#define FLAG_BATTERY_LEVEL_RECHARGEABLE BIT(2)
static int hidpp_map_battery_level(int capacity)
{
if (capacity < 11)
return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
else if (capacity < 31)
return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
else if (capacity < 81)
return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
}
static int hidpp20_batterylevel_map_status_capacity(u8 data[3], int *capacity,
int *next_capacity,
int *level)
{
int status;
*capacity = data[0];
*next_capacity = data[1];
*level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
/* When discharging, we can rely on the device reported capacity.
* For all other states the device reports 0 (unknown).
*/
switch (data[2]) {
case 0: /* discharging (in use) */
status = POWER_SUPPLY_STATUS_DISCHARGING;
*level = hidpp_map_battery_level(*capacity);
break;
case 1: /* recharging */
status = POWER_SUPPLY_STATUS_CHARGING;
break;
case 2: /* charge in final stage */
status = POWER_SUPPLY_STATUS_CHARGING;
break;
case 3: /* charge complete */
status = POWER_SUPPLY_STATUS_FULL;
*level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
*capacity = 100;
break;
case 4: /* recharging below optimal speed */
status = POWER_SUPPLY_STATUS_CHARGING;
break;
/* 5 = invalid battery type
6 = thermal error
7 = other charging error */
default:
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
}
return status;
}
static int hidpp20_batterylevel_get_battery_capacity(struct hidpp_device *hidpp,
u8 feature_index,
int *status,
int *capacity,
int *next_capacity,
int *level)
{
struct hidpp_report response;
int ret;
u8 *params = (u8 *)response.fap.params;
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS,
NULL, 0, &response);
if (ret > 0) {
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
__func__, ret);
return -EPROTO;
}
if (ret)
return ret;
*status = hidpp20_batterylevel_map_status_capacity(params, capacity,
next_capacity,
level);
return 0;
}
static int hidpp20_batterylevel_get_battery_info(struct hidpp_device *hidpp,
u8 feature_index)
{
struct hidpp_report response;
int ret;
u8 *params = (u8 *)response.fap.params;
unsigned int level_count, flags;
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_CAPABILITY,
NULL, 0, &response);
if (ret > 0) {
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
__func__, ret);
return -EPROTO;
}
if (ret)
return ret;
level_count = params[0];
flags = params[1];
if (level_count < 10 || !(flags & FLAG_BATTERY_LEVEL_MILEAGE))
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS;
else
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_MILEAGE;
return 0;
}
static int hidpp20_query_battery_info(struct hidpp_device *hidpp)
{
u8 feature_type;
int ret;
int status, capacity, next_capacity, level;
if (hidpp->battery.feature_index == 0xff) {
ret = hidpp_root_get_feature(hidpp,
HIDPP_PAGE_BATTERY_LEVEL_STATUS,
&hidpp->battery.feature_index,
&feature_type);
if (ret)
return ret;
}
ret = hidpp20_batterylevel_get_battery_capacity(hidpp,
hidpp->battery.feature_index,
&status, &capacity,
&next_capacity, &level);
if (ret)
return ret;
ret = hidpp20_batterylevel_get_battery_info(hidpp,
hidpp->battery.feature_index);
if (ret)
return ret;
hidpp->battery.status = status;
hidpp->battery.capacity = capacity;
hidpp->battery.level = level;
/* the capacity is only available when discharging or full */
hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
status == POWER_SUPPLY_STATUS_FULL;
return 0;
}
static int hidpp20_battery_event(struct hidpp_device *hidpp,
u8 *data, int size)
{
struct hidpp_report *report = (struct hidpp_report *)data;
int status, capacity, next_capacity, level;
bool changed;
if (report->fap.feature_index != hidpp->battery.feature_index ||
report->fap.funcindex_clientid != EVENT_BATTERY_LEVEL_STATUS_BROADCAST)
return 0;
status = hidpp20_batterylevel_map_status_capacity(report->fap.params,
&capacity,
&next_capacity,
&level);
/* the capacity is only available when discharging or full */
hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
status == POWER_SUPPLY_STATUS_FULL;
changed = capacity != hidpp->battery.capacity ||
level != hidpp->battery.level ||
status != hidpp->battery.status;
if (changed) {
hidpp->battery.level = level;
hidpp->battery.capacity = capacity;
hidpp->battery.status = status;
if (hidpp->battery.ps)
power_supply_changed(hidpp->battery.ps);
}
return 0;
}
static enum power_supply_property hidpp_battery_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY, */
0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY_LEVEL, */
};
static int hidpp_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct hidpp_device *hidpp = power_supply_get_drvdata(psy);
int ret = 0;
switch(psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = hidpp->battery.status;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = hidpp->battery.capacity;
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
val->intval = hidpp->battery.level;
break;
case POWER_SUPPLY_PROP_SCOPE:
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = hidpp->battery.online;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
if (!strncmp(hidpp->name, "Logitech ", 9))
val->strval = hidpp->name + 9;
else
val->strval = hidpp->name;
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = "Logitech";
break;
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
val->strval = hidpp->hid_dev->uniq;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
/* -------------------------------------------------------------------------- */
/* 0x4301: Solar Keyboard */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_SOLAR_KEYBOARD 0x4301
#define CMD_SOLAR_SET_LIGHT_MEASURE 0x00
#define EVENT_SOLAR_BATTERY_BROADCAST 0x00
#define EVENT_SOLAR_BATTERY_LIGHT_MEASURE 0x10
#define EVENT_SOLAR_CHECK_LIGHT_BUTTON 0x20
static int hidpp_solar_request_battery_event(struct hidpp_device *hidpp)
{
struct hidpp_report response;
u8 params[2] = { 1, 1 };
u8 feature_type;
int ret;
if (hidpp->battery.feature_index == 0xff) {
ret = hidpp_root_get_feature(hidpp,
HIDPP_PAGE_SOLAR_KEYBOARD,
&hidpp->battery.solar_feature_index,
&feature_type);
if (ret)
return ret;
}
ret = hidpp_send_fap_command_sync(hidpp,
hidpp->battery.solar_feature_index,
CMD_SOLAR_SET_LIGHT_MEASURE,
params, 2, &response);
if (ret > 0) {
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
__func__, ret);
return -EPROTO;
}
if (ret)
return ret;
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_MILEAGE;
return 0;
}
static int hidpp_solar_battery_event(struct hidpp_device *hidpp,
u8 *data, int size)
{
struct hidpp_report *report = (struct hidpp_report *)data;
int capacity, lux, status;
u8 function;
function = report->fap.funcindex_clientid;
if (report->fap.feature_index != hidpp->battery.solar_feature_index ||
!(function == EVENT_SOLAR_BATTERY_BROADCAST ||
function == EVENT_SOLAR_BATTERY_LIGHT_MEASURE ||
function == EVENT_SOLAR_CHECK_LIGHT_BUTTON))
return 0;
capacity = report->fap.params[0];
switch (function) {
case EVENT_SOLAR_BATTERY_LIGHT_MEASURE:
lux = (report->fap.params[1] << 8) | report->fap.params[2];
if (lux > 200)
status = POWER_SUPPLY_STATUS_CHARGING;
else
status = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case EVENT_SOLAR_CHECK_LIGHT_BUTTON:
default:
if (capacity < hidpp->battery.capacity)
status = POWER_SUPPLY_STATUS_DISCHARGING;
else
status = POWER_SUPPLY_STATUS_CHARGING;
}
if (capacity == 100)
status = POWER_SUPPLY_STATUS_FULL;
hidpp->battery.online = true;
if (capacity != hidpp->battery.capacity ||
status != hidpp->battery.status) {
hidpp->battery.capacity = capacity;
hidpp->battery.status = status;
if (hidpp->battery.ps)
power_supply_changed(hidpp->battery.ps);
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* 0x6010: Touchpad FW items */
/* -------------------------------------------------------------------------- */
......@@ -1599,9 +2224,6 @@ static int wtp_connect(struct hid_device *hdev, bool connected)
struct wtp_data *wd = hidpp->private_data;
int ret;
if (!connected)
return 0;
if (!wd->x_size) {
ret = wtp_get_config(hidpp);
if (ret) {
......@@ -1669,9 +2291,6 @@ static int m560_send_config_command(struct hid_device *hdev, bool connected)
hidpp_dev = hid_get_drvdata(hdev);
if (!connected)
return -ENODEV;
return hidpp_send_rap_command_sync(
hidpp_dev,
REPORT_ID_HIDPP_SHORT,
......@@ -1875,9 +2494,6 @@ static int k400_connect(struct hid_device *hdev, bool connected)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
if (!connected)
return 0;
if (!disable_tap_to_click)
return 0;
......@@ -1974,6 +2590,7 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
struct hidpp_report *question = hidpp->send_receive_buf;
struct hidpp_report *answer = hidpp->send_receive_buf;
struct hidpp_report *report = (struct hidpp_report *)data;
int ret;
/*
* If the mutex is locked then we have a pending answer from a
......@@ -2002,12 +2619,26 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
if (unlikely(hidpp_report_is_connect_event(report))) {
atomic_set(&hidpp->connected,
!(report->rap.params[0] & (1 << 6)));
if ((hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) &&
(schedule_work(&hidpp->work) == 0))
if (schedule_work(&hidpp->work) == 0)
dbg_hid("%s: connect event already queued\n", __func__);
return 1;
}
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) {
ret = hidpp20_battery_event(hidpp, data, size);
if (ret != 0)
return ret;
ret = hidpp_solar_battery_event(hidpp, data, size);
if (ret != 0)
return ret;
}
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
ret = hidpp10_battery_event(hidpp, data, size);
if (ret != 0)
return ret;
}
return 0;
}
......@@ -2058,20 +2689,90 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
return 0;
}
static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying)
static int hidpp_initialize_battery(struct hidpp_device *hidpp)
{
static atomic_t battery_no = ATOMIC_INIT(0);
struct power_supply_config cfg = { .drv_data = hidpp };
struct power_supply_desc *desc = &hidpp->battery.desc;
enum power_supply_property *battery_props;
struct hidpp_battery *battery;
unsigned int num_battery_props;
unsigned long n;
int ret;
if (hidpp->battery.ps)
return 0;
hidpp->battery.feature_index = 0xff;
hidpp->battery.solar_feature_index = 0xff;
if (hidpp->protocol_major >= 2) {
if (hidpp->quirks & HIDPP_QUIRK_CLASS_K750)
ret = hidpp_solar_request_battery_event(hidpp);
else
ret = hidpp20_query_battery_info(hidpp);
if (ret)
return ret;
hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP20_BATTERY;
} else {
ret = hidpp10_query_battery_status(hidpp);
if (ret) {
ret = hidpp10_query_battery_mileage(hidpp);
if (ret)
return -ENOENT;
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_MILEAGE;
} else {
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS;
}
hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP10_BATTERY;
}
battery_props = devm_kmemdup(&hidpp->hid_dev->dev,
hidpp_battery_props,
sizeof(hidpp_battery_props),
GFP_KERNEL);
num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 2;
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE)
battery_props[num_battery_props++] =
POWER_SUPPLY_PROP_CAPACITY;
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS)
battery_props[num_battery_props++] =
POWER_SUPPLY_PROP_CAPACITY_LEVEL;
battery = &hidpp->battery;
n = atomic_inc_return(&battery_no) - 1;
desc->properties = battery_props;
desc->num_properties = num_battery_props;
desc->get_property = hidpp_battery_get_property;
sprintf(battery->name, "hidpp_battery_%ld", n);
desc->name = battery->name;
desc->type = POWER_SUPPLY_TYPE_BATTERY;
desc->use_for_apm = 0;
battery->ps = devm_power_supply_register(&hidpp->hid_dev->dev,
&battery->desc,
&cfg);
if (IS_ERR(battery->ps))
return PTR_ERR(battery->ps);
power_supply_powers(battery->ps, &hidpp->hid_dev->dev);
return ret;
}
static void hidpp_overwrite_name(struct hid_device *hdev)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
char *name;
if (use_unifying)
/*
* the device is connected through an Unifying receiver, and
* might not be already connected.
* Ask the receiver for its name.
*/
name = hidpp_get_unifying_name(hidpp);
else
name = hidpp_get_device_name(hidpp);
if (hidpp->protocol_major < 2)
return;
name = hidpp_get_device_name(hidpp);
if (!name) {
hid_err(hdev, "unable to retrieve the name of the device");
......@@ -2129,6 +2830,16 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
struct input_dev *input;
char *name, *devm_name;
if (!connected) {
if (hidpp->battery.ps) {
hidpp->battery.online = false;
hidpp->battery.status = POWER_SUPPLY_STATUS_UNKNOWN;
hidpp->battery.level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
power_supply_changed(hidpp->battery.ps);
}
return;
}
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
ret = wtp_connect(hdev, connected);
if (ret)
......@@ -2143,9 +2854,6 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
return;
}
if (!connected || hidpp->delayed_input)
return;
/* the device is already connected, we can ask for its name and
* protocol */
if (!hidpp->protocol_major) {
......@@ -2158,11 +2866,7 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
hidpp->protocol_major, hidpp->protocol_minor);
}
if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT))
/* if HID created the input nodes for us, we can stop now */
return;
if (!hidpp->name || hidpp->name == hdev->name) {
if (hidpp->name == hdev->name && hidpp->protocol_major >= 2) {
name = hidpp_get_device_name(hidpp);
if (!name) {
hid_err(hdev,
......@@ -2178,6 +2882,25 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
hidpp->name = devm_name;
}
hidpp_initialize_battery(hidpp);
/* forward current battery state */
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
hidpp10_enable_battery_reporting(hidpp);
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE)
hidpp10_query_battery_mileage(hidpp);
else
hidpp10_query_battery_status(hidpp);
} else if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) {
hidpp20_query_battery_info(hidpp);
}
if (hidpp->battery.ps)
power_supply_changed(hidpp->battery.ps);
if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)
/* if the input nodes are already created, we can stop now */
return;
input = hidpp_allocate_input(hdev);
if (!input) {
hid_err(hdev, "cannot allocate new input device: %d\n", ret);
......@@ -2193,6 +2916,17 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
hidpp->delayed_input = input;
}
static DEVICE_ATTR(builtin_power_supply, 0000, NULL, NULL);
static struct attribute *sysfs_attrs[] = {
&dev_attr_builtin_power_supply.attr,
NULL
};
static struct attribute_group ps_attribute_group = {
.attrs = sysfs_attrs
};
static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct hidpp_device *hidpp;
......@@ -2211,9 +2945,11 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hidpp->quirks = id->driver_data;
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
hidpp->quirks |= HIDPP_QUIRK_UNIFYING;
if (disable_raw_mode) {
hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP;
hidpp->quirks &= ~HIDPP_QUIRK_CONNECT_EVENTS;
hidpp->quirks &= ~HIDPP_QUIRK_NO_HIDINPUT;
}
......@@ -2235,6 +2971,12 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
mutex_init(&hidpp->send_mutex);
init_waitqueue_head(&hidpp->wait);
/* indicates we are handling the battery properties in the kernel */
ret = sysfs_create_group(&hdev->dev.kobj, &ps_attribute_group);
if (ret)
hid_warn(hdev, "Cannot allocate sysfs group for %s\n",
hdev->name);
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "%s:parse failed\n", __func__);
......@@ -2263,8 +3005,12 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
/* Allow incoming packets */
hid_device_io_start(hdev);
if (hidpp->quirks & HIDPP_QUIRK_UNIFYING)
hidpp_unifying_init(hidpp);
connected = hidpp_is_connected(hidpp);
if (id->group != HID_GROUP_LOGITECH_DJ_DEVICE) {
atomic_set(&hidpp->connected, connected);
if (!(hidpp->quirks & HIDPP_QUIRK_UNIFYING)) {
if (!connected) {
ret = -ENODEV;
hid_err(hdev, "Device not connected");
......@@ -2273,10 +3019,9 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hid_info(hdev, "HID++ %u.%u device connected.\n",
hidpp->protocol_major, hidpp->protocol_minor);
}
hidpp_overwrite_name(hdev, id->group == HID_GROUP_LOGITECH_DJ_DEVICE);
atomic_set(&hidpp->connected, connected);
hidpp_overwrite_name(hdev);
}
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
ret = wtp_get_config(hidpp);
......@@ -2299,12 +3044,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
}
if (hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) {
/* Allow incoming packets */
hid_device_io_start(hdev);
/* Allow incoming packets */
hid_device_io_start(hdev);
hidpp_connect_event(hidpp);
}
hidpp_connect_event(hidpp);
return ret;
......@@ -2316,6 +3059,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
hid_hw_start_fail:
hid_parse_fail:
sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group);
cancel_work_sync(&hidpp->work);
mutex_destroy(&hidpp->send_mutex);
allocate_fail:
......@@ -2327,6 +3071,8 @@ static void hidpp_remove(struct hid_device *hdev)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group);
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
hidpp_ff_deinit(hdev);
hid_hw_close(hdev);
......@@ -2357,7 +3103,11 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* Keyboard logitech K400 */
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, 0x4024),
.driver_data = HIDPP_QUIRK_CONNECT_EVENTS | HIDPP_QUIRK_CLASS_K400 },
.driver_data = HIDPP_QUIRK_CLASS_K400 },
{ /* Solar Keyboard Logitech K750 */
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, 0x4002),
.driver_data = HIDPP_QUIRK_CLASS_K750 },
{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
......
......@@ -69,6 +69,7 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_CONTACT_CNT_ACCURATE (1 << 12)
#define MT_QUIRK_FORCE_GET_FEATURE (1 << 13)
#define MT_QUIRK_FIX_CONST_CONTACT_ID (1 << 14)
#define MT_QUIRK_TOUCH_SIZE_SCALING (1 << 15)
#define MT_INPUTMODE_TOUCHSCREEN 0x02
#define MT_INPUTMODE_TOUCHPAD 0x03
......@@ -222,7 +223,8 @@ static struct mt_class mt_classes[] = {
*/
{ .name = MT_CLS_3M,
.quirks = MT_QUIRK_VALID_IS_CONFIDENCE |
MT_QUIRK_SLOT_IS_CONTACTID,
MT_QUIRK_SLOT_IS_CONTACTID |
MT_QUIRK_TOUCH_SIZE_SCALING,
.sn_move = 2048,
.sn_width = 128,
.sn_height = 128,
......@@ -658,9 +660,17 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
if (active) {
/* this finger is in proximity of the sensor */
int wide = (s->w > s->h);
/* divided by two to match visual scale of touch */
int major = max(s->w, s->h) >> 1;
int minor = min(s->w, s->h) >> 1;
int major = max(s->w, s->h);
int minor = min(s->w, s->h);
/*
* divided by two to match visual scale of touch
* for devices with this quirk
*/
if (td->mtclass.quirks & MT_QUIRK_TOUCH_SIZE_SCALING) {
major = major >> 1;
minor = minor >> 1;
}
input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x);
input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y);
......
/*
* USB HID quirks support for Network Technologies, Inc. "USB-SUN" USB
* adapter for pre-USB Sun keyboards
*
* Copyright (c) 2011 Google, Inc.
*
* Based on HID apple driver by
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#include <linux/device.h>
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
MODULE_AUTHOR("Jonathan Klabunde Tomer <jktomer@google.com>");
MODULE_DESCRIPTION("HID driver for Network Technologies USB-SUN keyboard adapter");
/*
* NTI Sun keyboard adapter has wrong logical maximum in report descriptor
*/
static __u8 *nti_usbsun_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
if (*rsize >= 60 && rdesc[53] == 0x65 && rdesc[59] == 0x65) {
hid_info(hdev, "fixing up NTI USB-SUN keyboard adapter report descriptor\n");
rdesc[53] = rdesc[59] = 0xe7;
}
return rdesc;
}
static const struct hid_device_id nti_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_NTI, USB_DEVICE_ID_USB_SUN) },
{ }
};
MODULE_DEVICE_TABLE(hid, nti_devices);
static struct hid_driver nti_driver = {
.name = "nti",
.id_table = nti_devices,
.report_fixup = nti_usbsun_report_fixup
};
module_hid_driver(nti_driver);
MODULE_LICENSE("GPL");
......@@ -48,19 +48,21 @@
#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 SINO_LITE_CONTROLLER BIT(11)
#define FUTUREMAX_DANCE_MAT BIT(12)
#define DUALSHOCK4_DONGLE BIT(7)
#define MOTION_CONTROLLER_USB BIT(8)
#define MOTION_CONTROLLER_BT BIT(9)
#define NAVIGATION_CONTROLLER_USB BIT(10)
#define NAVIGATION_CONTROLLER_BT BIT(11)
#define SINO_LITE_CONTROLLER BIT(12)
#define FUTUREMAX_DANCE_MAT BIT(13)
#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)
DUALSHOCK4_CONTROLLER_BT | \
DUALSHOCK4_DONGLE)
#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\
DUALSHOCK4_CONTROLLER | MOTION_CONTROLLER |\
NAVIGATION_CONTROLLER)
......@@ -73,89 +75,6 @@
#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 (Joystick), */
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, 0x04, /* Report Count (4), */
0x35, 0x00, /* Physical Minimum (0), */
0x46, 0xFF, 0x00, /* Physical Maximum (255), */
0x09, 0x30, /* Usage (X), */
0x09, 0x31, /* Usage (Y), */
0x09, 0x32, /* Usage (Z), */
0x09, 0x35, /* Usage (Rz), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0x05, 0x01, /* Usage Page (Desktop), */
0x95, 0x13, /* Report Count (19), */
0x09, 0x01, /* Usage (Pointer), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x0C, /* Report Count (12), */
0x81, 0x01, /* Input (Constant), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x04, /* Report Count (4), */
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
0x09, 0x01, /* Usage (Pointer), */
0x81, 0x02, /* Input (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 Motion controller */
static u8 motion_rdesc[] = {
......@@ -254,567 +173,6 @@ static u8 motion_rdesc[] = {
0xC0 /* End Collection */
};
/* PS/3 Navigation controller */
static u8 navigation_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystick), */
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, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0x05, 0x01, /* Usage Page (Desktop), */
0x95, 0x01, /* Report Count (1), */
0x09, 0x01, /* Usage (Pointer), */
0x81, 0x02, /* Input (Variable), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x95, 0x1E, /* Report Count (24), */
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
* to usage values 0x40, 0x41 and 0x42 and maps the orientation sensors
* to usage values 0x43, 0x44 and 0x45.
*/
static u8 dualshock4_usb_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x05, /* Usage (Gamepad), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x01, /* Report ID (1), */
0x09, 0x30, /* Usage (X), */
0x09, 0x31, /* Usage (Y), */
0x09, 0x32, /* Usage (Z), */
0x09, 0x35, /* Usage (Rz), */
0x15, 0x00, /* Logical Minimum (0), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x39, /* Usage (Hat Switch), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x07, /* Logical Maximum (7), */
0x35, 0x00, /* Physical Minimum (0), */
0x46, 0x3B, 0x01, /* Physical Maximum (315), */
0x65, 0x14, /* Unit (Degrees), */
0x75, 0x04, /* Report Size (4), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x42, /* Input (Variable, Null State), */
0x65, 0x00, /* Unit, */
0x05, 0x09, /* Usage Page (Button), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x0D, /* Usage Maximum (0Dh), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x0E, /* Report Count (14), */
0x81, 0x02, /* Input (Variable), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x20, /* Usage (20h), */
0x75, 0x06, /* Report Size (6), */
0x95, 0x01, /* Report Count (1), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x3F, /* Logical Maximum (63), */
0x81, 0x02, /* Input (Variable), */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x33, /* Usage (Rx), */
0x09, 0x34, /* Usage (Ry), */
0x15, 0x00, /* Logical Minimum (0), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x02, /* Input (Variable), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x21, /* Usage (21h), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x05, 0x01, /* Usage Page (Desktop), */
0x19, 0x40, /* Usage Minimum (40h), */
0x29, 0x42, /* Usage Maximum (42h), */
0x16, 0x00, 0x80, /* Logical Minimum (-32768), */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x19, 0x43, /* Usage Minimum (43h), */
0x29, 0x45, /* Usage Maximum (45h), */
0x16, 0x00, 0x80, /* Logical Minimum (-32768), */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x21, /* Usage (21h), */
0x15, 0x00, /* Logical Minimum (0), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x27, /* Report Count (39), */
0x81, 0x02, /* Input (Variable), */
0x85, 0x05, /* Report ID (5), */
0x09, 0x22, /* Usage (22h), */
0x95, 0x1F, /* Report Count (31), */
0x91, 0x02, /* Output (Variable), */
0x85, 0x04, /* Report ID (4), */
0x09, 0x23, /* Usage (23h), */
0x95, 0x24, /* Report Count (36), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x02, /* Report ID (2), */
0x09, 0x24, /* Usage (24h), */
0x95, 0x24, /* Report Count (36), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x08, /* Report ID (8), */
0x09, 0x25, /* Usage (25h), */
0x95, 0x03, /* Report Count (3), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x10, /* Report ID (16), */
0x09, 0x26, /* Usage (26h), */
0x95, 0x04, /* Report Count (4), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x11, /* Report ID (17), */
0x09, 0x27, /* Usage (27h), */
0x95, 0x02, /* Report Count (2), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x12, /* Report ID (18), */
0x06, 0x02, 0xFF, /* Usage Page (FF02h), */
0x09, 0x21, /* Usage (21h), */
0x95, 0x0F, /* Report Count (15), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x13, /* Report ID (19), */
0x09, 0x22, /* Usage (22h), */
0x95, 0x16, /* Report Count (22), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x14, /* Report ID (20), */
0x06, 0x05, 0xFF, /* Usage Page (FF05h), */
0x09, 0x20, /* Usage (20h), */
0x95, 0x10, /* Report Count (16), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x15, /* Report ID (21), */
0x09, 0x21, /* Usage (21h), */
0x95, 0x2C, /* Report Count (44), */
0xB1, 0x02, /* Feature (Variable), */
0x06, 0x80, 0xFF, /* Usage Page (FF80h), */
0x85, 0x80, /* Report ID (128), */
0x09, 0x20, /* Usage (20h), */
0x95, 0x06, /* Report Count (6), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x81, /* Report ID (129), */
0x09, 0x21, /* Usage (21h), */
0x95, 0x06, /* Report Count (6), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x82, /* Report ID (130), */
0x09, 0x22, /* Usage (22h), */
0x95, 0x05, /* Report Count (5), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x83, /* Report ID (131), */
0x09, 0x23, /* Usage (23h), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x84, /* Report ID (132), */
0x09, 0x24, /* Usage (24h), */
0x95, 0x04, /* Report Count (4), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x85, /* Report ID (133), */
0x09, 0x25, /* Usage (25h), */
0x95, 0x06, /* Report Count (6), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x86, /* Report ID (134), */
0x09, 0x26, /* Usage (26h), */
0x95, 0x06, /* Report Count (6), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x87, /* Report ID (135), */
0x09, 0x27, /* Usage (27h), */
0x95, 0x23, /* Report Count (35), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x88, /* Report ID (136), */
0x09, 0x28, /* Usage (28h), */
0x95, 0x22, /* Report Count (34), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x89, /* Report ID (137), */
0x09, 0x29, /* Usage (29h), */
0x95, 0x02, /* Report Count (2), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x90, /* Report ID (144), */
0x09, 0x30, /* Usage (30h), */
0x95, 0x05, /* Report Count (5), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x91, /* Report ID (145), */
0x09, 0x31, /* Usage (31h), */
0x95, 0x03, /* Report Count (3), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x92, /* Report ID (146), */
0x09, 0x32, /* Usage (32h), */
0x95, 0x03, /* Report Count (3), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x93, /* Report ID (147), */
0x09, 0x33, /* Usage (33h), */
0x95, 0x0C, /* Report Count (12), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA0, /* Report ID (160), */
0x09, 0x40, /* Usage (40h), */
0x95, 0x06, /* Report Count (6), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA1, /* Report ID (161), */
0x09, 0x41, /* Usage (41h), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA2, /* Report ID (162), */
0x09, 0x42, /* Usage (42h), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA3, /* Report ID (163), */
0x09, 0x43, /* Usage (43h), */
0x95, 0x30, /* Report Count (48), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA4, /* Report ID (164), */
0x09, 0x44, /* Usage (44h), */
0x95, 0x0D, /* Report Count (13), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA5, /* Report ID (165), */
0x09, 0x45, /* Usage (45h), */
0x95, 0x15, /* Report Count (21), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA6, /* Report ID (166), */
0x09, 0x46, /* Usage (46h), */
0x95, 0x15, /* Report Count (21), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xF0, /* Report ID (240), */
0x09, 0x47, /* Usage (47h), */
0x95, 0x3F, /* Report Count (63), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xF1, /* Report ID (241), */
0x09, 0x48, /* Usage (48h), */
0x95, 0x3F, /* Report Count (63), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xF2, /* Report ID (242), */
0x09, 0x49, /* Usage (49h), */
0x95, 0x0F, /* Report Count (15), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA7, /* Report ID (167), */
0x09, 0x4A, /* Usage (4Ah), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA8, /* Report ID (168), */
0x09, 0x4B, /* Usage (4Bh), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA9, /* Report ID (169), */
0x09, 0x4C, /* Usage (4Ch), */
0x95, 0x08, /* Report Count (8), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xAA, /* Report ID (170), */
0x09, 0x4E, /* Usage (4Eh), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xAB, /* Report ID (171), */
0x09, 0x4F, /* Usage (4Fh), */
0x95, 0x39, /* Report Count (57), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xAC, /* Report ID (172), */
0x09, 0x50, /* Usage (50h), */
0x95, 0x39, /* Report Count (57), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xAD, /* Report ID (173), */
0x09, 0x51, /* Usage (51h), */
0x95, 0x0B, /* Report Count (11), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xAE, /* Report ID (174), */
0x09, 0x52, /* Usage (52h), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xAF, /* Report ID (175), */
0x09, 0x53, /* Usage (53h), */
0x95, 0x02, /* Report Count (2), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xB0, /* Report ID (176), */
0x09, 0x54, /* Usage (54h), */
0x95, 0x3F, /* Report Count (63), */
0xB1, 0x02, /* Feature (Variable), */
0xC0 /* End Collection */
};
/*
* The default behavior of the Dualshock 4 is to send reports using report
* type 1 when running over Bluetooth. However, when feature report 2 is
* requested during the controller initialization it starts sending input
* reports in report 17. Since report 17 is undefined in the default HID
* descriptor the button and axis definitions must be moved to report 17 or
* the HID layer won't process the received input.
*/
static u8 dualshock4_bt_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x05, /* Usage (Gamepad), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x01, /* Report ID (1), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x0A, /* Report Count (9), */
0x81, 0x02, /* Input (Variable), */
0x06, 0x04, 0xFF, /* Usage Page (FF04h), */
0x85, 0x02, /* Report ID (2), */
0x09, 0x24, /* Usage (24h), */
0x95, 0x24, /* Report Count (36), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA3, /* Report ID (163), */
0x09, 0x25, /* Usage (25h), */
0x95, 0x30, /* Report Count (48), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x05, /* Report ID (5), */
0x09, 0x26, /* Usage (26h), */
0x95, 0x28, /* Report Count (40), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x06, /* Report ID (6), */
0x09, 0x27, /* Usage (27h), */
0x95, 0x34, /* Report Count (52), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x07, /* Report ID (7), */
0x09, 0x28, /* Usage (28h), */
0x95, 0x30, /* Report Count (48), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x08, /* Report ID (8), */
0x09, 0x29, /* Usage (29h), */
0x95, 0x2F, /* Report Count (47), */
0xB1, 0x02, /* Feature (Variable), */
0x06, 0x03, 0xFF, /* Usage Page (FF03h), */
0x85, 0x03, /* Report ID (3), */
0x09, 0x21, /* Usage (21h), */
0x95, 0x26, /* Report Count (38), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x04, /* Report ID (4), */
0x09, 0x22, /* Usage (22h), */
0x95, 0x2E, /* Report Count (46), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xF0, /* Report ID (240), */
0x09, 0x47, /* Usage (47h), */
0x95, 0x3F, /* Report Count (63), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xF1, /* Report ID (241), */
0x09, 0x48, /* Usage (48h), */
0x95, 0x3F, /* Report Count (63), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xF2, /* Report ID (242), */
0x09, 0x49, /* Usage (49h), */
0x95, 0x0F, /* Report Count (15), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x11, /* Report ID (17), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x20, /* Usage (20h), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x02, /* Input (Variable), */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x30, /* Usage (X), */
0x09, 0x31, /* Usage (Y), */
0x09, 0x32, /* Usage (Z), */
0x09, 0x35, /* Usage (Rz), */
0x15, 0x00, /* Logical Minimum (0), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x39, /* Usage (Hat Switch), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x07, /* Logical Maximum (7), */
0x75, 0x04, /* Report Size (4), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x42, /* Input (Variable, Null State), */
0x05, 0x09, /* Usage Page (Button), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x0D, /* Usage Maximum (0Dh), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x0E, /* Report Count (14), */
0x81, 0x02, /* Input (Variable), */
0x75, 0x06, /* Report Size (6), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x01, /* Input (Constant), */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x33, /* Usage (Rx), */
0x09, 0x34, /* Usage (Ry), */
0x15, 0x00, /* Logical Minimum (0), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x02, /* Input (Variable), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x20, /* Usage (20h), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x05, 0x01, /* Usage Page (Desktop), */
0x19, 0x40, /* Usage Minimum (40h), */
0x29, 0x42, /* Usage Maximum (42h), */
0x16, 0x00, 0x80, /* Logical Minimum (-32768), */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x19, 0x43, /* Usage Minimum (43h), */
0x29, 0x45, /* Usage Maximum (45h), */
0x16, 0x00, 0x80, /* Logical Minimum (-32768), */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x20, /* Usage (20h), */
0x15, 0x00, /* Logical Minimum (0), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x31, /* Report Count (51), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x21, /* Usage (21h), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x4D, /* Report Count (77), */
0x91, 0x02, /* Output (Variable), */
0x85, 0x12, /* Report ID (18), */
0x09, 0x22, /* Usage (22h), */
0x95, 0x8D, /* Report Count (141), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x23, /* Usage (23h), */
0x91, 0x02, /* Output (Variable), */
0x85, 0x13, /* Report ID (19), */
0x09, 0x24, /* Usage (24h), */
0x95, 0xCD, /* Report Count (205), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x25, /* Usage (25h), */
0x91, 0x02, /* Output (Variable), */
0x85, 0x14, /* Report ID (20), */
0x09, 0x26, /* Usage (26h), */
0x96, 0x0D, 0x01, /* Report Count (269), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x27, /* Usage (27h), */
0x91, 0x02, /* Output (Variable), */
0x85, 0x15, /* Report ID (21), */
0x09, 0x28, /* Usage (28h), */
0x96, 0x4D, 0x01, /* Report Count (333), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x29, /* Usage (29h), */
0x91, 0x02, /* Output (Variable), */
0x85, 0x16, /* Report ID (22), */
0x09, 0x2A, /* Usage (2Ah), */
0x96, 0x8D, 0x01, /* Report Count (397), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x2B, /* Usage (2Bh), */
0x91, 0x02, /* Output (Variable), */
0x85, 0x17, /* Report ID (23), */
0x09, 0x2C, /* Usage (2Ch), */
0x96, 0xCD, 0x01, /* Report Count (461), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x2D, /* Usage (2Dh), */
0x91, 0x02, /* Output (Variable), */
0x85, 0x18, /* Report ID (24), */
0x09, 0x2E, /* Usage (2Eh), */
0x96, 0x0D, 0x02, /* Report Count (525), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x2F, /* Usage (2Fh), */
0x91, 0x02, /* Output (Variable), */
0x85, 0x19, /* Report ID (25), */
0x09, 0x30, /* Usage (30h), */
0x96, 0x22, 0x02, /* Report Count (546), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (31h), */
0x91, 0x02, /* Output (Variable), */
0x06, 0x80, 0xFF, /* Usage Page (FF80h), */
0x85, 0x82, /* Report ID (130), */
0x09, 0x22, /* Usage (22h), */
0x95, 0x3F, /* Report Count (63), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x83, /* Report ID (131), */
0x09, 0x23, /* Usage (23h), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x84, /* Report ID (132), */
0x09, 0x24, /* Usage (24h), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x90, /* Report ID (144), */
0x09, 0x30, /* Usage (30h), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x91, /* Report ID (145), */
0x09, 0x31, /* Usage (31h), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x92, /* Report ID (146), */
0x09, 0x32, /* Usage (32h), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x93, /* Report ID (147), */
0x09, 0x33, /* Usage (33h), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA0, /* Report ID (160), */
0x09, 0x40, /* Usage (40h), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA4, /* Report ID (164), */
0x09, 0x44, /* Usage (44h), */
0xB1, 0x02, /* Feature (Variable), */
0xC0 /* End Collection */
};
static u8 ps3remote_rdesc[] = {
0x05, 0x01, /* GUsagePage Generic Desktop */
0x09, 0x05, /* LUsage 0x05 [Game Pad] */
......@@ -977,6 +335,67 @@ static const unsigned int buzz_keymap[] = {
[20] = BTN_TRIGGER_HAPPY20,
};
/* The Navigation controller is a partial DS3 and uses the same HID report
* and hence the same keymap indices, however not not all axes/buttons
* are physically present. We use the same axis and button mapping as
* the DS3, which uses the Linux gamepad spec.
*/
static const unsigned int navigation_absmap[] = {
[0x30] = ABS_X,
[0x31] = ABS_Y,
[0x33] = ABS_Z, /* L2 */
};
/* Buttons not physically available on the device, but still available
* in the reports are explicitly set to 0 for documentation purposes.
*/
static const unsigned int navigation_keymap[] = {
[0x01] = 0, /* Select */
[0x02] = BTN_THUMBL, /* L3 */
[0x03] = 0, /* R3 */
[0x04] = 0, /* Start */
[0x05] = BTN_DPAD_UP, /* Up */
[0x06] = BTN_DPAD_RIGHT, /* Right */
[0x07] = BTN_DPAD_DOWN, /* Down */
[0x08] = BTN_DPAD_LEFT, /* Left */
[0x09] = BTN_TL2, /* L2 */
[0x0a] = 0, /* R2 */
[0x0b] = BTN_TL, /* L1 */
[0x0c] = 0, /* R1 */
[0x0d] = BTN_NORTH, /* Triangle */
[0x0e] = BTN_EAST, /* Circle */
[0x0f] = BTN_SOUTH, /* Cross */
[0x10] = BTN_WEST, /* Square */
[0x11] = BTN_MODE, /* PS */
};
static const unsigned int sixaxis_absmap[] = {
[0x30] = ABS_X,
[0x31] = ABS_Y,
[0x32] = ABS_RX, /* right stick X */
[0x35] = ABS_RY, /* right stick Y */
};
static const unsigned int sixaxis_keymap[] = {
[0x01] = BTN_SELECT, /* Select */
[0x02] = BTN_THUMBL, /* L3 */
[0x03] = BTN_THUMBR, /* R3 */
[0x04] = BTN_START, /* Start */
[0x05] = BTN_DPAD_UP, /* Up */
[0x06] = BTN_DPAD_RIGHT, /* Right */
[0x07] = BTN_DPAD_DOWN, /* Down */
[0x08] = BTN_DPAD_LEFT, /* Left */
[0x09] = BTN_TL2, /* L2 */
[0x0a] = BTN_TR2, /* R2 */
[0x0b] = BTN_TL, /* L1 */
[0x0c] = BTN_TR, /* R1 */
[0x0d] = BTN_NORTH, /* Triangle */
[0x0e] = BTN_EAST, /* Circle */
[0x0f] = BTN_SOUTH, /* Cross */
[0x10] = BTN_WEST, /* Square */
[0x11] = BTN_MODE, /* PS */
};
static const unsigned int ds4_absmap[] = {
[0x30] = ABS_X,
[0x31] = ABS_Y,
......@@ -1002,6 +421,10 @@ static const unsigned int ds4_keymap[] = {
[0xd] = BTN_MODE, /* PS */
};
static const struct {int x; int y; } ds4_hat_mapping[] = {
{0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1},
{0, 0}
};
static enum power_supply_property sony_battery_props[] = {
POWER_SUPPLY_PROP_PRESENT,
......@@ -1048,6 +471,7 @@ struct motion_output_report_02 {
};
#define DS4_FEATURE_REPORT_0x02_SIZE 37
#define DS4_FEATURE_REPORT_0x05_SIZE 41
#define DS4_FEATURE_REPORT_0x81_SIZE 7
#define DS4_INPUT_REPORT_0x11_SIZE 78
#define DS4_OUTPUT_REPORT_0x05_SIZE 32
......@@ -1059,23 +483,62 @@ struct motion_output_report_02 {
/* Offsets relative to USB input report (0x1). Bluetooth (0x11) requires an
* additional +2.
*/
#define DS4_INPUT_REPORT_AXIS_OFFSET 1
#define DS4_INPUT_REPORT_BUTTON_OFFSET 5
#define DS4_INPUT_REPORT_TIMESTAMP_OFFSET 10
#define DS4_INPUT_REPORT_GYRO_X_OFFSET 13
#define DS4_INPUT_REPORT_BATTERY_OFFSET 30
#define DS4_INPUT_REPORT_TOUCHPAD_OFFSET 33
#define SENSOR_SUFFIX " Motion Sensors"
#define DS4_TOUCHPAD_SUFFIX " Touchpad"
/* Default to 4ms poll interval, which is same as USB (not adjustable). */
#define DS4_BT_DEFAULT_POLL_INTERVAL_MS 4
#define DS4_BT_MAX_POLL_INTERVAL_MS 62
#define DS4_GYRO_RES_PER_DEG_S 1024
#define DS4_ACC_RES_PER_G 8192
#define SIXAXIS_INPUT_REPORT_ACC_X_OFFSET 41
#define SIXAXIS_ACC_RES_PER_G 113
static DEFINE_SPINLOCK(sony_dev_list_lock);
static LIST_HEAD(sony_device_list);
static DEFINE_IDA(sony_device_id_allocator);
/* Used for calibration of DS4 accelerometer and gyro. */
struct ds4_calibration_data {
int abs_code;
short bias;
/* Calibration requires scaling against a sensitivity value, which is a
* float. Store sensitivity as a fraction to limit floating point
* calculations until final calibration.
*/
int sens_numer;
int sens_denom;
};
enum ds4_dongle_state {
DONGLE_DISCONNECTED,
DONGLE_CALIBRATING,
DONGLE_CONNECTED,
DONGLE_DISABLED
};
enum sony_worker {
SONY_WORKER_STATE,
SONY_WORKER_HOTPLUG
};
struct sony_sc {
spinlock_t lock;
struct list_head list_node;
struct hid_device *hdev;
struct input_dev *touchpad;
struct input_dev *sensor_dev;
struct led_classdev *leds[MAX_LEDS];
unsigned long quirks;
struct work_struct hotplug_worker;
struct work_struct state_worker;
void (*send_output_report)(struct sony_sc *);
struct power_supply *battery;
......@@ -1089,46 +552,87 @@ struct sony_sc {
#endif
u8 mac_address[6];
u8 worker_initialized;
u8 hotplug_worker_initialized;
u8 state_worker_initialized;
u8 defer_initialization;
u8 cable_state;
u8 battery_charging;
u8 battery_capacity;
u8 led_state[MAX_LEDS];
u8 resume_led_state[MAX_LEDS];
u8 led_delay_on[MAX_LEDS];
u8 led_delay_off[MAX_LEDS];
u8 led_count;
bool ds4_dongle_connected;
bool timestamp_initialized;
u16 prev_timestamp;
unsigned int timestamp_us;
u8 ds4_bt_poll_interval;
enum ds4_dongle_state ds4_dongle_state;
/* DS4 calibration data */
struct ds4_calibration_data ds4_calib_data[6];
};
static void sony_set_leds(struct sony_sc *sc);
static inline void sony_schedule_work(struct sony_sc *sc)
static inline void sony_schedule_work(struct sony_sc *sc,
enum sony_worker which)
{
if (!sc->defer_initialization)
schedule_work(&sc->state_worker);
switch (which) {
case SONY_WORKER_STATE:
if (!sc->defer_initialization)
schedule_work(&sc->state_worker);
break;
case SONY_WORKER_HOTPLUG:
if (sc->hotplug_worker_initialized)
schedule_work(&sc->hotplug_worker);
break;
}
}
static u8 *sixaxis_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize)
static ssize_t ds4_show_poll_interval(struct device *dev,
struct device_attribute
*attr, char *buf)
{
*rsize = sizeof(sixaxis_rdesc);
return sixaxis_rdesc;
struct hid_device *hdev = to_hid_device(dev);
struct sony_sc *sc = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%i\n", sc->ds4_bt_poll_interval);
}
static u8 *motion_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize)
static ssize_t ds4_store_poll_interval(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
*rsize = sizeof(motion_rdesc);
return motion_rdesc;
struct hid_device *hdev = to_hid_device(dev);
struct sony_sc *sc = hid_get_drvdata(hdev);
unsigned long flags;
u8 interval;
if (kstrtou8(buf, 0, &interval))
return -EINVAL;
if (interval > DS4_BT_MAX_POLL_INTERVAL_MS)
return -EINVAL;
spin_lock_irqsave(&sc->lock, flags);
sc->ds4_bt_poll_interval = interval;
spin_unlock_irqrestore(&sc->lock, flags);
sony_schedule_work(sc, SONY_WORKER_STATE);
return count;
}
static u8 *navigation_fixup(struct hid_device *hdev, u8 *rdesc,
static DEVICE_ATTR(bt_poll_interval, 0644, ds4_show_poll_interval,
ds4_store_poll_interval);
static u8 *motion_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize)
{
*rsize = sizeof(navigation_rdesc);
return navigation_rdesc;
*rsize = sizeof(motion_rdesc);
return motion_rdesc;
}
static u8 *ps3remote_fixup(struct hid_device *hdev, u8 *rdesc,
......@@ -1172,6 +676,102 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
return 1;
}
static int navigation_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
unsigned int key = usage->hid & HID_USAGE;
if (key >= ARRAY_SIZE(sixaxis_keymap))
return -1;
key = navigation_keymap[key];
if (!key)
return -1;
hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key);
return 1;
} else if (usage->hid == HID_GD_POINTER) {
/* See comment in sixaxis_mapping, basically the L2 (and R2)
* triggers are reported through GD Pointer.
* In addition we ignore any analog button 'axes' and only
* support digital buttons.
*/
switch (usage->usage_index) {
case 8: /* L2 */
usage->hid = HID_GD_Z;
break;
default:
return -1;
}
hid_map_usage_clear(hi, usage, bit, max, EV_ABS, usage->hid & 0xf);
return 1;
} else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) {
unsigned int abs = usage->hid & HID_USAGE;
if (abs >= ARRAY_SIZE(navigation_absmap))
return -1;
abs = navigation_absmap[abs];
hid_map_usage_clear(hi, usage, bit, max, EV_ABS, abs);
return 1;
}
return -1;
}
static int sixaxis_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
unsigned int key = usage->hid & HID_USAGE;
if (key >= ARRAY_SIZE(sixaxis_keymap))
return -1;
key = sixaxis_keymap[key];
hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key);
return 1;
} else if (usage->hid == HID_GD_POINTER) {
/* The DS3 provides analog values for most buttons and even
* for HAT axes through GD Pointer. L2 and R2 are reported
* among these as well instead of as GD Z / RZ. Remap L2
* and R2 and ignore other analog 'button axes' as there is
* no good way for reporting them.
*/
switch (usage->usage_index) {
case 8: /* L2 */
usage->hid = HID_GD_Z;
break;
case 9: /* R2 */
usage->hid = HID_GD_RZ;
break;
default:
return -1;
}
hid_map_usage_clear(hi, usage, bit, max, EV_ABS, usage->hid & 0xf);
return 1;
} else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) {
unsigned int abs = usage->hid & HID_USAGE;
if (abs >= ARRAY_SIZE(sixaxis_absmap))
return -1;
abs = sixaxis_absmap[abs];
hid_map_usage_clear(hi, usage, bit, max, EV_ABS, abs);
return 1;
}
return -1;
}
static int ds4_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
......@@ -1227,30 +827,9 @@ static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc,
rdesc[55] = 0x06;
}
/*
* The default Dualshock 4 USB descriptor doesn't assign
* the gyroscope values to corresponding axes so we need a
* modified one.
*/
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
hid_info(hdev, "Using modified Dualshock 4 report descriptor with gyroscope axes\n");
rdesc = dualshock4_usb_rdesc;
*rsize = sizeof(dualshock4_usb_rdesc);
} else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
hid_info(hdev, "Using modified Dualshock 4 Bluetooth report descriptor\n");
rdesc = dualshock4_bt_rdesc;
*rsize = sizeof(dualshock4_bt_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);
......@@ -1288,21 +867,131 @@ static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size)
sc->battery_capacity = battery_capacity;
sc->battery_charging = battery_charging;
spin_unlock_irqrestore(&sc->lock, flags);
if (sc->quirks & SIXAXIS_CONTROLLER) {
int val;
offset = SIXAXIS_INPUT_REPORT_ACC_X_OFFSET;
val = ((rd[offset+1] << 8) | rd[offset]) - 511;
input_report_abs(sc->sensor_dev, ABS_X, val);
/* Y and Z are swapped and inversed */
val = 511 - ((rd[offset+5] << 8) | rd[offset+4]);
input_report_abs(sc->sensor_dev, ABS_Y, val);
val = 511 - ((rd[offset+3] << 8) | rd[offset+2]);
input_report_abs(sc->sensor_dev, ABS_Z, val);
input_sync(sc->sensor_dev);
}
}
static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
{
struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
struct hid_input, list);
struct input_dev *input_dev = hidinput->input;
unsigned long flags;
int n, m, offset, num_touch_data, max_touch_data;
u8 cable_state, battery_capacity, battery_charging;
u16 timestamp;
/* When using Bluetooth the header is 2 bytes longer, so skip these. */
int data_offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 0 : 2;
int data_offset = (sc->quirks & DUALSHOCK4_CONTROLLER_BT) ? 2 : 0;
/* Second bit of third button byte is for the touchpad button. */
offset = data_offset + DS4_INPUT_REPORT_BUTTON_OFFSET;
input_report_key(sc->touchpad, BTN_LEFT, rd[offset+2] & 0x2);
/*
* The default behavior of the Dualshock 4 is to send reports using
* report type 1 when running over Bluetooth. However, when feature
* report 2 is requested during the controller initialization it starts
* sending input reports in report 17. Since report 17 is undefined
* in the default HID descriptor, the HID layer won't generate events.
* While it is possible (and this was done before) to fixup the HID
* descriptor to add this mapping, it was better to do this manually.
* The reason is there were various pieces software both open and closed
* source, relying on the descriptors to be the same across various
* operating systems. If the descriptors wouldn't match some
* applications e.g. games on Wine would not be able to function due
* to different descriptors, which such applications are not parsing.
*/
if (rd[0] == 17) {
int value;
offset = data_offset + DS4_INPUT_REPORT_AXIS_OFFSET;
input_report_abs(input_dev, ABS_X, rd[offset]);
input_report_abs(input_dev, ABS_Y, rd[offset+1]);
input_report_abs(input_dev, ABS_RX, rd[offset+2]);
input_report_abs(input_dev, ABS_RY, rd[offset+3]);
value = rd[offset+4] & 0xf;
if (value > 7)
value = 8; /* Center 0, 0 */
input_report_abs(input_dev, ABS_HAT0X, ds4_hat_mapping[value].x);
input_report_abs(input_dev, ABS_HAT0Y, ds4_hat_mapping[value].y);
input_report_key(input_dev, BTN_WEST, rd[offset+4] & 0x10);
input_report_key(input_dev, BTN_SOUTH, rd[offset+4] & 0x20);
input_report_key(input_dev, BTN_EAST, rd[offset+4] & 0x40);
input_report_key(input_dev, BTN_NORTH, rd[offset+4] & 0x80);
input_report_key(input_dev, BTN_TL, rd[offset+5] & 0x1);
input_report_key(input_dev, BTN_TR, rd[offset+5] & 0x2);
input_report_key(input_dev, BTN_TL2, rd[offset+5] & 0x4);
input_report_key(input_dev, BTN_TR2, rd[offset+5] & 0x8);
input_report_key(input_dev, BTN_SELECT, rd[offset+5] & 0x10);
input_report_key(input_dev, BTN_START, rd[offset+5] & 0x20);
input_report_key(input_dev, BTN_THUMBL, rd[offset+5] & 0x40);
input_report_key(input_dev, BTN_THUMBR, rd[offset+5] & 0x80);
input_report_key(input_dev, BTN_MODE, rd[offset+6] & 0x1);
input_report_abs(input_dev, ABS_Z, rd[offset+7]);
input_report_abs(input_dev, ABS_RZ, rd[offset+8]);
input_sync(input_dev);
}
/* Convert timestamp (in 5.33us unit) to timestamp_us */
offset = data_offset + DS4_INPUT_REPORT_TIMESTAMP_OFFSET;
timestamp = get_unaligned_le16(&rd[offset]);
if (!sc->timestamp_initialized) {
sc->timestamp_us = ((unsigned int)timestamp * 16) / 3;
sc->timestamp_initialized = true;
} else {
u16 delta;
if (sc->prev_timestamp > timestamp)
delta = (U16_MAX - sc->prev_timestamp + timestamp + 1);
else
delta = timestamp - sc->prev_timestamp;
sc->timestamp_us += (delta * 16) / 3;
}
sc->prev_timestamp = timestamp;
input_event(sc->sensor_dev, EV_MSC, MSC_TIMESTAMP, sc->timestamp_us);
offset = data_offset + DS4_INPUT_REPORT_GYRO_X_OFFSET;
for (n = 0; n < 6; n++) {
/* Store data in int for more precision during mult_frac. */
int raw_data = (short)((rd[offset+1] << 8) | rd[offset]);
struct ds4_calibration_data *calib = &sc->ds4_calib_data[n];
/* High precision is needed during calibration, but the
* calibrated values are within 32-bit.
* Note: we swap numerator 'x' and 'numer' in mult_frac for
* precision reasons so we don't need 64-bit.
*/
int calib_data = mult_frac(calib->sens_numer,
raw_data - calib->bias,
calib->sens_denom);
input_report_abs(sc->sensor_dev, calib->abs_code, calib_data);
offset += 2;
}
input_sync(sc->sensor_dev);
/*
* The lower 4 bits of byte 30 (or 32 for BT) contain the battery level
* and the 5th bit contains the USB cable state.
......@@ -1341,7 +1030,7 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
* Trackpad data starts 2 bytes later (e.g. 35 for USB).
*/
offset = data_offset + DS4_INPUT_REPORT_TOUCHPAD_OFFSET;
max_touch_data = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 3 : 4;
max_touch_data = (sc->quirks & DUALSHOCK4_CONTROLLER_BT) ? 4 : 3;
if (rd[offset] > 0 && rd[offset] <= max_touch_data)
num_touch_data = rd[offset];
else
......@@ -1415,47 +1104,79 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
} 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)) {
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
/* CRC check */
u8 bthdr = 0xA1;
u32 crc;
u32 report_crc;
} else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
size == 64) {
dualshock4_parse_report(sc, rd, size);
} else if (((sc->quirks & DUALSHOCK4_CONTROLLER_BT) && rd[0] == 0x11 &&
size == 78)) {
/* CRC check */
u8 bthdr = 0xA1;
u32 crc;
u32 report_crc;
crc = crc32_le(0xFFFFFFFF, &bthdr, 1);
crc = ~crc32_le(crc, rd, DS4_INPUT_REPORT_0x11_SIZE-4);
report_crc = get_unaligned_le32(&rd[DS4_INPUT_REPORT_0x11_SIZE-4]);
if (crc != report_crc) {
hid_dbg(sc->hdev, "DualShock 4 input report's CRC check failed, received crc 0x%0x != 0x%0x\n",
report_crc, crc);
return -EILSEQ;
}
crc = crc32_le(0xFFFFFFFF, &bthdr, 1);
crc = ~crc32_le(crc, rd, DS4_INPUT_REPORT_0x11_SIZE-4);
report_crc = get_unaligned_le32(&rd[DS4_INPUT_REPORT_0x11_SIZE-4]);
if (crc != report_crc) {
hid_dbg(sc->hdev, "DualShock 4 input report's CRC check failed, received crc 0x%0x != 0x%0x\n",
report_crc, crc);
return -EILSEQ;
}
dualshock4_parse_report(sc, rd, size);
} else if ((sc->quirks & DUALSHOCK4_DONGLE) && rd[0] == 0x01 &&
size == 64) {
unsigned long flags;
enum ds4_dongle_state dongle_state;
/*
* In the case of a DS4 USB dongle, bit[2] of byte 31 indicates
* if a DS4 is actually connected (indicated by '0').
* For non-dongle, this bit is always 0 (connected).
*/
if (sc->hdev->vendor == USB_VENDOR_ID_SONY &&
sc->hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) {
bool connected = (rd[31] & 0x04) ? false : true;
if (!sc->ds4_dongle_connected && connected) {
hid_info(sc->hdev, "DualShock 4 USB dongle: controller connected\n");
sony_set_leds(sc);
sc->ds4_dongle_connected = true;
} else if (sc->ds4_dongle_connected && !connected) {
hid_info(sc->hdev, "DualShock 4 USB dongle: controller disconnected\n");
sc->ds4_dongle_connected = false;
/* Return 0, so hidraw can get the report. */
return 0;
} else if (!sc->ds4_dongle_connected) {
/* Return 0, so hidraw can get the report. */
return 0;
}
bool connected = (rd[31] & 0x04) ? false : true;
spin_lock_irqsave(&sc->lock, flags);
dongle_state = sc->ds4_dongle_state;
spin_unlock_irqrestore(&sc->lock, flags);
/*
* The dongle always sends input reports even when no
* DS4 is attached. When a DS4 is connected, we need to
* obtain calibration data before we can use it.
* The code below tracks dongle state and kicks of
* calibration when needed and only allows us to process
* input if a DS4 is actually connected.
*/
if (dongle_state == DONGLE_DISCONNECTED && connected) {
hid_info(sc->hdev, "DualShock 4 USB dongle: controller connected\n");
sony_set_leds(sc);
spin_lock_irqsave(&sc->lock, flags);
sc->ds4_dongle_state = DONGLE_CALIBRATING;
spin_unlock_irqrestore(&sc->lock, flags);
sony_schedule_work(sc, SONY_WORKER_HOTPLUG);
/* Don't process the report since we don't have
* calibration data, but let hidraw have it anyway.
*/
return 0;
} else if ((dongle_state == DONGLE_CONNECTED ||
dongle_state == DONGLE_DISABLED) && !connected) {
hid_info(sc->hdev, "DualShock 4 USB dongle: controller disconnected\n");
spin_lock_irqsave(&sc->lock, flags);
sc->ds4_dongle_state = DONGLE_DISCONNECTED;
spin_unlock_irqrestore(&sc->lock, flags);
/* Return 0, so hidraw can get the report. */
return 0;
} else if (dongle_state == DONGLE_CALIBRATING ||
dongle_state == DONGLE_DISABLED ||
dongle_state == DONGLE_DISCONNECTED) {
/* Return 0, so hidraw can get the report. */
return 0;
}
dualshock4_parse_report(sc, rd, size);
......@@ -1463,7 +1184,7 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
if (sc->defer_initialization) {
sc->defer_initialization = 0;
sony_schedule_work(sc);
sony_schedule_work(sc, SONY_WORKER_STATE);
}
return 0;
......@@ -1501,10 +1222,16 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
if (sc->quirks & PS3REMOTE)
return ps3remote_mapping(hdev, hi, field, usage, bit, max);
if (sc->quirks & NAVIGATION_CONTROLLER)
return navigation_mapping(hdev, hi, field, usage, bit, max);
if (sc->quirks & SIXAXIS_CONTROLLER)
return sixaxis_mapping(hdev, hi, field, usage, bit, max);
if (sc->quirks & DUALSHOCK4_CONTROLLER)
return ds4_mapping(hdev, hi, field, usage, bit, max);
/* Let hid-core decide for the others */
return 0;
}
......@@ -1541,7 +1268,7 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
snprintf(name, name_sz, "%s" DS4_TOUCHPAD_SUFFIX, sc->hdev->name);
sc->touchpad->name = name;
ret = input_mt_init_slots(sc->touchpad, touch_count, 0);
ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER);
if (ret < 0)
goto err;
......@@ -1581,6 +1308,103 @@ static void sony_unregister_touchpad(struct sony_sc *sc)
sc->touchpad = NULL;
}
static int sony_register_sensors(struct sony_sc *sc)
{
size_t name_sz;
char *name;
int ret;
int range;
sc->sensor_dev = input_allocate_device();
if (!sc->sensor_dev)
return -ENOMEM;
input_set_drvdata(sc->sensor_dev, sc);
sc->sensor_dev->dev.parent = &sc->hdev->dev;
sc->sensor_dev->phys = sc->hdev->phys;
sc->sensor_dev->uniq = sc->hdev->uniq;
sc->sensor_dev->id.bustype = sc->hdev->bus;
sc->sensor_dev->id.vendor = sc->hdev->vendor;
sc->sensor_dev->id.product = sc->hdev->product;
sc->sensor_dev->id.version = sc->hdev->version;
/* Append a suffix to the controller name as there are various
* DS4 compatible non-Sony devices with different names.
*/
name_sz = strlen(sc->hdev->name) + sizeof(SENSOR_SUFFIX);
name = kzalloc(name_sz, GFP_KERNEL);
if (!name) {
ret = -ENOMEM;
goto err;
}
snprintf(name, name_sz, "%s" SENSOR_SUFFIX, sc->hdev->name);
sc->sensor_dev->name = name;
if (sc->quirks & SIXAXIS_CONTROLLER) {
/* For the DS3 we only support the accelerometer, which works
* quite well even without calibration. The device also has
* a 1-axis gyro, but it is very difficult to manage from within
* the driver even to get data, the sensor is inaccurate and
* the behavior is very different between hardware revisions.
*/
input_set_abs_params(sc->sensor_dev, ABS_X, -512, 511, 4, 0);
input_set_abs_params(sc->sensor_dev, ABS_Y, -512, 511, 4, 0);
input_set_abs_params(sc->sensor_dev, ABS_Z, -512, 511, 4, 0);
input_abs_set_res(sc->sensor_dev, ABS_X, SIXAXIS_ACC_RES_PER_G);
input_abs_set_res(sc->sensor_dev, ABS_Y, SIXAXIS_ACC_RES_PER_G);
input_abs_set_res(sc->sensor_dev, ABS_Z, SIXAXIS_ACC_RES_PER_G);
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
range = DS4_ACC_RES_PER_G*4;
input_set_abs_params(sc->sensor_dev, ABS_X, -range, range, 16, 0);
input_set_abs_params(sc->sensor_dev, ABS_Y, -range, range, 16, 0);
input_set_abs_params(sc->sensor_dev, ABS_Z, -range, range, 16, 0);
input_abs_set_res(sc->sensor_dev, ABS_X, DS4_ACC_RES_PER_G);
input_abs_set_res(sc->sensor_dev, ABS_Y, DS4_ACC_RES_PER_G);
input_abs_set_res(sc->sensor_dev, ABS_Z, DS4_ACC_RES_PER_G);
range = DS4_GYRO_RES_PER_DEG_S*2048;
input_set_abs_params(sc->sensor_dev, ABS_RX, -range, range, 16, 0);
input_set_abs_params(sc->sensor_dev, ABS_RY, -range, range, 16, 0);
input_set_abs_params(sc->sensor_dev, ABS_RZ, -range, range, 16, 0);
input_abs_set_res(sc->sensor_dev, ABS_RX, DS4_GYRO_RES_PER_DEG_S);
input_abs_set_res(sc->sensor_dev, ABS_RY, DS4_GYRO_RES_PER_DEG_S);
input_abs_set_res(sc->sensor_dev, ABS_RZ, DS4_GYRO_RES_PER_DEG_S);
__set_bit(EV_MSC, sc->sensor_dev->evbit);
__set_bit(MSC_TIMESTAMP, sc->sensor_dev->mscbit);
}
__set_bit(INPUT_PROP_ACCELEROMETER, sc->sensor_dev->propbit);
ret = input_register_device(sc->sensor_dev);
if (ret < 0)
goto err;
return 0;
err:
kfree(sc->sensor_dev->name);
sc->sensor_dev->name = NULL;
input_free_device(sc->sensor_dev);
sc->sensor_dev = NULL;
return ret;
}
static void sony_unregister_sensors(struct sony_sc *sc)
{
if (!sc->sensor_dev)
return;
kfree(sc->sensor_dev->name);
sc->sensor_dev->name = NULL;
input_unregister_device(sc->sensor_dev);
sc->sensor_dev = NULL;
}
/*
* Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
* to "operational". Without this, the ps3 controller will not report any
......@@ -1646,26 +1470,176 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev)
}
/*
* Requesting feature report 0x02 in Bluetooth mode changes the state of the
* controller so that it sends full input reports of type 0x11.
* Request DS4 calibration data for the motion sensors.
* For Bluetooth this also affects the operating mode (see below).
*/
static int dualshock4_set_operational_bt(struct hid_device *hdev)
static int dualshock4_get_calibration_data(struct sony_sc *sc)
{
u8 *buf;
int ret;
short gyro_pitch_bias, gyro_pitch_plus, gyro_pitch_minus;
short gyro_yaw_bias, gyro_yaw_plus, gyro_yaw_minus;
short gyro_roll_bias, gyro_roll_plus, gyro_roll_minus;
short gyro_speed_plus, gyro_speed_minus;
short acc_x_plus, acc_x_minus;
short acc_y_plus, acc_y_minus;
short acc_z_plus, acc_z_minus;
int speed_2x;
int range_2g;
/* For Bluetooth we use a different request, which supports CRC.
* Note: in Bluetooth mode feature report 0x02 also changes the state
* of the controller, so that it sends input reports of type 0x11.
*/
if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE)) {
buf = kmalloc(DS4_FEATURE_REPORT_0x02_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf = kmalloc(DS4_FEATURE_REPORT_0x02_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = hid_hw_raw_request(sc->hdev, 0x02, buf,
DS4_FEATURE_REPORT_0x02_SIZE,
HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret < 0)
goto err_stop;
} else {
u8 bthdr = 0xA3;
u32 crc;
u32 report_crc;
int retries;
buf = kmalloc(DS4_FEATURE_REPORT_0x05_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = hid_hw_raw_request(hdev, 0x02, buf, DS4_FEATURE_REPORT_0x02_SIZE,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
for (retries = 0; retries < 3; retries++) {
ret = hid_hw_raw_request(sc->hdev, 0x05, buf,
DS4_FEATURE_REPORT_0x05_SIZE,
HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret < 0)
goto err_stop;
kfree(buf);
/* CRC check */
crc = crc32_le(0xFFFFFFFF, &bthdr, 1);
crc = ~crc32_le(crc, buf, DS4_FEATURE_REPORT_0x05_SIZE-4);
report_crc = get_unaligned_le32(&buf[DS4_FEATURE_REPORT_0x05_SIZE-4]);
if (crc != report_crc) {
hid_warn(sc->hdev, "DualShock 4 calibration report's CRC check failed, received crc 0x%0x != 0x%0x\n",
report_crc, crc);
if (retries < 2) {
hid_warn(sc->hdev, "Retrying DualShock 4 get calibration report request\n");
continue;
} else {
ret = -EILSEQ;
goto err_stop;
}
} else {
break;
}
}
}
gyro_pitch_bias = get_unaligned_le16(&buf[1]);
gyro_yaw_bias = get_unaligned_le16(&buf[3]);
gyro_roll_bias = get_unaligned_le16(&buf[5]);
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
gyro_pitch_plus = get_unaligned_le16(&buf[7]);
gyro_pitch_minus = get_unaligned_le16(&buf[9]);
gyro_yaw_plus = get_unaligned_le16(&buf[11]);
gyro_yaw_minus = get_unaligned_le16(&buf[13]);
gyro_roll_plus = get_unaligned_le16(&buf[15]);
gyro_roll_minus = get_unaligned_le16(&buf[17]);
} else {
/* BT + Dongle */
gyro_pitch_plus = get_unaligned_le16(&buf[7]);
gyro_yaw_plus = get_unaligned_le16(&buf[9]);
gyro_roll_plus = get_unaligned_le16(&buf[11]);
gyro_pitch_minus = get_unaligned_le16(&buf[13]);
gyro_yaw_minus = get_unaligned_le16(&buf[15]);
gyro_roll_minus = get_unaligned_le16(&buf[17]);
}
gyro_speed_plus = get_unaligned_le16(&buf[19]);
gyro_speed_minus = get_unaligned_le16(&buf[21]);
acc_x_plus = get_unaligned_le16(&buf[23]);
acc_x_minus = get_unaligned_le16(&buf[25]);
acc_y_plus = get_unaligned_le16(&buf[27]);
acc_y_minus = get_unaligned_le16(&buf[29]);
acc_z_plus = get_unaligned_le16(&buf[31]);
acc_z_minus = get_unaligned_le16(&buf[33]);
/* Set gyroscope calibration and normalization parameters.
* Data values will be normalized to 1/DS4_GYRO_RES_PER_DEG_S degree/s.
*/
speed_2x = (gyro_speed_plus + gyro_speed_minus);
sc->ds4_calib_data[0].abs_code = ABS_RX;
sc->ds4_calib_data[0].bias = gyro_pitch_bias;
sc->ds4_calib_data[0].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S;
sc->ds4_calib_data[0].sens_denom = gyro_pitch_plus - gyro_pitch_minus;
sc->ds4_calib_data[1].abs_code = ABS_RY;
sc->ds4_calib_data[1].bias = gyro_yaw_bias;
sc->ds4_calib_data[1].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S;
sc->ds4_calib_data[1].sens_denom = gyro_yaw_plus - gyro_yaw_minus;
sc->ds4_calib_data[2].abs_code = ABS_RZ;
sc->ds4_calib_data[2].bias = gyro_roll_bias;
sc->ds4_calib_data[2].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S;
sc->ds4_calib_data[2].sens_denom = gyro_roll_plus - gyro_roll_minus;
/* Set accelerometer calibration and normalization parameters.
* Data values will be normalized to 1/DS4_ACC_RES_PER_G G.
*/
range_2g = acc_x_plus - acc_x_minus;
sc->ds4_calib_data[3].abs_code = ABS_X;
sc->ds4_calib_data[3].bias = acc_x_plus - range_2g / 2;
sc->ds4_calib_data[3].sens_numer = 2*DS4_ACC_RES_PER_G;
sc->ds4_calib_data[3].sens_denom = range_2g;
range_2g = acc_y_plus - acc_y_minus;
sc->ds4_calib_data[4].abs_code = ABS_Y;
sc->ds4_calib_data[4].bias = acc_y_plus - range_2g / 2;
sc->ds4_calib_data[4].sens_numer = 2*DS4_ACC_RES_PER_G;
sc->ds4_calib_data[4].sens_denom = range_2g;
range_2g = acc_z_plus - acc_z_minus;
sc->ds4_calib_data[5].abs_code = ABS_Z;
sc->ds4_calib_data[5].bias = acc_z_plus - range_2g / 2;
sc->ds4_calib_data[5].sens_numer = 2*DS4_ACC_RES_PER_G;
sc->ds4_calib_data[5].sens_denom = range_2g;
err_stop:
kfree(buf);
return ret;
}
static void dualshock4_calibration_work(struct work_struct *work)
{
struct sony_sc *sc = container_of(work, struct sony_sc, hotplug_worker);
unsigned long flags;
enum ds4_dongle_state dongle_state;
int ret;
ret = dualshock4_get_calibration_data(sc);
if (ret < 0) {
/* This call is very unlikely to fail for the dongle. When it
* fails we are probably in a very bad state, so mark the
* dongle as disabled. We will re-enable the dongle if a new
* DS4 hotplug is detect from sony_raw_event as any issues
* are likely resolved then (the dongle is quite stupid).
*/
hid_err(sc->hdev, "DualShock 4 USB dongle: calibration failed, disabling device\n");
dongle_state = DONGLE_DISABLED;
} else {
hid_info(sc->hdev, "DualShock 4 USB dongle: calibration completed\n");
dongle_state = DONGLE_CONNECTED;
}
spin_lock_irqsave(&sc->lock, flags);
sc->ds4_dongle_state = dongle_state;
spin_unlock_irqrestore(&sc->lock, flags);
}
static void sixaxis_set_leds_from_id(struct sony_sc *sc)
{
static const u8 sixaxis_leds[10][4] = {
......@@ -1696,10 +1670,10 @@ 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] = {
/* Blue */ { 0x00, 0x00, 0x01 },
/* Red */ { 0x01, 0x00, 0x00 },
/* Green */ { 0x00, 0x01, 0x00 },
/* Pink */ { 0x02, 0x00, 0x01 },
/* Blue */ { 0x00, 0x00, 0x40 },
/* Red */ { 0x40, 0x00, 0x00 },
/* Green */ { 0x00, 0x40, 0x00 },
/* Pink */ { 0x20, 0x00, 0x20 },
/* Orange */ { 0x02, 0x01, 0x00 },
/* Teal */ { 0x00, 0x01, 0x01 },
/* White */ { 0x01, 0x01, 0x01 }
......@@ -1740,7 +1714,7 @@ static void buzz_set_leds(struct sony_sc *sc)
static void sony_set_leds(struct sony_sc *sc)
{
if (!(sc->quirks & BUZZ_CONTROLLER))
sony_schedule_work(sc);
sony_schedule_work(sc, SONY_WORKER_STATE);
else
buzz_set_leds(sc);
}
......@@ -1851,7 +1825,7 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
new_off != drv_data->led_delay_off[n]) {
drv_data->led_delay_on[n] = new_on;
drv_data->led_delay_off[n] = new_off;
sony_schedule_work(drv_data);
sony_schedule_work(drv_data, SONY_WORKER_STATE);
}
return 0;
......@@ -1964,6 +1938,7 @@ static int sony_leds_init(struct sony_sc *sc)
led->name = name;
led->brightness = sc->led_state[n];
led->max_brightness = max_brightness[n];
led->flags = LED_CORE_SUSPENDRESUME;
led->brightness_get = sony_led_get_brightness;
led->brightness_set = sony_led_set_brightness;
......@@ -2052,26 +2027,24 @@ static void dualshock4_send_output_report(struct sony_sc *sc)
int offset;
/*
* NOTE: The buf[1] field of the Bluetooth report controls
* the Dualshock 4 reporting rate.
*
* Known values include:
*
* 0x80 - 1000hz (full speed)
* 0xA0 - 31hz
* 0xB0 - 20hz
* 0xD0 - 66hz
* NOTE: The lower 6 bits of buf[1] field of the Bluetooth report
* control the interval at which Dualshock 4 reports data:
* 0x00 - 1ms
* 0x01 - 1ms
* 0x02 - 2ms
* 0x3E - 62ms
* 0x3F - disabled
*/
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE)) {
memset(buf, 0, DS4_OUTPUT_REPORT_0x05_SIZE);
buf[0] = 0x05;
buf[1] = 0xFF;
buf[1] = 0x07; /* blink + LEDs + motor */
offset = 4;
} else {
memset(buf, 0, DS4_OUTPUT_REPORT_0x11_SIZE);
buf[0] = 0x11;
buf[1] = 0xC0; /* HID + CRC */
buf[3] = 0x0F;
buf[1] = 0xC0 /* HID + CRC */ | sc->ds4_bt_poll_interval;
buf[3] = 0x07; /* blink + LEDs + motor */
offset = 6;
}
......@@ -2095,7 +2068,7 @@ static void dualshock4_send_output_report(struct sony_sc *sc)
buf[offset++] = sc->led_delay_on[3];
buf[offset++] = sc->led_delay_off[3];
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE))
hid_hw_output_report(hdev, buf, DS4_OUTPUT_REPORT_0x05_SIZE);
else {
/* CRC generation */
......@@ -2152,7 +2125,7 @@ static int sony_allocate_output_report(struct sony_sc *sc)
else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
sc->output_report_dmabuf = kmalloc(DS4_OUTPUT_REPORT_0x11_SIZE,
GFP_KERNEL);
else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
else if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE))
sc->output_report_dmabuf = kmalloc(DS4_OUTPUT_REPORT_0x05_SIZE,
GFP_KERNEL);
else if (sc->quirks & MOTION_CONTROLLER)
......@@ -2180,7 +2153,7 @@ static int sony_play_effect(struct input_dev *dev, void *data,
sc->left = effect->u.rumble.strong_magnitude / 256;
sc->right = effect->u.rumble.weak_magnitude / 256;
sony_schedule_work(sc);
sony_schedule_work(sc, SONY_WORKER_STATE);
return 0;
}
......@@ -2397,7 +2370,7 @@ static int sony_check_add(struct sony_sc *sc)
hid_warn(sc->hdev, "UNIQ does not contain a MAC address; duplicate check skipped\n");
return 0;
}
} else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
} else if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE)) {
buf = kmalloc(DS4_FEATURE_REPORT_0x81_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
......@@ -2451,6 +2424,12 @@ static int sony_check_add(struct sony_sc *sc)
*/
for (n = 0; n < 6; n++)
sc->mac_address[5-n] = buf[4+n];
snprintf(sc->hdev->uniq, sizeof(sc->hdev->uniq),
"%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
sc->mac_address[5], sc->mac_address[4],
sc->mac_address[3], sc->mac_address[2],
sc->mac_address[1], sc->mac_address[0]);
} else {
return 0;
}
......@@ -2501,18 +2480,21 @@ static inline void sony_init_output_report(struct sony_sc *sc,
{
sc->send_output_report = send_output_report;
if (!sc->worker_initialized)
if (!sc->state_worker_initialized)
INIT_WORK(&sc->state_worker, sony_state_worker);
sc->worker_initialized = 1;
sc->state_worker_initialized = 1;
}
static inline void sony_cancel_work_sync(struct sony_sc *sc)
{
if (sc->worker_initialized)
if (sc->hotplug_worker_initialized)
cancel_work_sync(&sc->hotplug_worker);
if (sc->state_worker_initialized)
cancel_work_sync(&sc->state_worker);
}
static int sony_input_configured(struct hid_device *hdev,
struct hid_input *hidinput)
{
......@@ -2526,14 +2508,17 @@ static int sony_input_configured(struct hid_device *hdev,
goto err_stop;
}
ret = append_dev_id = sony_check_add(sc);
if (ret < 0)
goto err_stop;
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)) {
if (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
......@@ -2553,24 +2538,79 @@ static int sony_input_configured(struct hid_device *hdev,
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
sc->defer_initialization = 1;
ret = sixaxis_set_operational_usb(hdev);
if (ret < 0) {
hid_err(hdev, "Failed to set controller into operational mode\n");
goto err_stop;
}
sony_init_output_report(sc, sixaxis_send_output_report);
} else if (sc->quirks & NAVIGATION_CONTROLLER_BT) {
/*
* The Navigation controller wants output reports sent on the ctrl
* endpoint when connected via Bluetooth.
*/
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
ret = sixaxis_set_operational_bt(hdev);
if (ret < 0) {
hid_err(hdev, "Failed to set controller into operational mode\n");
goto err_stop;
}
sony_init_output_report(sc, sixaxis_send_output_report);
} else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) ||
(sc->quirks & NAVIGATION_CONTROLLER_BT)) {
} else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
/*
* The Sony Sixaxis does not handle HID Output Reports on the
* Interrupt EP and the device only becomes active when the
* PS button is pressed. See comment for Navigation controller
* above for more details.
*/
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
sc->defer_initialization = 1;
ret = sixaxis_set_operational_usb(hdev);
if (ret < 0) {
hid_err(hdev, "Failed to set controller into operational mode\n");
goto err_stop;
}
ret = sony_register_sensors(sc);
if (ret) {
hid_err(sc->hdev,
"Unable to initialize motion sensors: %d\n", ret);
goto err_stop;
}
sony_init_output_report(sc, sixaxis_send_output_report);
} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
/*
* The Sixaxis wants output reports sent on the ctrl endpoint
* when connected via Bluetooth.
*/
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
ret = sixaxis_set_operational_bt(hdev);
if (ret < 0) {
hid_err(hdev, "Failed to set controller into operational mode\n");
goto err_stop;
}
ret = sony_register_sensors(sc);
if (ret) {
hid_err(sc->hdev,
"Unable to initialize motion sensors: %d\n", ret);
goto err_stop;
}
sony_init_output_report(sc, sixaxis_send_output_report);
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
ret = dualshock4_set_operational_bt(hdev);
if (ret < 0) {
hid_err(hdev, "failed to set the Dualshock 4 operational mode\n");
goto err_stop;
}
ret = dualshock4_get_calibration_data(sc);
if (ret < 0) {
hid_err(hdev, "Failed to get calibration data from Dualshock 4\n");
goto err_stop;
}
/*
......@@ -2585,6 +2625,28 @@ static int sony_input_configured(struct hid_device *hdev,
goto err_stop;
}
ret = sony_register_sensors(sc);
if (ret) {
hid_err(sc->hdev,
"Unable to initialize motion sensors: %d\n", ret);
goto err_stop;
}
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
sc->ds4_bt_poll_interval = DS4_BT_DEFAULT_POLL_INTERVAL_MS;
ret = device_create_file(&sc->hdev->dev, &dev_attr_bt_poll_interval);
if (ret)
hid_warn(sc->hdev,
"can't create sysfs bt_poll_interval attribute err: %d\n",
ret);
}
if (sc->quirks & DUALSHOCK4_DONGLE) {
INIT_WORK(&sc->hotplug_worker, dualshock4_calibration_work);
sc->hotplug_worker_initialized = 1;
sc->ds4_dongle_state = DONGLE_DISCONNECTED;
}
sony_init_output_report(sc, dualshock4_send_output_report);
} else if (sc->quirks & MOTION_CONTROLLER) {
sony_init_output_report(sc, motion_send_output_report);
......@@ -2592,13 +2654,6 @@ static int sony_input_configured(struct hid_device *hdev,
ret = 0;
}
if (ret < 0)
goto err_stop;
ret = append_dev_id = sony_check_add(sc);
if (ret < 0)
goto err_stop;
if (sc->quirks & SONY_LED_SUPPORT) {
ret = sony_leds_init(sc);
if (ret < 0)
......@@ -2628,12 +2683,20 @@ static int sony_input_configured(struct hid_device *hdev,
err_close:
hid_hw_close(hdev);
err_stop:
/* Piggy back on the default ds4_bt_ poll_interval to determine
* if we need to remove the file as we don't know for sure if we
* executed that logic.
*/
if (sc->ds4_bt_poll_interval)
device_remove_file(&sc->hdev->dev, &dev_attr_bt_poll_interval);
if (sc->quirks & SONY_LED_SUPPORT)
sony_leds_remove(sc);
if (sc->quirks & SONY_BATTERY_SUPPORT)
sony_battery_remove(sc);
if (sc->touchpad)
sony_unregister_touchpad(sc);
if (sc->sensor_dev)
sony_unregister_sensors(sc);
sony_cancel_work_sync(sc);
kfree(sc->output_report_dmabuf);
sony_remove_dev_list(sc);
......@@ -2675,13 +2738,13 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
else if (sc->quirks & SIXAXIS_CONTROLLER)
connect_mask |= HID_CONNECT_HIDDEV_FORCE;
/* Patch the hw version on DS4 compatible devices, so applications can
/* Patch the hw version on DS3/4 compatible devices, so applications can
* distinguish between the default HID mappings and the mappings defined
* by the Linux game controller spec. This is important for the SDL2
* library, which has a game controller database, which uses device ids
* in combination with version as a key.
*/
if (sc->quirks & DUALSHOCK4_CONTROLLER)
if (sc->quirks & (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER))
hdev->version |= 0x8000;
ret = hid_hw_start(hdev, connect_mask);
......@@ -2721,6 +2784,12 @@ static void sony_remove(struct hid_device *hdev)
if (sc->touchpad)
sony_unregister_touchpad(sc);
if (sc->sensor_dev)
sony_unregister_sensors(sc);
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
device_remove_file(&sc->hdev->dev, &dev_attr_bt_poll_interval);
sony_cancel_work_sync(sc);
kfree(sc->output_report_dmabuf);
......@@ -2736,47 +2805,32 @@ static void sony_remove(struct hid_device *hdev)
static int sony_suspend(struct hid_device *hdev, pm_message_t message)
{
/*
* On suspend save the current LED state,
* stop running force-feedback and blank the LEDS.
*/
if (SONY_LED_SUPPORT || SONY_FF_SUPPORT) {
struct sony_sc *sc = hid_get_drvdata(hdev);
#ifdef CONFIG_SONY_FF
sc->left = sc->right = 0;
#endif
memcpy(sc->resume_led_state, sc->led_state,
sizeof(sc->resume_led_state));
memset(sc->led_state, 0, sizeof(sc->led_state));
/* On suspend stop any running force-feedback events */
if (SONY_FF_SUPPORT) {
struct sony_sc *sc = hid_get_drvdata(hdev);
sc->left = sc->right = 0;
sony_send_output_report(sc);
}
#endif
return 0;
}
static int sony_resume(struct hid_device *hdev)
{
/* Restore the state of controller LEDs on resume */
if (SONY_LED_SUPPORT) {
struct sony_sc *sc = hid_get_drvdata(hdev);
memcpy(sc->led_state, sc->resume_led_state,
sizeof(sc->led_state));
/*
* The Sixaxis and navigation controllers on USB need to be
* reinitialized on resume or they won't behave properly.
*/
if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
(sc->quirks & NAVIGATION_CONTROLLER_USB)) {
sixaxis_set_operational_usb(sc->hdev);
sc->defer_initialization = 1;
}
struct sony_sc *sc = hid_get_drvdata(hdev);
sony_set_leds(sc);
/*
* The Sixaxis and navigation controllers on USB need to be
* reinitialized on resume or they won't behave properly.
*/
if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
(sc->quirks & NAVIGATION_CONTROLLER_USB)) {
sixaxis_set_operational_usb(sc->hdev);
sc->defer_initialization = 1;
}
return 0;
......@@ -2828,7 +2882,7 @@ static const struct hid_device_id sony_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2),
.driver_data = DUALSHOCK4_CONTROLLER_BT },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE),
.driver_data = DUALSHOCK4_CONTROLLER_USB },
.driver_data = DUALSHOCK4_DONGLE },
/* Nyko Core Controller for PS3 */
{ HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER),
.driver_data = SIXAXIS_CONTROLLER_USB | SINO_LITE_CONTROLLER },
......
......@@ -508,66 +508,6 @@ static int i2c_hid_get_report_length(struct hid_report *report)
report->device->report_enum[report->type].numbered + 2;
}
static void i2c_hid_init_report(struct hid_report *report, u8 *buffer,
size_t bufsize)
{
struct hid_device *hid = report->device;
struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client);
unsigned int size, ret_size;
size = i2c_hid_get_report_length(report);
if (i2c_hid_get_report(client,
report->type == HID_FEATURE_REPORT ? 0x03 : 0x01,
report->id, buffer, size))
return;
i2c_hid_dbg(ihid, "report (len=%d): %*ph\n", size, size, buffer);
ret_size = buffer[0] | (buffer[1] << 8);
if (ret_size != size) {
dev_err(&client->dev, "error in %s size:%d / ret_size:%d\n",
__func__, size, ret_size);
return;
}
/* hid->driver_lock is held as we are in probe function,
* we just need to setup the input fields, so using
* hid_report_raw_event is safe. */
hid_report_raw_event(hid, report->type, buffer + 2, size - 2, 1);
}
/*
* Initialize all reports
*/
static void i2c_hid_init_reports(struct hid_device *hid)
{
struct hid_report *report;
struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client);
u8 *inbuf = kzalloc(ihid->bufsize, GFP_KERNEL);
if (!inbuf) {
dev_err(&client->dev, "can not retrieve initial reports\n");
return;
}
/*
* The device must be powered on while we fetch initial reports
* from it.
*/
pm_runtime_get_sync(&client->dev);
list_for_each_entry(report,
&hid->report_enum[HID_FEATURE_REPORT].report_list, list)
i2c_hid_init_report(report, inbuf, ihid->bufsize);
pm_runtime_put(&client->dev);
kfree(inbuf);
}
/*
* Traverse the supplied list of reports and find the longest
*/
......@@ -789,9 +729,6 @@ static int i2c_hid_start(struct hid_device *hid)
return ret;
}
if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
i2c_hid_init_reports(hid);
return 0;
}
......@@ -994,6 +931,11 @@ static int i2c_hid_of_probe(struct i2c_client *client,
}
pdata->hid_descriptor_address = val;
ret = of_property_read_u32(dev->of_node, "post-power-on-delay-ms",
&val);
if (!ret)
pdata->post_power_delay_ms = val;
return 0;
}
......@@ -1053,6 +995,24 @@ static int i2c_hid_probe(struct i2c_client *client,
ihid->pdata = *platform_data;
}
ihid->pdata.supply = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(ihid->pdata.supply)) {
ret = PTR_ERR(ihid->pdata.supply);
if (ret != -EPROBE_DEFER)
dev_err(&client->dev, "Failed to get regulator: %d\n",
ret);
goto err;
}
ret = regulator_enable(ihid->pdata.supply);
if (ret < 0) {
dev_err(&client->dev, "Failed to enable regulator: %d\n",
ret);
goto err;
}
if (ihid->pdata.post_power_delay_ms)
msleep(ihid->pdata.post_power_delay_ms);
i2c_set_clientdata(client, ihid);
ihid->client = client;
......@@ -1068,7 +1028,7 @@ static int i2c_hid_probe(struct i2c_client *client,
* real computation later. */
ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE);
if (ret < 0)
goto err;
goto err_regulator;
pm_runtime_get_noresume(&client->dev);
pm_runtime_set_active(&client->dev);
......@@ -1125,6 +1085,9 @@ static int i2c_hid_probe(struct i2c_client *client,
pm_runtime_put_noidle(&client->dev);
pm_runtime_disable(&client->dev);
err_regulator:
regulator_disable(ihid->pdata.supply);
err:
i2c_hid_free_buffers(ihid);
kfree(ihid);
......@@ -1149,6 +1112,8 @@ static int i2c_hid_remove(struct i2c_client *client)
if (ihid->bufsize)
i2c_hid_free_buffers(ihid);
regulator_disable(ihid->pdata.supply);
kfree(ihid);
return 0;
......@@ -1199,6 +1164,10 @@ static int i2c_hid_suspend(struct device *dev)
else
hid_warn(hid, "Failed to enable irq wake: %d\n",
wake_status);
} else {
ret = regulator_disable(ihid->pdata.supply);
if (ret < 0)
hid_warn(hid, "Failed to disable supply: %d\n", ret);
}
return 0;
......@@ -1212,7 +1181,13 @@ static int i2c_hid_resume(struct device *dev)
struct hid_device *hid = ihid->hid;
int wake_status;
if (device_may_wakeup(&client->dev) && ihid->irq_wake_enabled) {
if (!device_may_wakeup(&client->dev)) {
ret = regulator_enable(ihid->pdata.supply);
if (ret < 0)
hid_warn(hid, "Failed to enable supply: %d\n", ret);
if (ihid->pdata.post_power_delay_ms)
msleep(ihid->pdata.post_power_delay_ms);
} else if (ihid->irq_wake_enabled) {
wake_status = disable_irq_wake(client->irq);
if (!wake_status)
ihid->irq_wake_enabled = false;
......
......@@ -52,6 +52,10 @@ static unsigned int hid_mousepoll_interval;
module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
static unsigned int hid_jspoll_interval;
module_param_named(jspoll, hid_jspoll_interval, uint, 0644);
MODULE_PARM_DESC(jspoll, "Polling interval of joysticks");
static unsigned int ignoreled;
module_param_named(ignoreled, ignoreled, uint, 0644);
MODULE_PARM_DESC(ignoreled, "Autosuspend with active leds");
......@@ -753,11 +757,9 @@ void usbhid_init_reports(struct hid_device *hid)
struct hid_report_enum *report_enum;
int err, ret;
if (!(hid->quirks & HID_QUIRK_NO_INIT_INPUT_REPORTS)) {
report_enum = &hid->report_enum[HID_INPUT_REPORT];
list_for_each_entry(report, &report_enum->report_list, list)
usbhid_submit_report(hid, report, USB_DIR_IN);
}
report_enum = &hid->report_enum[HID_INPUT_REPORT];
list_for_each_entry(report, &report_enum->report_list, list)
usbhid_submit_report(hid, report, USB_DIR_IN);
report_enum = &hid->report_enum[HID_FEATURE_REPORT];
list_for_each_entry(report, &report_enum->report_list, list)
......@@ -1004,10 +1006,9 @@ static int usbhid_parse(struct hid_device *hid)
return -EINVAL;
}
if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
dbg_hid("couldn't allocate rdesc memory\n");
rdesc = kmalloc(rsize, GFP_KERNEL);
if (!rdesc)
return -ENOMEM;
}
hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
......@@ -1077,13 +1078,21 @@ static int usbhid_start(struct hid_device *hid)
if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL &&
dev->speed == USB_SPEED_HIGH) {
interval = fls(endpoint->bInterval*8);
printk(KERN_INFO "%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
hid->name, endpoint->bInterval, interval);
pr_info("%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
hid->name, endpoint->bInterval, interval);
}
/* Change the polling interval of mice. */
if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
interval = hid_mousepoll_interval;
/* Change the polling interval of mice and joysticks. */
switch (hid->collection->usage) {
case HID_GD_MOUSE:
if (hid_mousepoll_interval > 0)
interval = hid_mousepoll_interval;
break;
case HID_GD_JOYSTICK:
if (hid_jspoll_interval > 0)
interval = hid_jspoll_interval;
break;
}
ret = -ENOMEM;
if (usb_endpoint_dir_in(endpoint)) {
......@@ -1120,9 +1129,6 @@ static int usbhid_start(struct hid_device *hid)
usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
usbhid->urbctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
usbhid_init_reports(hid);
set_bit(HID_STARTED, &usbhid->iofl);
if (hid->quirks & HID_QUIRK_ALWAYS_POLL) {
......@@ -1456,10 +1462,9 @@ static int hid_post_reset(struct usb_interface *intf)
* the size of the HID report descriptor has not changed.
*/
rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL);
if (!rdesc) {
dbg_hid("couldn't allocate rdesc memory (post_reset)\n");
if (!rdesc)
return -ENOMEM;
}
status = hid_get_class_descriptor(dev,
interface->desc.bInterfaceNumber,
HID_DT_REPORT, rdesc, hid->dev_rsize);
......@@ -1637,7 +1642,7 @@ static int __init hid_init(void)
retval = usb_register(&hid_driver);
if (retval)
goto usb_register_fail;
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
pr_info(KBUILD_MODNAME ": " DRIVER_DESC "\n");
return 0;
usb_register_fail:
......
......@@ -65,6 +65,7 @@ static const struct hid_blacklist {
{ 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_ATEN, USB_DEVICE_ID_ATEN_CS692, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS1758, 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 },
......@@ -161,10 +162,11 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS },
{ 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_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096, HID_QUIRK_NO_INIT_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 },
{ USB_VENDOR_ID_INNOMEDIA, USB_DEVICE_ID_INNEX_GENESIS_ATARI, HID_QUIRK_MULTI_INPUT },
{ 0, 0 }
};
......@@ -240,10 +242,8 @@ static int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct,
}
q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL);
if (!q_new) {
dbg_hid("Could not allocate quirks_list_struct\n");
if (!q_new)
return -ENOMEM;
}
q_new->hid_bl_item.idVendor = idVendor;
q_new->hid_bl_item.idProduct = idProduct;
......@@ -309,10 +309,9 @@ int usbhid_quirks_init(char **quirks_param)
&idVendor, &idProduct, &quirks);
if (m != 3 ||
usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {
printk(KERN_WARNING
"Could not parse HID quirk module param %s\n",
quirks_param[n]);
usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {
pr_warn("Could not parse HID quirk module param %s\n",
quirks_param[n]);
}
}
......
......@@ -47,16 +47,6 @@
#endif
#define HIDDEV_BUFFER_SIZE 2048
struct hiddev {
int exist;
int open;
struct mutex existancelock;
wait_queue_head_t wait;
struct hid_device *hid;
struct list_head list;
spinlock_t list_lock;
};
struct hiddev_list {
struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
int head;
......@@ -690,6 +680,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case HIDIOCINITREPORT:
usbhid_init_reports(hid);
hiddev->initialized = true;
r = 0;
break;
......@@ -791,6 +782,10 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case HIDIOCGUSAGES:
case HIDIOCSUSAGES:
case HIDIOCGCOLLECTIONINDEX:
if (!hiddev->initialized) {
usbhid_init_reports(hid);
hiddev->initialized = true;
}
r = hiddev_ioctl_usage(hiddev, cmd, user_arg);
break;
......@@ -911,6 +906,15 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
kfree(hiddev);
return -1;
}
/*
* If HID_QUIRK_NO_INIT_REPORTS is set, make sure we don't initialize
* the reports.
*/
hiddev->initialized = hid->quirks & HID_QUIRK_NO_INIT_REPORTS;
hiddev->minor = usbhid->intf->minor;
return 0;
}
......
......@@ -110,6 +110,7 @@ enum wacom_worker {
WACOM_WORKER_WIRELESS,
WACOM_WORKER_BATTERY,
WACOM_WORKER_REMOTE,
WACOM_WORKER_MODE_CHANGE,
};
struct wacom;
......@@ -167,6 +168,7 @@ struct wacom {
struct work_struct remote_work;
struct delayed_work init_work;
struct wacom_remote *remote;
struct work_struct mode_change_work;
bool generic_has_leds;
struct wacom_leds {
struct wacom_group_leds *groups;
......@@ -196,6 +198,9 @@ static inline void wacom_schedule_work(struct wacom_wac *wacom_wac,
case WACOM_WORKER_REMOTE:
schedule_work(&wacom->remote_work);
break;
case WACOM_WORKER_MODE_CHANGE:
schedule_work(&wacom->mode_change_work);
break;
}
}
......
......@@ -325,6 +325,13 @@ static void wacom_post_parse_hid(struct hid_device *hdev,
if (features->type == HID_GENERIC) {
/* Any last-minute generic device setup */
if (wacom_wac->has_mode_change) {
if (wacom_wac->is_direct_mode)
features->device_type |= WACOM_DEVICETYPE_DIRECT;
else
features->device_type &= ~WACOM_DEVICETYPE_DIRECT;
}
if (features->touch_max > 1) {
if (features->device_type & WACOM_DEVICETYPE_DIRECT)
input_mt_init_slots(wacom_wac->touch_input,
......@@ -2093,8 +2100,10 @@ static void wacom_set_shared_values(struct wacom_wac *wacom_wac)
wacom_wac->shared->touch_input = wacom_wac->touch_input;
}
if (wacom_wac->has_mute_touch_switch)
if (wacom_wac->has_mute_touch_switch) {
wacom_wac->shared->has_mute_touch_switch = true;
wacom_wac->shared->is_touch_on = true;
}
if (wacom_wac->shared->has_mute_touch_switch &&
wacom_wac->shared->touch_input) {
......@@ -2490,6 +2499,46 @@ static void wacom_remote_work(struct work_struct *work)
}
}
static void wacom_mode_change_work(struct work_struct *work)
{
struct wacom *wacom = container_of(work, struct wacom, mode_change_work);
struct wacom_shared *shared = wacom->wacom_wac.shared;
struct wacom *wacom1 = NULL;
struct wacom *wacom2 = NULL;
bool is_direct = wacom->wacom_wac.is_direct_mode;
int error = 0;
if (shared->pen) {
wacom1 = hid_get_drvdata(shared->pen);
wacom_release_resources(wacom1);
hid_hw_stop(wacom1->hdev);
wacom1->wacom_wac.has_mode_change = true;
wacom1->wacom_wac.is_direct_mode = is_direct;
}
if (shared->touch) {
wacom2 = hid_get_drvdata(shared->touch);
wacom_release_resources(wacom2);
hid_hw_stop(wacom2->hdev);
wacom2->wacom_wac.has_mode_change = true;
wacom2->wacom_wac.is_direct_mode = is_direct;
}
if (wacom1) {
error = wacom_parse_and_register(wacom1, false);
if (error)
return;
}
if (wacom2) {
error = wacom_parse_and_register(wacom2, false);
if (error)
return;
}
return;
}
static int wacom_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
......@@ -2534,6 +2583,7 @@ static int wacom_probe(struct hid_device *hdev,
INIT_WORK(&wacom->wireless_work, wacom_wireless_work);
INIT_WORK(&wacom->battery_work, wacom_battery_work);
INIT_WORK(&wacom->remote_work, wacom_remote_work);
INIT_WORK(&wacom->mode_change_work, wacom_mode_change_work);
/* ask for the report descriptor to be loaded by HID */
error = hid_parse(hdev);
......@@ -2576,6 +2626,7 @@ static void wacom_remove(struct hid_device *hdev)
cancel_work_sync(&wacom->wireless_work);
cancel_work_sync(&wacom->battery_work);
cancel_work_sync(&wacom->remote_work);
cancel_work_sync(&wacom->mode_change_work);
if (hdev->bus == BUS_BLUETOOTH)
device_remove_file(&hdev->dev, &dev_attr_speed);
......
......@@ -773,131 +773,6 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
return 0;
}
static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len)
{
unsigned char *data = wacom_wac->data;
struct input_dev *input;
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
struct wacom_remote *remote = wacom->remote;
int bat_charging, bat_percent, touch_ring_mode;
__u32 serial;
int i, index = -1;
unsigned long flags;
if (data[0] != WACOM_REPORT_REMOTE) {
hid_dbg(wacom->hdev, "%s: received unknown report #%d",
__func__, data[0]);
return 0;
}
serial = data[3] + (data[4] << 8) + (data[5] << 16);
wacom_wac->id[0] = PAD_DEVICE_ID;
spin_lock_irqsave(&remote->remote_lock, flags);
for (i = 0; i < WACOM_MAX_REMOTES; i++) {
if (remote->remotes[i].serial == serial) {
index = i;
break;
}
}
if (index < 0 || !remote->remotes[index].registered)
goto out;
input = remote->remotes[index].input;
input_report_key(input, BTN_0, (data[9] & 0x01));
input_report_key(input, BTN_1, (data[9] & 0x02));
input_report_key(input, BTN_2, (data[9] & 0x04));
input_report_key(input, BTN_3, (data[9] & 0x08));
input_report_key(input, BTN_4, (data[9] & 0x10));
input_report_key(input, BTN_5, (data[9] & 0x20));
input_report_key(input, BTN_6, (data[9] & 0x40));
input_report_key(input, BTN_7, (data[9] & 0x80));
input_report_key(input, BTN_8, (data[10] & 0x01));
input_report_key(input, BTN_9, (data[10] & 0x02));
input_report_key(input, BTN_A, (data[10] & 0x04));
input_report_key(input, BTN_B, (data[10] & 0x08));
input_report_key(input, BTN_C, (data[10] & 0x10));
input_report_key(input, BTN_X, (data[10] & 0x20));
input_report_key(input, BTN_Y, (data[10] & 0x40));
input_report_key(input, BTN_Z, (data[10] & 0x80));
input_report_key(input, BTN_BASE, (data[11] & 0x01));
input_report_key(input, BTN_BASE2, (data[11] & 0x02));
if (data[12] & 0x80)
input_report_abs(input, ABS_WHEEL, (data[12] & 0x7f));
else
input_report_abs(input, ABS_WHEEL, 0);
bat_percent = data[7] & 0x7f;
bat_charging = !!(data[7] & 0x80);
if (data[9] | data[10] | (data[11] & 0x03) | data[12])
input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
else
input_report_abs(input, ABS_MISC, 0);
input_event(input, EV_MSC, MSC_SERIAL, serial);
input_sync(input);
/*Which mode select (LED light) is currently on?*/
touch_ring_mode = (data[11] & 0xC0) >> 6;
for (i = 0; i < WACOM_MAX_REMOTES; i++) {
if (remote->remotes[i].serial == serial)
wacom->led.groups[i].select = touch_ring_mode;
}
__wacom_notify_battery(&remote->remotes[index].battery, bat_percent,
bat_charging, 1, bat_charging);
out:
spin_unlock_irqrestore(&remote->remote_lock, flags);
return 0;
}
static void wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len)
{
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
unsigned char *data = wacom_wac->data;
struct wacom_remote *remote = wacom->remote;
struct wacom_remote_data remote_data;
unsigned long flags;
int i, ret;
if (data[0] != WACOM_REPORT_DEVICE_LIST)
return;
memset(&remote_data, 0, sizeof(struct wacom_remote_data));
for (i = 0; i < WACOM_MAX_REMOTES; i++) {
int j = i * 6;
int serial = (data[j+6] << 16) + (data[j+5] << 8) + data[j+4];
bool connected = data[j+2];
remote_data.remote[i].serial = serial;
remote_data.remote[i].connected = connected;
}
spin_lock_irqsave(&remote->remote_lock, flags);
ret = kfifo_in(&remote->remote_fifo, &remote_data, sizeof(remote_data));
if (ret != sizeof(remote_data)) {
spin_unlock_irqrestore(&remote->remote_lock, flags);
hid_err(wacom->hdev, "Can't queue Remote status event.\n");
return;
}
spin_unlock_irqrestore(&remote->remote_lock, flags);
wacom_schedule_work(wacom_wac, WACOM_WORKER_REMOTE);
}
static inline bool report_touch_events(struct wacom_wac *wacom)
{
return (touch_arbitration ? !wacom->shared->stylus_in_proximity : 1);
......@@ -1116,6 +991,131 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
return 0;
}
static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len)
{
unsigned char *data = wacom_wac->data;
struct input_dev *input;
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
struct wacom_remote *remote = wacom->remote;
int bat_charging, bat_percent, touch_ring_mode;
__u32 serial;
int i, index = -1;
unsigned long flags;
if (data[0] != WACOM_REPORT_REMOTE) {
hid_dbg(wacom->hdev, "%s: received unknown report #%d",
__func__, data[0]);
return 0;
}
serial = data[3] + (data[4] << 8) + (data[5] << 16);
wacom_wac->id[0] = PAD_DEVICE_ID;
spin_lock_irqsave(&remote->remote_lock, flags);
for (i = 0; i < WACOM_MAX_REMOTES; i++) {
if (remote->remotes[i].serial == serial) {
index = i;
break;
}
}
if (index < 0 || !remote->remotes[index].registered)
goto out;
input = remote->remotes[index].input;
input_report_key(input, BTN_0, (data[9] & 0x01));
input_report_key(input, BTN_1, (data[9] & 0x02));
input_report_key(input, BTN_2, (data[9] & 0x04));
input_report_key(input, BTN_3, (data[9] & 0x08));
input_report_key(input, BTN_4, (data[9] & 0x10));
input_report_key(input, BTN_5, (data[9] & 0x20));
input_report_key(input, BTN_6, (data[9] & 0x40));
input_report_key(input, BTN_7, (data[9] & 0x80));
input_report_key(input, BTN_8, (data[10] & 0x01));
input_report_key(input, BTN_9, (data[10] & 0x02));
input_report_key(input, BTN_A, (data[10] & 0x04));
input_report_key(input, BTN_B, (data[10] & 0x08));
input_report_key(input, BTN_C, (data[10] & 0x10));
input_report_key(input, BTN_X, (data[10] & 0x20));
input_report_key(input, BTN_Y, (data[10] & 0x40));
input_report_key(input, BTN_Z, (data[10] & 0x80));
input_report_key(input, BTN_BASE, (data[11] & 0x01));
input_report_key(input, BTN_BASE2, (data[11] & 0x02));
if (data[12] & 0x80)
input_report_abs(input, ABS_WHEEL, (data[12] & 0x7f));
else
input_report_abs(input, ABS_WHEEL, 0);
bat_percent = data[7] & 0x7f;
bat_charging = !!(data[7] & 0x80);
if (data[9] | data[10] | (data[11] & 0x03) | data[12])
input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
else
input_report_abs(input, ABS_MISC, 0);
input_event(input, EV_MSC, MSC_SERIAL, serial);
input_sync(input);
/*Which mode select (LED light) is currently on?*/
touch_ring_mode = (data[11] & 0xC0) >> 6;
for (i = 0; i < WACOM_MAX_REMOTES; i++) {
if (remote->remotes[i].serial == serial)
wacom->led.groups[i].select = touch_ring_mode;
}
__wacom_notify_battery(&remote->remotes[index].battery, bat_percent,
bat_charging, 1, bat_charging);
out:
spin_unlock_irqrestore(&remote->remote_lock, flags);
return 0;
}
static void wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len)
{
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
unsigned char *data = wacom_wac->data;
struct wacom_remote *remote = wacom->remote;
struct wacom_remote_data remote_data;
unsigned long flags;
int i, ret;
if (data[0] != WACOM_REPORT_DEVICE_LIST)
return;
memset(&remote_data, 0, sizeof(struct wacom_remote_data));
for (i = 0; i < WACOM_MAX_REMOTES; i++) {
int j = i * 6;
int serial = (data[j+6] << 16) + (data[j+5] << 8) + data[j+4];
bool connected = data[j+2];
remote_data.remote[i].serial = serial;
remote_data.remote[i].connected = connected;
}
spin_lock_irqsave(&remote->remote_lock, flags);
ret = kfifo_in(&remote->remote_fifo, &remote_data, sizeof(remote_data));
if (ret != sizeof(remote_data)) {
spin_unlock_irqrestore(&remote->remote_lock, flags);
hid_err(wacom->hdev, "Can't queue Remote status event.\n");
return;
}
spin_unlock_irqrestore(&remote->remote_lock, flags);
wacom_schedule_work(wacom_wac, WACOM_WORKER_REMOTE);
}
static int int_dist(int x1, int y1, int x2, int y2)
{
int x = x2 - x1;
......@@ -1739,6 +1739,7 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
case WACOM_HID_WD_TOUCHONOFF:
case WACOM_HID_WD_MUTE_DEVICE:
/*
* This usage, which is used to mute touch events, comes
* from the pad packet, but is reported on the touch
......@@ -1768,6 +1769,26 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
case WACOM_HID_WD_BUTTONCONFIG:
wacom_map_usage(input, usage, field, EV_KEY, KEY_BUTTONCONFIG, 0);
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
case WACOM_HID_WD_ONSCREEN_KEYBOARD:
wacom_map_usage(input, usage, field, EV_KEY, KEY_ONSCREEN_KEYBOARD, 0);
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
case WACOM_HID_WD_CONTROLPANEL:
wacom_map_usage(input, usage, field, EV_KEY, KEY_CONTROLPANEL, 0);
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
case WACOM_HID_WD_MODE_CHANGE:
/* do not overwrite previous data */
if (!wacom_wac->has_mode_change) {
wacom_wac->has_mode_change = true;
wacom_wac->is_direct_mode = true;
}
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
}
switch (equivalent_usage & 0xfffffff0) {
......@@ -1811,12 +1832,13 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
struct wacom_features *features = &wacom_wac->features;
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
int i;
bool is_touch_on = value;
/*
* Avoid reporting this event and setting inrange_state if this usage
* hasn't been mapped.
*/
if (!usage->type)
if (!usage->type && equivalent_usage != WACOM_HID_WD_MODE_CHANGE)
return;
if (wacom_equivalent_usage(field->physical) == HID_DG_TABLETFUNCTIONKEY) {
......@@ -1830,14 +1852,28 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
input_event(input, usage->type, usage->code, 0);
break;
case WACOM_HID_WD_MUTE_DEVICE:
if (wacom_wac->shared->touch_input && value) {
wacom_wac->shared->is_touch_on = !wacom_wac->shared->is_touch_on;
is_touch_on = wacom_wac->shared->is_touch_on;
}
/* fall through*/
case WACOM_HID_WD_TOUCHONOFF:
if (wacom_wac->shared->touch_input) {
input_report_switch(wacom_wac->shared->touch_input,
SW_MUTE_DEVICE, !value);
SW_MUTE_DEVICE, !is_touch_on);
input_sync(wacom_wac->shared->touch_input);
}
break;
case WACOM_HID_WD_MODE_CHANGE:
if (wacom_wac->is_direct_mode != value) {
wacom_wac->is_direct_mode = value;
wacom_schedule_work(&wacom->wacom_wac, WACOM_WORKER_MODE_CHANGE);
}
break;
case WACOM_HID_WD_BUTTONCENTER:
for (i = 0; i < wacom->led.count; i++)
wacom_update_led(wacom, features->numbered_buttons,
......@@ -1845,6 +1881,8 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
/* fall through*/
default:
input_event(input, usage->type, usage->code, value);
if (value)
wacom_wac->hid_data.pad_input_event_flag = true;
break;
}
}
......@@ -1885,9 +1923,12 @@ static void wacom_wac_pad_report(struct hid_device *hdev,
bool active = wacom_wac->hid_data.inrange_state != 0;
/* report prox for expresskey events */
if (wacom_equivalent_usage(report->field[0]->physical) == HID_DG_TABLETFUNCTIONKEY) {
if ((wacom_equivalent_usage(report->field[0]->physical) == HID_DG_TABLETFUNCTIONKEY) &&
wacom_wac->hid_data.pad_input_event_flag) {
input_event(input, EV_ABS, ABS_MISC, active ? PAD_DEVICE_ID : 0);
input_sync(input);
if (!active)
wacom_wac->hid_data.pad_input_event_flag = false;
}
}
......@@ -2197,6 +2238,13 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
bool prox = hid_data->tipswitch &&
report_touch_events(wacom_wac);
if (wacom_wac->shared->has_mute_touch_switch &&
!wacom_wac->shared->is_touch_on) {
if (!wacom_wac->shared->touch_down)
return;
prox = 0;
}
wacom_wac->hid_data.num_received++;
if (wacom_wac->hid_data.num_received > wacom_wac->hid_data.num_expected)
return;
......@@ -4127,7 +4175,7 @@ static const struct wacom_features wacom_features_0x300 =
BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x301 =
{ "Wacom Bamboo One M", 21648, 13530, 1023, 31,
BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x302 =
{ "Wacom Intuos PT S", 15200, 9500, 1023, 31,
INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16,
......
......@@ -120,6 +120,11 @@
#define WACOM_HID_WD_BATTERY_LEVEL (WACOM_HID_UP_WACOMDIGITIZER | 0x043b)
#define WACOM_HID_WD_EXPRESSKEY00 (WACOM_HID_UP_WACOMDIGITIZER | 0x0910)
#define WACOM_HID_WD_EXPRESSKEYCAP00 (WACOM_HID_UP_WACOMDIGITIZER | 0x0950)
#define WACOM_HID_WD_MODE_CHANGE (WACOM_HID_UP_WACOMDIGITIZER | 0x0980)
#define WACOM_HID_WD_MUTE_DEVICE (WACOM_HID_UP_WACOMDIGITIZER | 0x0981)
#define WACOM_HID_WD_CONTROLPANEL (WACOM_HID_UP_WACOMDIGITIZER | 0x0982)
#define WACOM_HID_WD_ONSCREEN_KEYBOARD (WACOM_HID_UP_WACOMDIGITIZER | 0x0983)
#define WACOM_HID_WD_BUTTONCONFIG (WACOM_HID_UP_WACOMDIGITIZER | 0x0986)
#define WACOM_HID_WD_BUTTONHOME (WACOM_HID_UP_WACOMDIGITIZER | 0x0990)
#define WACOM_HID_WD_BUTTONUP (WACOM_HID_UP_WACOMDIGITIZER | 0x0991)
#define WACOM_HID_WD_BUTTONDOWN (WACOM_HID_UP_WACOMDIGITIZER | 0x0992)
......@@ -270,6 +275,7 @@ struct wacom_shared {
struct hid_device *pen;
struct hid_device *touch;
bool has_mute_touch_switch;
bool is_touch_on;
};
struct hid_data {
......@@ -295,6 +301,7 @@ struct hid_data {
int bat_charging;
int bat_connected;
int ps_connected;
bool pad_input_event_flag;
};
struct wacom_remote_data {
......@@ -327,6 +334,9 @@ struct wacom_wac {
int mode_value;
struct hid_data hid_data;
bool has_mute_touch_switch;
bool has_mode_change;
bool is_direct_mode;
};
#endif
......@@ -268,6 +268,8 @@ struct hid_item {
#define HID_CP_APPLICATIONLAUNCHBUTTONS 0x000c0180
#define HID_CP_GENERICGUIAPPLICATIONCONTROLS 0x000c0200
#define HID_DG_DEVICECONFIG 0x000d000e
#define HID_DG_DEVICESETTINGS 0x000d0023
#define HID_DG_CONFIDENCE 0x000d0047
#define HID_DG_WIDTH 0x000d0048
#define HID_DG_HEIGHT 0x000d0049
......@@ -322,7 +324,7 @@ struct hid_item {
#define HID_QUIRK_MULTI_INPUT 0x00000040
#define HID_QUIRK_HIDINPUT_FORCE 0x00000080
#define HID_QUIRK_NO_EMPTY_INPUT 0x00000100
#define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200
/* 0x00000200 reserved for backward compatibility, was NO_INIT_INPUT_REPORTS */
#define HID_QUIRK_ALWAYS_POLL 0x00000400
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
#define HID_QUIRK_SKIP_OUTPUT_REPORT_ID 0x00020000
......@@ -541,7 +543,6 @@ struct hid_device { /* device report descriptor */
struct list_head inputs; /* The list of inputs */
void *hiddev; /* The hiddev structure */
void *hidraw;
int minor; /* Hiddev minor number */
int open; /* is the device open by anyone? */
char name[128]; /* Device name */
......
......@@ -32,6 +32,18 @@
* In-kernel definitions.
*/
struct hiddev {
int minor;
int exist;
int open;
struct mutex existancelock;
wait_queue_head_t wait;
struct hid_device *hid;
struct list_head list;
spinlock_t list_lock;
bool initialized;
};
struct hid_device;
struct hid_usage;
struct hid_field;
......
......@@ -14,9 +14,13 @@
#include <linux/types.h>
struct regulator;
/**
* struct i2chid_platform_data - used by hid over i2c implementation.
* @hid_descriptor_address: i2c register where the HID descriptor is stored.
* @supply: regulator for powering on the device.
* @post_power_delay_ms: delay after powering on before device is usable.
*
* Note that it is the responsibility of the platform driver (or the acpi 5.0
* driver, or the flattened device tree) to setup the irq related to the gpio in
......@@ -31,6 +35,8 @@
*/
struct i2c_hid_platform_data {
u16 hid_descriptor_address;
struct regulator *supply;
int post_power_delay_ms;
};
#endif /* __LINUX_I2C_HID_H */
......@@ -641,6 +641,7 @@
* e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.)
*/
#define KEY_DATA 0x277
#define KEY_ONSCREEN_KEYBOARD 0x278
#define BTN_TRIGGER_HAPPY 0x2c0
#define BTN_TRIGGER_HAPPY1 0x2c0
......
......@@ -61,9 +61,14 @@ struct input_id {
* Note that input core does not clamp reported values to the
* [minimum, maximum] limits, such task is left to userspace.
*
* Resolution for main axes (ABS_X, ABS_Y, ABS_Z) is reported in
* units per millimeter (units/mm), resolution for rotational axes
* (ABS_RX, ABS_RY, ABS_RZ) is reported in units per radian.
* The default resolution for main axes (ABS_X, ABS_Y, ABS_Z)
* is reported in units per millimeter (units/mm), resolution
* for rotational axes (ABS_RX, ABS_RY, ABS_RZ) is reported
* in units per radian.
* When INPUT_PROP_ACCELEROMETER is set the resolution changes.
* The main axes (ABS_X, ABS_Y, ABS_Z) are then reported in
* in units per g (units/g) and in units per degree per second
* (units/deg/s) for rotational axes (ABS_RX, ABS_RY, ABS_RZ).
*/
struct input_absinfo {
__s32 value;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment