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
27 changed files with 1828 additions and 752 deletions
|
@ -25,7 +25,8 @@ device-specific compatible properties, which should be used in addition to the
|
|||
|
||||
- compatible:
|
||||
* "wacom,w9013" (Wacom W9013 digitizer). Supports:
|
||||
- vdd-supply
|
||||
- vdd-supply (3.3V)
|
||||
- vddl-supply (1.8V)
|
||||
- post-power-on-delay-ms
|
||||
|
||||
- vdd-supply: phandle of the regulator that provides the supply voltage.
|
||||
|
|
|
@ -310,12 +310,12 @@ ABS_MT_TOOL_Y
|
|||
ABS_MT_TOOL_TYPE
|
||||
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
|
||||
event should be omitted. The protocol currently supports MT_TOOL_FINGER,
|
||||
MT_TOOL_PEN, and MT_TOOL_PALM [#f2]_. For type B devices, this event is
|
||||
handled by input core; drivers should instead use
|
||||
input_mt_report_slot_state(). A contact's ABS_MT_TOOL_TYPE may change over
|
||||
time while still touching the device, because the firmware may not be able
|
||||
to determine which tool is being used when it first appears.
|
||||
event should be omitted. The protocol currently mainly supports
|
||||
MT_TOOL_FINGER, MT_TOOL_PEN, and MT_TOOL_PALM [#f2]_.
|
||||
For type B devices, this event is handled by input core; drivers should
|
||||
instead use input_mt_report_slot_state(). A contact's ABS_MT_TOOL_TYPE may
|
||||
change over time while still touching the device, because the firmware may
|
||||
not be able to determine which tool is being used when it first appears.
|
||||
|
||||
ABS_MT_BLOB_ID
|
||||
The BLOB_ID groups several packets together into one arbitrarily shaped
|
||||
|
|
|
@ -207,6 +207,16 @@ config HID_CORSAIR
|
|||
- Vengeance K90
|
||||
- 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
|
||||
tristate "Prodikeys PC-MIDI Keyboard support"
|
||||
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_CMEDIA) += hid-cmedia.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_CYPRESS) += hid-cypress.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];
|
||||
|
||||
if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
|
||||
hid_err(parser->device, "collection stack overflow\n");
|
||||
return -EINVAL;
|
||||
if (parser->collection_stack_ptr == parser->collection_stack_size) {
|
||||
unsigned int *collection_stack;
|
||||
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) {
|
||||
|
@ -840,6 +850,7 @@ static int hid_scan_report(struct hid_device *hid)
|
|||
break;
|
||||
}
|
||||
|
||||
kfree(parser->collection_stack);
|
||||
vfree(parser);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
struct hid_driver *hdrv = to_hid_driver(dev->driver);
|
||||
|
|
312
drivers/hid/hid-cougar.c
Normal file
312
drivers/hid/hid-cougar.c
Normal file
|
@ -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"
|
||||
|
||||
#define ELAN_MT_I2C 0x5d
|
||||
#define ELAN_SINGLE_FINGER 0x81
|
||||
#define ELAN_MT_FIRST_FINGER 0x82
|
||||
#define ELAN_MT_SECOND_FINGER 0x83
|
||||
#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_LED_REPORT_SIZE 8
|
||||
|
||||
struct elan_touchpad_settings {
|
||||
u8 max_fingers;
|
||||
u16 max_x;
|
||||
u16 max_y;
|
||||
u8 max_area_x;
|
||||
u8 max_area_y;
|
||||
u8 max_w;
|
||||
int usb_bInterfaceNumber;
|
||||
};
|
||||
#define ELAN_HAS_LED BIT(0)
|
||||
|
||||
struct elan_drvdata {
|
||||
struct input_dev *input;
|
||||
u8 prev_report[ELAN_INPUT_REPORT_SIZE];
|
||||
struct led_classdev mute_led;
|
||||
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)
|
||||
{
|
||||
if (hdev->bus == BUS_USB) {
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
||||
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,
|
||||
|
@ -62,12 +73,86 @@ static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
|
||||
if (field->report->id == ELAN_SINGLE_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 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)
|
||||
{
|
||||
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))
|
||||
return 0;
|
||||
|
||||
ret = elan_get_device_params(hdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
input = devm_input_allocate_device(&hdev->dev);
|
||||
if (!input)
|
||||
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->dev.parent = &hdev->dev;
|
||||
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
|
||||
drvdata->settings->max_x, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
|
||||
drvdata->settings->max_y, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
|
||||
drvdata->settings->max_fingers, 0, 0);
|
||||
input_set_abs_params(input, ABS_TOOL_WIDTH, 0,
|
||||
drvdata->settings->max_w, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0, drvdata->max_x,
|
||||
0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, drvdata->max_y,
|
||||
0, 0);
|
||||
input_set_abs_params(input, ABS_MT_PRESSURE, 0, ELAN_MAX_PRESSURE,
|
||||
0, 0);
|
||||
|
||||
__set_bit(BTN_LEFT, input->keybit);
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
||||
|
||||
ret = input_mt_init_slots(input, drvdata->settings->max_fingers,
|
||||
INPUT_MT_POINTER);
|
||||
ret = input_mt_init_slots(input, ELAN_MAX_FINGERS, INPUT_MT_POINTER);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Failed to init elan MT slots: %d\n", 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);
|
||||
if (ret) {
|
||||
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)
|
||||
{
|
||||
struct input_dev *input = drvdata->input;
|
||||
int x, y, w;
|
||||
int x, y, p;
|
||||
|
||||
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);
|
||||
if (active) {
|
||||
x = ((data[0] & 0xF0) << 4) | data[1];
|
||||
y = drvdata->settings->max_y -
|
||||
y = drvdata->max_y -
|
||||
(((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_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;
|
||||
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 6: y8 y7 y6 y5 y4 y3 y2 y1
|
||||
* 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:
|
||||
*
|
||||
|
@ -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 4: y8 y7 y6 y5 y4 y3 y2 y1
|
||||
* 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 8: 0 0 0 0 0 0 0 0
|
||||
*
|
||||
* f5-f1: finger touch bits
|
||||
* L: clickpad button
|
||||
* sy / sx: not sure yet, but this looks like rectangular
|
||||
* area for finger
|
||||
* w: looks like finger width
|
||||
* sy / sx: finger width / height expressed in traces, the total number
|
||||
* of traces can be queried by doing a HID_REQ_SET_REPORT
|
||||
* { 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) {
|
||||
for (i = 0; i < drvdata->settings->max_fingers; i++) {
|
||||
for (i = 0; i < ELAN_MAX_FINGERS; i++) {
|
||||
if (data[2] & BIT(i + 3))
|
||||
elan_report_mt_slot(drvdata, data + 3, i);
|
||||
else
|
||||
|
@ -210,7 +301,7 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
|
|||
if (prev_report[0] != ELAN_MT_FIRST_FINGER)
|
||||
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 (!first) {
|
||||
first = 1;
|
||||
|
@ -229,6 +320,46 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
|
|||
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,
|
||||
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_SECOND_FINGER) {
|
||||
if (size == ELAN_INPUT_REPORT_SIZE) {
|
||||
elan_report_input(drvdata, data);
|
||||
elan_usb_report_input(drvdata, data);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (data[0] == ELAN_MT_I2C && size == ELAN_I2C_REPORT_SIZE) {
|
||||
elan_i2c_report_input(drvdata, data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -343,7 +479,6 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->settings = (struct elan_touchpad_settings *)id->driver_data;
|
||||
hid_set_drvdata(hdev, drvdata);
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
|
@ -371,9 +506,11 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (id->driver_data & ELAN_HAS_LED) {
|
||||
ret = elan_init_mute_led(hdev);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
|
@ -386,22 +523,14 @@ static void elan_remove(struct hid_device *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[] = {
|
||||
{ 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),
|
||||
(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);
|
||||
|
||||
static struct hid_driver elan_driver = {
|
||||
|
|
|
@ -369,6 +369,8 @@
|
|||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
|
||||
|
||||
#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_VENDOR_ID_ELECOM 0x056e
|
||||
|
@ -1001,6 +1003,9 @@
|
|||
#define USB_VENDOR_ID_SINO_LITE 0x1345
|
||||
#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_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034
|
||||
#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:
|
||||
suffix = "Wireless Radio Control";
|
||||
break;
|
||||
case HID_GD_SYSTEM_MULTIAXIS:
|
||||
suffix = "System Multi Axis";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -22,12 +22,13 @@
|
|||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define MS_HIDINPUT 0x01
|
||||
#define MS_ERGONOMY 0x02
|
||||
#define MS_PRESENTER 0x04
|
||||
#define MS_RDESC 0x08
|
||||
#define MS_NOGET 0x10
|
||||
#define MS_DUPLICATE_USAGES 0x20
|
||||
#define MS_HIDINPUT BIT(0)
|
||||
#define MS_ERGONOMY BIT(1)
|
||||
#define MS_PRESENTER BIT(2)
|
||||
#define MS_RDESC BIT(3)
|
||||
#define MS_NOGET BIT(4)
|
||||
#define MS_DUPLICATE_USAGES BIT(5)
|
||||
#define MS_SURFACE_DIAL BIT(6)
|
||||
|
||||
static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
|
@ -130,6 +131,30 @@ static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,
|
|||
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,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
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))
|
||||
return 1;
|
||||
|
||||
if (quirks & MS_SURFACE_DIAL) {
|
||||
int ret = ms_surface_dial_quirk(hi, field, usage, bit, max);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -229,6 +261,9 @@ static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
if (quirks & MS_NOGET)
|
||||
hdev->quirks |= HID_QUIRK_NOGET;
|
||||
|
||||
if (quirks & MS_SURFACE_DIAL)
|
||||
hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
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),
|
||||
.driver_data = MS_PRESENTER },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B),
|
||||
.driver_data = MS_SURFACE_DIAL },
|
||||
{ }
|
||||
};
|
||||
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,
|
||||
&ntrig_attribute_group);
|
||||
if (ret)
|
||||
hid_err(hdev, "cannot create sysfs group\n");
|
||||
|
||||
return 0;
|
||||
err_free:
|
||||
|
|
|
@ -44,29 +44,6 @@ static __u8 *redragon_report_fixup(struct hid_device *hdev, __u8 *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[] = {
|
||||
{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 = {
|
||||
.name = "redragon",
|
||||
.id_table = redragon_devices,
|
||||
.report_fixup = redragon_report_fixup,
|
||||
.probe = redragon_probe
|
||||
.report_fixup = redragon_report_fixup
|
||||
};
|
||||
|
||||
module_hid_driver(redragon_driver);
|
||||
|
|
|
@ -1353,7 +1353,7 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
|
|||
char *name;
|
||||
int ret;
|
||||
|
||||
sc->touchpad = input_allocate_device();
|
||||
sc->touchpad = devm_input_allocate_device(&sc->hdev->dev);
|
||||
if (!sc->touchpad)
|
||||
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.
|
||||
*/
|
||||
name_sz = strlen(sc->hdev->name) + sizeof(DS4_TOUCHPAD_SUFFIX);
|
||||
name = kzalloc(name_sz, GFP_KERNEL);
|
||||
if (!name) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
name = devm_kzalloc(&sc->hdev->dev, name_sz, GFP_KERNEL);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
snprintf(name, name_sz, "%s" DS4_TOUCHPAD_SUFFIX, sc->hdev->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);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
ret = input_register_device(sc->touchpad);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
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)
|
||||
|
@ -1440,7 +1417,7 @@ static int sony_register_sensors(struct sony_sc *sc)
|
|||
int ret;
|
||||
int range;
|
||||
|
||||
sc->sensor_dev = input_allocate_device();
|
||||
sc->sensor_dev = devm_input_allocate_device(&sc->hdev->dev);
|
||||
if (!sc->sensor_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1457,11 +1434,9 @@ static int sony_register_sensors(struct sony_sc *sc)
|
|||
* DS4 compatible non-Sony devices with different names.
|
||||
*/
|
||||
name_sz = strlen(sc->hdev->name) + sizeof(SENSOR_SUFFIX);
|
||||
name = kzalloc(name_sz, GFP_KERNEL);
|
||||
if (!name) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
name = devm_kzalloc(&sc->hdev->dev, name_sz, GFP_KERNEL);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
snprintf(name, name_sz, "%s" SENSOR_SUFFIX, sc->hdev->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);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(sc->sensor_dev->name);
|
||||
sc->sensor_dev->name = NULL;
|
||||
|
||||
input_free_device(sc->sensor_dev);
|
||||
sc->sensor_dev = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sony_unregister_sensors(struct sony_sc *sc)
|
||||
{
|
||||
if (!sc->sensor_dev)
|
||||
return;
|
||||
|
||||
kfree(sc->sensor_dev->name);
|
||||
sc->sensor_dev->name = NULL;
|
||||
|
||||
input_unregister_device(sc->sensor_dev);
|
||||
sc->sensor_dev = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
|
||||
* to "operational". Without this, the ps3 controller will not report any
|
||||
|
@ -1987,25 +1940,6 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
|
|||
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)
|
||||
{
|
||||
struct hid_device *hdev = sc->hdev;
|
||||
|
@ -2078,11 +2012,10 @@ static int sony_leds_init(struct sony_sc *sc)
|
|||
if (use_ds4_names)
|
||||
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) {
|
||||
hid_err(hdev, "Couldn't allocate memory for LED %d\n", n);
|
||||
ret = -ENOMEM;
|
||||
goto error_leds;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
name = (void *)(&led[1]);
|
||||
|
@ -2103,21 +2036,14 @@ static int sony_leds_init(struct sony_sc *sc)
|
|||
|
||||
sc->leds[n] = led;
|
||||
|
||||
ret = led_classdev_register(&hdev->dev, led);
|
||||
ret = devm_led_classdev_register(&hdev->dev, led);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Failed to register LED %d\n", n);
|
||||
sc->leds[n] = NULL;
|
||||
kfree(led);
|
||||
goto error_leds;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error_leds:
|
||||
sony_leds_remove(sc);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) ||
|
||||
(sc->quirks & NAVIGATION_CONTROLLER))
|
||||
sc->output_report_dmabuf =
|
||||
kmalloc(sizeof(union sixaxis_output_report_01),
|
||||
devm_kmalloc(&sc->hdev->dev,
|
||||
sizeof(union sixaxis_output_report_01),
|
||||
GFP_KERNEL);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
else
|
||||
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.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
sc->battery_desc.use_for_apm = 0;
|
||||
sc->battery_desc.name = kasprintf(GFP_KERNEL, battery_str_fmt,
|
||||
sc->mac_address, sc->device_id);
|
||||
sc->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
|
||||
battery_str_fmt, sc->mac_address, sc->device_id);
|
||||
if (!sc->battery_desc.name)
|
||||
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);
|
||||
if (IS_ERR(sc->battery)) {
|
||||
ret = PTR_ERR(sc->battery);
|
||||
hid_err(hdev, "Unable to register battery device\n");
|
||||
goto err_free;
|
||||
return ret;
|
||||
}
|
||||
|
||||
power_supply_powers(sc->battery, &hdev->dev);
|
||||
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 @@ static int sony_input_configured(struct hid_device *hdev,
|
|||
device_remove_file(&sc->hdev->dev, &dev_attr_firmware_version);
|
||||
if (sc->hw_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);
|
||||
kfree(sc->output_report_dmabuf);
|
||||
sony_remove_dev_list(sc);
|
||||
sony_release_device_id(sc);
|
||||
hid_hw_stop(hdev);
|
||||
|
@ -2965,18 +2871,6 @@ static void sony_remove(struct hid_device *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)
|
||||
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);
|
||||
|
||||
kfree(sc->output_report_dmabuf);
|
||||
|
||||
sony_remove_dev_list(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;
|
||||
if (rmem[4] == 0x01 && rmem[5] == 0x20)
|
||||
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;
|
||||
}
|
||||
|
@ -488,6 +494,8 @@ static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype)
|
|||
/* map MP with correct pass-through mode */
|
||||
switch (exttype) {
|
||||
case WIIMOTE_EXT_CLASSIC_CONTROLLER:
|
||||
case WIIMOTE_EXT_DRUMS:
|
||||
case WIIMOTE_EXT_GUITAR:
|
||||
wmem = 0x07;
|
||||
break;
|
||||
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_BALANCE_BOARD] = "Nintendo Wii Balance Board",
|
||||
[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");
|
||||
case WIIMOTE_EXT_PRO_CONTROLLER:
|
||||
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:
|
||||
/* fallthrough */
|
||||
default:
|
||||
|
|
|
@ -1949,6 +1949,444 @@ static const struct wiimod_ops wiimod_pro = {
|
|||
.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
|
||||
* 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_BALANCE_BOARD] = &wiimod_bboard,
|
||||
[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_BALANCE_BOARD,
|
||||
WIIMOTE_EXT_PRO_CONTROLLER,
|
||||
WIIMOTE_EXT_DRUMS,
|
||||
WIIMOTE_EXT_GUITAR,
|
||||
WIIMOTE_EXT_NUM,
|
||||
};
|
||||
|
||||
|
@ -137,6 +139,7 @@ struct wiimote_state {
|
|||
/* calibration/cache data */
|
||||
__u16 calib_bboard[4][3];
|
||||
__s16 calib_pro_sticks[4];
|
||||
__u8 pressure_drums[7];
|
||||
__u8 cache_rumble;
|
||||
};
|
||||
|
||||
|
|
|
@ -1002,18 +1002,18 @@ static int i2c_hid_probe(struct i2c_client *client,
|
|||
return client->irq;
|
||||
}
|
||||
|
||||
ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL);
|
||||
ihid = devm_kzalloc(&client->dev, sizeof(*ihid), GFP_KERNEL);
|
||||
if (!ihid)
|
||||
return -ENOMEM;
|
||||
|
||||
if (client->dev.of_node) {
|
||||
ret = i2c_hid_of_probe(client, &ihid->pdata);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
} else if (!platform_data) {
|
||||
ret = i2c_hid_acpi_pdata(client, &ihid->pdata);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
} else {
|
||||
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 */
|
||||
i2c_hid_fwnode_probe(client, &ihid->pdata);
|
||||
|
||||
ihid->pdata.supply = devm_regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(ihid->pdata.supply)) {
|
||||
ret = PTR_ERR(ihid->pdata.supply);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&client->dev, "Failed to get regulator: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
ihid->pdata.supplies[0].supply = "vdd";
|
||||
ihid->pdata.supplies[1].supply = "vddl";
|
||||
|
||||
ret = devm_regulator_bulk_get(&client->dev,
|
||||
ARRAY_SIZE(ihid->pdata.supplies),
|
||||
ihid->pdata.supplies);
|
||||
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)
|
||||
msleep(ihid->pdata.post_power_delay_ms);
|
||||
|
||||
|
@ -1122,11 +1121,9 @@ static int i2c_hid_probe(struct i2c_client *client,
|
|||
pm_runtime_disable(&client->dev);
|
||||
|
||||
err_regulator:
|
||||
regulator_disable(ihid->pdata.supply);
|
||||
|
||||
err:
|
||||
regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||
ihid->pdata.supplies);
|
||||
i2c_hid_free_buffers(ihid);
|
||||
kfree(ihid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1148,9 +1145,8 @@ static int i2c_hid_remove(struct i2c_client *client)
|
|||
if (ihid->bufsize)
|
||||
i2c_hid_free_buffers(ihid);
|
||||
|
||||
regulator_disable(ihid->pdata.supply);
|
||||
|
||||
kfree(ihid);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||
ihid->pdata.supplies);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1201,9 +1197,8 @@ static int i2c_hid_suspend(struct device *dev)
|
|||
hid_warn(hid, "Failed to enable irq wake: %d\n",
|
||||
wake_status);
|
||||
} else {
|
||||
ret = regulator_disable(ihid->pdata.supply);
|
||||
if (ret < 0)
|
||||
hid_warn(hid, "Failed to disable supply: %d\n", ret);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||
ihid->pdata.supplies);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1218,9 +1213,11 @@ static int i2c_hid_resume(struct device *dev)
|
|||
int wake_status;
|
||||
|
||||
if (!device_may_wakeup(&client->dev)) {
|
||||
ret = regulator_enable(ihid->pdata.supply);
|
||||
if (ret < 0)
|
||||
hid_warn(hid, "Failed to enable supply: %d\n", ret);
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||
ihid->pdata.supplies);
|
||||
if (ret)
|
||||
hid_warn(hid, "Failed to enable supplies: %d\n", ret);
|
||||
|
||||
if (ihid->pdata.post_power_delay_ms)
|
||||
msleep(ihid->pdata.post_power_delay_ms);
|
||||
} else if (ihid->irq_wake_enabled) {
|
||||
|
|
|
@ -907,7 +907,8 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
|
|||
struct ishtp_device *dev;
|
||||
int i;
|
||||
|
||||
dev = kzalloc(sizeof(struct ishtp_device) + sizeof(struct ish_hw),
|
||||
dev = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ishtp_device) + sizeof(struct ish_hw),
|
||||
GFP_KERNEL);
|
||||
if (!dev)
|
||||
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) {
|
||||
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) {
|
||||
/*
|
||||
* IPC buffers may be limited or not available
|
||||
|
|
|
@ -95,6 +95,13 @@ static int ish_init(struct ishtp_device *dev)
|
|||
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
|
||||
* @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;
|
||||
int ret;
|
||||
|
||||
/* Check for invalid platforms for ISH support */
|
||||
if (pci_dev_present(ish_invalid_pci_ids))
|
||||
return -ENODEV;
|
||||
|
||||
/* enable pci dev */
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret) {
|
||||
|
@ -172,7 +183,6 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
free_irq(pdev->irq, dev);
|
||||
free_device:
|
||||
pci_iounmap(pdev, hw->mem_addr);
|
||||
kfree(dev);
|
||||
release_regions:
|
||||
pci_release_regions(pdev);
|
||||
disable_device:
|
||||
|
@ -202,7 +212,6 @@ static void ish_remove(struct pci_dev *pdev)
|
|||
pci_release_regions(pdev);
|
||||
pci_clear_master(pdev);
|
||||
pci_disable_device(pdev);
|
||||
kfree(ishtp_dev);
|
||||
}
|
||||
|
||||
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;
|
||||
const size_t len = sizeof(struct hbm_flow_control);
|
||||
int rv;
|
||||
unsigned int num_frags;
|
||||
unsigned long 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;
|
||||
}
|
||||
|
||||
num_frags = cl->recv_msg_num_frags;
|
||||
cl->recv_msg_num_frags = 0;
|
||||
|
||||
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 usbhid_device *usbhid = hid->driver_data;
|
||||
unsigned long flags;
|
||||
int unplug = 0, status = urb->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);
|
||||
}
|
||||
|
||||
spin_lock(&usbhid->lock);
|
||||
spin_lock_irqsave(&usbhid->lock, flags);
|
||||
|
||||
if (unplug) {
|
||||
usbhid->ctrltail = usbhid->ctrlhead;
|
||||
|
@ -511,13 +512,13 @@ static void hid_ctrl(struct urb *urb)
|
|||
if (usbhid->ctrlhead != usbhid->ctrltail &&
|
||||
hid_submit_ctrl(hid) == 0) {
|
||||
/* Successfully submitted next urb in queue */
|
||||
spin_unlock(&usbhid->lock);
|
||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
||||
spin_unlock(&usbhid->lock);
|
||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||
usb_autopm_put_interface_async(usbhid->intf);
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
struct hid_field *field, struct hid_usage *usage)
|
||||
{
|
||||
|
@ -221,6 +272,8 @@ static void wacom_feature_mapping(struct hid_device *hdev,
|
|||
int ret;
|
||||
u32 n;
|
||||
|
||||
wacom_hid_usage_quirk(hdev, field, usage);
|
||||
|
||||
switch (equivalent_usage) {
|
||||
case HID_DG_CONTACTMAX:
|
||||
/* leave touch_max as is if predefined */
|
||||
|
@ -300,13 +353,6 @@ static void wacom_feature_mapping(struct hid_device *hdev,
|
|||
kfree(data);
|
||||
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;
|
||||
bool finger = WACOM_FINGER_FIELD(field);
|
||||
bool pen = WACOM_PEN_FIELD(field);
|
||||
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
|
||||
|
||||
/*
|
||||
* Requiring Stylus Usage will ignore boot mouse
|
||||
|
@ -361,49 +408,9 @@ static void wacom_usage_mapping(struct hid_device *hdev,
|
|||
else
|
||||
return;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
wacom_hid_usage_quirk(hdev, field, 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) {
|
||||
switch (equivalent_usage) {
|
||||
case HID_GD_X:
|
||||
features->x_max = field->logical_maximum;
|
||||
if (finger) {
|
||||
|
@ -703,18 +710,6 @@ struct wacom_hdev_data {
|
|||
static LIST_HEAD(wacom_udev_list);
|
||||
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,
|
||||
struct hid_device *sibling)
|
||||
{
|
||||
|
@ -737,10 +732,10 @@ static bool wacom_are_sibling(struct hid_device *hdev,
|
|||
* the same physical parent device path.
|
||||
*/
|
||||
if (hdev->vendor == sibling->vendor && hdev->product == sibling->product) {
|
||||
if (!compare_device_paths(hdev, sibling, '/'))
|
||||
if (!hid_compare_device_paths(hdev, sibling, '/'))
|
||||
return false;
|
||||
} else {
|
||||
if (!compare_device_paths(hdev, sibling, '.'))
|
||||
if (!hid_compare_device_paths(hdev, sibling, '.'))
|
||||
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 */
|
||||
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);
|
||||
return data;
|
||||
}
|
||||
|
|
|
@ -4357,19 +4357,19 @@ static const struct wacom_features wacom_features_0x5E =
|
|||
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
|
||||
static const struct wacom_features wacom_features_0x90 =
|
||||
{ "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 =
|
||||
{ "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 =
|
||||
{ "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 =
|
||||
{ "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 =
|
||||
{ "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 =
|
||||
{ "Wacom ISDv4 E2", 26202, 16325, 255, 0,
|
||||
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 };
|
||||
static const struct wacom_features wacom_features_0xEC =
|
||||
{ "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 =
|
||||
{ "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 =
|
||||
{ "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 =
|
||||
{ "Wacom ISDv4 100", 26202, 16325, 255, 0,
|
||||
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 };
|
||||
static const struct wacom_features wacom_features_0x116 =
|
||||
{ "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 =
|
||||
{ "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 =
|
||||
{ "Wacom ISDv4 4001", 26202, 16325, 255, 0,
|
||||
MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
|
|
|
@ -190,6 +190,12 @@ struct hid_item {
|
|||
* http://www.usb.org/developers/hidpage/HUTRR40RadioHIDUsagesFinal.pdf
|
||||
*/
|
||||
#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_Y 0x00010031
|
||||
#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_global global;
|
||||
struct hid_global global_stack[HID_GLOBAL_STACK_SIZE];
|
||||
unsigned global_stack_ptr;
|
||||
unsigned int global_stack_ptr;
|
||||
struct hid_local local;
|
||||
unsigned collection_stack[HID_COLLECTION_STACK_SIZE];
|
||||
unsigned collection_stack_ptr;
|
||||
unsigned int *collection_stack;
|
||||
unsigned int collection_stack_ptr;
|
||||
unsigned int collection_stack_size;
|
||||
struct hid_device *device;
|
||||
unsigned scan_flags;
|
||||
unsigned int scan_flags;
|
||||
};
|
||||
|
||||
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 *hid_match_device(struct hid_device *hdev,
|
||||
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);
|
||||
__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
|
||||
unsigned offset, unsigned n);
|
||||
|
|
|
@ -12,14 +12,13 @@
|
|||
#ifndef __LINUX_I2C_HID_H
|
||||
#define __LINUX_I2C_HID_H
|
||||
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct regulator;
|
||||
|
||||
/**
|
||||
* struct i2chid_platform_data - used by hid over i2c implementation.
|
||||
* @hid_descriptor_address: i2c register where the HID descriptor is stored.
|
||||
* @supply: regulator for powering on the device.
|
||||
* @supplies: regulators for powering on the device.
|
||||
* @post_power_delay_ms: delay after powering on before device is usable.
|
||||
*
|
||||
* Note that it is the responsibility of the platform driver (or the acpi 5.0
|
||||
|
@ -35,7 +34,7 @@ struct regulator;
|
|||
*/
|
||||
struct i2c_hid_platform_data {
|
||||
u16 hid_descriptor_address;
|
||||
struct regulator *supply;
|
||||
struct regulator_bulk_data supplies[2];
|
||||
int post_power_delay_ms;
|
||||
};
|
||||
|
||||
|
|
|
@ -270,10 +270,11 @@ struct input_mask {
|
|||
/*
|
||||
* MT_TOOL types
|
||||
*/
|
||||
#define MT_TOOL_FINGER 0
|
||||
#define MT_TOOL_PEN 1
|
||||
#define MT_TOOL_PALM 2
|
||||
#define MT_TOOL_MAX 2
|
||||
#define MT_TOOL_FINGER 0x00
|
||||
#define MT_TOOL_PEN 0x01
|
||||
#define MT_TOOL_PALM 0x02
|
||||
#define MT_TOOL_DIAL 0x0a
|
||||
#define MT_TOOL_MAX 0x0f
|
||||
|
||||
/*
|
||||
* Values describing the status of a force-feedback effect
|
||||
|
|
Loading…
Reference in a new issue