Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID updates from Jiri Kosina: - touch_max detection improvements and quirk handling fixes in wacom driver from Jason Gerecke and Ping Cheng - Palm rejection from Dmitry Torokhov and _dial support from Benjamin Tissoires for hid-multitouch driver - Low voltage support for i2c-hid driver from Stephen Boyd - Guitar-Hero support from Nicolas Adenis-Lamarre - other assorted small fixes and device ID additions * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (40 commits) HID: intel_ish-hid: tx_buf memory leak on probe/remove HID: intel-ish-hid: Prevent loading of driver on Mehlow HID: cougar: Add support for the Cougar 500k Gaming Keyboard HID: cougar: make compare_device_paths reusable HID: intel-ish-hid: remove redundant variable num_frags HID: multitouch: handle palm for touchscreens HID: multitouch: touchscreens also use confidence reports HID: multitouch: report MT_TOOL_PALM for non-confident touches HID: microsoft: support the Surface Dial HID: core: do not upper bound the collection stack HID: input: enable Totem on the Dell Canvas 27 HID: multitouch: remove one copy of values HID: multitouch: ditch mt_report_id HID: multitouch: store a per application quirks value HID: multitouch: Store per collection multitouch data HID: multitouch: make sure the static list of class is not changed input: add MT_TOOL_DIAL HID: elan: Add support for touchpad on the Toshiba Click Mini L9W HID: elan: Add USB-id for HP x2 10-n000nd touchpad HID: elan: Add a flag for selecting if the touchpad has a LED ...
This commit is contained in:
commit
7a324b3f05
|
@ -25,7 +25,8 @@ device-specific compatible properties, which should be used in addition to the
|
||||||
|
|
||||||
- compatible:
|
- compatible:
|
||||||
* "wacom,w9013" (Wacom W9013 digitizer). Supports:
|
* "wacom,w9013" (Wacom W9013 digitizer). Supports:
|
||||||
- vdd-supply
|
- vdd-supply (3.3V)
|
||||||
|
- vddl-supply (1.8V)
|
||||||
- post-power-on-delay-ms
|
- post-power-on-delay-ms
|
||||||
|
|
||||||
- vdd-supply: phandle of the regulator that provides the supply voltage.
|
- vdd-supply: phandle of the regulator that provides the supply voltage.
|
||||||
|
|
|
@ -310,12 +310,12 @@ ABS_MT_TOOL_Y
|
||||||
ABS_MT_TOOL_TYPE
|
ABS_MT_TOOL_TYPE
|
||||||
The type of approaching tool. A lot of kernel drivers cannot distinguish
|
The type of approaching tool. A lot of kernel drivers cannot distinguish
|
||||||
between different tool types, such as a finger or a pen. In such cases, the
|
between different tool types, such as a finger or a pen. In such cases, the
|
||||||
event should be omitted. The protocol currently supports MT_TOOL_FINGER,
|
event should be omitted. The protocol currently mainly supports
|
||||||
MT_TOOL_PEN, and MT_TOOL_PALM [#f2]_. For type B devices, this event is
|
MT_TOOL_FINGER, MT_TOOL_PEN, and MT_TOOL_PALM [#f2]_.
|
||||||
handled by input core; drivers should instead use
|
For type B devices, this event is handled by input core; drivers should
|
||||||
input_mt_report_slot_state(). A contact's ABS_MT_TOOL_TYPE may change over
|
instead use input_mt_report_slot_state(). A contact's ABS_MT_TOOL_TYPE may
|
||||||
time while still touching the device, because the firmware may not be able
|
change over time while still touching the device, because the firmware may
|
||||||
to determine which tool is being used when it first appears.
|
not be able to determine which tool is being used when it first appears.
|
||||||
|
|
||||||
ABS_MT_BLOB_ID
|
ABS_MT_BLOB_ID
|
||||||
The BLOB_ID groups several packets together into one arbitrarily shaped
|
The BLOB_ID groups several packets together into one arbitrarily shaped
|
||||||
|
|
|
@ -207,6 +207,16 @@ config HID_CORSAIR
|
||||||
- Vengeance K90
|
- Vengeance K90
|
||||||
- Scimitar PRO RGB
|
- Scimitar PRO RGB
|
||||||
|
|
||||||
|
config HID_COUGAR
|
||||||
|
tristate "Cougar devices"
|
||||||
|
depends on HID
|
||||||
|
help
|
||||||
|
Support for Cougar devices that are not fully compliant with the
|
||||||
|
HID standard.
|
||||||
|
|
||||||
|
Supported devices:
|
||||||
|
- Cougar 500k Gaming Keyboard
|
||||||
|
|
||||||
config HID_PRODIKEYS
|
config HID_PRODIKEYS
|
||||||
tristate "Prodikeys PC-MIDI Keyboard support"
|
tristate "Prodikeys PC-MIDI Keyboard support"
|
||||||
depends on HID && SND
|
depends on HID && SND
|
||||||
|
|
|
@ -35,6 +35,7 @@ obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
|
||||||
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
|
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
|
||||||
obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o
|
obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o
|
||||||
obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o
|
obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o
|
||||||
|
obj-$(CONFIG_HID_COUGAR) += hid-cougar.o
|
||||||
obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
|
obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
|
||||||
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
|
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
|
||||||
obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o
|
obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o
|
||||||
|
|
|
@ -128,9 +128,19 @@ static int open_collection(struct hid_parser *parser, unsigned type)
|
||||||
|
|
||||||
usage = parser->local.usage[0];
|
usage = parser->local.usage[0];
|
||||||
|
|
||||||
if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
|
if (parser->collection_stack_ptr == parser->collection_stack_size) {
|
||||||
hid_err(parser->device, "collection stack overflow\n");
|
unsigned int *collection_stack;
|
||||||
return -EINVAL;
|
unsigned int new_size = parser->collection_stack_size +
|
||||||
|
HID_COLLECTION_STACK_SIZE;
|
||||||
|
|
||||||
|
collection_stack = krealloc(parser->collection_stack,
|
||||||
|
new_size * sizeof(unsigned int),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!collection_stack)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
parser->collection_stack = collection_stack;
|
||||||
|
parser->collection_stack_size = new_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parser->device->maxcollection == parser->device->collection_size) {
|
if (parser->device->maxcollection == parser->device->collection_size) {
|
||||||
|
@ -840,6 +850,7 @@ static int hid_scan_report(struct hid_device *hid)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kfree(parser->collection_stack);
|
||||||
vfree(parser);
|
vfree(parser);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1939,6 +1950,29 @@ static int hid_bus_match(struct device *dev, struct device_driver *drv)
|
||||||
return hid_match_device(hdev, hdrv) != NULL;
|
return hid_match_device(hdev, hdrv) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hid_compare_device_paths - check if both devices share the same path
|
||||||
|
* @hdev_a: hid device
|
||||||
|
* @hdev_b: hid device
|
||||||
|
* @separator: char to use as separator
|
||||||
|
*
|
||||||
|
* Check if two devices share the same path up to the last occurrence of
|
||||||
|
* the separator char. Both paths must exist (i.e., zero-length paths
|
||||||
|
* don't match).
|
||||||
|
*/
|
||||||
|
bool hid_compare_device_paths(struct hid_device *hdev_a,
|
||||||
|
struct hid_device *hdev_b, char separator)
|
||||||
|
{
|
||||||
|
int n1 = strrchr(hdev_a->phys, separator) - hdev_a->phys;
|
||||||
|
int n2 = strrchr(hdev_b->phys, separator) - hdev_b->phys;
|
||||||
|
|
||||||
|
if (n1 != n2 || n1 <= 0 || n2 <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !strncmp(hdev_a->phys, hdev_b->phys, n1);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(hid_compare_device_paths);
|
||||||
|
|
||||||
static int hid_device_probe(struct device *dev)
|
static int hid_device_probe(struct device *dev)
|
||||||
{
|
{
|
||||||
struct hid_driver *hdrv = to_hid_driver(dev->driver);
|
struct hid_driver *hdrv = to_hid_driver(dev->driver);
|
||||||
|
|
|
@ -0,0 +1,312 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* HID driver for Cougar 500k Gaming Keyboard
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Daniel M. Lambea <dmlambea@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/hid.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
|
#include "hid-ids.h"
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Daniel M. Lambea <dmlambea@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("Cougar 500k Gaming Keyboard");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_INFO(key_mappings, "G1-G6 are mapped to F13-F18");
|
||||||
|
|
||||||
|
static int cougar_g6_is_space = 1;
|
||||||
|
module_param_named(g6_is_space, cougar_g6_is_space, int, 0600);
|
||||||
|
MODULE_PARM_DESC(g6_is_space,
|
||||||
|
"If set, G6 programmable key sends SPACE instead of F18 (0=off, 1=on) (default=1)");
|
||||||
|
|
||||||
|
|
||||||
|
#define COUGAR_VENDOR_USAGE 0xff00ff00
|
||||||
|
|
||||||
|
#define COUGAR_FIELD_CODE 1
|
||||||
|
#define COUGAR_FIELD_ACTION 2
|
||||||
|
|
||||||
|
#define COUGAR_KEY_G1 0x83
|
||||||
|
#define COUGAR_KEY_G2 0x84
|
||||||
|
#define COUGAR_KEY_G3 0x85
|
||||||
|
#define COUGAR_KEY_G4 0x86
|
||||||
|
#define COUGAR_KEY_G5 0x87
|
||||||
|
#define COUGAR_KEY_G6 0x78
|
||||||
|
#define COUGAR_KEY_FN 0x0d
|
||||||
|
#define COUGAR_KEY_MR 0x6f
|
||||||
|
#define COUGAR_KEY_M1 0x80
|
||||||
|
#define COUGAR_KEY_M2 0x81
|
||||||
|
#define COUGAR_KEY_M3 0x82
|
||||||
|
#define COUGAR_KEY_LEDS 0x67
|
||||||
|
#define COUGAR_KEY_LOCK 0x6e
|
||||||
|
|
||||||
|
|
||||||
|
/* Default key mappings. The special key COUGAR_KEY_G6 is defined first
|
||||||
|
* because it is more frequent to use the spacebar rather than any other
|
||||||
|
* special keys. Depending on the value of the parameter 'g6_is_space',
|
||||||
|
* the mapping will be updated in the probe function.
|
||||||
|
*/
|
||||||
|
static unsigned char cougar_mapping[][2] = {
|
||||||
|
{ COUGAR_KEY_G6, KEY_SPACE },
|
||||||
|
{ COUGAR_KEY_G1, KEY_F13 },
|
||||||
|
{ COUGAR_KEY_G2, KEY_F14 },
|
||||||
|
{ COUGAR_KEY_G3, KEY_F15 },
|
||||||
|
{ COUGAR_KEY_G4, KEY_F16 },
|
||||||
|
{ COUGAR_KEY_G5, KEY_F17 },
|
||||||
|
{ COUGAR_KEY_LOCK, KEY_SCREENLOCK },
|
||||||
|
/* The following keys are handled by the hardware itself, so no special
|
||||||
|
* treatment is required:
|
||||||
|
{ COUGAR_KEY_FN, KEY_RESERVED },
|
||||||
|
{ COUGAR_KEY_MR, KEY_RESERVED },
|
||||||
|
{ COUGAR_KEY_M1, KEY_RESERVED },
|
||||||
|
{ COUGAR_KEY_M2, KEY_RESERVED },
|
||||||
|
{ COUGAR_KEY_M3, KEY_RESERVED },
|
||||||
|
{ COUGAR_KEY_LEDS, KEY_RESERVED },
|
||||||
|
*/
|
||||||
|
{ 0, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cougar_shared {
|
||||||
|
struct list_head list;
|
||||||
|
struct kref kref;
|
||||||
|
bool enabled;
|
||||||
|
struct hid_device *dev;
|
||||||
|
struct input_dev *input;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cougar {
|
||||||
|
bool special_intf;
|
||||||
|
struct cougar_shared *shared;
|
||||||
|
};
|
||||||
|
|
||||||
|
static LIST_HEAD(cougar_udev_list);
|
||||||
|
static DEFINE_MUTEX(cougar_udev_list_lock);
|
||||||
|
|
||||||
|
static void cougar_fix_g6_mapping(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; cougar_mapping[i][0]; i++) {
|
||||||
|
if (cougar_mapping[i][0] == COUGAR_KEY_G6) {
|
||||||
|
cougar_mapping[i][1] =
|
||||||
|
cougar_g6_is_space ? KEY_SPACE : KEY_F18;
|
||||||
|
hid_info(hdev, "G6 mapped to %s\n",
|
||||||
|
cougar_g6_is_space ? "space" : "F18");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hid_warn(hdev, "no mapping defined for G6/spacebar");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constant-friendly rdesc fixup for mouse interface
|
||||||
|
*/
|
||||||
|
static __u8 *cougar_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||||
|
unsigned int *rsize)
|
||||||
|
{
|
||||||
|
if (rdesc[2] == 0x09 && rdesc[3] == 0x02 &&
|
||||||
|
(rdesc[115] | rdesc[116] << 8) >= HID_MAX_USAGES) {
|
||||||
|
hid_info(hdev,
|
||||||
|
"usage count exceeds max: fixing up report descriptor\n");
|
||||||
|
rdesc[115] = ((HID_MAX_USAGES-1) & 0xff);
|
||||||
|
rdesc[116] = ((HID_MAX_USAGES-1) >> 8);
|
||||||
|
}
|
||||||
|
return rdesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct cougar_shared *cougar_get_shared_data(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
struct cougar_shared *shared;
|
||||||
|
|
||||||
|
/* Try to find an already-probed interface from the same device */
|
||||||
|
list_for_each_entry(shared, &cougar_udev_list, list) {
|
||||||
|
if (hid_compare_device_paths(hdev, shared->dev, '/')) {
|
||||||
|
kref_get(&shared->kref);
|
||||||
|
return shared;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cougar_release_shared_data(struct kref *kref)
|
||||||
|
{
|
||||||
|
struct cougar_shared *shared = container_of(kref,
|
||||||
|
struct cougar_shared, kref);
|
||||||
|
|
||||||
|
mutex_lock(&cougar_udev_list_lock);
|
||||||
|
list_del(&shared->list);
|
||||||
|
mutex_unlock(&cougar_udev_list_lock);
|
||||||
|
|
||||||
|
kfree(shared);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cougar_remove_shared_data(void *resource)
|
||||||
|
{
|
||||||
|
struct cougar *cougar = resource;
|
||||||
|
|
||||||
|
if (cougar->shared) {
|
||||||
|
kref_put(&cougar->shared->kref, cougar_release_shared_data);
|
||||||
|
cougar->shared = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bind the device group's shared data to this cougar struct.
|
||||||
|
* If no shared data exists for this group, create and initialize it.
|
||||||
|
*/
|
||||||
|
static int cougar_bind_shared_data(struct hid_device *hdev, struct cougar *cougar)
|
||||||
|
{
|
||||||
|
struct cougar_shared *shared;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
mutex_lock(&cougar_udev_list_lock);
|
||||||
|
|
||||||
|
shared = cougar_get_shared_data(hdev);
|
||||||
|
if (!shared) {
|
||||||
|
shared = kzalloc(sizeof(*shared), GFP_KERNEL);
|
||||||
|
if (!shared) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
kref_init(&shared->kref);
|
||||||
|
shared->dev = hdev;
|
||||||
|
list_add_tail(&shared->list, &cougar_udev_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
cougar->shared = shared;
|
||||||
|
|
||||||
|
error = devm_add_action(&hdev->dev, cougar_remove_shared_data, cougar);
|
||||||
|
if (error) {
|
||||||
|
mutex_unlock(&cougar_udev_list_lock);
|
||||||
|
cougar_remove_shared_data(cougar);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&cougar_udev_list_lock);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cougar_probe(struct hid_device *hdev,
|
||||||
|
const struct hid_device_id *id)
|
||||||
|
{
|
||||||
|
struct cougar *cougar;
|
||||||
|
struct hid_input *next, *hidinput = NULL;
|
||||||
|
unsigned int connect_mask;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
cougar = devm_kzalloc(&hdev->dev, sizeof(*cougar), GFP_KERNEL);
|
||||||
|
if (!cougar)
|
||||||
|
return -ENOMEM;
|
||||||
|
hid_set_drvdata(hdev, cougar);
|
||||||
|
|
||||||
|
error = hid_parse(hdev);
|
||||||
|
if (error) {
|
||||||
|
hid_err(hdev, "parse failed\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdev->collection->usage == COUGAR_VENDOR_USAGE) {
|
||||||
|
cougar->special_intf = true;
|
||||||
|
connect_mask = HID_CONNECT_HIDRAW;
|
||||||
|
} else
|
||||||
|
connect_mask = HID_CONNECT_DEFAULT;
|
||||||
|
|
||||||
|
error = hid_hw_start(hdev, connect_mask);
|
||||||
|
if (error) {
|
||||||
|
hid_err(hdev, "hw start failed\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = cougar_bind_shared_data(hdev, cougar);
|
||||||
|
if (error)
|
||||||
|
goto fail_stop_and_cleanup;
|
||||||
|
|
||||||
|
/* The custom vendor interface will use the hid_input registered
|
||||||
|
* for the keyboard interface, in order to send translated key codes
|
||||||
|
* to it.
|
||||||
|
*/
|
||||||
|
if (hdev->collection->usage == HID_GD_KEYBOARD) {
|
||||||
|
cougar_fix_g6_mapping(hdev);
|
||||||
|
list_for_each_entry_safe(hidinput, next, &hdev->inputs, list) {
|
||||||
|
if (hidinput->registered && hidinput->input != NULL) {
|
||||||
|
cougar->shared->input = hidinput->input;
|
||||||
|
cougar->shared->enabled = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (hdev->collection->usage == COUGAR_VENDOR_USAGE) {
|
||||||
|
error = hid_hw_open(hdev);
|
||||||
|
if (error)
|
||||||
|
goto fail_stop_and_cleanup;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail_stop_and_cleanup:
|
||||||
|
hid_hw_stop(hdev);
|
||||||
|
fail:
|
||||||
|
hid_set_drvdata(hdev, NULL);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert events from vendor intf to input key events
|
||||||
|
*/
|
||||||
|
static int cougar_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||||
|
u8 *data, int size)
|
||||||
|
{
|
||||||
|
struct cougar *cougar;
|
||||||
|
unsigned char code, action;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
cougar = hid_get_drvdata(hdev);
|
||||||
|
if (!cougar->special_intf || !cougar->shared ||
|
||||||
|
!cougar->shared->input || !cougar->shared->enabled)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
code = data[COUGAR_FIELD_CODE];
|
||||||
|
action = data[COUGAR_FIELD_ACTION];
|
||||||
|
for (i = 0; cougar_mapping[i][0]; i++) {
|
||||||
|
if (code == cougar_mapping[i][0]) {
|
||||||
|
input_event(cougar->shared->input, EV_KEY,
|
||||||
|
cougar_mapping[i][1], action);
|
||||||
|
input_sync(cougar->shared->input);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hid_warn(hdev, "unmapped special key code %x: ignoring\n", code);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cougar_remove(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
struct cougar *cougar = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
|
if (cougar) {
|
||||||
|
/* Stop the vendor intf to process more events */
|
||||||
|
if (cougar->shared)
|
||||||
|
cougar->shared->enabled = false;
|
||||||
|
if (cougar->special_intf)
|
||||||
|
hid_hw_close(hdev);
|
||||||
|
}
|
||||||
|
hid_hw_stop(hdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct hid_device_id cougar_id_table[] = {
|
||||||
|
{ HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR,
|
||||||
|
USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(hid, cougar_id_table);
|
||||||
|
|
||||||
|
static struct hid_driver cougar_driver = {
|
||||||
|
.name = "cougar",
|
||||||
|
.id_table = cougar_id_table,
|
||||||
|
.report_fixup = cougar_report_fixup,
|
||||||
|
.probe = cougar_probe,
|
||||||
|
.remove = cougar_remove,
|
||||||
|
.raw_event = cougar_raw_event,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_hid_driver(cougar_driver);
|
|
@ -19,38 +19,49 @@
|
||||||
|
|
||||||
#include "hid-ids.h"
|
#include "hid-ids.h"
|
||||||
|
|
||||||
|
#define ELAN_MT_I2C 0x5d
|
||||||
#define ELAN_SINGLE_FINGER 0x81
|
#define ELAN_SINGLE_FINGER 0x81
|
||||||
#define ELAN_MT_FIRST_FINGER 0x82
|
#define ELAN_MT_FIRST_FINGER 0x82
|
||||||
#define ELAN_MT_SECOND_FINGER 0x83
|
#define ELAN_MT_SECOND_FINGER 0x83
|
||||||
#define ELAN_INPUT_REPORT_SIZE 8
|
#define ELAN_INPUT_REPORT_SIZE 8
|
||||||
|
#define ELAN_I2C_REPORT_SIZE 32
|
||||||
|
#define ELAN_FINGER_DATA_LEN 5
|
||||||
|
#define ELAN_MAX_FINGERS 5
|
||||||
|
#define ELAN_MAX_PRESSURE 255
|
||||||
|
#define ELAN_TP_USB_INTF 1
|
||||||
|
|
||||||
|
#define ELAN_FEATURE_REPORT 0x0d
|
||||||
|
#define ELAN_FEATURE_SIZE 5
|
||||||
|
#define ELAN_PARAM_MAX_X 6
|
||||||
|
#define ELAN_PARAM_MAX_Y 7
|
||||||
|
#define ELAN_PARAM_RES 8
|
||||||
|
|
||||||
#define ELAN_MUTE_LED_REPORT 0xBC
|
#define ELAN_MUTE_LED_REPORT 0xBC
|
||||||
#define ELAN_LED_REPORT_SIZE 8
|
#define ELAN_LED_REPORT_SIZE 8
|
||||||
|
|
||||||
struct elan_touchpad_settings {
|
#define ELAN_HAS_LED BIT(0)
|
||||||
u8 max_fingers;
|
|
||||||
u16 max_x;
|
|
||||||
u16 max_y;
|
|
||||||
u8 max_area_x;
|
|
||||||
u8 max_area_y;
|
|
||||||
u8 max_w;
|
|
||||||
int usb_bInterfaceNumber;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct elan_drvdata {
|
struct elan_drvdata {
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
u8 prev_report[ELAN_INPUT_REPORT_SIZE];
|
u8 prev_report[ELAN_INPUT_REPORT_SIZE];
|
||||||
struct led_classdev mute_led;
|
struct led_classdev mute_led;
|
||||||
u8 mute_led_state;
|
u8 mute_led_state;
|
||||||
struct elan_touchpad_settings *settings;
|
u16 max_x;
|
||||||
|
u16 max_y;
|
||||||
|
u16 res_x;
|
||||||
|
u16 res_y;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int is_not_elan_touchpad(struct hid_device *hdev)
|
static int is_not_elan_touchpad(struct hid_device *hdev)
|
||||||
{
|
{
|
||||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
if (hdev->bus == BUS_USB) {
|
||||||
struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
|
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||||
|
|
||||||
return (intf->altsetting->desc.bInterfaceNumber != drvdata->settings->usb_bInterfaceNumber);
|
return (intf->altsetting->desc.bInterfaceNumber !=
|
||||||
|
ELAN_TP_USB_INTF);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||||
|
@ -62,12 +73,86 @@ static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||||
|
|
||||||
if (field->report->id == ELAN_SINGLE_FINGER ||
|
if (field->report->id == ELAN_SINGLE_FINGER ||
|
||||||
field->report->id == ELAN_MT_FIRST_FINGER ||
|
field->report->id == ELAN_MT_FIRST_FINGER ||
|
||||||
field->report->id == ELAN_MT_SECOND_FINGER)
|
field->report->id == ELAN_MT_SECOND_FINGER ||
|
||||||
|
field->report->id == ELAN_MT_I2C)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int elan_get_device_param(struct hid_device *hdev,
|
||||||
|
unsigned char *dmabuf, unsigned char param)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dmabuf[0] = ELAN_FEATURE_REPORT;
|
||||||
|
dmabuf[1] = 0x05;
|
||||||
|
dmabuf[2] = 0x03;
|
||||||
|
dmabuf[3] = param;
|
||||||
|
dmabuf[4] = 0x01;
|
||||||
|
|
||||||
|
ret = hid_hw_raw_request(hdev, ELAN_FEATURE_REPORT, dmabuf,
|
||||||
|
ELAN_FEATURE_SIZE, HID_FEATURE_REPORT,
|
||||||
|
HID_REQ_SET_REPORT);
|
||||||
|
if (ret != ELAN_FEATURE_SIZE) {
|
||||||
|
hid_err(hdev, "Set report error for parm %d: %d\n", param, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = hid_hw_raw_request(hdev, ELAN_FEATURE_REPORT, dmabuf,
|
||||||
|
ELAN_FEATURE_SIZE, HID_FEATURE_REPORT,
|
||||||
|
HID_REQ_GET_REPORT);
|
||||||
|
if (ret != ELAN_FEATURE_SIZE) {
|
||||||
|
hid_err(hdev, "Get report error for parm %d: %d\n", param, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int elan_convert_res(char val)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* (value from firmware) * 10 + 790 = dpi
|
||||||
|
* dpi * 10 / 254 = dots/mm
|
||||||
|
*/
|
||||||
|
return (val * 10 + 790) * 10 / 254;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elan_get_device_params(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||||
|
unsigned char *dmabuf;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dmabuf = kmalloc(ELAN_FEATURE_SIZE, GFP_KERNEL);
|
||||||
|
if (!dmabuf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_MAX_X);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
drvdata->max_x = (dmabuf[4] << 8) | dmabuf[3];
|
||||||
|
|
||||||
|
ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_MAX_Y);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
drvdata->max_y = (dmabuf[4] << 8) | dmabuf[3];
|
||||||
|
|
||||||
|
ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_RES);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
drvdata->res_x = elan_convert_res(dmabuf[3]);
|
||||||
|
drvdata->res_y = elan_convert_res(dmabuf[4]);
|
||||||
|
|
||||||
|
err:
|
||||||
|
kfree(dmabuf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
|
static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -77,6 +162,10 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
|
||||||
if (is_not_elan_touchpad(hdev))
|
if (is_not_elan_touchpad(hdev))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
ret = elan_get_device_params(hdev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
input = devm_input_allocate_device(&hdev->dev);
|
input = devm_input_allocate_device(&hdev->dev);
|
||||||
if (!input)
|
if (!input)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -90,25 +179,25 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
|
||||||
input->id.version = hdev->version;
|
input->id.version = hdev->version;
|
||||||
input->dev.parent = &hdev->dev;
|
input->dev.parent = &hdev->dev;
|
||||||
|
|
||||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
|
input_set_abs_params(input, ABS_MT_POSITION_X, 0, drvdata->max_x,
|
||||||
drvdata->settings->max_x, 0, 0);
|
0, 0);
|
||||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
|
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, drvdata->max_y,
|
||||||
drvdata->settings->max_y, 0, 0);
|
0, 0);
|
||||||
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
|
input_set_abs_params(input, ABS_MT_PRESSURE, 0, ELAN_MAX_PRESSURE,
|
||||||
drvdata->settings->max_fingers, 0, 0);
|
0, 0);
|
||||||
input_set_abs_params(input, ABS_TOOL_WIDTH, 0,
|
|
||||||
drvdata->settings->max_w, 0, 0);
|
|
||||||
|
|
||||||
__set_bit(BTN_LEFT, input->keybit);
|
__set_bit(BTN_LEFT, input->keybit);
|
||||||
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
||||||
|
|
||||||
ret = input_mt_init_slots(input, drvdata->settings->max_fingers,
|
ret = input_mt_init_slots(input, ELAN_MAX_FINGERS, INPUT_MT_POINTER);
|
||||||
INPUT_MT_POINTER);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
hid_err(hdev, "Failed to init elan MT slots: %d\n", ret);
|
hid_err(hdev, "Failed to init elan MT slots: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input_abs_set_res(input, ABS_X, drvdata->res_x);
|
||||||
|
input_abs_set_res(input, ABS_Y, drvdata->res_y);
|
||||||
|
|
||||||
ret = input_register_device(input);
|
ret = input_register_device(input);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
hid_err(hdev, "Failed to register elan input device: %d\n",
|
hid_err(hdev, "Failed to register elan input device: %d\n",
|
||||||
|
@ -126,7 +215,7 @@ static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data,
|
||||||
unsigned int slot_num)
|
unsigned int slot_num)
|
||||||
{
|
{
|
||||||
struct input_dev *input = drvdata->input;
|
struct input_dev *input = drvdata->input;
|
||||||
int x, y, w;
|
int x, y, p;
|
||||||
|
|
||||||
bool active = !!data;
|
bool active = !!data;
|
||||||
|
|
||||||
|
@ -134,17 +223,17 @@ static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data,
|
||||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, active);
|
input_mt_report_slot_state(input, MT_TOOL_FINGER, active);
|
||||||
if (active) {
|
if (active) {
|
||||||
x = ((data[0] & 0xF0) << 4) | data[1];
|
x = ((data[0] & 0xF0) << 4) | data[1];
|
||||||
y = drvdata->settings->max_y -
|
y = drvdata->max_y -
|
||||||
(((data[0] & 0x07) << 8) | data[2]);
|
(((data[0] & 0x07) << 8) | data[2]);
|
||||||
w = data[4];
|
p = data[4];
|
||||||
|
|
||||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||||
input_report_abs(input, ABS_TOOL_WIDTH, w);
|
input_report_abs(input, ABS_MT_PRESSURE, p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
|
static void elan_usb_report_input(struct elan_drvdata *drvdata, u8 *data)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct input_dev *input = drvdata->input;
|
struct input_dev *input = drvdata->input;
|
||||||
|
@ -162,7 +251,7 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
|
||||||
* byte 5: x8 x7 x6 x5 x4 x3 x2 x1
|
* byte 5: x8 x7 x6 x5 x4 x3 x2 x1
|
||||||
* byte 6: y8 y7 y6 y5 y4 y3 y2 y1
|
* byte 6: y8 y7 y6 y5 y4 y3 y2 y1
|
||||||
* byte 7: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
|
* byte 7: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
|
||||||
* byte 8: w8 w7 w6 w5 w4 w3 w2 w1
|
* byte 8: p8 p7 p6 p5 p4 p3 p2 p1
|
||||||
*
|
*
|
||||||
* packet structure for ELAN_MT_SECOND_FINGER:
|
* packet structure for ELAN_MT_SECOND_FINGER:
|
||||||
*
|
*
|
||||||
|
@ -171,19 +260,21 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
|
||||||
* byte 3: x8 x7 x6 x5 x4 x3 x2 x1
|
* byte 3: x8 x7 x6 x5 x4 x3 x2 x1
|
||||||
* byte 4: y8 y7 y6 y5 y4 y3 y2 y1
|
* byte 4: y8 y7 y6 y5 y4 y3 y2 y1
|
||||||
* byte 5: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
|
* byte 5: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
|
||||||
* byte 6: w8 w7 w6 w5 w4 w3 w2 w1
|
* byte 6: p8 p7 p6 p5 p4 p3 p2 p1
|
||||||
* byte 7: 0 0 0 0 0 0 0 0
|
* byte 7: 0 0 0 0 0 0 0 0
|
||||||
* byte 8: 0 0 0 0 0 0 0 0
|
* byte 8: 0 0 0 0 0 0 0 0
|
||||||
*
|
*
|
||||||
* f5-f1: finger touch bits
|
* f5-f1: finger touch bits
|
||||||
* L: clickpad button
|
* L: clickpad button
|
||||||
* sy / sx: not sure yet, but this looks like rectangular
|
* sy / sx: finger width / height expressed in traces, the total number
|
||||||
* area for finger
|
* of traces can be queried by doing a HID_REQ_SET_REPORT
|
||||||
* w: looks like finger width
|
* { 0x0d, 0x05, 0x03, 0x05, 0x01 } followed by a GET, in the
|
||||||
|
* returned buf, buf[3]=no-x-traces, buf[4]=no-y-traces.
|
||||||
|
* p: pressure
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (data[0] == ELAN_SINGLE_FINGER) {
|
if (data[0] == ELAN_SINGLE_FINGER) {
|
||||||
for (i = 0; i < drvdata->settings->max_fingers; i++) {
|
for (i = 0; i < ELAN_MAX_FINGERS; i++) {
|
||||||
if (data[2] & BIT(i + 3))
|
if (data[2] & BIT(i + 3))
|
||||||
elan_report_mt_slot(drvdata, data + 3, i);
|
elan_report_mt_slot(drvdata, data + 3, i);
|
||||||
else
|
else
|
||||||
|
@ -210,7 +301,7 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
|
||||||
if (prev_report[0] != ELAN_MT_FIRST_FINGER)
|
if (prev_report[0] != ELAN_MT_FIRST_FINGER)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < drvdata->settings->max_fingers; i++) {
|
for (i = 0; i < ELAN_MAX_FINGERS; i++) {
|
||||||
if (prev_report[2] & BIT(i + 3)) {
|
if (prev_report[2] & BIT(i + 3)) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
first = 1;
|
first = 1;
|
||||||
|
@ -229,6 +320,46 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
|
||||||
input_sync(input);
|
input_sync(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void elan_i2c_report_input(struct elan_drvdata *drvdata, u8 *data)
|
||||||
|
{
|
||||||
|
struct input_dev *input = drvdata->input;
|
||||||
|
u8 *finger_data;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Elan MT touchpads in i2c mode send finger data in the same format
|
||||||
|
* as in USB mode, but then with all fingers in a single packet.
|
||||||
|
*
|
||||||
|
* packet structure for ELAN_MT_I2C:
|
||||||
|
*
|
||||||
|
* byte 1: 1 0 0 1 1 1 0 1 // 0x5d
|
||||||
|
* byte 2: f5 f4 f3 f2 f1 0 0 L
|
||||||
|
* byte 3: x12 x11 x10 x9 0? y11 y10 y9
|
||||||
|
* byte 4: x8 x7 x6 x5 x4 x3 x2 x1
|
||||||
|
* byte 5: y8 y7 y6 y5 y4 y3 y2 y1
|
||||||
|
* byte 6: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
|
||||||
|
* byte 7: p8 p7 p6 p5 p4 p3 p2 p1
|
||||||
|
* byte 8-12: Same as byte 3-7 for second finger down
|
||||||
|
* byte 13-17: Same as byte 3-7 for third finger down
|
||||||
|
* byte 18-22: Same as byte 3-7 for fourth finger down
|
||||||
|
* byte 23-27: Same as byte 3-7 for fifth finger down
|
||||||
|
*/
|
||||||
|
|
||||||
|
finger_data = data + 2;
|
||||||
|
for (i = 0; i < ELAN_MAX_FINGERS; i++) {
|
||||||
|
if (data[1] & BIT(i + 3)) {
|
||||||
|
elan_report_mt_slot(drvdata, finger_data, i);
|
||||||
|
finger_data += ELAN_FINGER_DATA_LEN;
|
||||||
|
} else {
|
||||||
|
elan_report_mt_slot(drvdata, NULL, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input_report_key(input, BTN_LEFT, data[1] & 0x01);
|
||||||
|
input_mt_sync_frame(input);
|
||||||
|
input_sync(input);
|
||||||
|
}
|
||||||
|
|
||||||
static int elan_raw_event(struct hid_device *hdev,
|
static int elan_raw_event(struct hid_device *hdev,
|
||||||
struct hid_report *report, u8 *data, int size)
|
struct hid_report *report, u8 *data, int size)
|
||||||
{
|
{
|
||||||
|
@ -241,11 +372,16 @@ static int elan_raw_event(struct hid_device *hdev,
|
||||||
data[0] == ELAN_MT_FIRST_FINGER ||
|
data[0] == ELAN_MT_FIRST_FINGER ||
|
||||||
data[0] == ELAN_MT_SECOND_FINGER) {
|
data[0] == ELAN_MT_SECOND_FINGER) {
|
||||||
if (size == ELAN_INPUT_REPORT_SIZE) {
|
if (size == ELAN_INPUT_REPORT_SIZE) {
|
||||||
elan_report_input(drvdata, data);
|
elan_usb_report_input(drvdata, data);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data[0] == ELAN_MT_I2C && size == ELAN_I2C_REPORT_SIZE) {
|
||||||
|
elan_i2c_report_input(drvdata, data);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,7 +479,6 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||||
if (!drvdata)
|
if (!drvdata)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
drvdata->settings = (struct elan_touchpad_settings *)id->driver_data;
|
|
||||||
hid_set_drvdata(hdev, drvdata);
|
hid_set_drvdata(hdev, drvdata);
|
||||||
|
|
||||||
ret = hid_parse(hdev);
|
ret = hid_parse(hdev);
|
||||||
|
@ -371,9 +506,11 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = elan_init_mute_led(hdev);
|
if (id->driver_data & ELAN_HAS_LED) {
|
||||||
if (ret)
|
ret = elan_init_mute_led(hdev);
|
||||||
goto err;
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
err:
|
||||||
|
@ -386,22 +523,14 @@ static void elan_remove(struct hid_device *hdev)
|
||||||
hid_hw_stop(hdev);
|
hid_hw_stop(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct elan_touchpad_settings hp_x2_10_touchpad_data = {
|
|
||||||
.max_fingers = 5,
|
|
||||||
.max_x = 2930,
|
|
||||||
.max_y = 1250,
|
|
||||||
.max_area_x = 15,
|
|
||||||
.max_area_y = 15,
|
|
||||||
.max_w = 255,
|
|
||||||
.usb_bInterfaceNumber = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct hid_device_id elan_devices[] = {
|
static const struct hid_device_id elan_devices[] = {
|
||||||
|
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2),
|
||||||
|
.driver_data = ELAN_HAS_LED },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER),
|
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER),
|
||||||
(kernel_ulong_t)&hp_x2_10_touchpad_data},
|
.driver_data = ELAN_HAS_LED },
|
||||||
|
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_TOSHIBA_CLICK_L9W) },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(hid, elan_devices);
|
MODULE_DEVICE_TABLE(hid, elan_devices);
|
||||||
|
|
||||||
static struct hid_driver elan_driver = {
|
static struct hid_driver elan_driver = {
|
||||||
|
|
|
@ -369,6 +369,8 @@
|
||||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
|
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
|
||||||
|
|
||||||
#define USB_VENDOR_ID_ELAN 0x04f3
|
#define USB_VENDOR_ID_ELAN 0x04f3
|
||||||
|
#define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401
|
||||||
|
#define USB_DEVICE_ID_HP_X2 0x074d
|
||||||
#define USB_DEVICE_ID_HP_X2_10_COVER 0x0755
|
#define USB_DEVICE_ID_HP_X2_10_COVER 0x0755
|
||||||
|
|
||||||
#define USB_VENDOR_ID_ELECOM 0x056e
|
#define USB_VENDOR_ID_ELECOM 0x056e
|
||||||
|
@ -1001,6 +1003,9 @@
|
||||||
#define USB_VENDOR_ID_SINO_LITE 0x1345
|
#define USB_VENDOR_ID_SINO_LITE 0x1345
|
||||||
#define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008
|
#define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008
|
||||||
|
|
||||||
|
#define USB_VENDOR_ID_SOLID_YEAR 0x060b
|
||||||
|
#define USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD 0x500a
|
||||||
|
|
||||||
#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2
|
#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2
|
||||||
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034
|
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034
|
||||||
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046
|
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046
|
||||||
|
|
|
@ -1550,6 +1550,9 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid,
|
||||||
case HID_GD_WIRELESS_RADIO_CTLS:
|
case HID_GD_WIRELESS_RADIO_CTLS:
|
||||||
suffix = "Wireless Radio Control";
|
suffix = "Wireless Radio Control";
|
||||||
break;
|
break;
|
||||||
|
case HID_GD_SYSTEM_MULTIAXIS:
|
||||||
|
suffix = "System Multi Axis";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,13 @@
|
||||||
|
|
||||||
#include "hid-ids.h"
|
#include "hid-ids.h"
|
||||||
|
|
||||||
#define MS_HIDINPUT 0x01
|
#define MS_HIDINPUT BIT(0)
|
||||||
#define MS_ERGONOMY 0x02
|
#define MS_ERGONOMY BIT(1)
|
||||||
#define MS_PRESENTER 0x04
|
#define MS_PRESENTER BIT(2)
|
||||||
#define MS_RDESC 0x08
|
#define MS_RDESC BIT(3)
|
||||||
#define MS_NOGET 0x10
|
#define MS_NOGET BIT(4)
|
||||||
#define MS_DUPLICATE_USAGES 0x20
|
#define MS_DUPLICATE_USAGES BIT(5)
|
||||||
|
#define MS_SURFACE_DIAL BIT(6)
|
||||||
|
|
||||||
static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||||
unsigned int *rsize)
|
unsigned int *rsize)
|
||||||
|
@ -130,6 +131,30 @@ static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ms_surface_dial_quirk(struct hid_input *hi, struct hid_field *field,
|
||||||
|
struct hid_usage *usage, unsigned long **bit, int *max)
|
||||||
|
{
|
||||||
|
switch (usage->hid & HID_USAGE_PAGE) {
|
||||||
|
case 0xff070000:
|
||||||
|
/* fall-through */
|
||||||
|
case HID_UP_DIGITIZER:
|
||||||
|
/* ignore those axis */
|
||||||
|
return -1;
|
||||||
|
case HID_UP_GENDESK:
|
||||||
|
switch (usage->hid) {
|
||||||
|
case HID_GD_X:
|
||||||
|
/* fall-through */
|
||||||
|
case HID_GD_Y:
|
||||||
|
/* fall-through */
|
||||||
|
case HID_GD_RFKILL_BTN:
|
||||||
|
/* ignore those axis */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||||
struct hid_field *field, struct hid_usage *usage,
|
struct hid_field *field, struct hid_usage *usage,
|
||||||
unsigned long **bit, int *max)
|
unsigned long **bit, int *max)
|
||||||
|
@ -146,6 +171,13 @@ static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||||
ms_presenter_8k_quirk(hi, usage, bit, max))
|
ms_presenter_8k_quirk(hi, usage, bit, max))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
if (quirks & MS_SURFACE_DIAL) {
|
||||||
|
int ret = ms_surface_dial_quirk(hi, field, usage, bit, max);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,6 +261,9 @@ static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||||
if (quirks & MS_NOGET)
|
if (quirks & MS_NOGET)
|
||||||
hdev->quirks |= HID_QUIRK_NOGET;
|
hdev->quirks |= HID_QUIRK_NOGET;
|
||||||
|
|
||||||
|
if (quirks & MS_SURFACE_DIAL)
|
||||||
|
hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
|
||||||
|
|
||||||
ret = hid_parse(hdev);
|
ret = hid_parse(hdev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
hid_err(hdev, "parse failed\n");
|
hid_err(hdev, "parse failed\n");
|
||||||
|
@ -281,6 +316,8 @@ static const struct hid_device_id ms_devices[] = {
|
||||||
|
|
||||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
|
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
|
||||||
.driver_data = MS_PRESENTER },
|
.driver_data = MS_PRESENTER },
|
||||||
|
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B),
|
||||||
|
.driver_data = MS_SURFACE_DIAL },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(hid, ms_devices);
|
MODULE_DEVICE_TABLE(hid, ms_devices);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -955,6 +955,8 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||||
|
|
||||||
ret = sysfs_create_group(&hdev->dev.kobj,
|
ret = sysfs_create_group(&hdev->dev.kobj,
|
||||||
&ntrig_attribute_group);
|
&ntrig_attribute_group);
|
||||||
|
if (ret)
|
||||||
|
hid_err(hdev, "cannot create sysfs group\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err_free:
|
err_free:
|
||||||
|
|
|
@ -44,29 +44,6 @@ static __u8 *redragon_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||||
return rdesc;
|
return rdesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int redragon_probe(struct hid_device *dev,
|
|
||||||
const struct hid_device_id *id)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = hid_parse(dev);
|
|
||||||
if (ret) {
|
|
||||||
hid_err(dev, "parse failed\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* do not register unused input device */
|
|
||||||
if (dev->maxapplication == 1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
ret = hid_hw_start(dev, HID_CONNECT_DEFAULT);
|
|
||||||
if (ret) {
|
|
||||||
hid_err(dev, "hw start failed\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static const struct hid_device_id redragon_devices[] = {
|
static const struct hid_device_id redragon_devices[] = {
|
||||||
{HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_REDRAGON_ASURA)},
|
{HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_REDRAGON_ASURA)},
|
||||||
{}
|
{}
|
||||||
|
@ -77,8 +54,7 @@ MODULE_DEVICE_TABLE(hid, redragon_devices);
|
||||||
static struct hid_driver redragon_driver = {
|
static struct hid_driver redragon_driver = {
|
||||||
.name = "redragon",
|
.name = "redragon",
|
||||||
.id_table = redragon_devices,
|
.id_table = redragon_devices,
|
||||||
.report_fixup = redragon_report_fixup,
|
.report_fixup = redragon_report_fixup
|
||||||
.probe = redragon_probe
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module_hid_driver(redragon_driver);
|
module_hid_driver(redragon_driver);
|
||||||
|
|
|
@ -1353,7 +1353,7 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
|
||||||
char *name;
|
char *name;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
sc->touchpad = input_allocate_device();
|
sc->touchpad = devm_input_allocate_device(&sc->hdev->dev);
|
||||||
if (!sc->touchpad)
|
if (!sc->touchpad)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -1370,11 +1370,9 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
|
||||||
* DS4 compatible non-Sony devices with different names.
|
* DS4 compatible non-Sony devices with different names.
|
||||||
*/
|
*/
|
||||||
name_sz = strlen(sc->hdev->name) + sizeof(DS4_TOUCHPAD_SUFFIX);
|
name_sz = strlen(sc->hdev->name) + sizeof(DS4_TOUCHPAD_SUFFIX);
|
||||||
name = kzalloc(name_sz, GFP_KERNEL);
|
name = devm_kzalloc(&sc->hdev->dev, name_sz, GFP_KERNEL);
|
||||||
if (!name) {
|
if (!name)
|
||||||
ret = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
snprintf(name, name_sz, "%s" DS4_TOUCHPAD_SUFFIX, sc->hdev->name);
|
snprintf(name, name_sz, "%s" DS4_TOUCHPAD_SUFFIX, sc->hdev->name);
|
||||||
sc->touchpad->name = name;
|
sc->touchpad->name = name;
|
||||||
|
|
||||||
|
@ -1403,34 +1401,13 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
|
||||||
|
|
||||||
ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER);
|
ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err;
|
return ret;
|
||||||
|
|
||||||
ret = input_register_device(sc->touchpad);
|
ret = input_register_device(sc->touchpad);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err;
|
return ret;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
|
||||||
kfree(sc->touchpad->name);
|
|
||||||
sc->touchpad->name = NULL;
|
|
||||||
|
|
||||||
input_free_device(sc->touchpad);
|
|
||||||
sc->touchpad = NULL;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sony_unregister_touchpad(struct sony_sc *sc)
|
|
||||||
{
|
|
||||||
if (!sc->touchpad)
|
|
||||||
return;
|
|
||||||
|
|
||||||
kfree(sc->touchpad->name);
|
|
||||||
sc->touchpad->name = NULL;
|
|
||||||
|
|
||||||
input_unregister_device(sc->touchpad);
|
|
||||||
sc->touchpad = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sony_register_sensors(struct sony_sc *sc)
|
static int sony_register_sensors(struct sony_sc *sc)
|
||||||
|
@ -1440,7 +1417,7 @@ static int sony_register_sensors(struct sony_sc *sc)
|
||||||
int ret;
|
int ret;
|
||||||
int range;
|
int range;
|
||||||
|
|
||||||
sc->sensor_dev = input_allocate_device();
|
sc->sensor_dev = devm_input_allocate_device(&sc->hdev->dev);
|
||||||
if (!sc->sensor_dev)
|
if (!sc->sensor_dev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -1457,11 +1434,9 @@ static int sony_register_sensors(struct sony_sc *sc)
|
||||||
* DS4 compatible non-Sony devices with different names.
|
* DS4 compatible non-Sony devices with different names.
|
||||||
*/
|
*/
|
||||||
name_sz = strlen(sc->hdev->name) + sizeof(SENSOR_SUFFIX);
|
name_sz = strlen(sc->hdev->name) + sizeof(SENSOR_SUFFIX);
|
||||||
name = kzalloc(name_sz, GFP_KERNEL);
|
name = devm_kzalloc(&sc->hdev->dev, name_sz, GFP_KERNEL);
|
||||||
if (!name) {
|
if (!name)
|
||||||
ret = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
snprintf(name, name_sz, "%s" SENSOR_SUFFIX, sc->hdev->name);
|
snprintf(name, name_sz, "%s" SENSOR_SUFFIX, sc->hdev->name);
|
||||||
sc->sensor_dev->name = name;
|
sc->sensor_dev->name = name;
|
||||||
|
|
||||||
|
@ -1503,33 +1478,11 @@ static int sony_register_sensors(struct sony_sc *sc)
|
||||||
|
|
||||||
ret = input_register_device(sc->sensor_dev);
|
ret = input_register_device(sc->sensor_dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err;
|
return ret;
|
||||||
|
|
||||||
return 0;
|
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
|
* Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
|
||||||
* to "operational". Without this, the ps3 controller will not report any
|
* to "operational". Without this, the ps3 controller will not report any
|
||||||
|
@ -1987,25 +1940,6 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sony_leds_remove(struct sony_sc *sc)
|
|
||||||
{
|
|
||||||
struct led_classdev *led;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
|
|
||||||
|
|
||||||
for (n = 0; n < sc->led_count; n++) {
|
|
||||||
led = sc->leds[n];
|
|
||||||
sc->leds[n] = NULL;
|
|
||||||
if (!led)
|
|
||||||
continue;
|
|
||||||
led_classdev_unregister(led);
|
|
||||||
kfree(led);
|
|
||||||
}
|
|
||||||
|
|
||||||
sc->led_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sony_leds_init(struct sony_sc *sc)
|
static int sony_leds_init(struct sony_sc *sc)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = sc->hdev;
|
struct hid_device *hdev = sc->hdev;
|
||||||
|
@ -2078,11 +2012,10 @@ static int sony_leds_init(struct sony_sc *sc)
|
||||||
if (use_ds4_names)
|
if (use_ds4_names)
|
||||||
name_sz = strlen(dev_name(&hdev->dev)) + strlen(ds4_name_str[n]) + 2;
|
name_sz = strlen(dev_name(&hdev->dev)) + strlen(ds4_name_str[n]) + 2;
|
||||||
|
|
||||||
led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL);
|
led = devm_kzalloc(&hdev->dev, sizeof(struct led_classdev) + name_sz, GFP_KERNEL);
|
||||||
if (!led) {
|
if (!led) {
|
||||||
hid_err(hdev, "Couldn't allocate memory for LED %d\n", n);
|
hid_err(hdev, "Couldn't allocate memory for LED %d\n", n);
|
||||||
ret = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto error_leds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
name = (void *)(&led[1]);
|
name = (void *)(&led[1]);
|
||||||
|
@ -2103,21 +2036,14 @@ static int sony_leds_init(struct sony_sc *sc)
|
||||||
|
|
||||||
sc->leds[n] = led;
|
sc->leds[n] = led;
|
||||||
|
|
||||||
ret = led_classdev_register(&hdev->dev, led);
|
ret = devm_led_classdev_register(&hdev->dev, led);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
hid_err(hdev, "Failed to register LED %d\n", n);
|
hid_err(hdev, "Failed to register LED %d\n", n);
|
||||||
sc->leds[n] = NULL;
|
return ret;
|
||||||
kfree(led);
|
|
||||||
goto error_leds;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
|
|
||||||
error_leds:
|
|
||||||
sony_leds_remove(sc);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sixaxis_send_output_report(struct sony_sc *sc)
|
static void sixaxis_send_output_report(struct sony_sc *sc)
|
||||||
|
@ -2276,16 +2202,20 @@ static int sony_allocate_output_report(struct sony_sc *sc)
|
||||||
if ((sc->quirks & SIXAXIS_CONTROLLER) ||
|
if ((sc->quirks & SIXAXIS_CONTROLLER) ||
|
||||||
(sc->quirks & NAVIGATION_CONTROLLER))
|
(sc->quirks & NAVIGATION_CONTROLLER))
|
||||||
sc->output_report_dmabuf =
|
sc->output_report_dmabuf =
|
||||||
kmalloc(sizeof(union sixaxis_output_report_01),
|
devm_kmalloc(&sc->hdev->dev,
|
||||||
|
sizeof(union sixaxis_output_report_01),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
|
else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
|
||||||
sc->output_report_dmabuf = kmalloc(DS4_OUTPUT_REPORT_0x11_SIZE,
|
sc->output_report_dmabuf = devm_kmalloc(&sc->hdev->dev,
|
||||||
|
DS4_OUTPUT_REPORT_0x11_SIZE,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
else if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE))
|
else if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE))
|
||||||
sc->output_report_dmabuf = kmalloc(DS4_OUTPUT_REPORT_0x05_SIZE,
|
sc->output_report_dmabuf = devm_kmalloc(&sc->hdev->dev,
|
||||||
|
DS4_OUTPUT_REPORT_0x05_SIZE,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
else if (sc->quirks & MOTION_CONTROLLER)
|
else if (sc->quirks & MOTION_CONTROLLER)
|
||||||
sc->output_report_dmabuf = kmalloc(MOTION_REPORT_0x02_SIZE,
|
sc->output_report_dmabuf = devm_kmalloc(&sc->hdev->dev,
|
||||||
|
MOTION_REPORT_0x02_SIZE,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2392,36 +2322,21 @@ static int sony_battery_probe(struct sony_sc *sc, int append_dev_id)
|
||||||
sc->battery_desc.get_property = sony_battery_get_property;
|
sc->battery_desc.get_property = sony_battery_get_property;
|
||||||
sc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
|
sc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||||
sc->battery_desc.use_for_apm = 0;
|
sc->battery_desc.use_for_apm = 0;
|
||||||
sc->battery_desc.name = kasprintf(GFP_KERNEL, battery_str_fmt,
|
sc->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
|
||||||
sc->mac_address, sc->device_id);
|
battery_str_fmt, sc->mac_address, sc->device_id);
|
||||||
if (!sc->battery_desc.name)
|
if (!sc->battery_desc.name)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
sc->battery = power_supply_register(&hdev->dev, &sc->battery_desc,
|
sc->battery = devm_power_supply_register(&hdev->dev, &sc->battery_desc,
|
||||||
&psy_cfg);
|
&psy_cfg);
|
||||||
if (IS_ERR(sc->battery)) {
|
if (IS_ERR(sc->battery)) {
|
||||||
ret = PTR_ERR(sc->battery);
|
ret = PTR_ERR(sc->battery);
|
||||||
hid_err(hdev, "Unable to register battery device\n");
|
hid_err(hdev, "Unable to register battery device\n");
|
||||||
goto err_free;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
power_supply_powers(sc->battery, &hdev->dev);
|
power_supply_powers(sc->battery, &hdev->dev);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free:
|
|
||||||
kfree(sc->battery_desc.name);
|
|
||||||
sc->battery_desc.name = NULL;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sony_battery_remove(struct sony_sc *sc)
|
|
||||||
{
|
|
||||||
if (!sc->battery_desc.name)
|
|
||||||
return;
|
|
||||||
|
|
||||||
power_supply_unregister(sc->battery);
|
|
||||||
kfree(sc->battery_desc.name);
|
|
||||||
sc->battery_desc.name = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2879,16 +2794,7 @@ err_stop:
|
||||||
device_remove_file(&sc->hdev->dev, &dev_attr_firmware_version);
|
device_remove_file(&sc->hdev->dev, &dev_attr_firmware_version);
|
||||||
if (sc->hw_version)
|
if (sc->hw_version)
|
||||||
device_remove_file(&sc->hdev->dev, &dev_attr_hardware_version);
|
device_remove_file(&sc->hdev->dev, &dev_attr_hardware_version);
|
||||||
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);
|
sony_cancel_work_sync(sc);
|
||||||
kfree(sc->output_report_dmabuf);
|
|
||||||
sony_remove_dev_list(sc);
|
sony_remove_dev_list(sc);
|
||||||
sony_release_device_id(sc);
|
sony_release_device_id(sc);
|
||||||
hid_hw_stop(hdev);
|
hid_hw_stop(hdev);
|
||||||
|
@ -2965,18 +2871,6 @@ static void sony_remove(struct hid_device *hdev)
|
||||||
|
|
||||||
hid_hw_close(hdev);
|
hid_hw_close(hdev);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
|
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
|
||||||
device_remove_file(&sc->hdev->dev, &dev_attr_bt_poll_interval);
|
device_remove_file(&sc->hdev->dev, &dev_attr_bt_poll_interval);
|
||||||
|
|
||||||
|
@ -2988,8 +2882,6 @@ static void sony_remove(struct hid_device *hdev)
|
||||||
|
|
||||||
sony_cancel_work_sync(sc);
|
sony_cancel_work_sync(sc);
|
||||||
|
|
||||||
kfree(sc->output_report_dmabuf);
|
|
||||||
|
|
||||||
sony_remove_dev_list(sc);
|
sony_remove_dev_list(sc);
|
||||||
|
|
||||||
sony_release_device_id(sc);
|
sony_release_device_id(sc);
|
||||||
|
|
|
@ -455,6 +455,12 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
|
||||||
return WIIMOTE_EXT_BALANCE_BOARD;
|
return WIIMOTE_EXT_BALANCE_BOARD;
|
||||||
if (rmem[4] == 0x01 && rmem[5] == 0x20)
|
if (rmem[4] == 0x01 && rmem[5] == 0x20)
|
||||||
return WIIMOTE_EXT_PRO_CONTROLLER;
|
return WIIMOTE_EXT_PRO_CONTROLLER;
|
||||||
|
if (rmem[0] == 0x01 && rmem[1] == 0x00 &&
|
||||||
|
rmem[4] == 0x01 && rmem[5] == 0x03)
|
||||||
|
return WIIMOTE_EXT_DRUMS;
|
||||||
|
if (rmem[0] == 0x00 && rmem[1] == 0x00 &&
|
||||||
|
rmem[4] == 0x01 && rmem[5] == 0x03)
|
||||||
|
return WIIMOTE_EXT_GUITAR;
|
||||||
|
|
||||||
return WIIMOTE_EXT_UNKNOWN;
|
return WIIMOTE_EXT_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
@ -488,6 +494,8 @@ static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype)
|
||||||
/* map MP with correct pass-through mode */
|
/* map MP with correct pass-through mode */
|
||||||
switch (exttype) {
|
switch (exttype) {
|
||||||
case WIIMOTE_EXT_CLASSIC_CONTROLLER:
|
case WIIMOTE_EXT_CLASSIC_CONTROLLER:
|
||||||
|
case WIIMOTE_EXT_DRUMS:
|
||||||
|
case WIIMOTE_EXT_GUITAR:
|
||||||
wmem = 0x07;
|
wmem = 0x07;
|
||||||
break;
|
break;
|
||||||
case WIIMOTE_EXT_NUNCHUK:
|
case WIIMOTE_EXT_NUNCHUK:
|
||||||
|
@ -1075,6 +1083,8 @@ static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = {
|
||||||
[WIIMOTE_EXT_CLASSIC_CONTROLLER] = "Nintendo Wii Classic Controller",
|
[WIIMOTE_EXT_CLASSIC_CONTROLLER] = "Nintendo Wii Classic Controller",
|
||||||
[WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board",
|
[WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board",
|
||||||
[WIIMOTE_EXT_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller",
|
[WIIMOTE_EXT_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller",
|
||||||
|
[WIIMOTE_EXT_DRUMS] = "Nintendo Wii Drums",
|
||||||
|
[WIIMOTE_EXT_GUITAR] = "Nintendo Wii Guitar",
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1660,6 +1670,10 @@ static ssize_t wiimote_ext_show(struct device *dev,
|
||||||
return sprintf(buf, "balanceboard\n");
|
return sprintf(buf, "balanceboard\n");
|
||||||
case WIIMOTE_EXT_PRO_CONTROLLER:
|
case WIIMOTE_EXT_PRO_CONTROLLER:
|
||||||
return sprintf(buf, "procontroller\n");
|
return sprintf(buf, "procontroller\n");
|
||||||
|
case WIIMOTE_EXT_DRUMS:
|
||||||
|
return sprintf(buf, "drums\n");
|
||||||
|
case WIIMOTE_EXT_GUITAR:
|
||||||
|
return sprintf(buf, "guitar\n");
|
||||||
case WIIMOTE_EXT_UNKNOWN:
|
case WIIMOTE_EXT_UNKNOWN:
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1949,6 +1949,444 @@ static const struct wiimod_ops wiimod_pro = {
|
||||||
.in_ext = wiimod_pro_in_ext,
|
.in_ext = wiimod_pro_in_ext,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Drums
|
||||||
|
* Guitar-Hero, Rock-Band and other games came bundled with drums which can
|
||||||
|
* be plugged as extension to a Wiimote. Drum-reports are still not entirely
|
||||||
|
* figured out, but the most important information is known.
|
||||||
|
* We create a separate device for drums and report all information via this
|
||||||
|
* input device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline void wiimod_drums_report_pressure(struct wiimote_data *wdata,
|
||||||
|
__u8 none, __u8 which,
|
||||||
|
__u8 pressure, __u8 onoff,
|
||||||
|
__u8 *store, __u16 code,
|
||||||
|
__u8 which_code)
|
||||||
|
{
|
||||||
|
static const __u8 default_pressure = 3;
|
||||||
|
|
||||||
|
if (!none && which == which_code) {
|
||||||
|
*store = pressure;
|
||||||
|
input_report_abs(wdata->extension.input, code, *store);
|
||||||
|
} else if (onoff != !!*store) {
|
||||||
|
*store = onoff ? default_pressure : 0;
|
||||||
|
input_report_abs(wdata->extension.input, code, *store);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wiimod_drums_in_ext(struct wiimote_data *wdata, const __u8 *ext)
|
||||||
|
{
|
||||||
|
__u8 pressure, which, none, hhp, sx, sy;
|
||||||
|
__u8 o, r, y, g, b, bass, bm, bp;
|
||||||
|
|
||||||
|
/* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 1 | 0 | 0 | SX <5:0> |
|
||||||
|
* 2 | 0 | 0 | SY <5:0> |
|
||||||
|
* -----+-----+-----+-----------------------------+-----+
|
||||||
|
* 3 | HPP | NON | WHICH <5:1> | ? |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 4 | SOFT <7:5> | 0 | 1 | 1 | 0 | ? |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 5 | ? | 1 | 1 | B- | 1 | B+ | 1 | ? |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 6 | O | R | Y | G | B | BSS | 1 | 1 |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* All buttons are 0 if pressed
|
||||||
|
*
|
||||||
|
* With Motion+ enabled, the following bits will get invalid:
|
||||||
|
* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 1 | 0 | 0 | SX <5:1> |XXXXX|
|
||||||
|
* 2 | 0 | 0 | SY <5:1> |XXXXX|
|
||||||
|
* -----+-----+-----+-----------------------------+-----+
|
||||||
|
* 3 | HPP | NON | WHICH <5:1> | ? |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 4 | SOFT <7:5> | 0 | 1 | 1 | 0 | ? |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 5 | ? | 1 | 1 | B- | 1 | B+ | 1 |XXXXX|
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 6 | O | R | Y | G | B | BSS |XXXXX|XXXXX|
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
*/
|
||||||
|
|
||||||
|
pressure = 7 - (ext[3] >> 5);
|
||||||
|
which = (ext[2] >> 1) & 0x1f;
|
||||||
|
none = !!(ext[2] & 0x40);
|
||||||
|
hhp = !(ext[2] & 0x80);
|
||||||
|
sx = ext[0] & 0x3f;
|
||||||
|
sy = ext[1] & 0x3f;
|
||||||
|
o = !(ext[5] & 0x80);
|
||||||
|
r = !(ext[5] & 0x40);
|
||||||
|
y = !(ext[5] & 0x20);
|
||||||
|
g = !(ext[5] & 0x10);
|
||||||
|
b = !(ext[5] & 0x08);
|
||||||
|
bass = !(ext[5] & 0x04);
|
||||||
|
bm = !(ext[4] & 0x10);
|
||||||
|
bp = !(ext[4] & 0x04);
|
||||||
|
|
||||||
|
if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
|
||||||
|
sx &= 0x3e;
|
||||||
|
sy &= 0x3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
wiimod_drums_report_pressure(wdata, none, which, pressure,
|
||||||
|
o, &wdata->state.pressure_drums[0],
|
||||||
|
ABS_HAT2Y, 0x0e);
|
||||||
|
wiimod_drums_report_pressure(wdata, none, which, pressure,
|
||||||
|
r, &wdata->state.pressure_drums[1],
|
||||||
|
ABS_HAT0X, 0x19);
|
||||||
|
wiimod_drums_report_pressure(wdata, none, which, pressure,
|
||||||
|
y, &wdata->state.pressure_drums[2],
|
||||||
|
ABS_HAT2X, 0x11);
|
||||||
|
wiimod_drums_report_pressure(wdata, none, which, pressure,
|
||||||
|
g, &wdata->state.pressure_drums[3],
|
||||||
|
ABS_HAT1X, 0x12);
|
||||||
|
wiimod_drums_report_pressure(wdata, none, which, pressure,
|
||||||
|
b, &wdata->state.pressure_drums[4],
|
||||||
|
ABS_HAT0Y, 0x0f);
|
||||||
|
|
||||||
|
/* Bass shares pressure with hi-hat (set via hhp) */
|
||||||
|
wiimod_drums_report_pressure(wdata, none, hhp ? 0xff : which, pressure,
|
||||||
|
bass, &wdata->state.pressure_drums[5],
|
||||||
|
ABS_HAT3X, 0x1b);
|
||||||
|
/* Hi-hat has no on/off values, just pressure. Force to off/0. */
|
||||||
|
wiimod_drums_report_pressure(wdata, none, hhp ? which : 0xff, pressure,
|
||||||
|
0, &wdata->state.pressure_drums[6],
|
||||||
|
ABS_HAT3Y, 0x0e);
|
||||||
|
|
||||||
|
input_report_abs(wdata->extension.input, ABS_X, sx - 0x20);
|
||||||
|
input_report_abs(wdata->extension.input, ABS_Y, sy - 0x20);
|
||||||
|
|
||||||
|
input_report_key(wdata->extension.input, BTN_START, bp);
|
||||||
|
input_report_key(wdata->extension.input, BTN_SELECT, bm);
|
||||||
|
|
||||||
|
input_sync(wdata->extension.input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wiimod_drums_open(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct wiimote_data *wdata = input_get_drvdata(dev);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||||
|
wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
|
||||||
|
wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
|
||||||
|
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wiimod_drums_close(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct wiimote_data *wdata = input_get_drvdata(dev);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||||
|
wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
|
||||||
|
wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
|
||||||
|
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wiimod_drums_probe(const struct wiimod_ops *ops,
|
||||||
|
struct wiimote_data *wdata)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
wdata->extension.input = input_allocate_device();
|
||||||
|
if (!wdata->extension.input)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
input_set_drvdata(wdata->extension.input, wdata);
|
||||||
|
wdata->extension.input->open = wiimod_drums_open;
|
||||||
|
wdata->extension.input->close = wiimod_drums_close;
|
||||||
|
wdata->extension.input->dev.parent = &wdata->hdev->dev;
|
||||||
|
wdata->extension.input->id.bustype = wdata->hdev->bus;
|
||||||
|
wdata->extension.input->id.vendor = wdata->hdev->vendor;
|
||||||
|
wdata->extension.input->id.product = wdata->hdev->product;
|
||||||
|
wdata->extension.input->id.version = wdata->hdev->version;
|
||||||
|
wdata->extension.input->name = WIIMOTE_NAME " Drums";
|
||||||
|
|
||||||
|
set_bit(EV_KEY, wdata->extension.input->evbit);
|
||||||
|
set_bit(BTN_START, wdata->extension.input->keybit);
|
||||||
|
set_bit(BTN_SELECT, wdata->extension.input->keybit);
|
||||||
|
|
||||||
|
set_bit(EV_ABS, wdata->extension.input->evbit);
|
||||||
|
set_bit(ABS_X, wdata->extension.input->absbit);
|
||||||
|
set_bit(ABS_Y, wdata->extension.input->absbit);
|
||||||
|
set_bit(ABS_HAT0X, wdata->extension.input->absbit);
|
||||||
|
set_bit(ABS_HAT0Y, wdata->extension.input->absbit);
|
||||||
|
set_bit(ABS_HAT1X, wdata->extension.input->absbit);
|
||||||
|
set_bit(ABS_HAT2X, wdata->extension.input->absbit);
|
||||||
|
set_bit(ABS_HAT2Y, wdata->extension.input->absbit);
|
||||||
|
set_bit(ABS_HAT3X, wdata->extension.input->absbit);
|
||||||
|
set_bit(ABS_HAT3Y, wdata->extension.input->absbit);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_X, -32, 31, 1, 1);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_Y, -32, 31, 1, 1);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_HAT0X, 0, 7, 0, 0);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_HAT0Y, 0, 7, 0, 0);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_HAT1X, 0, 7, 0, 0);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_HAT2X, 0, 7, 0, 0);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_HAT2Y, 0, 7, 0, 0);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_HAT3X, 0, 7, 0, 0);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_HAT3Y, 0, 7, 0, 0);
|
||||||
|
|
||||||
|
ret = input_register_device(wdata->extension.input);
|
||||||
|
if (ret)
|
||||||
|
goto err_free;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free:
|
||||||
|
input_free_device(wdata->extension.input);
|
||||||
|
wdata->extension.input = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wiimod_drums_remove(const struct wiimod_ops *ops,
|
||||||
|
struct wiimote_data *wdata)
|
||||||
|
{
|
||||||
|
if (!wdata->extension.input)
|
||||||
|
return;
|
||||||
|
|
||||||
|
input_unregister_device(wdata->extension.input);
|
||||||
|
wdata->extension.input = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wiimod_ops wiimod_drums = {
|
||||||
|
.flags = 0,
|
||||||
|
.arg = 0,
|
||||||
|
.probe = wiimod_drums_probe,
|
||||||
|
.remove = wiimod_drums_remove,
|
||||||
|
.in_ext = wiimod_drums_in_ext,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Guitar
|
||||||
|
* Guitar-Hero, Rock-Band and other games came bundled with guitars which can
|
||||||
|
* be plugged as extension to a Wiimote.
|
||||||
|
* We create a separate device for guitars and report all information via this
|
||||||
|
* input device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum wiimod_guitar_keys {
|
||||||
|
WIIMOD_GUITAR_KEY_G,
|
||||||
|
WIIMOD_GUITAR_KEY_R,
|
||||||
|
WIIMOD_GUITAR_KEY_Y,
|
||||||
|
WIIMOD_GUITAR_KEY_B,
|
||||||
|
WIIMOD_GUITAR_KEY_O,
|
||||||
|
WIIMOD_GUITAR_KEY_UP,
|
||||||
|
WIIMOD_GUITAR_KEY_DOWN,
|
||||||
|
WIIMOD_GUITAR_KEY_PLUS,
|
||||||
|
WIIMOD_GUITAR_KEY_MINUS,
|
||||||
|
WIIMOD_GUITAR_KEY_NUM,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const __u16 wiimod_guitar_map[] = {
|
||||||
|
BTN_1, /* WIIMOD_GUITAR_KEY_G */
|
||||||
|
BTN_2, /* WIIMOD_GUITAR_KEY_R */
|
||||||
|
BTN_3, /* WIIMOD_GUITAR_KEY_Y */
|
||||||
|
BTN_4, /* WIIMOD_GUITAR_KEY_B */
|
||||||
|
BTN_5, /* WIIMOD_GUITAR_KEY_O */
|
||||||
|
BTN_DPAD_UP, /* WIIMOD_GUITAR_KEY_UP */
|
||||||
|
BTN_DPAD_DOWN, /* WIIMOD_GUITAR_KEY_DOWN */
|
||||||
|
BTN_START, /* WIIMOD_GUITAR_KEY_PLUS */
|
||||||
|
BTN_SELECT, /* WIIMOD_GUITAR_KEY_MINUS */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void wiimod_guitar_in_ext(struct wiimote_data *wdata, const __u8 *ext)
|
||||||
|
{
|
||||||
|
__u8 sx, sy, tb, wb, bd, bm, bp, bo, br, bb, bg, by, bu;
|
||||||
|
|
||||||
|
/* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 1 | 0 | 0 | SX <5:0> |
|
||||||
|
* 2 | 0 | 0 | SY <5:0> |
|
||||||
|
* -----+-----+-----+-----+-----------------------------+
|
||||||
|
* 3 | 0 | 0 | 0 | TB <4:0> |
|
||||||
|
* -----+-----+-----+-----+-----------------------------+
|
||||||
|
* 4 | 0 | 0 | 0 | WB <4:0> |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 5 | 1 | BD | 1 | B- | 1 | B+ | 1 | 1 |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 6 | BO | BR | BB | BG | BY | 1 | 1 | BU |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* All buttons are 0 if pressed
|
||||||
|
*
|
||||||
|
* With Motion+ enabled, it will look like this:
|
||||||
|
* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 1 | 0 | 0 | SX <5:1> | BU |
|
||||||
|
* 2 | 0 | 0 | SY <5:1> | 1 |
|
||||||
|
* -----+-----+-----+-----+-----------------------+-----+
|
||||||
|
* 3 | 0 | 0 | 0 | TB <4:0> |
|
||||||
|
* -----+-----+-----+-----+-----------------------------+
|
||||||
|
* 4 | 0 | 0 | 0 | WB <4:0> |
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 5 | 1 | BD | 1 | B- | 1 | B+ | 1 |XXXXX|
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* 6 | BO | BR | BB | BG | BY | 1 |XXXXX|XXXXX|
|
||||||
|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
*/
|
||||||
|
|
||||||
|
sx = ext[0] & 0x3f;
|
||||||
|
sy = ext[1] & 0x3f;
|
||||||
|
tb = ext[2] & 0x1f;
|
||||||
|
wb = ext[3] & 0x1f;
|
||||||
|
bd = !(ext[4] & 0x40);
|
||||||
|
bm = !(ext[4] & 0x10);
|
||||||
|
bp = !(ext[4] & 0x04);
|
||||||
|
bo = !(ext[5] & 0x80);
|
||||||
|
br = !(ext[5] & 0x40);
|
||||||
|
bb = !(ext[5] & 0x20);
|
||||||
|
bg = !(ext[5] & 0x10);
|
||||||
|
by = !(ext[5] & 0x08);
|
||||||
|
bu = !(ext[5] & 0x01);
|
||||||
|
|
||||||
|
if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
|
||||||
|
bu = !(ext[0] & 0x01);
|
||||||
|
sx &= 0x3e;
|
||||||
|
sy &= 0x3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_report_abs(wdata->extension.input, ABS_X, sx - 0x20);
|
||||||
|
input_report_abs(wdata->extension.input, ABS_Y, sy - 0x20);
|
||||||
|
input_report_abs(wdata->extension.input, ABS_HAT0X, tb);
|
||||||
|
input_report_abs(wdata->extension.input, ABS_HAT1X, wb - 0x10);
|
||||||
|
|
||||||
|
input_report_key(wdata->extension.input,
|
||||||
|
wiimod_guitar_map[WIIMOD_GUITAR_KEY_G],
|
||||||
|
bg);
|
||||||
|
input_report_key(wdata->extension.input,
|
||||||
|
wiimod_guitar_map[WIIMOD_GUITAR_KEY_R],
|
||||||
|
br);
|
||||||
|
input_report_key(wdata->extension.input,
|
||||||
|
wiimod_guitar_map[WIIMOD_GUITAR_KEY_Y],
|
||||||
|
by);
|
||||||
|
input_report_key(wdata->extension.input,
|
||||||
|
wiimod_guitar_map[WIIMOD_GUITAR_KEY_B],
|
||||||
|
bb);
|
||||||
|
input_report_key(wdata->extension.input,
|
||||||
|
wiimod_guitar_map[WIIMOD_GUITAR_KEY_O],
|
||||||
|
bo);
|
||||||
|
input_report_key(wdata->extension.input,
|
||||||
|
wiimod_guitar_map[WIIMOD_GUITAR_KEY_UP],
|
||||||
|
bu);
|
||||||
|
input_report_key(wdata->extension.input,
|
||||||
|
wiimod_guitar_map[WIIMOD_GUITAR_KEY_DOWN],
|
||||||
|
bd);
|
||||||
|
input_report_key(wdata->extension.input,
|
||||||
|
wiimod_guitar_map[WIIMOD_GUITAR_KEY_PLUS],
|
||||||
|
bp);
|
||||||
|
input_report_key(wdata->extension.input,
|
||||||
|
wiimod_guitar_map[WIIMOD_GUITAR_KEY_MINUS],
|
||||||
|
bm);
|
||||||
|
|
||||||
|
input_sync(wdata->extension.input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wiimod_guitar_open(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct wiimote_data *wdata = input_get_drvdata(dev);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||||
|
wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
|
||||||
|
wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
|
||||||
|
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wiimod_guitar_close(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct wiimote_data *wdata = input_get_drvdata(dev);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||||
|
wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
|
||||||
|
wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
|
||||||
|
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wiimod_guitar_probe(const struct wiimod_ops *ops,
|
||||||
|
struct wiimote_data *wdata)
|
||||||
|
{
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
wdata->extension.input = input_allocate_device();
|
||||||
|
if (!wdata->extension.input)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
input_set_drvdata(wdata->extension.input, wdata);
|
||||||
|
wdata->extension.input->open = wiimod_guitar_open;
|
||||||
|
wdata->extension.input->close = wiimod_guitar_close;
|
||||||
|
wdata->extension.input->dev.parent = &wdata->hdev->dev;
|
||||||
|
wdata->extension.input->id.bustype = wdata->hdev->bus;
|
||||||
|
wdata->extension.input->id.vendor = wdata->hdev->vendor;
|
||||||
|
wdata->extension.input->id.product = wdata->hdev->product;
|
||||||
|
wdata->extension.input->id.version = wdata->hdev->version;
|
||||||
|
wdata->extension.input->name = WIIMOTE_NAME " Guitar";
|
||||||
|
|
||||||
|
set_bit(EV_KEY, wdata->extension.input->evbit);
|
||||||
|
for (i = 0; i < WIIMOD_GUITAR_KEY_NUM; ++i)
|
||||||
|
set_bit(wiimod_guitar_map[i],
|
||||||
|
wdata->extension.input->keybit);
|
||||||
|
|
||||||
|
set_bit(EV_ABS, wdata->extension.input->evbit);
|
||||||
|
set_bit(ABS_X, wdata->extension.input->absbit);
|
||||||
|
set_bit(ABS_Y, wdata->extension.input->absbit);
|
||||||
|
set_bit(ABS_HAT0X, wdata->extension.input->absbit);
|
||||||
|
set_bit(ABS_HAT1X, wdata->extension.input->absbit);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_X, -32, 31, 1, 1);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_Y, -32, 31, 1, 1);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_HAT0X, 0, 0x1f, 1, 1);
|
||||||
|
input_set_abs_params(wdata->extension.input,
|
||||||
|
ABS_HAT1X, 0, 0x0f, 1, 1);
|
||||||
|
|
||||||
|
ret = input_register_device(wdata->extension.input);
|
||||||
|
if (ret)
|
||||||
|
goto err_free;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free:
|
||||||
|
input_free_device(wdata->extension.input);
|
||||||
|
wdata->extension.input = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wiimod_guitar_remove(const struct wiimod_ops *ops,
|
||||||
|
struct wiimote_data *wdata)
|
||||||
|
{
|
||||||
|
if (!wdata->extension.input)
|
||||||
|
return;
|
||||||
|
|
||||||
|
input_unregister_device(wdata->extension.input);
|
||||||
|
wdata->extension.input = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wiimod_ops wiimod_guitar = {
|
||||||
|
.flags = 0,
|
||||||
|
.arg = 0,
|
||||||
|
.probe = wiimod_guitar_probe,
|
||||||
|
.remove = wiimod_guitar_remove,
|
||||||
|
.in_ext = wiimod_guitar_in_ext,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Builtin Motion Plus
|
* Builtin Motion Plus
|
||||||
* This module simply sets the WIIPROTO_FLAG_BUILTIN_MP protocol flag which
|
* This module simply sets the WIIPROTO_FLAG_BUILTIN_MP protocol flag which
|
||||||
|
@ -2201,4 +2639,6 @@ const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = {
|
||||||
[WIIMOTE_EXT_CLASSIC_CONTROLLER] = &wiimod_classic,
|
[WIIMOTE_EXT_CLASSIC_CONTROLLER] = &wiimod_classic,
|
||||||
[WIIMOTE_EXT_BALANCE_BOARD] = &wiimod_bboard,
|
[WIIMOTE_EXT_BALANCE_BOARD] = &wiimod_bboard,
|
||||||
[WIIMOTE_EXT_PRO_CONTROLLER] = &wiimod_pro,
|
[WIIMOTE_EXT_PRO_CONTROLLER] = &wiimod_pro,
|
||||||
|
[WIIMOTE_EXT_DRUMS] = &wiimod_drums,
|
||||||
|
[WIIMOTE_EXT_GUITAR] = &wiimod_guitar,
|
||||||
};
|
};
|
||||||
|
|
|
@ -89,6 +89,8 @@ enum wiimote_exttype {
|
||||||
WIIMOTE_EXT_CLASSIC_CONTROLLER,
|
WIIMOTE_EXT_CLASSIC_CONTROLLER,
|
||||||
WIIMOTE_EXT_BALANCE_BOARD,
|
WIIMOTE_EXT_BALANCE_BOARD,
|
||||||
WIIMOTE_EXT_PRO_CONTROLLER,
|
WIIMOTE_EXT_PRO_CONTROLLER,
|
||||||
|
WIIMOTE_EXT_DRUMS,
|
||||||
|
WIIMOTE_EXT_GUITAR,
|
||||||
WIIMOTE_EXT_NUM,
|
WIIMOTE_EXT_NUM,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -137,6 +139,7 @@ struct wiimote_state {
|
||||||
/* calibration/cache data */
|
/* calibration/cache data */
|
||||||
__u16 calib_bboard[4][3];
|
__u16 calib_bboard[4][3];
|
||||||
__s16 calib_pro_sticks[4];
|
__s16 calib_pro_sticks[4];
|
||||||
|
__u8 pressure_drums[7];
|
||||||
__u8 cache_rumble;
|
__u8 cache_rumble;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1002,18 +1002,18 @@ static int i2c_hid_probe(struct i2c_client *client,
|
||||||
return client->irq;
|
return client->irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL);
|
ihid = devm_kzalloc(&client->dev, sizeof(*ihid), GFP_KERNEL);
|
||||||
if (!ihid)
|
if (!ihid)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (client->dev.of_node) {
|
if (client->dev.of_node) {
|
||||||
ret = i2c_hid_of_probe(client, &ihid->pdata);
|
ret = i2c_hid_of_probe(client, &ihid->pdata);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
return ret;
|
||||||
} else if (!platform_data) {
|
} else if (!platform_data) {
|
||||||
ret = i2c_hid_acpi_pdata(client, &ihid->pdata);
|
ret = i2c_hid_acpi_pdata(client, &ihid->pdata);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
ihid->pdata = *platform_data;
|
ihid->pdata = *platform_data;
|
||||||
}
|
}
|
||||||
|
@ -1021,21 +1021,20 @@ static int i2c_hid_probe(struct i2c_client *client,
|
||||||
/* Parse platform agnostic common properties from ACPI / device tree */
|
/* Parse platform agnostic common properties from ACPI / device tree */
|
||||||
i2c_hid_fwnode_probe(client, &ihid->pdata);
|
i2c_hid_fwnode_probe(client, &ihid->pdata);
|
||||||
|
|
||||||
ihid->pdata.supply = devm_regulator_get(&client->dev, "vdd");
|
ihid->pdata.supplies[0].supply = "vdd";
|
||||||
if (IS_ERR(ihid->pdata.supply)) {
|
ihid->pdata.supplies[1].supply = "vddl";
|
||||||
ret = PTR_ERR(ihid->pdata.supply);
|
|
||||||
if (ret != -EPROBE_DEFER)
|
ret = devm_regulator_bulk_get(&client->dev,
|
||||||
dev_err(&client->dev, "Failed to get regulator: %d\n",
|
ARRAY_SIZE(ihid->pdata.supplies),
|
||||||
ret);
|
ihid->pdata.supplies);
|
||||||
goto err;
|
if (ret)
|
||||||
}
|
return ret;
|
||||||
|
|
||||||
|
ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||||
|
ihid->pdata.supplies);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
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)
|
if (ihid->pdata.post_power_delay_ms)
|
||||||
msleep(ihid->pdata.post_power_delay_ms);
|
msleep(ihid->pdata.post_power_delay_ms);
|
||||||
|
|
||||||
|
@ -1122,11 +1121,9 @@ err_pm:
|
||||||
pm_runtime_disable(&client->dev);
|
pm_runtime_disable(&client->dev);
|
||||||
|
|
||||||
err_regulator:
|
err_regulator:
|
||||||
regulator_disable(ihid->pdata.supply);
|
regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||||
|
ihid->pdata.supplies);
|
||||||
err:
|
|
||||||
i2c_hid_free_buffers(ihid);
|
i2c_hid_free_buffers(ihid);
|
||||||
kfree(ihid);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1148,9 +1145,8 @@ static int i2c_hid_remove(struct i2c_client *client)
|
||||||
if (ihid->bufsize)
|
if (ihid->bufsize)
|
||||||
i2c_hid_free_buffers(ihid);
|
i2c_hid_free_buffers(ihid);
|
||||||
|
|
||||||
regulator_disable(ihid->pdata.supply);
|
regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||||
|
ihid->pdata.supplies);
|
||||||
kfree(ihid);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1201,9 +1197,8 @@ static int i2c_hid_suspend(struct device *dev)
|
||||||
hid_warn(hid, "Failed to enable irq wake: %d\n",
|
hid_warn(hid, "Failed to enable irq wake: %d\n",
|
||||||
wake_status);
|
wake_status);
|
||||||
} else {
|
} else {
|
||||||
ret = regulator_disable(ihid->pdata.supply);
|
regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||||
if (ret < 0)
|
ihid->pdata.supplies);
|
||||||
hid_warn(hid, "Failed to disable supply: %d\n", ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1218,9 +1213,11 @@ static int i2c_hid_resume(struct device *dev)
|
||||||
int wake_status;
|
int wake_status;
|
||||||
|
|
||||||
if (!device_may_wakeup(&client->dev)) {
|
if (!device_may_wakeup(&client->dev)) {
|
||||||
ret = regulator_enable(ihid->pdata.supply);
|
ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||||
if (ret < 0)
|
ihid->pdata.supplies);
|
||||||
hid_warn(hid, "Failed to enable supply: %d\n", ret);
|
if (ret)
|
||||||
|
hid_warn(hid, "Failed to enable supplies: %d\n", ret);
|
||||||
|
|
||||||
if (ihid->pdata.post_power_delay_ms)
|
if (ihid->pdata.post_power_delay_ms)
|
||||||
msleep(ihid->pdata.post_power_delay_ms);
|
msleep(ihid->pdata.post_power_delay_ms);
|
||||||
} else if (ihid->irq_wake_enabled) {
|
} else if (ihid->irq_wake_enabled) {
|
||||||
|
|
|
@ -907,8 +907,9 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
|
||||||
struct ishtp_device *dev;
|
struct ishtp_device *dev;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
dev = kzalloc(sizeof(struct ishtp_device) + sizeof(struct ish_hw),
|
dev = devm_kzalloc(&pdev->dev,
|
||||||
GFP_KERNEL);
|
sizeof(struct ishtp_device) + sizeof(struct ish_hw),
|
||||||
|
GFP_KERNEL);
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -925,7 +926,9 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
|
||||||
for (i = 0; i < IPC_TX_FIFO_SIZE; ++i) {
|
for (i = 0; i < IPC_TX_FIFO_SIZE; ++i) {
|
||||||
struct wr_msg_ctl_info *tx_buf;
|
struct wr_msg_ctl_info *tx_buf;
|
||||||
|
|
||||||
tx_buf = kzalloc(sizeof(struct wr_msg_ctl_info), GFP_KERNEL);
|
tx_buf = devm_kzalloc(&pdev->dev,
|
||||||
|
sizeof(struct wr_msg_ctl_info),
|
||||||
|
GFP_KERNEL);
|
||||||
if (!tx_buf) {
|
if (!tx_buf) {
|
||||||
/*
|
/*
|
||||||
* IPC buffers may be limited or not available
|
* IPC buffers may be limited or not available
|
||||||
|
|
|
@ -95,6 +95,13 @@ static int ish_init(struct ishtp_device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct pci_device_id ish_invalid_pci_ids[] = {
|
||||||
|
/* Mehlow platform special pci ids */
|
||||||
|
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA309)},
|
||||||
|
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA30A)},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ish_probe() - PCI driver probe callback
|
* ish_probe() - PCI driver probe callback
|
||||||
* @pdev: pci device
|
* @pdev: pci device
|
||||||
|
@ -110,6 +117,10 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||||
struct ish_hw *hw;
|
struct ish_hw *hw;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* Check for invalid platforms for ISH support */
|
||||||
|
if (pci_dev_present(ish_invalid_pci_ids))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
/* enable pci dev */
|
/* enable pci dev */
|
||||||
ret = pci_enable_device(pdev);
|
ret = pci_enable_device(pdev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -172,7 +183,6 @@ free_irq:
|
||||||
free_irq(pdev->irq, dev);
|
free_irq(pdev->irq, dev);
|
||||||
free_device:
|
free_device:
|
||||||
pci_iounmap(pdev, hw->mem_addr);
|
pci_iounmap(pdev, hw->mem_addr);
|
||||||
kfree(dev);
|
|
||||||
release_regions:
|
release_regions:
|
||||||
pci_release_regions(pdev);
|
pci_release_regions(pdev);
|
||||||
disable_device:
|
disable_device:
|
||||||
|
@ -202,7 +212,6 @@ static void ish_remove(struct pci_dev *pdev)
|
||||||
pci_release_regions(pdev);
|
pci_release_regions(pdev);
|
||||||
pci_clear_master(pdev);
|
pci_clear_master(pdev);
|
||||||
pci_disable_device(pdev);
|
pci_disable_device(pdev);
|
||||||
kfree(ishtp_dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct device __maybe_unused *ish_resume_device;
|
static struct device __maybe_unused *ish_resume_device;
|
||||||
|
|
|
@ -298,7 +298,6 @@ int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev,
|
||||||
struct ishtp_msg_hdr *ishtp_hdr = &hdr;
|
struct ishtp_msg_hdr *ishtp_hdr = &hdr;
|
||||||
const size_t len = sizeof(struct hbm_flow_control);
|
const size_t len = sizeof(struct hbm_flow_control);
|
||||||
int rv;
|
int rv;
|
||||||
unsigned int num_frags;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&cl->fc_spinlock, flags);
|
spin_lock_irqsave(&cl->fc_spinlock, flags);
|
||||||
|
@ -314,7 +313,6 @@ int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
num_frags = cl->recv_msg_num_frags;
|
|
||||||
cl->recv_msg_num_frags = 0;
|
cl->recv_msg_num_frags = 0;
|
||||||
|
|
||||||
rv = ishtp_write_message(dev, ishtp_hdr, data);
|
rv = ishtp_write_message(dev, ishtp_hdr, data);
|
||||||
|
|
|
@ -480,6 +480,7 @@ static void hid_ctrl(struct urb *urb)
|
||||||
{
|
{
|
||||||
struct hid_device *hid = urb->context;
|
struct hid_device *hid = urb->context;
|
||||||
struct usbhid_device *usbhid = hid->driver_data;
|
struct usbhid_device *usbhid = hid->driver_data;
|
||||||
|
unsigned long flags;
|
||||||
int unplug = 0, status = urb->status;
|
int unplug = 0, status = urb->status;
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
|
@ -501,7 +502,7 @@ static void hid_ctrl(struct urb *urb)
|
||||||
hid_warn(urb->dev, "ctrl urb status %d received\n", status);
|
hid_warn(urb->dev, "ctrl urb status %d received\n", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock(&usbhid->lock);
|
spin_lock_irqsave(&usbhid->lock, flags);
|
||||||
|
|
||||||
if (unplug) {
|
if (unplug) {
|
||||||
usbhid->ctrltail = usbhid->ctrlhead;
|
usbhid->ctrltail = usbhid->ctrlhead;
|
||||||
|
@ -511,13 +512,13 @@ static void hid_ctrl(struct urb *urb)
|
||||||
if (usbhid->ctrlhead != usbhid->ctrltail &&
|
if (usbhid->ctrlhead != usbhid->ctrltail &&
|
||||||
hid_submit_ctrl(hid) == 0) {
|
hid_submit_ctrl(hid) == 0) {
|
||||||
/* Successfully submitted next urb in queue */
|
/* Successfully submitted next urb in queue */
|
||||||
spin_unlock(&usbhid->lock);
|
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
||||||
spin_unlock(&usbhid->lock);
|
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||||
usb_autopm_put_interface_async(usbhid->intf);
|
usb_autopm_put_interface_async(usbhid->intf);
|
||||||
wake_up(&usbhid->wait);
|
wake_up(&usbhid->wait);
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,6 +210,57 @@ static int wacom_calc_hid_res(int logical_extents, int physical_extents,
|
||||||
return hidinput_calc_abs_res(&field, ABS_X);
|
return hidinput_calc_abs_res(&field, ABS_X);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wacom_hid_usage_quirk(struct hid_device *hdev,
|
||||||
|
struct hid_field *field, struct hid_usage *usage)
|
||||||
|
{
|
||||||
|
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||||
|
struct wacom_features *features = &wacom->wacom_wac.features;
|
||||||
|
unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Dell Canvas 27 needs to be switched to its vendor-defined
|
||||||
|
* report to provide the best resolution.
|
||||||
|
*/
|
||||||
|
if (hdev->vendor == USB_VENDOR_ID_WACOM &&
|
||||||
|
hdev->product == 0x4200 &&
|
||||||
|
field->application == HID_UP_MSVENDOR) {
|
||||||
|
wacom->wacom_wac.mode_report = field->report->id;
|
||||||
|
wacom->wacom_wac.mode_value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ISDv4 devices which predate HID's adoption of the
|
||||||
|
* HID_DG_BARELSWITCH2 usage use 0x000D0000 in its
|
||||||
|
* position instead. We can accurately detect if a
|
||||||
|
* usage with that value should be HID_DG_BARRELSWITCH2
|
||||||
|
* based on the surrounding usages, which have remained
|
||||||
|
* constant across generations.
|
||||||
|
*/
|
||||||
|
if (features->type == HID_GENERIC &&
|
||||||
|
usage->hid == 0x000D0000 &&
|
||||||
|
field->application == HID_DG_PEN &&
|
||||||
|
field->physical == HID_DG_STYLUS) {
|
||||||
|
int i = usage->usage_index;
|
||||||
|
|
||||||
|
if (i-4 >= 0 && i+1 < field->maxusage &&
|
||||||
|
field->usage[i-4].hid == HID_DG_TIPSWITCH &&
|
||||||
|
field->usage[i-3].hid == HID_DG_BARRELSWITCH &&
|
||||||
|
field->usage[i-2].hid == HID_DG_ERASER &&
|
||||||
|
field->usage[i-1].hid == HID_DG_INVERT &&
|
||||||
|
field->usage[i+1].hid == HID_DG_INRANGE) {
|
||||||
|
usage->hid = HID_DG_BARRELSWITCH2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2nd-generation Intuos Pro Large has incorrect Y maximum */
|
||||||
|
if (hdev->vendor == USB_VENDOR_ID_WACOM &&
|
||||||
|
hdev->product == 0x0358 &&
|
||||||
|
WACOM_PEN_FIELD(field) &&
|
||||||
|
equivalent_usage == HID_GD_Y) {
|
||||||
|
field->logical_maximum = 43200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void wacom_feature_mapping(struct hid_device *hdev,
|
static void wacom_feature_mapping(struct hid_device *hdev,
|
||||||
struct hid_field *field, struct hid_usage *usage)
|
struct hid_field *field, struct hid_usage *usage)
|
||||||
{
|
{
|
||||||
|
@ -221,6 +272,8 @@ static void wacom_feature_mapping(struct hid_device *hdev,
|
||||||
int ret;
|
int ret;
|
||||||
u32 n;
|
u32 n;
|
||||||
|
|
||||||
|
wacom_hid_usage_quirk(hdev, field, usage);
|
||||||
|
|
||||||
switch (equivalent_usage) {
|
switch (equivalent_usage) {
|
||||||
case HID_DG_CONTACTMAX:
|
case HID_DG_CONTACTMAX:
|
||||||
/* leave touch_max as is if predefined */
|
/* leave touch_max as is if predefined */
|
||||||
|
@ -300,13 +353,6 @@ static void wacom_feature_mapping(struct hid_device *hdev,
|
||||||
kfree(data);
|
kfree(data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdev->vendor == USB_VENDOR_ID_WACOM &&
|
|
||||||
hdev->product == 0x4200 /* Dell Canvas 27 */ &&
|
|
||||||
field->application == HID_UP_MSVENDOR) {
|
|
||||||
wacom->wacom_wac.mode_report = field->report->id;
|
|
||||||
wacom->wacom_wac.mode_value = 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -348,6 +394,7 @@ static void wacom_usage_mapping(struct hid_device *hdev,
|
||||||
struct wacom_features *features = &wacom->wacom_wac.features;
|
struct wacom_features *features = &wacom->wacom_wac.features;
|
||||||
bool finger = WACOM_FINGER_FIELD(field);
|
bool finger = WACOM_FINGER_FIELD(field);
|
||||||
bool pen = WACOM_PEN_FIELD(field);
|
bool pen = WACOM_PEN_FIELD(field);
|
||||||
|
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Requiring Stylus Usage will ignore boot mouse
|
* Requiring Stylus Usage will ignore boot mouse
|
||||||
|
@ -361,49 +408,9 @@ static void wacom_usage_mapping(struct hid_device *hdev,
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
wacom_hid_usage_quirk(hdev, field, usage);
|
||||||
* Bamboo models do not support HID_DG_CONTACTMAX.
|
|
||||||
* And, Bamboo Pen only descriptor contains touch.
|
|
||||||
*/
|
|
||||||
if (features->type > BAMBOO_PT) {
|
|
||||||
/* ISDv4 touch devices at least supports one touch point */
|
|
||||||
if (finger && !features->touch_max)
|
|
||||||
features->touch_max = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
switch (equivalent_usage) {
|
||||||
* ISDv4 devices which predate HID's adoption of the
|
|
||||||
* HID_DG_BARELSWITCH2 usage use 0x000D0000 in its
|
|
||||||
* position instead. We can accurately detect if a
|
|
||||||
* usage with that value should be HID_DG_BARRELSWITCH2
|
|
||||||
* based on the surrounding usages, which have remained
|
|
||||||
* constant across generations.
|
|
||||||
*/
|
|
||||||
if (features->type == HID_GENERIC &&
|
|
||||||
usage->hid == 0x000D0000 &&
|
|
||||||
field->application == HID_DG_PEN &&
|
|
||||||
field->physical == HID_DG_STYLUS) {
|
|
||||||
int i = usage->usage_index;
|
|
||||||
|
|
||||||
if (i-4 >= 0 && i+1 < field->maxusage &&
|
|
||||||
field->usage[i-4].hid == HID_DG_TIPSWITCH &&
|
|
||||||
field->usage[i-3].hid == HID_DG_BARRELSWITCH &&
|
|
||||||
field->usage[i-2].hid == HID_DG_ERASER &&
|
|
||||||
field->usage[i-1].hid == HID_DG_INVERT &&
|
|
||||||
field->usage[i+1].hid == HID_DG_INRANGE) {
|
|
||||||
usage->hid = HID_DG_BARRELSWITCH2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 2nd-generation Intuos Pro Large has incorrect Y maximum */
|
|
||||||
if (hdev->vendor == USB_VENDOR_ID_WACOM &&
|
|
||||||
hdev->product == 0x0358 &&
|
|
||||||
WACOM_PEN_FIELD(field) &&
|
|
||||||
wacom_equivalent_usage(usage->hid) == HID_GD_Y) {
|
|
||||||
field->logical_maximum = 43200;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (usage->hid) {
|
|
||||||
case HID_GD_X:
|
case HID_GD_X:
|
||||||
features->x_max = field->logical_maximum;
|
features->x_max = field->logical_maximum;
|
||||||
if (finger) {
|
if (finger) {
|
||||||
|
@ -703,18 +710,6 @@ struct wacom_hdev_data {
|
||||||
static LIST_HEAD(wacom_udev_list);
|
static LIST_HEAD(wacom_udev_list);
|
||||||
static DEFINE_MUTEX(wacom_udev_list_lock);
|
static DEFINE_MUTEX(wacom_udev_list_lock);
|
||||||
|
|
||||||
static bool compare_device_paths(struct hid_device *hdev_a,
|
|
||||||
struct hid_device *hdev_b, char separator)
|
|
||||||
{
|
|
||||||
int n1 = strrchr(hdev_a->phys, separator) - hdev_a->phys;
|
|
||||||
int n2 = strrchr(hdev_b->phys, separator) - hdev_b->phys;
|
|
||||||
|
|
||||||
if (n1 != n2 || n1 <= 0 || n2 <= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return !strncmp(hdev_a->phys, hdev_b->phys, n1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool wacom_are_sibling(struct hid_device *hdev,
|
static bool wacom_are_sibling(struct hid_device *hdev,
|
||||||
struct hid_device *sibling)
|
struct hid_device *sibling)
|
||||||
{
|
{
|
||||||
|
@ -737,10 +732,10 @@ static bool wacom_are_sibling(struct hid_device *hdev,
|
||||||
* the same physical parent device path.
|
* the same physical parent device path.
|
||||||
*/
|
*/
|
||||||
if (hdev->vendor == sibling->vendor && hdev->product == sibling->product) {
|
if (hdev->vendor == sibling->vendor && hdev->product == sibling->product) {
|
||||||
if (!compare_device_paths(hdev, sibling, '/'))
|
if (!hid_compare_device_paths(hdev, sibling, '/'))
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!compare_device_paths(hdev, sibling, '.'))
|
if (!hid_compare_device_paths(hdev, sibling, '.'))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -787,7 +782,7 @@ static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev)
|
||||||
|
|
||||||
/* Try to find an already-probed interface from the same device */
|
/* Try to find an already-probed interface from the same device */
|
||||||
list_for_each_entry(data, &wacom_udev_list, list) {
|
list_for_each_entry(data, &wacom_udev_list, list) {
|
||||||
if (compare_device_paths(hdev, data->dev, '/')) {
|
if (hid_compare_device_paths(hdev, data->dev, '/')) {
|
||||||
kref_get(&data->kref);
|
kref_get(&data->kref);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4357,19 +4357,19 @@ static const struct wacom_features wacom_features_0x5E =
|
||||||
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
|
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
|
||||||
static const struct wacom_features wacom_features_0x90 =
|
static const struct wacom_features wacom_features_0x90 =
|
||||||
{ "Wacom ISDv4 90", 26202, 16325, 255, 0,
|
{ "Wacom ISDv4 90", 26202, 16325, 255, 0,
|
||||||
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; /* Pen-only */
|
||||||
static const struct wacom_features wacom_features_0x93 =
|
static const struct wacom_features wacom_features_0x93 =
|
||||||
{ "Wacom ISDv4 93", 26202, 16325, 255, 0,
|
{ "Wacom ISDv4 93", 26202, 16325, 255, 0,
|
||||||
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 1 };
|
||||||
static const struct wacom_features wacom_features_0x97 =
|
static const struct wacom_features wacom_features_0x97 =
|
||||||
{ "Wacom ISDv4 97", 26202, 16325, 511, 0,
|
{ "Wacom ISDv4 97", 26202, 16325, 511, 0,
|
||||||
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; /* Pen-only */
|
||||||
static const struct wacom_features wacom_features_0x9A =
|
static const struct wacom_features wacom_features_0x9A =
|
||||||
{ "Wacom ISDv4 9A", 26202, 16325, 255, 0,
|
{ "Wacom ISDv4 9A", 26202, 16325, 255, 0,
|
||||||
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 1 };
|
||||||
static const struct wacom_features wacom_features_0x9F =
|
static const struct wacom_features wacom_features_0x9F =
|
||||||
{ "Wacom ISDv4 9F", 26202, 16325, 255, 0,
|
{ "Wacom ISDv4 9F", 26202, 16325, 255, 0,
|
||||||
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 1 };
|
||||||
static const struct wacom_features wacom_features_0xE2 =
|
static const struct wacom_features wacom_features_0xE2 =
|
||||||
{ "Wacom ISDv4 E2", 26202, 16325, 255, 0,
|
{ "Wacom ISDv4 E2", 26202, 16325, 255, 0,
|
||||||
TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
|
TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
|
||||||
|
@ -4384,13 +4384,13 @@ static const struct wacom_features wacom_features_0xE6 =
|
||||||
TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
|
TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
|
||||||
static const struct wacom_features wacom_features_0xEC =
|
static const struct wacom_features wacom_features_0xEC =
|
||||||
{ "Wacom ISDv4 EC", 25710, 14500, 255, 0,
|
{ "Wacom ISDv4 EC", 25710, 14500, 255, 0,
|
||||||
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; /* Pen-only */
|
||||||
static const struct wacom_features wacom_features_0xED =
|
static const struct wacom_features wacom_features_0xED =
|
||||||
{ "Wacom ISDv4 ED", 26202, 16325, 255, 0,
|
{ "Wacom ISDv4 ED", 26202, 16325, 255, 0,
|
||||||
TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 1 };
|
||||||
static const struct wacom_features wacom_features_0xEF =
|
static const struct wacom_features wacom_features_0xEF =
|
||||||
{ "Wacom ISDv4 EF", 26202, 16325, 255, 0,
|
{ "Wacom ISDv4 EF", 26202, 16325, 255, 0,
|
||||||
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; /* Pen-only */
|
||||||
static const struct wacom_features wacom_features_0x100 =
|
static const struct wacom_features wacom_features_0x100 =
|
||||||
{ "Wacom ISDv4 100", 26202, 16325, 255, 0,
|
{ "Wacom ISDv4 100", 26202, 16325, 255, 0,
|
||||||
MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||||
|
@ -4408,10 +4408,10 @@ static const struct wacom_features wacom_features_0x10F =
|
||||||
MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||||
static const struct wacom_features wacom_features_0x116 =
|
static const struct wacom_features wacom_features_0x116 =
|
||||||
{ "Wacom ISDv4 116", 26202, 16325, 255, 0,
|
{ "Wacom ISDv4 116", 26202, 16325, 255, 0,
|
||||||
TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 1 };
|
||||||
static const struct wacom_features wacom_features_0x12C =
|
static const struct wacom_features wacom_features_0x12C =
|
||||||
{ "Wacom ISDv4 12C", 27848, 15752, 2047, 0,
|
{ "Wacom ISDv4 12C", 27848, 15752, 2047, 0,
|
||||||
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; /* Pen-only */
|
||||||
static const struct wacom_features wacom_features_0x4001 =
|
static const struct wacom_features wacom_features_0x4001 =
|
||||||
{ "Wacom ISDv4 4001", 26202, 16325, 255, 0,
|
{ "Wacom ISDv4 4001", 26202, 16325, 255, 0,
|
||||||
MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||||
|
|
|
@ -190,6 +190,12 @@ struct hid_item {
|
||||||
* http://www.usb.org/developers/hidpage/HUTRR40RadioHIDUsagesFinal.pdf
|
* http://www.usb.org/developers/hidpage/HUTRR40RadioHIDUsagesFinal.pdf
|
||||||
*/
|
*/
|
||||||
#define HID_GD_WIRELESS_RADIO_CTLS 0x0001000c
|
#define HID_GD_WIRELESS_RADIO_CTLS 0x0001000c
|
||||||
|
/*
|
||||||
|
* System Multi-Axis, see:
|
||||||
|
* http://www.usb.org/developers/hidpage/HUTRR62_-_Generic_Desktop_CA_for_System_Multi-Axis_Controllers.txt
|
||||||
|
*/
|
||||||
|
#define HID_GD_SYSTEM_MULTIAXIS 0x0001000e
|
||||||
|
|
||||||
#define HID_GD_X 0x00010030
|
#define HID_GD_X 0x00010030
|
||||||
#define HID_GD_Y 0x00010031
|
#define HID_GD_Y 0x00010031
|
||||||
#define HID_GD_Z 0x00010032
|
#define HID_GD_Z 0x00010032
|
||||||
|
@ -638,12 +644,13 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data)
|
||||||
struct hid_parser {
|
struct hid_parser {
|
||||||
struct hid_global global;
|
struct hid_global global;
|
||||||
struct hid_global global_stack[HID_GLOBAL_STACK_SIZE];
|
struct hid_global global_stack[HID_GLOBAL_STACK_SIZE];
|
||||||
unsigned global_stack_ptr;
|
unsigned int global_stack_ptr;
|
||||||
struct hid_local local;
|
struct hid_local local;
|
||||||
unsigned collection_stack[HID_COLLECTION_STACK_SIZE];
|
unsigned int *collection_stack;
|
||||||
unsigned collection_stack_ptr;
|
unsigned int collection_stack_ptr;
|
||||||
|
unsigned int collection_stack_size;
|
||||||
struct hid_device *device;
|
struct hid_device *device;
|
||||||
unsigned scan_flags;
|
unsigned int scan_flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hid_class_descriptor {
|
struct hid_class_descriptor {
|
||||||
|
@ -894,6 +901,8 @@ const struct hid_device_id *hid_match_id(const struct hid_device *hdev,
|
||||||
const struct hid_device_id *id);
|
const struct hid_device_id *id);
|
||||||
const struct hid_device_id *hid_match_device(struct hid_device *hdev,
|
const struct hid_device_id *hid_match_device(struct hid_device *hdev,
|
||||||
struct hid_driver *hdrv);
|
struct hid_driver *hdrv);
|
||||||
|
bool hid_compare_device_paths(struct hid_device *hdev_a,
|
||||||
|
struct hid_device *hdev_b, char separator);
|
||||||
s32 hid_snto32(__u32 value, unsigned n);
|
s32 hid_snto32(__u32 value, unsigned n);
|
||||||
__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
|
__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
|
||||||
unsigned offset, unsigned n);
|
unsigned offset, unsigned n);
|
||||||
|
|
|
@ -12,14 +12,13 @@
|
||||||
#ifndef __LINUX_I2C_HID_H
|
#ifndef __LINUX_I2C_HID_H
|
||||||
#define __LINUX_I2C_HID_H
|
#define __LINUX_I2C_HID_H
|
||||||
|
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
struct regulator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct i2chid_platform_data - used by hid over i2c implementation.
|
* struct i2chid_platform_data - used by hid over i2c implementation.
|
||||||
* @hid_descriptor_address: i2c register where the HID descriptor is stored.
|
* @hid_descriptor_address: i2c register where the HID descriptor is stored.
|
||||||
* @supply: regulator for powering on the device.
|
* @supplies: regulators for powering on the device.
|
||||||
* @post_power_delay_ms: delay after powering on before device is usable.
|
* @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
|
* Note that it is the responsibility of the platform driver (or the acpi 5.0
|
||||||
|
@ -35,7 +34,7 @@ struct regulator;
|
||||||
*/
|
*/
|
||||||
struct i2c_hid_platform_data {
|
struct i2c_hid_platform_data {
|
||||||
u16 hid_descriptor_address;
|
u16 hid_descriptor_address;
|
||||||
struct regulator *supply;
|
struct regulator_bulk_data supplies[2];
|
||||||
int post_power_delay_ms;
|
int post_power_delay_ms;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -270,10 +270,11 @@ struct input_mask {
|
||||||
/*
|
/*
|
||||||
* MT_TOOL types
|
* MT_TOOL types
|
||||||
*/
|
*/
|
||||||
#define MT_TOOL_FINGER 0
|
#define MT_TOOL_FINGER 0x00
|
||||||
#define MT_TOOL_PEN 1
|
#define MT_TOOL_PEN 0x01
|
||||||
#define MT_TOOL_PALM 2
|
#define MT_TOOL_PALM 0x02
|
||||||
#define MT_TOOL_MAX 2
|
#define MT_TOOL_DIAL 0x0a
|
||||||
|
#define MT_TOOL_MAX 0x0f
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Values describing the status of a force-feedback effect
|
* Values describing the status of a force-feedback effect
|
||||||
|
|
Loading…
Reference in New Issue