mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-04 09:34:12 +08:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID updates from Jiri Kosina: - rumble support for Xbox One S, from Andrey Smirnov - high-resolution support for Logitech mice, from Harry Cutts - support for recent devices requiring the HID parse to be able to cope with tag report sizes > 256 * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (35 commits) HID: usbhid: Add quirk for Redragon/Dragonrise Seymur 2 HID: wacom: Work around HID descriptor bug in DTK-2451 and DTH-2452 HID: google: add dependency on Cros EC for Hammer HID: elan: fix spelling mistake "registred" -> "registered" HID: google: drop superfluous const before SIMPLE_DEV_PM_OPS() HID: google: add support tablet mode switch for Whiskers mfd: cros: add "base attached" MKBP switch definition Input: reserve 2 events code because of HID HID: magicmouse: add support for Apple Magic Trackpad 2 HID: i2c-hid: override HID descriptors for certain devices HID: hid-bigbenff: driver for BigBen Interactive PS3OFMINIPAD gamepad HID: logitech: fix a used uninitialized GCC warning HID: intel-ish-hid: using list_head for ipc write queue HID: intel-ish-hid: use resource-managed api HID: intel_ish-hid: Enhance API to get ring buffer sizes HID: intel-ish-hid: use helper function to search client id HID: intel-ish-hid: ishtp: add helper function for client search HID: intel-ish-hid: use helper function to access client buffer HID: intel-ish-hid: ishtp: add helper functions for client buffer operation HID: intel-ish-hid: use helper function for private driver data set/get ...
This commit is contained in:
commit
96f2f66a98
@ -190,7 +190,16 @@ A few EV_REL codes have special meanings:
|
||||
* REL_WHEEL, REL_HWHEEL:
|
||||
|
||||
- These codes are used for vertical and horizontal scroll wheels,
|
||||
respectively.
|
||||
respectively. The value is the number of "notches" moved on the wheel, the
|
||||
physical size of which varies by device. For high-resolution wheels (which
|
||||
report multiple events for each notch of movement, or do not have notches)
|
||||
this may be an approximation based on the high-resolution scroll events.
|
||||
|
||||
* REL_WHEEL_HI_RES:
|
||||
|
||||
- If a vertical scroll wheel supports high-resolution scrolling, this code
|
||||
will be emitted in addition to REL_WHEEL. The value is the (approximate)
|
||||
distance travelled by the user's finger, in microns.
|
||||
|
||||
EV_ABS
|
||||
------
|
||||
|
@ -182,6 +182,19 @@ config HID_BETOP_FF
|
||||
Currently the following devices are known to be supported:
|
||||
- BETOP 2185 PC & BFM MODE
|
||||
|
||||
config HID_BIGBEN_FF
|
||||
tristate "BigBen Interactive Kids' gamepad support"
|
||||
depends on USB_HID
|
||||
depends on NEW_LEDS
|
||||
depends on LEDS_CLASS
|
||||
select INPUT_FF_MEMLESS
|
||||
default !EXPERT
|
||||
help
|
||||
Support for the "Kid-friendly Wired Controller" PS3OFMINIPAD
|
||||
gamepad made by BigBen Interactive, originally sold as a PS3
|
||||
accessory. This driver fixes input mapping and adds support for
|
||||
force feedback effects and LEDs on the device.
|
||||
|
||||
config HID_CHERRY
|
||||
tristate "Cherry Cymotion keyboard"
|
||||
depends on HID
|
||||
@ -351,7 +364,7 @@ config HOLTEK_FF
|
||||
|
||||
config HID_GOOGLE_HAMMER
|
||||
tristate "Google Hammer Keyboard"
|
||||
depends on USB_HID && LEDS_CLASS
|
||||
depends on USB_HID && LEDS_CLASS && MFD_CROS_EC
|
||||
---help---
|
||||
Say Y here if you have a Google Hammer device.
|
||||
|
||||
@ -596,6 +609,7 @@ config HID_MICROSOFT
|
||||
tristate "Microsoft non-fully HID-compliant devices"
|
||||
depends on HID
|
||||
default !EXPERT
|
||||
select INPUT_FF_MEMLESS
|
||||
---help---
|
||||
Support for Microsoft devices that are not fully compliant with HID standard.
|
||||
|
||||
|
@ -31,6 +31,7 @@ obj-$(CONFIG_HID_ASUS) += hid-asus.o
|
||||
obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
|
||||
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
|
||||
obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o
|
||||
obj-$(CONFIG_HID_BIGBEN_FF) += hid-bigbenff.o
|
||||
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
|
||||
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
|
||||
obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o
|
||||
|
414
drivers/hid/hid-bigbenff.c
Normal file
414
drivers/hid/hid-bigbenff.c
Normal file
@ -0,0 +1,414 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/*
|
||||
* LED & force feedback support for BigBen Interactive
|
||||
*
|
||||
* 0x146b:0x0902 "Bigben Interactive Bigben Game Pad"
|
||||
* "Kid-friendly Wired Controller" PS3OFMINIPAD SONY
|
||||
* sold for use with the PS3
|
||||
*
|
||||
* Copyright (c) 2018 Hanno Zulla <kontakt@hanno.de>
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/hid.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
|
||||
/*
|
||||
* The original descriptor for 0x146b:0x0902
|
||||
*
|
||||
* 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
* 0x09, 0x05, // Usage (Game Pad)
|
||||
* 0xA1, 0x01, // Collection (Application)
|
||||
* 0x15, 0x00, // Logical Minimum (0)
|
||||
* 0x25, 0x01, // Logical Maximum (1)
|
||||
* 0x35, 0x00, // Physical Minimum (0)
|
||||
* 0x45, 0x01, // Physical Maximum (1)
|
||||
* 0x75, 0x01, // Report Size (1)
|
||||
* 0x95, 0x0D, // Report Count (13)
|
||||
* 0x05, 0x09, // Usage Page (Button)
|
||||
* 0x19, 0x01, // Usage Minimum (0x01)
|
||||
* 0x29, 0x0D, // Usage Maximum (0x0D)
|
||||
* 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
* 0x95, 0x03, // Report Count (3)
|
||||
* 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
* 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
* 0x25, 0x07, // Logical Maximum (7)
|
||||
* 0x46, 0x3B, 0x01, // Physical Maximum (315)
|
||||
* 0x75, 0x04, // Report Size (4)
|
||||
* 0x95, 0x01, // Report Count (1)
|
||||
* 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
|
||||
* 0x09, 0x39, // Usage (Hat switch)
|
||||
* 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
|
||||
* 0x65, 0x00, // Unit (None)
|
||||
* 0x95, 0x01, // Report Count (1)
|
||||
* 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
* 0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
* 0x46, 0xFF, 0x00, // Physical Maximum (255)
|
||||
* 0x09, 0x30, // Usage (X)
|
||||
* 0x09, 0x31, // Usage (Y)
|
||||
* 0x09, 0x32, // Usage (Z)
|
||||
* 0x09, 0x35, // Usage (Rz)
|
||||
* 0x75, 0x08, // Report Size (8)
|
||||
* 0x95, 0x04, // Report Count (4)
|
||||
* 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
* 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
|
||||
* 0x09, 0x20, // Usage (0x20)
|
||||
* 0x09, 0x21, // Usage (0x21)
|
||||
* 0x09, 0x22, // Usage (0x22)
|
||||
* 0x09, 0x23, // Usage (0x23)
|
||||
* 0x09, 0x24, // Usage (0x24)
|
||||
* 0x09, 0x25, // Usage (0x25)
|
||||
* 0x09, 0x26, // Usage (0x26)
|
||||
* 0x09, 0x27, // Usage (0x27)
|
||||
* 0x09, 0x28, // Usage (0x28)
|
||||
* 0x09, 0x29, // Usage (0x29)
|
||||
* 0x09, 0x2A, // Usage (0x2A)
|
||||
* 0x09, 0x2B, // Usage (0x2B)
|
||||
* 0x95, 0x0C, // Report Count (12)
|
||||
* 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
* 0x0A, 0x21, 0x26, // Usage (0x2621)
|
||||
* 0x95, 0x08, // Report Count (8)
|
||||
* 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
* 0x0A, 0x21, 0x26, // Usage (0x2621)
|
||||
* 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
* 0x26, 0xFF, 0x03, // Logical Maximum (1023)
|
||||
* 0x46, 0xFF, 0x03, // Physical Maximum (1023)
|
||||
* 0x09, 0x2C, // Usage (0x2C)
|
||||
* 0x09, 0x2D, // Usage (0x2D)
|
||||
* 0x09, 0x2E, // Usage (0x2E)
|
||||
* 0x09, 0x2F, // Usage (0x2F)
|
||||
* 0x75, 0x10, // Report Size (16)
|
||||
* 0x95, 0x04, // Report Count (4)
|
||||
* 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
* 0xC0, // End Collection
|
||||
*/
|
||||
|
||||
#define PID0902_RDESC_ORIG_SIZE 137
|
||||
|
||||
/*
|
||||
* The fixed descriptor for 0x146b:0x0902
|
||||
*
|
||||
* - map buttons according to gamepad.rst
|
||||
* - assign right stick from Z/Rz to Rx/Ry
|
||||
* - map previously unused analog trigger data to Z/RZ
|
||||
* - simplify feature and output descriptor
|
||||
*/
|
||||
static __u8 pid0902_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
|
||||
0x09, 0x05, /* Usage (Game Pad) */
|
||||
0xA1, 0x01, /* Collection (Application) */
|
||||
0x15, 0x00, /* Logical Minimum (0) */
|
||||
0x25, 0x01, /* Logical Maximum (1) */
|
||||
0x35, 0x00, /* Physical Minimum (0) */
|
||||
0x45, 0x01, /* Physical Maximum (1) */
|
||||
0x75, 0x01, /* Report Size (1) */
|
||||
0x95, 0x0D, /* Report Count (13) */
|
||||
0x05, 0x09, /* Usage Page (Button) */
|
||||
0x09, 0x05, /* Usage (BTN_WEST) */
|
||||
0x09, 0x01, /* Usage (BTN_SOUTH) */
|
||||
0x09, 0x02, /* Usage (BTN_EAST) */
|
||||
0x09, 0x04, /* Usage (BTN_NORTH) */
|
||||
0x09, 0x07, /* Usage (BTN_TL) */
|
||||
0x09, 0x08, /* Usage (BTN_TR) */
|
||||
0x09, 0x09, /* Usage (BTN_TL2) */
|
||||
0x09, 0x0A, /* Usage (BTN_TR2) */
|
||||
0x09, 0x0B, /* Usage (BTN_SELECT) */
|
||||
0x09, 0x0C, /* Usage (BTN_START) */
|
||||
0x09, 0x0E, /* Usage (BTN_THUMBL) */
|
||||
0x09, 0x0F, /* Usage (BTN_THUMBR) */
|
||||
0x09, 0x0D, /* Usage (BTN_MODE) */
|
||||
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
|
||||
0x75, 0x01, /* Report Size (1) */
|
||||
0x95, 0x03, /* Report Count (3) */
|
||||
0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
|
||||
0x25, 0x07, /* Logical Maximum (7) */
|
||||
0x46, 0x3B, 0x01, /* Physical Maximum (315) */
|
||||
0x75, 0x04, /* Report Size (4) */
|
||||
0x95, 0x01, /* Report Count (1) */
|
||||
0x65, 0x14, /* Unit (System: English Rotation, Length: Centimeter) */
|
||||
0x09, 0x39, /* Usage (Hat switch) */
|
||||
0x81, 0x42, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) */
|
||||
0x65, 0x00, /* Unit (None) */
|
||||
0x95, 0x01, /* Report Count (1) */
|
||||
0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
|
||||
0x26, 0xFF, 0x00, /* Logical Maximum (255) */
|
||||
0x46, 0xFF, 0x00, /* Physical Maximum (255) */
|
||||
0x09, 0x30, /* Usage (X) */
|
||||
0x09, 0x31, /* Usage (Y) */
|
||||
0x09, 0x33, /* Usage (Rx) */
|
||||
0x09, 0x34, /* Usage (Ry) */
|
||||
0x75, 0x08, /* Report Size (8) */
|
||||
0x95, 0x04, /* Report Count (4) */
|
||||
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
|
||||
0x95, 0x0A, /* Report Count (10) */
|
||||
0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
|
||||
0x26, 0xFF, 0x00, /* Logical Maximum (255) */
|
||||
0x46, 0xFF, 0x00, /* Physical Maximum (255) */
|
||||
0x09, 0x32, /* Usage (Z) */
|
||||
0x09, 0x35, /* Usage (Rz) */
|
||||
0x95, 0x02, /* Report Count (2) */
|
||||
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
|
||||
0x95, 0x08, /* Report Count (8) */
|
||||
0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */
|
||||
0xB1, 0x02, /* Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */
|
||||
0x0A, 0x21, 0x26, /* Usage (0x2621) */
|
||||
0x95, 0x08, /* Report Count (8) */
|
||||
0x91, 0x02, /* Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */
|
||||
0x0A, 0x21, 0x26, /* Usage (0x2621) */
|
||||
0x95, 0x08, /* Report Count (8) */
|
||||
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
|
||||
0xC0, /* End Collection */
|
||||
};
|
||||
|
||||
#define NUM_LEDS 4
|
||||
|
||||
struct bigben_device {
|
||||
struct hid_device *hid;
|
||||
struct hid_report *report;
|
||||
u8 led_state; /* LED1 = 1 .. LED4 = 8 */
|
||||
u8 right_motor_on; /* right motor off/on 0/1 */
|
||||
u8 left_motor_force; /* left motor force 0-255 */
|
||||
struct led_classdev *leds[NUM_LEDS];
|
||||
bool work_led;
|
||||
bool work_ff;
|
||||
struct work_struct worker;
|
||||
};
|
||||
|
||||
|
||||
static void bigben_worker(struct work_struct *work)
|
||||
{
|
||||
struct bigben_device *bigben = container_of(work,
|
||||
struct bigben_device, worker);
|
||||
struct hid_field *report_field = bigben->report->field[0];
|
||||
|
||||
if (bigben->work_led) {
|
||||
bigben->work_led = false;
|
||||
report_field->value[0] = 0x01; /* 1 = led message */
|
||||
report_field->value[1] = 0x08; /* reserved value, always 8 */
|
||||
report_field->value[2] = bigben->led_state;
|
||||
report_field->value[3] = 0x00; /* padding */
|
||||
report_field->value[4] = 0x00; /* padding */
|
||||
report_field->value[5] = 0x00; /* padding */
|
||||
report_field->value[6] = 0x00; /* padding */
|
||||
report_field->value[7] = 0x00; /* padding */
|
||||
hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
||||
if (bigben->work_ff) {
|
||||
bigben->work_ff = false;
|
||||
report_field->value[0] = 0x02; /* 2 = rumble effect message */
|
||||
report_field->value[1] = 0x08; /* reserved value, always 8 */
|
||||
report_field->value[2] = bigben->right_motor_on;
|
||||
report_field->value[3] = bigben->left_motor_force;
|
||||
report_field->value[4] = 0xff; /* duration 0-254 (255 = nonstop) */
|
||||
report_field->value[5] = 0x00; /* padding */
|
||||
report_field->value[6] = 0x00; /* padding */
|
||||
report_field->value[7] = 0x00; /* padding */
|
||||
hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT);
|
||||
}
|
||||
}
|
||||
|
||||
static int hid_bigben_play_effect(struct input_dev *dev, void *data,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct bigben_device *bigben = data;
|
||||
u8 right_motor_on;
|
||||
u8 left_motor_force;
|
||||
|
||||
if (effect->type != FF_RUMBLE)
|
||||
return 0;
|
||||
|
||||
right_motor_on = effect->u.rumble.weak_magnitude ? 1 : 0;
|
||||
left_motor_force = effect->u.rumble.strong_magnitude / 256;
|
||||
|
||||
if (right_motor_on != bigben->right_motor_on ||
|
||||
left_motor_force != bigben->left_motor_force) {
|
||||
bigben->right_motor_on = right_motor_on;
|
||||
bigben->left_motor_force = left_motor_force;
|
||||
bigben->work_ff = true;
|
||||
schedule_work(&bigben->worker);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bigben_set_led(struct led_classdev *led,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct device *dev = led->dev->parent;
|
||||
struct hid_device *hid = to_hid_device(dev);
|
||||
struct bigben_device *bigben = hid_get_drvdata(hid);
|
||||
int n;
|
||||
bool work;
|
||||
|
||||
if (!bigben) {
|
||||
hid_err(hid, "no device data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (n = 0; n < NUM_LEDS; n++) {
|
||||
if (led == bigben->leds[n]) {
|
||||
if (value == LED_OFF) {
|
||||
work = (bigben->led_state & BIT(n));
|
||||
bigben->led_state &= ~BIT(n);
|
||||
} else {
|
||||
work = !(bigben->led_state & BIT(n));
|
||||
bigben->led_state |= BIT(n);
|
||||
}
|
||||
|
||||
if (work) {
|
||||
bigben->work_led = true;
|
||||
schedule_work(&bigben->worker);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static enum led_brightness bigben_get_led(struct led_classdev *led)
|
||||
{
|
||||
struct device *dev = led->dev->parent;
|
||||
struct hid_device *hid = to_hid_device(dev);
|
||||
struct bigben_device *bigben = hid_get_drvdata(hid);
|
||||
int n;
|
||||
|
||||
if (!bigben) {
|
||||
hid_err(hid, "no device data\n");
|
||||
return LED_OFF;
|
||||
}
|
||||
|
||||
for (n = 0; n < NUM_LEDS; n++) {
|
||||
if (led == bigben->leds[n])
|
||||
return (bigben->led_state & BIT(n)) ? LED_ON : LED_OFF;
|
||||
}
|
||||
|
||||
return LED_OFF;
|
||||
}
|
||||
|
||||
static void bigben_remove(struct hid_device *hid)
|
||||
{
|
||||
struct bigben_device *bigben = hid_get_drvdata(hid);
|
||||
|
||||
cancel_work_sync(&bigben->worker);
|
||||
hid_hw_close(hid);
|
||||
hid_hw_stop(hid);
|
||||
}
|
||||
|
||||
static int bigben_probe(struct hid_device *hid,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
struct bigben_device *bigben;
|
||||
struct hid_input *hidinput;
|
||||
struct list_head *report_list;
|
||||
struct led_classdev *led;
|
||||
char *name;
|
||||
size_t name_sz;
|
||||
int n, error;
|
||||
|
||||
bigben = devm_kzalloc(&hid->dev, sizeof(*bigben), GFP_KERNEL);
|
||||
if (!bigben)
|
||||
return -ENOMEM;
|
||||
hid_set_drvdata(hid, bigben);
|
||||
bigben->hid = hid;
|
||||
|
||||
error = hid_parse(hid);
|
||||
if (error) {
|
||||
hid_err(hid, "parse failed\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
|
||||
if (error) {
|
||||
hid_err(hid, "hw start failed\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
bigben->report = list_entry(report_list->next,
|
||||
struct hid_report, list);
|
||||
|
||||
hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
|
||||
set_bit(FF_RUMBLE, hidinput->input->ffbit);
|
||||
|
||||
INIT_WORK(&bigben->worker, bigben_worker);
|
||||
|
||||
error = input_ff_create_memless(hidinput->input, bigben,
|
||||
hid_bigben_play_effect);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
name_sz = strlen(dev_name(&hid->dev)) + strlen(":red:bigben#") + 1;
|
||||
|
||||
for (n = 0; n < NUM_LEDS; n++) {
|
||||
led = devm_kzalloc(
|
||||
&hid->dev,
|
||||
sizeof(struct led_classdev) + name_sz,
|
||||
GFP_KERNEL
|
||||
);
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
name = (void *)(&led[1]);
|
||||
snprintf(name, name_sz,
|
||||
"%s:red:bigben%d",
|
||||
dev_name(&hid->dev), n + 1
|
||||
);
|
||||
led->name = name;
|
||||
led->brightness = (n == 0) ? LED_ON : LED_OFF;
|
||||
led->max_brightness = 1;
|
||||
led->brightness_get = bigben_get_led;
|
||||
led->brightness_set = bigben_set_led;
|
||||
bigben->leds[n] = led;
|
||||
error = devm_led_classdev_register(&hid->dev, led);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/* initial state: LED1 is on, no rumble effect */
|
||||
bigben->led_state = BIT(0);
|
||||
bigben->right_motor_on = 0;
|
||||
bigben->left_motor_force = 0;
|
||||
bigben->work_led = true;
|
||||
bigben->work_ff = true;
|
||||
schedule_work(&bigben->worker);
|
||||
|
||||
hid_info(hid, "LED and force feedback support for BigBen gamepad\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize == PID0902_RDESC_ORIG_SIZE) {
|
||||
rdesc = pid0902_rdesc_fixed;
|
||||
*rsize = sizeof(pid0902_rdesc_fixed);
|
||||
} else
|
||||
hid_warn(hid, "unexpected rdesc, please submit for review\n");
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static const struct hid_device_id bigben_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_BIGBEN, USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, bigben_devices);
|
||||
|
||||
static struct hid_driver bigben_driver = {
|
||||
.name = "bigben",
|
||||
.id_table = bigben_devices,
|
||||
.probe = bigben_probe,
|
||||
.report_fixup = bigben_report_fixup,
|
||||
.remove = bigben_remove,
|
||||
};
|
||||
module_hid_driver(bigben_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
@ -406,7 +406,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
|
||||
|
||||
case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
|
||||
parser->global.report_size = item_udata(item);
|
||||
if (parser->global.report_size > 128) {
|
||||
if (parser->global.report_size > 256) {
|
||||
hid_err(parser->device, "invalid report_size %d\n",
|
||||
parser->global.report_size);
|
||||
return -1;
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/printk.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
@ -15,11 +16,9 @@ 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);
|
||||
static bool g6_is_space = true;
|
||||
MODULE_PARM_DESC(g6_is_space,
|
||||
"If set, G6 programmable key sends SPACE instead of F18 (0=off, 1=on) (default=1)");
|
||||
|
||||
"If true, G6 programmable key sends SPACE instead of F18 (default=true)");
|
||||
|
||||
#define COUGAR_VENDOR_USAGE 0xff00ff00
|
||||
|
||||
@ -82,20 +81,23 @@ struct cougar {
|
||||
static LIST_HEAD(cougar_udev_list);
|
||||
static DEFINE_MUTEX(cougar_udev_list_lock);
|
||||
|
||||
static void cougar_fix_g6_mapping(struct hid_device *hdev)
|
||||
/**
|
||||
* cougar_fix_g6_mapping - configure the mapping for key G6/Spacebar
|
||||
*/
|
||||
static void cougar_fix_g6_mapping(void)
|
||||
{
|
||||
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");
|
||||
g6_is_space ? KEY_SPACE : KEY_F18;
|
||||
pr_info("cougar: G6 mapped to %s\n",
|
||||
g6_is_space ? "space" : "F18");
|
||||
return;
|
||||
}
|
||||
}
|
||||
hid_warn(hdev, "no mapping defined for G6/spacebar");
|
||||
pr_warn("cougar: no mappings defined for G6/spacebar");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -154,7 +156,8 @@ static void cougar_remove_shared_data(void *resource)
|
||||
* 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)
|
||||
static int cougar_bind_shared_data(struct hid_device *hdev,
|
||||
struct cougar *cougar)
|
||||
{
|
||||
struct cougar_shared *shared;
|
||||
int error = 0;
|
||||
@ -228,7 +231,6 @@ static int cougar_probe(struct hid_device *hdev,
|
||||
* 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;
|
||||
@ -237,6 +239,8 @@ static int cougar_probe(struct hid_device *hdev,
|
||||
}
|
||||
}
|
||||
} else if (hdev->collection->usage == COUGAR_VENDOR_USAGE) {
|
||||
/* Preinit the mapping table */
|
||||
cougar_fix_g6_mapping();
|
||||
error = hid_hw_open(hdev);
|
||||
if (error)
|
||||
goto fail_stop_and_cleanup;
|
||||
@ -257,26 +261,32 @@ static int cougar_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
u8 *data, int size)
|
||||
{
|
||||
struct cougar *cougar;
|
||||
struct cougar_shared *shared;
|
||||
unsigned char code, action;
|
||||
int i;
|
||||
|
||||
cougar = hid_get_drvdata(hdev);
|
||||
if (!cougar->special_intf || !cougar->shared ||
|
||||
!cougar->shared->input || !cougar->shared->enabled)
|
||||
shared = cougar->shared;
|
||||
if (!cougar->special_intf || !shared)
|
||||
return 0;
|
||||
|
||||
if (!shared->enabled || !shared->input)
|
||||
return -EPERM;
|
||||
|
||||
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,
|
||||
input_event(shared->input, EV_KEY,
|
||||
cougar_mapping[i][1], action);
|
||||
input_sync(cougar->shared->input);
|
||||
return 0;
|
||||
input_sync(shared->input);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
hid_warn(hdev, "unmapped special key code %x: ignoring\n", code);
|
||||
return 0;
|
||||
/* Avoid warnings on the same unmapped key twice */
|
||||
if (action != 0)
|
||||
hid_warn(hdev, "unmapped special key code %0x: ignoring\n", code);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
static void cougar_remove(struct hid_device *hdev)
|
||||
@ -293,6 +303,26 @@ static void cougar_remove(struct hid_device *hdev)
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static int cougar_param_set_g6_is_space(const char *val,
|
||||
const struct kernel_param *kp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = param_set_bool(val, kp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cougar_fix_g6_mapping();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops cougar_g6_is_space_ops = {
|
||||
.set = cougar_param_set_g6_is_space,
|
||||
.get = param_get_bool,
|
||||
};
|
||||
module_param_cb(g6_is_space, &cougar_g6_is_space_ops, &g6_is_space, 0644);
|
||||
|
||||
static struct hid_device_id cougar_id_table[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR,
|
||||
USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) },
|
||||
|
@ -497,7 +497,7 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
return 0;
|
||||
|
||||
if (!drvdata->input) {
|
||||
hid_err(hdev, "Input device is not registred\n");
|
||||
hid_err(hdev, "Input device is not registered\n");
|
||||
ret = -ENAVAIL;
|
||||
goto err;
|
||||
}
|
||||
|
@ -13,16 +13,268 @@
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <linux/mfd/cros_ec_commands.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define MAX_BRIGHTNESS 100
|
||||
/*
|
||||
* C(hrome)B(ase)A(ttached)S(witch) - switch exported by Chrome EC and reporting
|
||||
* state of the "Whiskers" base - attached or detached. Whiskers USB device also
|
||||
* reports position of the keyboard - folded or not. Combining base state and
|
||||
* position allows us to generate proper "Tablet mode" events.
|
||||
*/
|
||||
struct cbas_ec {
|
||||
struct device *dev; /* The platform device (EC) */
|
||||
struct input_dev *input;
|
||||
bool base_present;
|
||||
struct notifier_block notifier;
|
||||
};
|
||||
|
||||
/* HID usage for keyboard backlight (Alphanumeric display brightness) */
|
||||
#define HID_AD_BRIGHTNESS 0x00140046
|
||||
static struct cbas_ec cbas_ec;
|
||||
static DEFINE_SPINLOCK(cbas_ec_lock);
|
||||
static DEFINE_MUTEX(cbas_ec_reglock);
|
||||
|
||||
static bool cbas_parse_base_state(const void *data)
|
||||
{
|
||||
u32 switches = get_unaligned_le32(data);
|
||||
|
||||
return !!(switches & BIT(EC_MKBP_BASE_ATTACHED));
|
||||
}
|
||||
|
||||
static int cbas_ec_query_base(struct cros_ec_device *ec_dev, bool get_state,
|
||||
bool *state)
|
||||
{
|
||||
struct ec_params_mkbp_info *params;
|
||||
struct cros_ec_command *msg;
|
||||
int ret;
|
||||
|
||||
msg = kzalloc(sizeof(*msg) + max(sizeof(u32), sizeof(*params)),
|
||||
GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msg->command = EC_CMD_MKBP_INFO;
|
||||
msg->version = 1;
|
||||
msg->outsize = sizeof(*params);
|
||||
msg->insize = sizeof(u32);
|
||||
params = (struct ec_params_mkbp_info *)msg->data;
|
||||
params->info_type = get_state ?
|
||||
EC_MKBP_INFO_CURRENT : EC_MKBP_INFO_SUPPORTED;
|
||||
params->event_type = EC_MKBP_EVENT_SWITCH;
|
||||
|
||||
ret = cros_ec_cmd_xfer_status(ec_dev, msg);
|
||||
if (ret >= 0) {
|
||||
if (ret != sizeof(u32)) {
|
||||
dev_warn(ec_dev->dev, "wrong result size: %d != %zu\n",
|
||||
ret, sizeof(u32));
|
||||
ret = -EPROTO;
|
||||
} else {
|
||||
*state = cbas_parse_base_state(msg->data);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cbas_ec_notify(struct notifier_block *nb,
|
||||
unsigned long queued_during_suspend,
|
||||
void *_notify)
|
||||
{
|
||||
struct cros_ec_device *ec = _notify;
|
||||
unsigned long flags;
|
||||
bool base_present;
|
||||
|
||||
if (ec->event_data.event_type == EC_MKBP_EVENT_SWITCH) {
|
||||
base_present = cbas_parse_base_state(
|
||||
&ec->event_data.data.switches);
|
||||
dev_dbg(cbas_ec.dev,
|
||||
"%s: base: %d\n", __func__, base_present);
|
||||
|
||||
if (device_may_wakeup(cbas_ec.dev) ||
|
||||
!queued_during_suspend) {
|
||||
|
||||
pm_wakeup_event(cbas_ec.dev, 0);
|
||||
|
||||
spin_lock_irqsave(&cbas_ec_lock, flags);
|
||||
|
||||
/*
|
||||
* While input layer dedupes the events, we do not want
|
||||
* to disrupt the state reported by the base by
|
||||
* overriding it with state reported by the LID. Only
|
||||
* report changes, as we assume that on attach the base
|
||||
* is not folded.
|
||||
*/
|
||||
if (base_present != cbas_ec.base_present) {
|
||||
input_report_switch(cbas_ec.input,
|
||||
SW_TABLET_MODE,
|
||||
!base_present);
|
||||
input_sync(cbas_ec.input);
|
||||
cbas_ec.base_present = base_present;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cbas_ec_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static __maybe_unused int cbas_ec_resume(struct device *dev)
|
||||
{
|
||||
struct cros_ec_device *ec = dev_get_drvdata(dev->parent);
|
||||
bool base_present;
|
||||
int error;
|
||||
|
||||
error = cbas_ec_query_base(ec, true, &base_present);
|
||||
if (error) {
|
||||
dev_warn(dev, "failed to fetch base state on resume: %d\n",
|
||||
error);
|
||||
} else {
|
||||
spin_lock_irq(&cbas_ec_lock);
|
||||
|
||||
cbas_ec.base_present = base_present;
|
||||
|
||||
/*
|
||||
* Only report if base is disconnected. If base is connected,
|
||||
* it will resend its state on resume, and we'll update it
|
||||
* in hammer_event().
|
||||
*/
|
||||
if (!cbas_ec.base_present) {
|
||||
input_report_switch(cbas_ec.input, SW_TABLET_MODE, 1);
|
||||
input_sync(cbas_ec.input);
|
||||
}
|
||||
|
||||
spin_unlock_irq(&cbas_ec_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(cbas_ec_pm_ops, NULL, cbas_ec_resume);
|
||||
|
||||
static void cbas_ec_set_input(struct input_dev *input)
|
||||
{
|
||||
/* Take the lock so hammer_event() does not race with us here */
|
||||
spin_lock_irq(&cbas_ec_lock);
|
||||
cbas_ec.input = input;
|
||||
spin_unlock_irq(&cbas_ec_lock);
|
||||
}
|
||||
|
||||
static int __cbas_ec_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
|
||||
struct input_dev *input;
|
||||
bool base_supported;
|
||||
int error;
|
||||
|
||||
error = cbas_ec_query_base(ec, false, &base_supported);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (!base_supported)
|
||||
return -ENXIO;
|
||||
|
||||
input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
input->name = "Whiskers Tablet Mode Switch";
|
||||
input->id.bustype = BUS_HOST;
|
||||
|
||||
input_set_capability(input, EV_SW, SW_TABLET_MODE);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "cannot register input device: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Seed the state */
|
||||
error = cbas_ec_query_base(ec, true, &cbas_ec.base_present);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "cannot query base state: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
input_report_switch(input, SW_TABLET_MODE, !cbas_ec.base_present);
|
||||
|
||||
cbas_ec_set_input(input);
|
||||
|
||||
cbas_ec.dev = &pdev->dev;
|
||||
cbas_ec.notifier.notifier_call = cbas_ec_notify;
|
||||
error = blocking_notifier_chain_register(&ec->event_notifier,
|
||||
&cbas_ec.notifier);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "cannot register notifier: %d\n", error);
|
||||
cbas_ec_set_input(NULL);
|
||||
return error;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cbas_ec_probe(struct platform_device *pdev)
|
||||
{
|
||||
int retval;
|
||||
|
||||
mutex_lock(&cbas_ec_reglock);
|
||||
|
||||
if (cbas_ec.input) {
|
||||
retval = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = __cbas_ec_probe(pdev);
|
||||
|
||||
out:
|
||||
mutex_unlock(&cbas_ec_reglock);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cbas_ec_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
mutex_lock(&cbas_ec_reglock);
|
||||
|
||||
blocking_notifier_chain_unregister(&ec->event_notifier,
|
||||
&cbas_ec.notifier);
|
||||
cbas_ec_set_input(NULL);
|
||||
|
||||
mutex_unlock(&cbas_ec_reglock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id cbas_ec_acpi_ids[] = {
|
||||
{ "GOOG000B", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, cbas_ec_acpi_ids);
|
||||
|
||||
static struct platform_driver cbas_ec_driver = {
|
||||
.probe = cbas_ec_probe,
|
||||
.remove = cbas_ec_remove,
|
||||
.driver = {
|
||||
.name = "cbas_ec",
|
||||
.acpi_match_table = ACPI_PTR(cbas_ec_acpi_ids),
|
||||
.pm = &cbas_ec_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
#define MAX_BRIGHTNESS 100
|
||||
|
||||
struct hammer_kbd_leds {
|
||||
struct led_classdev cdev;
|
||||
@ -90,33 +342,130 @@ static int hammer_register_leds(struct hid_device *hdev)
|
||||
return devm_led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
|
||||
}
|
||||
|
||||
static int hammer_input_configured(struct hid_device *hdev,
|
||||
struct hid_input *hi)
|
||||
#define HID_UP_GOOGLEVENDOR 0xffd10000
|
||||
#define HID_VD_KBD_FOLDED 0x00000019
|
||||
#define WHISKERS_KBD_FOLDED (HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED)
|
||||
|
||||
/* HID usage for keyboard backlight (Alphanumeric display brightness) */
|
||||
#define HID_AD_BRIGHTNESS 0x00140046
|
||||
|
||||
static int hammer_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field,
|
||||
struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
struct list_head *report_list =
|
||||
&hdev->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct hid_report *report;
|
||||
|
||||
if (list_empty(report_list))
|
||||
return 0;
|
||||
|
||||
report = list_first_entry(report_list, struct hid_report, list);
|
||||
|
||||
if (report->maxfield == 1 &&
|
||||
report->field[0]->application == HID_GD_KEYBOARD &&
|
||||
report->field[0]->maxusage == 1 &&
|
||||
report->field[0]->usage[0].hid == HID_AD_BRIGHTNESS) {
|
||||
int err = hammer_register_leds(hdev);
|
||||
|
||||
if (err)
|
||||
hid_warn(hdev,
|
||||
"Failed to register keyboard backlight: %d\n",
|
||||
err);
|
||||
if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS &&
|
||||
usage->hid == WHISKERS_KBD_FOLDED) {
|
||||
/*
|
||||
* We do not want to have this usage mapped as it will get
|
||||
* mixed in with "base attached" signal and delivered over
|
||||
* separate input device for tablet switch mode.
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hammer_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (hid->product == USB_DEVICE_ID_GOOGLE_WHISKERS &&
|
||||
usage->hid == WHISKERS_KBD_FOLDED) {
|
||||
spin_lock_irqsave(&cbas_ec_lock, flags);
|
||||
|
||||
hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__,
|
||||
cbas_ec.base_present, value);
|
||||
|
||||
/*
|
||||
* We should not get event if base is detached, but in case
|
||||
* we happen to service HID and EC notifications out of order
|
||||
* let's still check the "base present" flag.
|
||||
*/
|
||||
if (cbas_ec.input && cbas_ec.base_present) {
|
||||
input_report_switch(cbas_ec.input,
|
||||
SW_TABLET_MODE, value);
|
||||
input_sync(cbas_ec.input);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cbas_ec_lock, flags);
|
||||
return 1; /* We handled this event */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool hammer_is_keyboard_interface(struct hid_device *hdev)
|
||||
{
|
||||
struct hid_report_enum *re = &hdev->report_enum[HID_INPUT_REPORT];
|
||||
struct hid_report *report;
|
||||
|
||||
list_for_each_entry(report, &re->report_list, list)
|
||||
if (report->application == HID_GD_KEYBOARD)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hammer_has_backlight_control(struct hid_device *hdev)
|
||||
{
|
||||
struct hid_report_enum *re = &hdev->report_enum[HID_OUTPUT_REPORT];
|
||||
struct hid_report *report;
|
||||
int i, j;
|
||||
|
||||
list_for_each_entry(report, &re->report_list, list) {
|
||||
if (report->application != HID_GD_KEYBOARD)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < report->maxfield; i++) {
|
||||
struct hid_field *field = report->field[i];
|
||||
|
||||
for (j = 0; j < field->maxusage; j++)
|
||||
if (field->usage[j].hid == HID_AD_BRIGHTNESS)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int hammer_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
int error;
|
||||
|
||||
/*
|
||||
* We always want to poll for, and handle tablet mode events from
|
||||
* Whiskers, even when nobody has opened the input device. This also
|
||||
* prevents the hid core from dropping early tablet mode events from
|
||||
* the device.
|
||||
*/
|
||||
if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS &&
|
||||
hammer_is_keyboard_interface(hdev))
|
||||
hdev->quirks |= HID_QUIRK_ALWAYS_POLL;
|
||||
|
||||
error = hid_parse(hdev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (hammer_has_backlight_control(hdev)) {
|
||||
error = hammer_register_leds(hdev);
|
||||
if (error)
|
||||
hid_warn(hdev,
|
||||
"Failed to register keyboard backlight: %d\n",
|
||||
error);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct hid_device_id hammer_devices[] = {
|
||||
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
|
||||
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) },
|
||||
@ -133,8 +482,34 @@ MODULE_DEVICE_TABLE(hid, hammer_devices);
|
||||
static struct hid_driver hammer_driver = {
|
||||
.name = "hammer",
|
||||
.id_table = hammer_devices,
|
||||
.input_configured = hammer_input_configured,
|
||||
.probe = hammer_probe,
|
||||
.input_mapping = hammer_input_mapping,
|
||||
.event = hammer_event,
|
||||
};
|
||||
module_hid_driver(hammer_driver);
|
||||
|
||||
static int __init hammer_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = platform_driver_register(&cbas_ec_driver);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = hid_register_driver(&hammer_driver);
|
||||
if (error) {
|
||||
platform_driver_unregister(&cbas_ec_driver);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(hammer_init);
|
||||
|
||||
static void __exit hammer_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&hammer_driver);
|
||||
platform_driver_unregister(&cbas_ec_driver);
|
||||
}
|
||||
module_exit(hammer_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -92,6 +92,7 @@
|
||||
#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
|
||||
#define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d
|
||||
#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e
|
||||
#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 0x0265
|
||||
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e
|
||||
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f
|
||||
#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214
|
||||
@ -229,6 +230,9 @@
|
||||
#define USB_VENDOR_ID_BETOP_2185V2PC 0x8380
|
||||
#define USB_VENDOR_ID_BETOP_2185V2BFM 0x20bc
|
||||
|
||||
#define USB_VENDOR_ID_BIGBEN 0x146b
|
||||
#define USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD 0x0902
|
||||
|
||||
#define USB_VENDOR_ID_BTC 0x046e
|
||||
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578
|
||||
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577
|
||||
@ -342,6 +346,7 @@
|
||||
#define USB_DEVICE_ID_DMI_ENC 0x5fab
|
||||
|
||||
#define USB_VENDOR_ID_DRAGONRISE 0x0079
|
||||
#define USB_DEVICE_ID_REDRAGON_SEYMUR2 0x0006
|
||||
#define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800
|
||||
#define USB_DEVICE_ID_DRAGONRISE_PS3 0x1801
|
||||
#define USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR 0x1803
|
||||
@ -799,6 +804,7 @@
|
||||
#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7
|
||||
#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9
|
||||
#define USB_DEVICE_ID_MS_POWER_COVER 0x07da
|
||||
#define USB_DEVICE_ID_MS_XBOX_ONE_S_CONTROLLER 0x02fd
|
||||
|
||||
#define USB_VENDOR_ID_MOJO 0x8282
|
||||
#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201
|
||||
|
@ -758,6 +758,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
break;
|
||||
|
||||
case HID_UP_DIGITIZER:
|
||||
if ((field->application & 0xff) == 0x01) /* Digitizer */
|
||||
__set_bit(INPUT_PROP_POINTER, input->propbit);
|
||||
else if ((field->application & 0xff) == 0x02) /* Pen */
|
||||
__set_bit(INPUT_PROP_DIRECT, input->propbit);
|
||||
|
||||
switch (usage->hid & 0xff) {
|
||||
case 0x00: /* Undefined */
|
||||
goto ignore;
|
||||
@ -1516,6 +1521,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid,
|
||||
struct hid_input *hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL);
|
||||
struct input_dev *input_dev = input_allocate_device();
|
||||
const char *suffix = NULL;
|
||||
size_t suffix_len, name_len;
|
||||
|
||||
if (!hidinput || !input_dev)
|
||||
goto fail;
|
||||
@ -1559,10 +1565,15 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid,
|
||||
}
|
||||
|
||||
if (suffix) {
|
||||
hidinput->name = kasprintf(GFP_KERNEL, "%s %s",
|
||||
hid->name, suffix);
|
||||
if (!hidinput->name)
|
||||
goto fail;
|
||||
name_len = strlen(hid->name);
|
||||
suffix_len = strlen(suffix);
|
||||
if ((name_len < suffix_len) ||
|
||||
strcmp(hid->name + name_len - suffix_len, suffix)) {
|
||||
hidinput->name = kasprintf(GFP_KERNEL, "%s %s",
|
||||
hid->name, suffix);
|
||||
if (!hidinput->name)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
input_set_drvdata(input_dev, hid);
|
||||
@ -1827,3 +1838,48 @@ void hidinput_disconnect(struct hid_device *hid)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hidinput_disconnect);
|
||||
|
||||
/**
|
||||
* hid_scroll_counter_handle_scroll() - Send high- and low-resolution scroll
|
||||
* events given a high-resolution wheel
|
||||
* movement.
|
||||
* @counter: a hid_scroll_counter struct describing the wheel.
|
||||
* @hi_res_value: the movement of the wheel, in the mouse's high-resolution
|
||||
* units.
|
||||
*
|
||||
* Given a high-resolution movement, this function converts the movement into
|
||||
* microns and emits high-resolution scroll events for the input device. It also
|
||||
* uses the multiplier from &struct hid_scroll_counter to emit low-resolution
|
||||
* scroll events when appropriate for backwards-compatibility with userspace
|
||||
* input libraries.
|
||||
*/
|
||||
void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
|
||||
int hi_res_value)
|
||||
{
|
||||
int low_res_scroll_amount;
|
||||
/* Some wheels will rest 7/8ths of a notch from the previous notch
|
||||
* after slow movement, so we want the threshold for low-res events to
|
||||
* be in the middle of the notches (e.g. after 4/8ths) as opposed to on
|
||||
* the notches themselves (8/8ths).
|
||||
*/
|
||||
int threshold = counter->resolution_multiplier / 2;
|
||||
|
||||
input_report_rel(counter->dev, REL_WHEEL_HI_RES,
|
||||
hi_res_value * counter->microns_per_hi_res_unit);
|
||||
|
||||
counter->remainder += hi_res_value;
|
||||
if (abs(counter->remainder) >= threshold) {
|
||||
/* Add (or subtract) 1 because we want to trigger when the wheel
|
||||
* is half-way to the next notch (i.e. scroll 1 notch after a
|
||||
* 1/2 notch movement, 2 notches after a 1 1/2 notch movement,
|
||||
* etc.).
|
||||
*/
|
||||
low_res_scroll_amount =
|
||||
counter->remainder / counter->resolution_multiplier
|
||||
+ (hi_res_value > 0 ? 1 : -1);
|
||||
input_report_rel(counter->dev, REL_WHEEL,
|
||||
low_res_scroll_amount);
|
||||
counter->remainder -=
|
||||
low_res_scroll_amount * counter->resolution_multiplier;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_scroll_counter_handle_scroll);
|
||||
|
@ -64,6 +64,14 @@ MODULE_PARM_DESC(disable_tap_to_click,
|
||||
#define HIDPP_QUIRK_NO_HIDINPUT BIT(23)
|
||||
#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
|
||||
#define HIDPP_QUIRK_UNIFYING BIT(25)
|
||||
#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(26)
|
||||
#define HIDPP_QUIRK_HI_RES_SCROLL_X2120 BIT(27)
|
||||
#define HIDPP_QUIRK_HI_RES_SCROLL_X2121 BIT(28)
|
||||
|
||||
/* Convenience constant to check for any high-res support. */
|
||||
#define HIDPP_QUIRK_HI_RES_SCROLL (HIDPP_QUIRK_HI_RES_SCROLL_1P0 | \
|
||||
HIDPP_QUIRK_HI_RES_SCROLL_X2120 | \
|
||||
HIDPP_QUIRK_HI_RES_SCROLL_X2121)
|
||||
|
||||
#define HIDPP_QUIRK_DELAYED_INIT HIDPP_QUIRK_NO_HIDINPUT
|
||||
|
||||
@ -149,6 +157,7 @@ struct hidpp_device {
|
||||
unsigned long capabilities;
|
||||
|
||||
struct hidpp_battery battery;
|
||||
struct hid_scroll_counter vertical_wheel_counter;
|
||||
};
|
||||
|
||||
/* HID++ 1.0 error codes */
|
||||
@ -400,32 +409,53 @@ static void hidpp_prefix_name(char **name, int name_length)
|
||||
#define HIDPP_SET_LONG_REGISTER 0x82
|
||||
#define HIDPP_GET_LONG_REGISTER 0x83
|
||||
|
||||
#define HIDPP_REG_GENERAL 0x00
|
||||
|
||||
static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
|
||||
/**
|
||||
* hidpp10_set_register_bit() - Sets a single bit in a HID++ 1.0 register.
|
||||
* @hidpp_dev: the device to set the register on.
|
||||
* @register_address: the address of the register to modify.
|
||||
* @byte: the byte of the register to modify. Should be less than 3.
|
||||
* Return: 0 if successful, otherwise a negative error code.
|
||||
*/
|
||||
static int hidpp10_set_register_bit(struct hidpp_device *hidpp_dev,
|
||||
u8 register_address, u8 byte, u8 bit)
|
||||
{
|
||||
struct hidpp_report response;
|
||||
int ret;
|
||||
u8 params[3] = { 0 };
|
||||
|
||||
ret = hidpp_send_rap_command_sync(hidpp_dev,
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
HIDPP_GET_REGISTER,
|
||||
HIDPP_REG_GENERAL,
|
||||
NULL, 0, &response);
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
HIDPP_GET_REGISTER,
|
||||
register_address,
|
||||
NULL, 0, &response);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(params, response.rap.params, 3);
|
||||
|
||||
/* Set the battery bit */
|
||||
params[0] |= BIT(4);
|
||||
params[byte] |= BIT(bit);
|
||||
|
||||
return hidpp_send_rap_command_sync(hidpp_dev,
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
HIDPP_SET_REGISTER,
|
||||
HIDPP_REG_GENERAL,
|
||||
params, 3, &response);
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
HIDPP_SET_REGISTER,
|
||||
register_address,
|
||||
params, 3, &response);
|
||||
}
|
||||
|
||||
|
||||
#define HIDPP_REG_GENERAL 0x00
|
||||
|
||||
static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
|
||||
{
|
||||
return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_GENERAL, 0, 4);
|
||||
}
|
||||
|
||||
#define HIDPP_REG_FEATURES 0x01
|
||||
|
||||
/* On HID++ 1.0 devices, high-res scroll was called "scrolling acceleration". */
|
||||
static int hidpp10_enable_scrolling_acceleration(struct hidpp_device *hidpp_dev)
|
||||
{
|
||||
return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_FEATURES, 0, 6);
|
||||
}
|
||||
|
||||
#define HIDPP_REG_BATTERY_STATUS 0x07
|
||||
@ -1136,6 +1166,100 @@ static int hidpp_battery_get_property(struct power_supply *psy,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x2120: Hi-resolution scrolling */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define HIDPP_PAGE_HI_RESOLUTION_SCROLLING 0x2120
|
||||
|
||||
#define CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE 0x10
|
||||
|
||||
static int hidpp_hrs_set_highres_scrolling_mode(struct hidpp_device *hidpp,
|
||||
bool enabled, u8 *multiplier)
|
||||
{
|
||||
u8 feature_index;
|
||||
u8 feature_type;
|
||||
int ret;
|
||||
u8 params[1];
|
||||
struct hidpp_report response;
|
||||
|
||||
ret = hidpp_root_get_feature(hidpp,
|
||||
HIDPP_PAGE_HI_RESOLUTION_SCROLLING,
|
||||
&feature_index,
|
||||
&feature_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
params[0] = enabled ? BIT(0) : 0;
|
||||
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
||||
CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE,
|
||||
params, sizeof(params), &response);
|
||||
if (ret)
|
||||
return ret;
|
||||
*multiplier = response.fap.params[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x2121: HiRes Wheel */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define HIDPP_PAGE_HIRES_WHEEL 0x2121
|
||||
|
||||
#define CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY 0x00
|
||||
#define CMD_HIRES_WHEEL_SET_WHEEL_MODE 0x20
|
||||
|
||||
static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp,
|
||||
u8 *multiplier)
|
||||
{
|
||||
u8 feature_index;
|
||||
u8 feature_type;
|
||||
int ret;
|
||||
struct hidpp_report response;
|
||||
|
||||
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
|
||||
&feature_index, &feature_type);
|
||||
if (ret)
|
||||
goto return_default;
|
||||
|
||||
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
||||
CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY,
|
||||
NULL, 0, &response);
|
||||
if (ret)
|
||||
goto return_default;
|
||||
|
||||
*multiplier = response.fap.params[0];
|
||||
return 0;
|
||||
return_default:
|
||||
hid_warn(hidpp->hid_dev,
|
||||
"Couldn't get wheel multiplier (error %d), assuming %d.\n",
|
||||
ret, *multiplier);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert,
|
||||
bool high_resolution, bool use_hidpp)
|
||||
{
|
||||
u8 feature_index;
|
||||
u8 feature_type;
|
||||
int ret;
|
||||
u8 params[1];
|
||||
struct hidpp_report response;
|
||||
|
||||
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
|
||||
&feature_index, &feature_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
params[0] = (invert ? BIT(2) : 0) |
|
||||
(high_resolution ? BIT(1) : 0) |
|
||||
(use_hidpp ? BIT(0) : 0);
|
||||
|
||||
return hidpp_send_fap_command_sync(hidpp, feature_index,
|
||||
CMD_HIRES_WHEEL_SET_WHEEL_MODE,
|
||||
params, sizeof(params), &response);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x4301: Solar Keyboard */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@ -2399,7 +2523,8 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
|
||||
input_report_rel(mydata->input, REL_Y, v);
|
||||
|
||||
v = hid_snto32(data[6], 8);
|
||||
input_report_rel(mydata->input, REL_WHEEL, v);
|
||||
hid_scroll_counter_handle_scroll(
|
||||
&hidpp->vertical_wheel_counter, v);
|
||||
|
||||
input_sync(mydata->input);
|
||||
}
|
||||
@ -2527,6 +2652,72 @@ static int g920_get_config(struct hidpp_device *hidpp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* High-resolution scroll wheels */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* struct hi_res_scroll_info - Stores info on a device's high-res scroll wheel.
|
||||
* @product_id: the HID product ID of the device being described.
|
||||
* @microns_per_hi_res_unit: the distance moved by the user's finger for each
|
||||
* high-resolution unit reported by the device, in
|
||||
* 256ths of a millimetre.
|
||||
*/
|
||||
struct hi_res_scroll_info {
|
||||
__u32 product_id;
|
||||
int microns_per_hi_res_unit;
|
||||
};
|
||||
|
||||
static struct hi_res_scroll_info hi_res_scroll_devices[] = {
|
||||
{ /* Anywhere MX */
|
||||
.product_id = 0x1017, .microns_per_hi_res_unit = 445 },
|
||||
{ /* Performance MX */
|
||||
.product_id = 0x101a, .microns_per_hi_res_unit = 406 },
|
||||
{ /* M560 */
|
||||
.product_id = 0x402d, .microns_per_hi_res_unit = 435 },
|
||||
{ /* MX Master 2S */
|
||||
.product_id = 0x4069, .microns_per_hi_res_unit = 406 },
|
||||
};
|
||||
|
||||
static int hi_res_scroll_look_up_microns(__u32 product_id)
|
||||
{
|
||||
int i;
|
||||
int num_devices = sizeof(hi_res_scroll_devices)
|
||||
/ sizeof(hi_res_scroll_devices[0]);
|
||||
for (i = 0; i < num_devices; i++) {
|
||||
if (hi_res_scroll_devices[i].product_id == product_id)
|
||||
return hi_res_scroll_devices[i].microns_per_hi_res_unit;
|
||||
}
|
||||
/* We don't have a value for this device, so use a sensible default. */
|
||||
return 406;
|
||||
}
|
||||
|
||||
static int hi_res_scroll_enable(struct hidpp_device *hidpp)
|
||||
{
|
||||
int ret;
|
||||
u8 multiplier = 8;
|
||||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2121) {
|
||||
ret = hidpp_hrw_set_wheel_mode(hidpp, false, true, false);
|
||||
hidpp_hrw_get_wheel_capability(hidpp, &multiplier);
|
||||
} else if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2120) {
|
||||
ret = hidpp_hrs_set_highres_scrolling_mode(hidpp, true,
|
||||
&multiplier);
|
||||
} else /* if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_1P0) */
|
||||
ret = hidpp10_enable_scrolling_acceleration(hidpp);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hidpp->vertical_wheel_counter.resolution_multiplier = multiplier;
|
||||
hidpp->vertical_wheel_counter.microns_per_hi_res_unit =
|
||||
hi_res_scroll_look_up_microns(hidpp->hid_dev->product);
|
||||
hid_info(hidpp->hid_dev, "multiplier = %d, microns = %d\n",
|
||||
multiplier,
|
||||
hidpp->vertical_wheel_counter.microns_per_hi_res_unit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Generic HID++ devices */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@ -2572,6 +2763,11 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,
|
||||
wtp_populate_input(hidpp, input, origin_is_hid_core);
|
||||
else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
|
||||
m560_populate_input(hidpp, input, origin_is_hid_core);
|
||||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) {
|
||||
input_set_capability(input, EV_REL, REL_WHEEL_HI_RES);
|
||||
hidpp->vertical_wheel_counter.dev = input;
|
||||
}
|
||||
}
|
||||
|
||||
static int hidpp_input_configured(struct hid_device *hdev,
|
||||
@ -2690,6 +2886,27 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hidpp_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
/* This function will only be called for scroll events, due to the
|
||||
* restriction imposed in hidpp_usages.
|
||||
*/
|
||||
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
||||
struct hid_scroll_counter *counter = &hidpp->vertical_wheel_counter;
|
||||
/* A scroll event may occur before the multiplier has been retrieved or
|
||||
* the input device set, or high-res scroll enabling may fail. In such
|
||||
* cases we must return early (falling back to default behaviour) to
|
||||
* avoid a crash in hid_scroll_counter_handle_scroll.
|
||||
*/
|
||||
if (!(hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) || value == 0
|
||||
|| counter->dev == NULL || counter->resolution_multiplier == 0)
|
||||
return 0;
|
||||
|
||||
hid_scroll_counter_handle_scroll(counter, value);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hidpp_initialize_battery(struct hidpp_device *hidpp)
|
||||
{
|
||||
static atomic_t battery_no = ATOMIC_INIT(0);
|
||||
@ -2901,6 +3118,9 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
|
||||
if (hidpp->battery.ps)
|
||||
power_supply_changed(hidpp->battery.ps);
|
||||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL)
|
||||
hi_res_scroll_enable(hidpp);
|
||||
|
||||
if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)
|
||||
/* if the input nodes are already created, we can stop now */
|
||||
return;
|
||||
@ -3086,35 +3306,63 @@ static void hidpp_remove(struct hid_device *hdev)
|
||||
mutex_destroy(&hidpp->send_mutex);
|
||||
}
|
||||
|
||||
#define LDJ_DEVICE(product) \
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, \
|
||||
USB_VENDOR_ID_LOGITECH, (product))
|
||||
|
||||
static const struct hid_device_id hidpp_devices[] = {
|
||||
{ /* wireless touchpad */
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, 0x4011),
|
||||
LDJ_DEVICE(0x4011),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |
|
||||
HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS },
|
||||
{ /* wireless touchpad T650 */
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, 0x4101),
|
||||
LDJ_DEVICE(0x4101),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT },
|
||||
{ /* wireless touchpad T651 */
|
||||
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
|
||||
USB_DEVICE_ID_LOGITECH_T651),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_WTP },
|
||||
{ /* Mouse Logitech Anywhere MX */
|
||||
LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
|
||||
{ /* Mouse Logitech Cube */
|
||||
LDJ_DEVICE(0x4010), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
|
||||
{ /* Mouse Logitech M335 */
|
||||
LDJ_DEVICE(0x4050), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech M515 */
|
||||
LDJ_DEVICE(0x4007), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
|
||||
{ /* Mouse logitech M560 */
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, 0x402d),
|
||||
.driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
|
||||
LDJ_DEVICE(0x402d),
|
||||
.driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560
|
||||
| HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
|
||||
{ /* Mouse Logitech M705 (firmware RQM17) */
|
||||
LDJ_DEVICE(0x101b), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
|
||||
{ /* Mouse Logitech M705 (firmware RQM67) */
|
||||
LDJ_DEVICE(0x406d), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech M720 */
|
||||
LDJ_DEVICE(0x405e), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech MX Anywhere 2 */
|
||||
LDJ_DEVICE(0x404a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ LDJ_DEVICE(0xb013), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ LDJ_DEVICE(0xb018), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ LDJ_DEVICE(0xb01f), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech MX Anywhere 2S */
|
||||
LDJ_DEVICE(0x406a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech MX Master */
|
||||
LDJ_DEVICE(0x4041), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ LDJ_DEVICE(0x4060), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ LDJ_DEVICE(0x4071), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech MX Master 2S */
|
||||
LDJ_DEVICE(0x4069), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech Performance MX */
|
||||
LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
|
||||
{ /* Keyboard logitech K400 */
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, 0x4024),
|
||||
LDJ_DEVICE(0x4024),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_K400 },
|
||||
{ /* Solar Keyboard Logitech K750 */
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, 0x4002),
|
||||
LDJ_DEVICE(0x4002),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_K750 },
|
||||
|
||||
{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
|
||||
{ LDJ_DEVICE(HID_ANY_ID) },
|
||||
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
|
||||
@ -3123,12 +3371,19 @@ static const struct hid_device_id hidpp_devices[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(hid, hidpp_devices);
|
||||
|
||||
static const struct hid_usage_id hidpp_usages[] = {
|
||||
{ HID_GD_WHEEL, EV_REL, REL_WHEEL },
|
||||
{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
|
||||
};
|
||||
|
||||
static struct hid_driver hidpp_driver = {
|
||||
.name = "logitech-hidpp-device",
|
||||
.id_table = hidpp_devices,
|
||||
.probe = hidpp_probe,
|
||||
.remove = hidpp_remove,
|
||||
.raw_event = hidpp_raw_event,
|
||||
.usage_table = hidpp_usages,
|
||||
.event = hidpp_event,
|
||||
.input_configured = hidpp_input_configured,
|
||||
.input_mapping = hidpp_input_mapping,
|
||||
.input_mapped = hidpp_input_mapped,
|
||||
|
@ -54,6 +54,8 @@ module_param(report_undeciphered, bool, 0644);
|
||||
MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
|
||||
|
||||
#define TRACKPAD_REPORT_ID 0x28
|
||||
#define TRACKPAD2_USB_REPORT_ID 0x02
|
||||
#define TRACKPAD2_BT_REPORT_ID 0x31
|
||||
#define MOUSE_REPORT_ID 0x29
|
||||
#define DOUBLE_REPORT_ID 0xf7
|
||||
/* These definitions are not precise, but they're close enough. (Bits
|
||||
@ -91,6 +93,17 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
|
||||
#define TRACKPAD_RES_Y \
|
||||
((TRACKPAD_MAX_Y - TRACKPAD_MIN_Y) / (TRACKPAD_DIMENSION_Y / 100))
|
||||
|
||||
#define TRACKPAD2_DIMENSION_X (float)16000
|
||||
#define TRACKPAD2_MIN_X -3678
|
||||
#define TRACKPAD2_MAX_X 3934
|
||||
#define TRACKPAD2_RES_X \
|
||||
((TRACKPAD2_MAX_X - TRACKPAD2_MIN_X) / (TRACKPAD2_DIMENSION_X / 100))
|
||||
#define TRACKPAD2_DIMENSION_Y (float)11490
|
||||
#define TRACKPAD2_MIN_Y -2478
|
||||
#define TRACKPAD2_MAX_Y 2587
|
||||
#define TRACKPAD2_RES_Y \
|
||||
((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100))
|
||||
|
||||
/**
|
||||
* struct magicmouse_sc - Tracks Magic Mouse-specific data.
|
||||
* @input: Input device through which we report events.
|
||||
@ -183,6 +196,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
|
||||
{
|
||||
struct input_dev *input = msc->input;
|
||||
int id, x, y, size, orientation, touch_major, touch_minor, state, down;
|
||||
int pressure = 0;
|
||||
|
||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
|
||||
id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
|
||||
@ -194,6 +208,17 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
|
||||
touch_minor = tdata[4];
|
||||
state = tdata[7] & TOUCH_STATE_MASK;
|
||||
down = state != TOUCH_STATE_NONE;
|
||||
} else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
|
||||
id = tdata[8] & 0xf;
|
||||
x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
|
||||
y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19);
|
||||
size = tdata[6];
|
||||
orientation = (tdata[8] >> 5) - 4;
|
||||
touch_major = tdata[4];
|
||||
touch_minor = tdata[5];
|
||||
pressure = tdata[7];
|
||||
state = tdata[3] & 0xC0;
|
||||
down = state == 0x80;
|
||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf;
|
||||
x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
|
||||
@ -215,7 +240,8 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
|
||||
/* If requested, emulate a scroll wheel by detecting small
|
||||
* vertical touch motions.
|
||||
*/
|
||||
if (emulate_scroll_wheel) {
|
||||
if (emulate_scroll_wheel && (input->id.product !=
|
||||
USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)) {
|
||||
unsigned long now = jiffies;
|
||||
int step_x = msc->touches[id].scroll_x - x;
|
||||
int step_y = msc->touches[id].scroll_y - y;
|
||||
@ -269,10 +295,14 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
|
||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)
|
||||
input_report_abs(input, ABS_MT_PRESSURE, pressure);
|
||||
|
||||
if (report_undeciphered) {
|
||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
|
||||
input_event(input, EV_MSC, MSC_RAW, tdata[7]);
|
||||
else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
else if (input->id.product !=
|
||||
USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)
|
||||
input_event(input, EV_MSC, MSC_RAW, tdata[8]);
|
||||
}
|
||||
}
|
||||
@ -287,6 +317,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
|
||||
|
||||
switch (data[0]) {
|
||||
case TRACKPAD_REPORT_ID:
|
||||
case TRACKPAD2_BT_REPORT_ID:
|
||||
/* Expect four bytes of prefix, and N*9 bytes of touch data. */
|
||||
if (size < 4 || ((size - 4) % 9) != 0)
|
||||
return 0;
|
||||
@ -308,6 +339,22 @@ static int magicmouse_raw_event(struct hid_device *hdev,
|
||||
* ts = data[1] >> 6 | data[2] << 2 | data[3] << 10;
|
||||
*/
|
||||
break;
|
||||
case TRACKPAD2_USB_REPORT_ID:
|
||||
/* Expect twelve bytes of prefix and N*9 bytes of touch data. */
|
||||
if (size < 12 || ((size - 12) % 9) != 0)
|
||||
return 0;
|
||||
npoints = (size - 12) / 9;
|
||||
if (npoints > 15) {
|
||||
hid_warn(hdev, "invalid size value (%d) for TRACKPAD2_USB_REPORT_ID\n",
|
||||
size);
|
||||
return 0;
|
||||
}
|
||||
msc->ntouches = 0;
|
||||
for (ii = 0; ii < npoints; ii++)
|
||||
magicmouse_emit_touch(msc, ii, data + ii * 9 + 12);
|
||||
|
||||
clicks = data[1];
|
||||
break;
|
||||
case MOUSE_REPORT_ID:
|
||||
/* Expect six bytes of prefix, and N*8 bytes of touch data. */
|
||||
if (size < 6 || ((size - 6) % 8) != 0)
|
||||
@ -352,6 +399,9 @@ static int magicmouse_raw_event(struct hid_device *hdev,
|
||||
magicmouse_emit_buttons(msc, clicks & 3);
|
||||
input_report_rel(input, REL_X, x);
|
||||
input_report_rel(input, REL_Y, y);
|
||||
} else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
|
||||
input_mt_sync_frame(input);
|
||||
input_report_key(input, BTN_MOUSE, clicks & 1);
|
||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
input_report_key(input, BTN_MOUSE, clicks & 1);
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
@ -364,6 +414,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
|
||||
static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev)
|
||||
{
|
||||
int error;
|
||||
int mt_flags = 0;
|
||||
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
|
||||
@ -380,6 +431,22 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
|
||||
__set_bit(REL_WHEEL, input->relbit);
|
||||
__set_bit(REL_HWHEEL, input->relbit);
|
||||
}
|
||||
} else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
|
||||
/* setting the device name to ensure the same driver settings
|
||||
* get loaded, whether connected through bluetooth or USB
|
||||
*/
|
||||
input->name = "Apple Inc. Magic Trackpad 2";
|
||||
|
||||
__clear_bit(EV_MSC, input->evbit);
|
||||
__clear_bit(BTN_0, input->keybit);
|
||||
__clear_bit(BTN_RIGHT, input->keybit);
|
||||
__clear_bit(BTN_MIDDLE, input->keybit);
|
||||
__set_bit(BTN_MOUSE, input->keybit);
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
||||
__set_bit(BTN_TOOL_FINGER, input->keybit);
|
||||
|
||||
mt_flags = INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
|
||||
INPUT_MT_TRACK;
|
||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
/* input->keybit is initialized with incorrect button info
|
||||
* for Magic Trackpad. There really is only one physical
|
||||
@ -402,14 +469,13 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
|
||||
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
|
||||
error = input_mt_init_slots(input, 16, 0);
|
||||
error = input_mt_init_slots(input, 16, mt_flags);
|
||||
if (error)
|
||||
return error;
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
|
||||
4, 0);
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2,
|
||||
4, 0);
|
||||
input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
|
||||
|
||||
/* Note: Touch Y position from the device is inverted relative
|
||||
* to how pointer motion is reported (and relative to how USB
|
||||
@ -418,6 +484,7 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
|
||||
* inverse of the reported Y.
|
||||
*/
|
||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
|
||||
input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X,
|
||||
MOUSE_MIN_X, MOUSE_MAX_X, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y,
|
||||
@ -427,7 +494,25 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
|
||||
MOUSE_RES_X);
|
||||
input_abs_set_res(input, ABS_MT_POSITION_Y,
|
||||
MOUSE_RES_Y);
|
||||
} else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
|
||||
input_set_abs_params(input, ABS_MT_PRESSURE, 0, 253, 0, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE, 0, 253, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_ORIENTATION, -3, 4, 0, 0);
|
||||
input_set_abs_params(input, ABS_X, TRACKPAD2_MIN_X,
|
||||
TRACKPAD2_MAX_X, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, TRACKPAD2_MIN_Y,
|
||||
TRACKPAD2_MAX_Y, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X,
|
||||
TRACKPAD2_MIN_X, TRACKPAD2_MAX_X, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y,
|
||||
TRACKPAD2_MIN_Y, TRACKPAD2_MAX_Y, 0, 0);
|
||||
|
||||
input_abs_set_res(input, ABS_X, TRACKPAD2_RES_X);
|
||||
input_abs_set_res(input, ABS_Y, TRACKPAD2_RES_Y);
|
||||
input_abs_set_res(input, ABS_MT_POSITION_X, TRACKPAD2_RES_X);
|
||||
input_abs_set_res(input, ABS_MT_POSITION_Y, TRACKPAD2_RES_Y);
|
||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
|
||||
input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X,
|
||||
TRACKPAD_MAX_X, 4, 0);
|
||||
input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y,
|
||||
@ -447,7 +532,8 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
|
||||
|
||||
input_set_events_per_packet(input, 60);
|
||||
|
||||
if (report_undeciphered) {
|
||||
if (report_undeciphered &&
|
||||
input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
|
||||
__set_bit(EV_MSC, input->evbit);
|
||||
__set_bit(MSC_RAW, input->mscbit);
|
||||
}
|
||||
@ -465,7 +551,8 @@ static int magicmouse_input_mapping(struct hid_device *hdev,
|
||||
msc->input = hi->input;
|
||||
|
||||
/* Magic Trackpad does not give relative data after switching to MT */
|
||||
if (hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD &&
|
||||
if ((hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD ||
|
||||
hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) &&
|
||||
field->flags & HID_MAIN_ITEM_RELATIVE)
|
||||
return -1;
|
||||
|
||||
@ -494,11 +581,20 @@ static int magicmouse_input_configured(struct hid_device *hdev,
|
||||
static int magicmouse_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
const u8 feature[] = { 0xd7, 0x01 };
|
||||
const u8 *feature;
|
||||
const u8 feature_mt[] = { 0xD7, 0x01 };
|
||||
const u8 feature_mt_trackpad2_usb[] = { 0x02, 0x01 };
|
||||
const u8 feature_mt_trackpad2_bt[] = { 0xF1, 0x02, 0x01 };
|
||||
u8 *buf;
|
||||
struct magicmouse_sc *msc;
|
||||
struct hid_report *report;
|
||||
int ret;
|
||||
int feature_size;
|
||||
|
||||
if (id->vendor == USB_VENDOR_ID_APPLE &&
|
||||
id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 &&
|
||||
hdev->type != HID_TYPE_USBMOUSE)
|
||||
return 0;
|
||||
|
||||
msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
|
||||
if (msc == NULL) {
|
||||
@ -532,7 +628,14 @@ static int magicmouse_probe(struct hid_device *hdev,
|
||||
if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
|
||||
report = hid_register_report(hdev, HID_INPUT_REPORT,
|
||||
MOUSE_REPORT_ID, 0);
|
||||
else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
else if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
|
||||
if (id->vendor == BT_VENDOR_ID_APPLE)
|
||||
report = hid_register_report(hdev, HID_INPUT_REPORT,
|
||||
TRACKPAD2_BT_REPORT_ID, 0);
|
||||
else /* USB_VENDOR_ID_APPLE */
|
||||
report = hid_register_report(hdev, HID_INPUT_REPORT,
|
||||
TRACKPAD2_USB_REPORT_ID, 0);
|
||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
report = hid_register_report(hdev, HID_INPUT_REPORT,
|
||||
TRACKPAD_REPORT_ID, 0);
|
||||
report = hid_register_report(hdev, HID_INPUT_REPORT,
|
||||
@ -546,7 +649,20 @@ static int magicmouse_probe(struct hid_device *hdev,
|
||||
}
|
||||
report->size = 6;
|
||||
|
||||
buf = kmemdup(feature, sizeof(feature), GFP_KERNEL);
|
||||
if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
|
||||
if (id->vendor == BT_VENDOR_ID_APPLE) {
|
||||
feature_size = sizeof(feature_mt_trackpad2_bt);
|
||||
feature = feature_mt_trackpad2_bt;
|
||||
} else { /* USB_VENDOR_ID_APPLE */
|
||||
feature_size = sizeof(feature_mt_trackpad2_usb);
|
||||
feature = feature_mt_trackpad2_usb;
|
||||
}
|
||||
} else {
|
||||
feature_size = sizeof(feature_mt);
|
||||
feature = feature_mt;
|
||||
}
|
||||
|
||||
buf = kmemdup(feature, feature_size, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
goto err_stop_hw;
|
||||
@ -560,10 +676,10 @@ static int magicmouse_probe(struct hid_device *hdev,
|
||||
* but there seems to be no other way of switching the mode.
|
||||
* Thus the super-ugly hacky success check below.
|
||||
*/
|
||||
ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(feature),
|
||||
ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size,
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
kfree(buf);
|
||||
if (ret != -EIO && ret != sizeof(feature)) {
|
||||
if (ret != -EIO && ret != feature_size) {
|
||||
hid_err(hdev, "unable to request touch data (%d)\n", ret);
|
||||
goto err_stop_hw;
|
||||
}
|
||||
@ -579,6 +695,10 @@ static const struct hid_device_id magic_mice[] = {
|
||||
USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
|
||||
USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
|
||||
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
|
||||
USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
|
||||
USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, magic_mice);
|
||||
|
@ -29,11 +29,41 @@
|
||||
#define MS_NOGET BIT(4)
|
||||
#define MS_DUPLICATE_USAGES BIT(5)
|
||||
#define MS_SURFACE_DIAL BIT(6)
|
||||
#define MS_QUIRK_FF BIT(7)
|
||||
|
||||
struct ms_data {
|
||||
unsigned long quirks;
|
||||
struct hid_device *hdev;
|
||||
struct work_struct ff_worker;
|
||||
__u8 strong;
|
||||
__u8 weak;
|
||||
void *output_report_dmabuf;
|
||||
};
|
||||
|
||||
#define XB1S_FF_REPORT 3
|
||||
#define ENABLE_WEAK BIT(0)
|
||||
#define ENABLE_STRONG BIT(1)
|
||||
|
||||
enum {
|
||||
MAGNITUDE_STRONG = 2,
|
||||
MAGNITUDE_WEAK,
|
||||
MAGNITUDE_NUM
|
||||
};
|
||||
|
||||
struct xb1s_ff_report {
|
||||
__u8 report_id;
|
||||
__u8 enable;
|
||||
__u8 magnitude[MAGNITUDE_NUM];
|
||||
__u8 duration_10ms;
|
||||
__u8 start_delay_10ms;
|
||||
__u8 loop_count;
|
||||
} __packed;
|
||||
|
||||
static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
||||
struct ms_data *ms = hid_get_drvdata(hdev);
|
||||
unsigned long quirks = ms->quirks;
|
||||
|
||||
/*
|
||||
* Microsoft Wireless Desktop Receiver (Model 1028) has
|
||||
@ -159,7 +189,8 @@ 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)
|
||||
{
|
||||
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
||||
struct ms_data *ms = hid_get_drvdata(hdev);
|
||||
unsigned long quirks = ms->quirks;
|
||||
|
||||
if (quirks & MS_ERGONOMY) {
|
||||
int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max);
|
||||
@ -185,7 +216,8 @@ static int ms_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
||||
struct ms_data *ms = hid_get_drvdata(hdev);
|
||||
unsigned long quirks = ms->quirks;
|
||||
|
||||
if (quirks & MS_DUPLICATE_USAGES)
|
||||
clear_bit(usage->code, *bit);
|
||||
@ -196,7 +228,8 @@ static int ms_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
static int ms_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
||||
struct ms_data *ms = hid_get_drvdata(hdev);
|
||||
unsigned long quirks = ms->quirks;
|
||||
struct input_dev *input;
|
||||
|
||||
if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
|
||||
@ -251,12 +284,97 @@ static int ms_event(struct hid_device *hdev, struct hid_field *field,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ms_ff_worker(struct work_struct *work)
|
||||
{
|
||||
struct ms_data *ms = container_of(work, struct ms_data, ff_worker);
|
||||
struct hid_device *hdev = ms->hdev;
|
||||
struct xb1s_ff_report *r = ms->output_report_dmabuf;
|
||||
int ret;
|
||||
|
||||
memset(r, 0, sizeof(*r));
|
||||
|
||||
r->report_id = XB1S_FF_REPORT;
|
||||
r->enable = ENABLE_WEAK | ENABLE_STRONG;
|
||||
/*
|
||||
* Specifying maximum duration and maximum loop count should
|
||||
* cover maximum duration of a single effect, which is 65536
|
||||
* ms
|
||||
*/
|
||||
r->duration_10ms = U8_MAX;
|
||||
r->loop_count = U8_MAX;
|
||||
r->magnitude[MAGNITUDE_STRONG] = ms->strong; /* left actuator */
|
||||
r->magnitude[MAGNITUDE_WEAK] = ms->weak; /* right actuator */
|
||||
|
||||
ret = hid_hw_output_report(hdev, (__u8 *)r, sizeof(*r));
|
||||
if (ret)
|
||||
hid_warn(hdev, "failed to send FF report\n");
|
||||
}
|
||||
|
||||
static int ms_play_effect(struct input_dev *dev, void *data,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = input_get_drvdata(dev);
|
||||
struct ms_data *ms = hid_get_drvdata(hid);
|
||||
|
||||
if (effect->type != FF_RUMBLE)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Magnitude is 0..100 so scale the 16-bit input here
|
||||
*/
|
||||
ms->strong = ((u32) effect->u.rumble.strong_magnitude * 100) / U16_MAX;
|
||||
ms->weak = ((u32) effect->u.rumble.weak_magnitude * 100) / U16_MAX;
|
||||
|
||||
schedule_work(&ms->ff_worker);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms_init_ff(struct hid_device *hdev)
|
||||
{
|
||||
struct hid_input *hidinput = list_entry(hdev->inputs.next,
|
||||
struct hid_input, list);
|
||||
struct input_dev *input_dev = hidinput->input;
|
||||
struct ms_data *ms = hid_get_drvdata(hdev);
|
||||
|
||||
if (!(ms->quirks & MS_QUIRK_FF))
|
||||
return 0;
|
||||
|
||||
ms->hdev = hdev;
|
||||
INIT_WORK(&ms->ff_worker, ms_ff_worker);
|
||||
|
||||
ms->output_report_dmabuf = devm_kzalloc(&hdev->dev,
|
||||
sizeof(struct xb1s_ff_report),
|
||||
GFP_KERNEL);
|
||||
if (ms->output_report_dmabuf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
input_set_capability(input_dev, EV_FF, FF_RUMBLE);
|
||||
return input_ff_create_memless(input_dev, NULL, ms_play_effect);
|
||||
}
|
||||
|
||||
static void ms_remove_ff(struct hid_device *hdev)
|
||||
{
|
||||
struct ms_data *ms = hid_get_drvdata(hdev);
|
||||
|
||||
if (!(ms->quirks & MS_QUIRK_FF))
|
||||
return;
|
||||
|
||||
cancel_work_sync(&ms->ff_worker);
|
||||
}
|
||||
|
||||
static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
unsigned long quirks = id->driver_data;
|
||||
struct ms_data *ms;
|
||||
int ret;
|
||||
|
||||
hid_set_drvdata(hdev, (void *)quirks);
|
||||
ms = devm_kzalloc(&hdev->dev, sizeof(*ms), GFP_KERNEL);
|
||||
if (ms == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ms->quirks = quirks;
|
||||
|
||||
hid_set_drvdata(hdev, ms);
|
||||
|
||||
if (quirks & MS_NOGET)
|
||||
hdev->quirks |= HID_QUIRK_NOGET;
|
||||
@ -277,11 +395,21 @@ static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = ms_init_ff(hdev);
|
||||
if (ret)
|
||||
hid_err(hdev, "could not initialize ff, continuing anyway");
|
||||
|
||||
return 0;
|
||||
err_free:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ms_remove(struct hid_device *hdev)
|
||||
{
|
||||
hid_hw_stop(hdev);
|
||||
ms_remove_ff(hdev);
|
||||
}
|
||||
|
||||
static const struct hid_device_id ms_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV),
|
||||
.driver_data = MS_HIDINPUT },
|
||||
@ -318,6 +446,8 @@ static const struct hid_device_id ms_devices[] = {
|
||||
.driver_data = MS_PRESENTER },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B),
|
||||
.driver_data = MS_SURFACE_DIAL },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_XBOX_ONE_S_CONTROLLER),
|
||||
.driver_data = MS_QUIRK_FF },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, ms_devices);
|
||||
@ -330,6 +460,7 @@ static struct hid_driver ms_driver = {
|
||||
.input_mapped = ms_input_mapped,
|
||||
.event = ms_event,
|
||||
.probe = ms_probe,
|
||||
.remove = ms_remove,
|
||||
};
|
||||
module_hid_driver(ms_driver);
|
||||
|
||||
|
@ -1319,6 +1319,13 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
return mt_touch_input_mapping(hdev, hi, field, usage, bit, max,
|
||||
application);
|
||||
|
||||
/*
|
||||
* some egalax touchscreens have "application == DG_TOUCHSCREEN"
|
||||
* for the stylus. Overwrite the hid_input application
|
||||
*/
|
||||
if (field->physical == HID_DG_STYLUS)
|
||||
hi->application = HID_DG_STYLUS;
|
||||
|
||||
/* let hid-core decide for the others */
|
||||
return 0;
|
||||
}
|
||||
@ -1507,14 +1514,12 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
|
||||
struct mt_device *td = hid_get_drvdata(hdev);
|
||||
char *name;
|
||||
const char *suffix = NULL;
|
||||
unsigned int application = 0;
|
||||
struct mt_report_data *rdata;
|
||||
struct mt_application *mt_application = NULL;
|
||||
struct hid_report *report;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(report, &hi->reports, hidinput_list) {
|
||||
application = report->application;
|
||||
rdata = mt_find_report_data(td, report);
|
||||
if (!rdata) {
|
||||
hid_err(hdev, "failed to allocate data for report\n");
|
||||
@ -1529,46 +1534,33 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* some egalax touchscreens have "application == DG_TOUCHSCREEN"
|
||||
* for the stylus. Check this first, and then rely on
|
||||
* the application field.
|
||||
*/
|
||||
if (report->field[0]->physical == HID_DG_STYLUS) {
|
||||
suffix = "Pen";
|
||||
/* force BTN_STYLUS to allow tablet matching in udev */
|
||||
__set_bit(BTN_STYLUS, hi->input->keybit);
|
||||
}
|
||||
}
|
||||
|
||||
if (!suffix) {
|
||||
switch (application) {
|
||||
case HID_GD_KEYBOARD:
|
||||
case HID_GD_KEYPAD:
|
||||
case HID_GD_MOUSE:
|
||||
case HID_DG_TOUCHPAD:
|
||||
case HID_GD_SYSTEM_CONTROL:
|
||||
case HID_CP_CONSUMER_CONTROL:
|
||||
case HID_GD_WIRELESS_RADIO_CTLS:
|
||||
case HID_GD_SYSTEM_MULTIAXIS:
|
||||
/* already handled by hid core */
|
||||
break;
|
||||
case HID_DG_TOUCHSCREEN:
|
||||
/* we do not set suffix = "Touchscreen" */
|
||||
hi->input->name = hdev->name;
|
||||
break;
|
||||
case HID_DG_STYLUS:
|
||||
/* force BTN_STYLUS to allow tablet matching in udev */
|
||||
__set_bit(BTN_STYLUS, hi->input->keybit);
|
||||
break;
|
||||
case HID_VD_ASUS_CUSTOM_MEDIA_KEYS:
|
||||
suffix = "Custom Media Keys";
|
||||
break;
|
||||
default:
|
||||
suffix = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
switch (hi->application) {
|
||||
case HID_GD_KEYBOARD:
|
||||
case HID_GD_KEYPAD:
|
||||
case HID_GD_MOUSE:
|
||||
case HID_DG_TOUCHPAD:
|
||||
case HID_GD_SYSTEM_CONTROL:
|
||||
case HID_CP_CONSUMER_CONTROL:
|
||||
case HID_GD_WIRELESS_RADIO_CTLS:
|
||||
case HID_GD_SYSTEM_MULTIAXIS:
|
||||
/* already handled by hid core */
|
||||
break;
|
||||
case HID_DG_TOUCHSCREEN:
|
||||
/* we do not set suffix = "Touchscreen" */
|
||||
hi->input->name = hdev->name;
|
||||
break;
|
||||
case HID_DG_STYLUS:
|
||||
/* force BTN_STYLUS to allow tablet matching in udev */
|
||||
__set_bit(BTN_STYLUS, hi->input->keybit);
|
||||
break;
|
||||
case HID_VD_ASUS_CUSTOM_MEDIA_KEYS:
|
||||
suffix = "Custom Media Keys";
|
||||
break;
|
||||
default:
|
||||
suffix = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
if (suffix) {
|
||||
|
@ -70,6 +70,7 @@ static const struct hid_device_id hid_quirks[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC), HID_QUIRK_NOGET },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_2NES2SNES), HID_QUIRK_MULTI_INPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_4NES4SNES), HID_QUIRK_MULTI_INPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_REDRAGON_SEYMUR2), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR), HID_QUIRK_MULTI_INPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE1), HID_QUIRK_MULTI_INPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3), HID_QUIRK_MULTI_INPUT },
|
||||
|
@ -3,3 +3,6 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_I2C_HID) += i2c-hid.o
|
||||
|
||||
i2c-hid-objs = i2c-hid-core.o
|
||||
i2c-hid-$(CONFIG_DMI) += i2c-hid-dmi-quirks.o
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include <linux/platform_data/i2c-hid.h>
|
||||
|
||||
#include "../hid-ids.h"
|
||||
#include "i2c-hid.h"
|
||||
|
||||
/* quirks to control the device */
|
||||
#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0)
|
||||
@ -668,6 +669,7 @@ static int i2c_hid_parse(struct hid_device *hid)
|
||||
char *rdesc;
|
||||
int ret;
|
||||
int tries = 3;
|
||||
char *use_override;
|
||||
|
||||
i2c_hid_dbg(ihid, "entering %s\n", __func__);
|
||||
|
||||
@ -686,26 +688,37 @@ static int i2c_hid_parse(struct hid_device *hid)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rdesc = kzalloc(rsize, GFP_KERNEL);
|
||||
use_override = i2c_hid_get_dmi_hid_report_desc_override(client->name,
|
||||
&rsize);
|
||||
|
||||
if (!rdesc) {
|
||||
dbg_hid("couldn't allocate rdesc memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (use_override) {
|
||||
rdesc = use_override;
|
||||
i2c_hid_dbg(ihid, "Using a HID report descriptor override\n");
|
||||
} else {
|
||||
rdesc = kzalloc(rsize, GFP_KERNEL);
|
||||
|
||||
i2c_hid_dbg(ihid, "asking HID report descriptor\n");
|
||||
if (!rdesc) {
|
||||
dbg_hid("couldn't allocate rdesc memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = i2c_hid_command(client, &hid_report_descr_cmd, rdesc, rsize);
|
||||
if (ret) {
|
||||
hid_err(hid, "reading report descriptor failed\n");
|
||||
kfree(rdesc);
|
||||
return -EIO;
|
||||
i2c_hid_dbg(ihid, "asking HID report descriptor\n");
|
||||
|
||||
ret = i2c_hid_command(client, &hid_report_descr_cmd,
|
||||
rdesc, rsize);
|
||||
if (ret) {
|
||||
hid_err(hid, "reading report descriptor failed\n");
|
||||
kfree(rdesc);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_hid_dbg(ihid, "Report Descriptor: %*ph\n", rsize, rdesc);
|
||||
|
||||
ret = hid_parse_report(hid, rdesc, rsize);
|
||||
kfree(rdesc);
|
||||
if (!use_override)
|
||||
kfree(rdesc);
|
||||
|
||||
if (ret) {
|
||||
dbg_hid("parsing report descriptor failed\n");
|
||||
return ret;
|
||||
@ -832,12 +845,19 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
|
||||
int ret;
|
||||
|
||||
/* i2c hid fetch using a fixed descriptor size (30 bytes) */
|
||||
i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
|
||||
ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer,
|
||||
sizeof(struct i2c_hid_desc));
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "hid_descr_cmd failed\n");
|
||||
return -ENODEV;
|
||||
if (i2c_hid_get_dmi_i2c_hid_desc_override(client->name)) {
|
||||
i2c_hid_dbg(ihid, "Using a HID descriptor override\n");
|
||||
ihid->hdesc =
|
||||
*i2c_hid_get_dmi_i2c_hid_desc_override(client->name);
|
||||
} else {
|
||||
i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
|
||||
ret = i2c_hid_command(client, &hid_descr_cmd,
|
||||
ihid->hdesc_buffer,
|
||||
sizeof(struct i2c_hid_desc));
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "hid_descr_cmd failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate the length of HID descriptor, the 4 first bytes:
|
376
drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
Normal file
376
drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
Normal file
@ -0,0 +1,376 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/*
|
||||
* Quirks for I2C-HID devices that do not supply proper descriptors
|
||||
*
|
||||
* Copyright (c) 2018 Julian Sax <jsbc@gmx.de>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
#include "i2c-hid.h"
|
||||
|
||||
|
||||
struct i2c_hid_desc_override {
|
||||
union {
|
||||
struct i2c_hid_desc *i2c_hid_desc;
|
||||
uint8_t *i2c_hid_desc_buffer;
|
||||
};
|
||||
uint8_t *hid_report_desc;
|
||||
unsigned int hid_report_desc_size;
|
||||
uint8_t *i2c_name;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* descriptors for the SIPODEV SP1064 touchpad
|
||||
*
|
||||
* This device does not supply any descriptors and on windows a filter
|
||||
* driver operates between the i2c-hid layer and the device and injects
|
||||
* these descriptors when the device is prompted. The descriptors were
|
||||
* extracted by listening to the i2c-hid traffic that occurs between the
|
||||
* windows filter driver and the windows i2c-hid driver.
|
||||
*/
|
||||
|
||||
static const struct i2c_hid_desc_override sipodev_desc = {
|
||||
.i2c_hid_desc_buffer = (uint8_t [])
|
||||
{0x1e, 0x00, /* Length of descriptor */
|
||||
0x00, 0x01, /* Version of descriptor */
|
||||
0xdb, 0x01, /* Length of report descriptor */
|
||||
0x21, 0x00, /* Location of report descriptor */
|
||||
0x24, 0x00, /* Location of input report */
|
||||
0x1b, 0x00, /* Max input report length */
|
||||
0x25, 0x00, /* Location of output report */
|
||||
0x11, 0x00, /* Max output report length */
|
||||
0x22, 0x00, /* Location of command register */
|
||||
0x23, 0x00, /* Location of data register */
|
||||
0x11, 0x09, /* Vendor ID */
|
||||
0x88, 0x52, /* Product ID */
|
||||
0x06, 0x00, /* Version ID */
|
||||
0x00, 0x00, 0x00, 0x00 /* Reserved */
|
||||
},
|
||||
|
||||
.hid_report_desc = (uint8_t [])
|
||||
{0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x02, /* Usage (Mouse), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x01, /* Report ID (1), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xA1, 0x00, /* Collection (Physical), */
|
||||
0x05, 0x09, /* Usage Page (Button), */
|
||||
0x19, 0x01, /* Usage Minimum (01h), */
|
||||
0x29, 0x02, /* Usage Maximum (02h), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x06, /* Report Count (6), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x15, 0x81, /* Logical Minimum (-127), */
|
||||
0x25, 0x7F, /* Logical Maximum (127), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x81, 0x06, /* Input (Variable, Relative), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x05, /* Usage (Touchpad), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x04, /* Report ID (4), */
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x22, /* Usage (Finger), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x09, 0x47, /* Usage (Touch Valid), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x75, 0x03, /* Report Size (3), */
|
||||
0x25, 0x05, /* Logical Maximum (5), */
|
||||
0x09, 0x51, /* Usage (Contact Identifier), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x26, 0x44, 0x0A, /* Logical Maximum (2628), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x55, 0x0E, /* Unit Exponent (14), */
|
||||
0x65, 0x11, /* Unit (Centimeter), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x46, 0x1A, 0x04, /* Physical Maximum (1050), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x46, 0xBC, 0x02, /* Physical Maximum (700), */
|
||||
0x26, 0x34, 0x05, /* Logical Maximum (1332), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x22, /* Usage (Finger), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x09, 0x47, /* Usage (Touch Valid), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x75, 0x03, /* Report Size (3), */
|
||||
0x25, 0x05, /* Logical Maximum (5), */
|
||||
0x09, 0x51, /* Usage (Contact Identifier), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x26, 0x44, 0x0A, /* Logical Maximum (2628), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x46, 0x1A, 0x04, /* Physical Maximum (1050), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x46, 0xBC, 0x02, /* Physical Maximum (700), */
|
||||
0x26, 0x34, 0x05, /* Logical Maximum (1332), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x22, /* Usage (Finger), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x09, 0x47, /* Usage (Touch Valid), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x75, 0x03, /* Report Size (3), */
|
||||
0x25, 0x05, /* Logical Maximum (5), */
|
||||
0x09, 0x51, /* Usage (Contact Identifier), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x26, 0x44, 0x0A, /* Logical Maximum (2628), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x46, 0x1A, 0x04, /* Physical Maximum (1050), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x46, 0xBC, 0x02, /* Physical Maximum (700), */
|
||||
0x26, 0x34, 0x05, /* Logical Maximum (1332), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x22, /* Usage (Finger), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x09, 0x47, /* Usage (Touch Valid), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x75, 0x03, /* Report Size (3), */
|
||||
0x25, 0x05, /* Logical Maximum (5), */
|
||||
0x09, 0x51, /* Usage (Contact Identifier), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x26, 0x44, 0x0A, /* Logical Maximum (2628), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x46, 0x1A, 0x04, /* Physical Maximum (1050), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x46, 0xBC, 0x02, /* Physical Maximum (700), */
|
||||
0x26, 0x34, 0x05, /* Logical Maximum (1332), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x55, 0x0C, /* Unit Exponent (12), */
|
||||
0x66, 0x01, 0x10, /* Unit (Seconds), */
|
||||
0x47, 0xFF, 0xFF, 0x00, 0x00,/* Physical Maximum (65535), */
|
||||
0x27, 0xFF, 0xFF, 0x00, 0x00,/* Logical Maximum (65535), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x09, 0x56, /* Usage (Scan Time), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x54, /* Usage (Contact Count), */
|
||||
0x25, 0x7F, /* Logical Maximum (127), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x05, 0x09, /* Usage Page (Button), */
|
||||
0x09, 0x01, /* Usage (01h), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x07, /* Report Count (7), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x85, 0x02, /* Report ID (2), */
|
||||
0x09, 0x55, /* Usage (Contact Count Maximum), */
|
||||
0x09, 0x59, /* Usage (59h), */
|
||||
0x75, 0x04, /* Report Size (4), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x25, 0x0F, /* Logical Maximum (15), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x85, 0x07, /* Report ID (7), */
|
||||
0x09, 0x60, /* Usage (60h), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0x95, 0x07, /* Report Count (7), */
|
||||
0xB1, 0x03, /* Feature (Constant, Variable), */
|
||||
0x85, 0x06, /* Report ID (6), */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
||||
0x09, 0xC5, /* Usage (C5h), */
|
||||
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x96, 0x00, 0x01, /* Report Count (256), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
||||
0x09, 0x01, /* Usage (01h), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x0D, /* Report ID (13), */
|
||||
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
|
||||
0x19, 0x01, /* Usage Minimum (01h), */
|
||||
0x29, 0x02, /* Usage Maximum (02h), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x0E, /* Usage (Configuration), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x03, /* Report ID (3), */
|
||||
0x09, 0x22, /* Usage (Finger), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x09, 0x52, /* Usage (Device Mode), */
|
||||
0x25, 0x0A, /* Logical Maximum (10), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0x09, 0x22, /* Usage (Finger), */
|
||||
0xA1, 0x00, /* Collection (Physical), */
|
||||
0x85, 0x05, /* Report ID (5), */
|
||||
0x09, 0x57, /* Usage (57h), */
|
||||
0x09, 0x58, /* Usage (58h), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0xB1, 0x02, /* Feature (Variable), */
|
||||
0x95, 0x06, /* Report Count (6), */
|
||||
0xB1, 0x03, /* Feature (Constant, Variable),*/
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
},
|
||||
.hid_report_desc_size = 475,
|
||||
.i2c_name = "SYNA3602:00"
|
||||
};
|
||||
|
||||
|
||||
static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
|
||||
{
|
||||
.ident = "Teclast F6 Pro",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TECLAST"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "F6 Pro"),
|
||||
},
|
||||
.driver_data = (void *)&sipodev_desc
|
||||
},
|
||||
{
|
||||
.ident = "Teclast F7",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TECLAST"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "F7"),
|
||||
},
|
||||
.driver_data = (void *)&sipodev_desc
|
||||
},
|
||||
{
|
||||
.ident = "Trekstor Primebook C13",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Primebook C13"),
|
||||
},
|
||||
.driver_data = (void *)&sipodev_desc
|
||||
},
|
||||
{
|
||||
.ident = "Trekstor Primebook C11",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Primebook C11"),
|
||||
},
|
||||
.driver_data = (void *)&sipodev_desc
|
||||
},
|
||||
{
|
||||
.ident = "Direkt-Tek DTLAPY116-2",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Direkt-Tek"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "DTLAPY116-2"),
|
||||
},
|
||||
.driver_data = (void *)&sipodev_desc
|
||||
},
|
||||
{
|
||||
.ident = "Mediacom Flexbook Edge 11",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MEDIACOM"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "FlexBook edge11 - M-FBE11"),
|
||||
},
|
||||
.driver_data = (void *)&sipodev_desc
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name)
|
||||
{
|
||||
struct i2c_hid_desc_override *override;
|
||||
const struct dmi_system_id *system_id;
|
||||
|
||||
system_id = dmi_first_match(i2c_hid_dmi_desc_override_table);
|
||||
if (!system_id)
|
||||
return NULL;
|
||||
|
||||
override = system_id->driver_data;
|
||||
if (strcmp(override->i2c_name, i2c_name))
|
||||
return NULL;
|
||||
|
||||
return override->i2c_hid_desc;
|
||||
}
|
||||
|
||||
char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
|
||||
unsigned int *size)
|
||||
{
|
||||
struct i2c_hid_desc_override *override;
|
||||
const struct dmi_system_id *system_id;
|
||||
|
||||
system_id = dmi_first_match(i2c_hid_dmi_desc_override_table);
|
||||
if (!system_id)
|
||||
return NULL;
|
||||
|
||||
override = system_id->driver_data;
|
||||
if (strcmp(override->i2c_name, i2c_name))
|
||||
return NULL;
|
||||
|
||||
*size = override->hid_report_desc_size;
|
||||
return override->hid_report_desc;
|
||||
}
|
20
drivers/hid/i2c-hid/i2c-hid.h
Normal file
20
drivers/hid/i2c-hid/i2c-hid.h
Normal file
@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
|
||||
#ifndef I2C_HID_H
|
||||
#define I2C_HID_H
|
||||
|
||||
|
||||
#ifdef CONFIG_DMI
|
||||
struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name);
|
||||
char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
|
||||
unsigned int *size);
|
||||
#else
|
||||
static inline struct i2c_hid_desc
|
||||
*i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name)
|
||||
{ return NULL; }
|
||||
static inline char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
|
||||
unsigned int *size)
|
||||
{ return NULL; }
|
||||
#endif
|
||||
|
||||
#endif
|
@ -280,14 +280,14 @@ static int write_ipc_from_queue(struct ishtp_device *dev)
|
||||
* if tx send list is empty - return 0;
|
||||
* may happen, as RX_COMPLETE handler doesn't check list emptiness.
|
||||
*/
|
||||
if (list_empty(&dev->wr_processing_list_head.link)) {
|
||||
if (list_empty(&dev->wr_processing_list)) {
|
||||
spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
|
||||
out_ipc_locked = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ipc_link = list_entry(dev->wr_processing_list_head.link.next,
|
||||
struct wr_msg_ctl_info, link);
|
||||
ipc_link = list_first_entry(&dev->wr_processing_list,
|
||||
struct wr_msg_ctl_info, link);
|
||||
/* first 4 bytes of the data is the doorbell value (IPC header) */
|
||||
length = ipc_link->length - sizeof(uint32_t);
|
||||
doorbell_val = *(uint32_t *)ipc_link->inline_data;
|
||||
@ -338,7 +338,7 @@ static int write_ipc_from_queue(struct ishtp_device *dev)
|
||||
ipc_send_compl = ipc_link->ipc_send_compl;
|
||||
ipc_send_compl_prm = ipc_link->ipc_send_compl_prm;
|
||||
list_del_init(&ipc_link->link);
|
||||
list_add_tail(&ipc_link->link, &dev->wr_free_list_head.link);
|
||||
list_add(&ipc_link->link, &dev->wr_free_list);
|
||||
spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
|
||||
|
||||
/*
|
||||
@ -372,18 +372,18 @@ static int write_ipc_to_queue(struct ishtp_device *dev,
|
||||
unsigned char *msg, int length)
|
||||
{
|
||||
struct wr_msg_ctl_info *ipc_link;
|
||||
unsigned long flags;
|
||||
unsigned long flags;
|
||||
|
||||
if (length > IPC_FULL_MSG_SIZE)
|
||||
return -EMSGSIZE;
|
||||
|
||||
spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
|
||||
if (list_empty(&dev->wr_free_list_head.link)) {
|
||||
if (list_empty(&dev->wr_free_list)) {
|
||||
spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ipc_link = list_entry(dev->wr_free_list_head.link.next,
|
||||
struct wr_msg_ctl_info, link);
|
||||
ipc_link = list_first_entry(&dev->wr_free_list,
|
||||
struct wr_msg_ctl_info, link);
|
||||
list_del_init(&ipc_link->link);
|
||||
|
||||
ipc_link->ipc_send_compl = ipc_send_compl;
|
||||
@ -391,7 +391,7 @@ static int write_ipc_to_queue(struct ishtp_device *dev,
|
||||
ipc_link->length = length;
|
||||
memcpy(ipc_link->inline_data, msg, length);
|
||||
|
||||
list_add_tail(&ipc_link->link, &dev->wr_processing_list_head.link);
|
||||
list_add_tail(&ipc_link->link, &dev->wr_processing_list);
|
||||
spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
|
||||
|
||||
write_ipc_from_queue(dev);
|
||||
@ -487,17 +487,13 @@ static int ish_fw_reset_handler(struct ishtp_device *dev)
|
||||
{
|
||||
uint32_t reset_id;
|
||||
unsigned long flags;
|
||||
struct wr_msg_ctl_info *processing, *next;
|
||||
|
||||
/* Read reset ID */
|
||||
reset_id = ish_reg_read(dev, IPC_REG_ISH2HOST_MSG) & 0xFFFF;
|
||||
|
||||
/* Clear IPC output queue */
|
||||
spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
|
||||
list_for_each_entry_safe(processing, next,
|
||||
&dev->wr_processing_list_head.link, link) {
|
||||
list_move_tail(&processing->link, &dev->wr_free_list_head.link);
|
||||
}
|
||||
list_splice_init(&dev->wr_processing_list, &dev->wr_free_list);
|
||||
spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
|
||||
|
||||
/* ISHTP notification in IPC_RESET */
|
||||
@ -921,9 +917,9 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
|
||||
spin_lock_init(&dev->out_ipc_spinlock);
|
||||
|
||||
/* Init IPC processing and free lists */
|
||||
INIT_LIST_HEAD(&dev->wr_processing_list_head.link);
|
||||
INIT_LIST_HEAD(&dev->wr_free_list_head.link);
|
||||
for (i = 0; i < IPC_TX_FIFO_SIZE; ++i) {
|
||||
INIT_LIST_HEAD(&dev->wr_processing_list);
|
||||
INIT_LIST_HEAD(&dev->wr_free_list);
|
||||
for (i = 0; i < IPC_TX_FIFO_SIZE; i++) {
|
||||
struct wr_msg_ctl_info *tx_buf;
|
||||
|
||||
tx_buf = devm_kzalloc(&pdev->dev,
|
||||
@ -939,7 +935,7 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
|
||||
i);
|
||||
break;
|
||||
}
|
||||
list_add_tail(&tx_buf->link, &dev->wr_free_list_head.link);
|
||||
list_add_tail(&tx_buf->link, &dev->wr_free_list);
|
||||
}
|
||||
|
||||
dev->ops = &ish_hw_ops;
|
||||
|
@ -115,18 +115,19 @@ static const struct pci_device_id ish_invalid_pci_ids[] = {
|
||||
*/
|
||||
static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
struct ishtp_device *dev;
|
||||
int ret;
|
||||
struct ish_hw *hw;
|
||||
int ret;
|
||||
struct ishtp_device *ishtp;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
/* 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);
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "ISH: Failed to enable PCI device\n");
|
||||
dev_err(dev, "ISH: Failed to enable PCI device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -134,65 +135,44 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
pci_set_master(pdev);
|
||||
|
||||
/* pci request regions for ISH driver */
|
||||
ret = pci_request_regions(pdev, KBUILD_MODNAME);
|
||||
ret = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "ISH: Failed to get PCI regions\n");
|
||||
goto disable_device;
|
||||
dev_err(dev, "ISH: Failed to get PCI regions\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* allocates and initializes the ISH dev structure */
|
||||
dev = ish_dev_init(pdev);
|
||||
if (!dev) {
|
||||
ishtp = ish_dev_init(pdev);
|
||||
if (!ishtp) {
|
||||
ret = -ENOMEM;
|
||||
goto release_regions;
|
||||
return ret;
|
||||
}
|
||||
hw = to_ish_hw(dev);
|
||||
dev->print_log = ish_event_tracer;
|
||||
hw = to_ish_hw(ishtp);
|
||||
ishtp->print_log = ish_event_tracer;
|
||||
|
||||
/* mapping IO device memory */
|
||||
hw->mem_addr = pci_iomap(pdev, 0, 0);
|
||||
if (!hw->mem_addr) {
|
||||
dev_err(&pdev->dev, "ISH: mapping I/O range failure\n");
|
||||
ret = -ENOMEM;
|
||||
goto free_device;
|
||||
}
|
||||
|
||||
dev->pdev = pdev;
|
||||
|
||||
hw->mem_addr = pcim_iomap_table(pdev)[0];
|
||||
ishtp->pdev = pdev;
|
||||
pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
|
||||
|
||||
/* request and enable interrupt */
|
||||
ret = request_irq(pdev->irq, ish_irq_handler, IRQF_SHARED,
|
||||
KBUILD_MODNAME, dev);
|
||||
ret = devm_request_irq(dev, pdev->irq, ish_irq_handler,
|
||||
IRQF_SHARED, KBUILD_MODNAME, ishtp);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "ISH: request IRQ failure (%d)\n",
|
||||
pdev->irq);
|
||||
goto free_device;
|
||||
dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev->devc, dev);
|
||||
dev_set_drvdata(ishtp->devc, ishtp);
|
||||
|
||||
init_waitqueue_head(&dev->suspend_wait);
|
||||
init_waitqueue_head(&dev->resume_wait);
|
||||
init_waitqueue_head(&ishtp->suspend_wait);
|
||||
init_waitqueue_head(&ishtp->resume_wait);
|
||||
|
||||
ret = ish_init(dev);
|
||||
ret = ish_init(ishtp);
|
||||
if (ret)
|
||||
goto free_irq;
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
free_irq:
|
||||
free_irq(pdev->irq, dev);
|
||||
free_device:
|
||||
pci_iounmap(pdev, hw->mem_addr);
|
||||
release_regions:
|
||||
pci_release_regions(pdev);
|
||||
disable_device:
|
||||
pci_clear_master(pdev);
|
||||
pci_disable_device(pdev);
|
||||
dev_err(&pdev->dev, "ISH: PCI driver initialization failed.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,16 +184,9 @@ disable_device:
|
||||
static void ish_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev);
|
||||
struct ish_hw *hw = to_ish_hw(ishtp_dev);
|
||||
|
||||
ishtp_bus_remove_all_clients(ishtp_dev, false);
|
||||
ish_device_disable(ishtp_dev);
|
||||
|
||||
free_irq(pdev->irq, ishtp_dev);
|
||||
pci_iounmap(pdev, hw->mem_addr);
|
||||
pci_release_regions(pdev);
|
||||
pci_clear_master(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static struct device __maybe_unused *ish_resume_device;
|
||||
|
@ -320,23 +320,14 @@ do_get_report:
|
||||
*/
|
||||
static void ish_cl_event_cb(struct ishtp_cl_device *device)
|
||||
{
|
||||
struct ishtp_cl *hid_ishtp_cl = device->driver_data;
|
||||
struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(device);
|
||||
struct ishtp_cl_rb *rb_in_proc;
|
||||
size_t r_length;
|
||||
unsigned long flags;
|
||||
|
||||
if (!hid_ishtp_cl)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&hid_ishtp_cl->in_process_spinlock, flags);
|
||||
while (!list_empty(&hid_ishtp_cl->in_process_list.list)) {
|
||||
rb_in_proc = list_entry(
|
||||
hid_ishtp_cl->in_process_list.list.next,
|
||||
struct ishtp_cl_rb, list);
|
||||
list_del_init(&rb_in_proc->list);
|
||||
spin_unlock_irqrestore(&hid_ishtp_cl->in_process_spinlock,
|
||||
flags);
|
||||
|
||||
while ((rb_in_proc = ishtp_cl_rx_get_rb(hid_ishtp_cl)) != NULL) {
|
||||
if (!rb_in_proc->buffer.data)
|
||||
return;
|
||||
|
||||
@ -346,9 +337,7 @@ static void ish_cl_event_cb(struct ishtp_cl_device *device)
|
||||
process_recv(hid_ishtp_cl, rb_in_proc->buffer.data, r_length);
|
||||
|
||||
ishtp_cl_io_rb_recycle(rb_in_proc);
|
||||
spin_lock_irqsave(&hid_ishtp_cl->in_process_spinlock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&hid_ishtp_cl->in_process_spinlock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -637,8 +626,8 @@ static int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl,
|
||||
static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
|
||||
{
|
||||
struct ishtp_device *dev;
|
||||
unsigned long flags;
|
||||
struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
|
||||
struct ishtp_fw_client *fw_client;
|
||||
int i;
|
||||
int rv;
|
||||
|
||||
@ -660,16 +649,14 @@ static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
|
||||
hid_ishtp_cl->rx_ring_size = HID_CL_RX_RING_SIZE;
|
||||
hid_ishtp_cl->tx_ring_size = HID_CL_TX_RING_SIZE;
|
||||
|
||||
spin_lock_irqsave(&dev->fw_clients_lock, flags);
|
||||
i = ishtp_fw_cl_by_uuid(dev, &hid_ishtp_guid);
|
||||
if (i < 0) {
|
||||
spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
|
||||
fw_client = ishtp_fw_cl_get_client(dev, &hid_ishtp_guid);
|
||||
if (!fw_client) {
|
||||
dev_err(&client_data->cl_device->dev,
|
||||
"ish client uuid not found\n");
|
||||
return i;
|
||||
return -ENOENT;
|
||||
}
|
||||
hid_ishtp_cl->fw_client_id = dev->fw_clients[i].client_id;
|
||||
spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
|
||||
|
||||
hid_ishtp_cl->fw_client_id = fw_client->client_id;
|
||||
hid_ishtp_cl->state = ISHTP_CL_CONNECTING;
|
||||
|
||||
rv = ishtp_cl_connect(hid_ishtp_cl);
|
||||
@ -765,7 +752,7 @@ static void hid_ishtp_cl_reset_handler(struct work_struct *work)
|
||||
if (!hid_ishtp_cl)
|
||||
return;
|
||||
|
||||
cl_device->driver_data = hid_ishtp_cl;
|
||||
ishtp_set_drvdata(cl_device, hid_ishtp_cl);
|
||||
hid_ishtp_cl->client_data = client_data;
|
||||
client_data->hid_ishtp_cl = hid_ishtp_cl;
|
||||
|
||||
@ -814,7 +801,7 @@ static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
|
||||
if (!hid_ishtp_cl)
|
||||
return -ENOMEM;
|
||||
|
||||
cl_device->driver_data = hid_ishtp_cl;
|
||||
ishtp_set_drvdata(cl_device, hid_ishtp_cl);
|
||||
hid_ishtp_cl->client_data = client_data;
|
||||
client_data->hid_ishtp_cl = hid_ishtp_cl;
|
||||
client_data->cl_device = cl_device;
|
||||
@ -844,7 +831,7 @@ static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
|
||||
*/
|
||||
static int hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
|
||||
{
|
||||
struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data;
|
||||
struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
|
||||
struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
|
||||
|
||||
hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
|
||||
@ -874,7 +861,7 @@ static int hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
|
||||
*/
|
||||
static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
|
||||
{
|
||||
struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data;
|
||||
struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
|
||||
struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
|
||||
|
||||
hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
|
||||
@ -898,7 +885,7 @@ static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
|
||||
static int hid_ishtp_cl_suspend(struct device *device)
|
||||
{
|
||||
struct ishtp_cl_device *cl_device = to_ishtp_cl_device(device);
|
||||
struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data;
|
||||
struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
|
||||
struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
|
||||
|
||||
hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
|
||||
@ -919,7 +906,7 @@ static int hid_ishtp_cl_suspend(struct device *device)
|
||||
static int hid_ishtp_cl_resume(struct device *device)
|
||||
{
|
||||
struct ishtp_cl_device *cl_device = to_ishtp_cl_device(device);
|
||||
struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data;
|
||||
struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
|
||||
struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
|
||||
|
||||
hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
|
||||
|
@ -148,6 +148,31 @@ int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *uuid)
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_fw_cl_by_uuid);
|
||||
|
||||
/**
|
||||
* ishtp_fw_cl_get_client() - return client information to client
|
||||
* @dev: the ishtp device structure
|
||||
* @uuid: uuid of the client to search
|
||||
*
|
||||
* Search firmware client using UUID and reture related client information.
|
||||
*
|
||||
* Return: pointer of client information on success, NULL on failure.
|
||||
*/
|
||||
struct ishtp_fw_client *ishtp_fw_cl_get_client(struct ishtp_device *dev,
|
||||
const uuid_le *uuid)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->fw_clients_lock, flags);
|
||||
i = ishtp_fw_cl_by_uuid(dev, uuid);
|
||||
spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
|
||||
if (i < 0 || dev->fw_clients[i].props.fixed_address)
|
||||
return NULL;
|
||||
|
||||
return &dev->fw_clients[i];
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_fw_cl_get_client);
|
||||
|
||||
/**
|
||||
* ishtp_fw_cl_by_id() - return index to fw_clients for client_id
|
||||
* @dev: the ishtp device structure
|
||||
@ -563,6 +588,33 @@ void ishtp_put_device(struct ishtp_cl_device *cl_device)
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_put_device);
|
||||
|
||||
/**
|
||||
* ishtp_set_drvdata() - set client driver data
|
||||
* @cl_device: client device instance
|
||||
* @data: driver data need to be set
|
||||
*
|
||||
* Set client driver data to cl_device->driver_data.
|
||||
*/
|
||||
void ishtp_set_drvdata(struct ishtp_cl_device *cl_device, void *data)
|
||||
{
|
||||
cl_device->driver_data = data;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_set_drvdata);
|
||||
|
||||
/**
|
||||
* ishtp_get_drvdata() - get client driver data
|
||||
* @cl_device: client device instance
|
||||
*
|
||||
* Get client driver data from cl_device->driver_data.
|
||||
*
|
||||
* Return: pointer of driver data
|
||||
*/
|
||||
void *ishtp_get_drvdata(struct ishtp_cl_device *cl_device)
|
||||
{
|
||||
return cl_device->driver_data;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_get_drvdata);
|
||||
|
||||
/**
|
||||
* ishtp_bus_new_client() - Create a new client
|
||||
* @dev: ISHTP device instance
|
||||
|
@ -101,6 +101,9 @@ void ishtp_reset_compl_handler(struct ishtp_device *dev);
|
||||
void ishtp_put_device(struct ishtp_cl_device *);
|
||||
void ishtp_get_device(struct ishtp_cl_device *);
|
||||
|
||||
void ishtp_set_drvdata(struct ishtp_cl_device *cl_device, void *data);
|
||||
void *ishtp_get_drvdata(struct ishtp_cl_device *cl_device);
|
||||
|
||||
int __ishtp_cl_driver_register(struct ishtp_cl_driver *driver,
|
||||
struct module *owner);
|
||||
#define ishtp_cl_driver_register(driver) \
|
||||
@ -110,5 +113,7 @@ void ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver);
|
||||
int ishtp_register_event_cb(struct ishtp_cl_device *device,
|
||||
void (*read_cb)(struct ishtp_cl_device *));
|
||||
int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *cuuid);
|
||||
struct ishtp_fw_client *ishtp_fw_cl_get_client(struct ishtp_device *dev,
|
||||
const uuid_le *uuid);
|
||||
|
||||
#endif /* _LINUX_ISHTP_CL_BUS_H */
|
||||
|
@ -69,6 +69,8 @@ int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl)
|
||||
int j;
|
||||
unsigned long flags;
|
||||
|
||||
cl->tx_ring_free_size = 0;
|
||||
|
||||
/* Allocate pool to free Tx bufs */
|
||||
for (j = 0; j < cl->tx_ring_size; ++j) {
|
||||
struct ishtp_cl_tx_ring *tx_buf;
|
||||
@ -85,6 +87,7 @@ int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl)
|
||||
|
||||
spin_lock_irqsave(&cl->tx_free_list_spinlock, flags);
|
||||
list_add_tail(&tx_buf->list, &cl->tx_free_list.list);
|
||||
++cl->tx_ring_free_size;
|
||||
spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags);
|
||||
}
|
||||
return 0;
|
||||
@ -144,6 +147,7 @@ void ishtp_cl_free_tx_ring(struct ishtp_cl *cl)
|
||||
tx_buf = list_entry(cl->tx_free_list.list.next,
|
||||
struct ishtp_cl_tx_ring, list);
|
||||
list_del(&tx_buf->list);
|
||||
--cl->tx_ring_free_size;
|
||||
kfree(tx_buf->send_buf.data);
|
||||
kfree(tx_buf);
|
||||
}
|
||||
@ -255,3 +259,48 @@ int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb)
|
||||
return rets;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_cl_io_rb_recycle);
|
||||
|
||||
/**
|
||||
* ishtp_cl_tx_empty() -test whether client device tx buffer is empty
|
||||
* @cl: Pointer to client device instance
|
||||
*
|
||||
* Look client device tx buffer list, and check whether this list is empty
|
||||
*
|
||||
* Return: true if client tx buffer list is empty else false
|
||||
*/
|
||||
bool ishtp_cl_tx_empty(struct ishtp_cl *cl)
|
||||
{
|
||||
int tx_list_empty;
|
||||
unsigned long tx_flags;
|
||||
|
||||
spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags);
|
||||
tx_list_empty = list_empty(&cl->tx_list.list);
|
||||
spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags);
|
||||
|
||||
return !!tx_list_empty;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_cl_tx_empty);
|
||||
|
||||
/**
|
||||
* ishtp_cl_rx_get_rb() -Get a rb from client device rx buffer list
|
||||
* @cl: Pointer to client device instance
|
||||
*
|
||||
* Check client device in-processing buffer list and get a rb from it.
|
||||
*
|
||||
* Return: rb pointer if buffer list isn't empty else NULL
|
||||
*/
|
||||
struct ishtp_cl_rb *ishtp_cl_rx_get_rb(struct ishtp_cl *cl)
|
||||
{
|
||||
unsigned long rx_flags;
|
||||
struct ishtp_cl_rb *rb;
|
||||
|
||||
spin_lock_irqsave(&cl->in_process_spinlock, rx_flags);
|
||||
rb = list_first_entry_or_null(&cl->in_process_list.list,
|
||||
struct ishtp_cl_rb, list);
|
||||
if (rb)
|
||||
list_del_init(&rb->list);
|
||||
spin_unlock_irqrestore(&cl->in_process_spinlock, rx_flags);
|
||||
|
||||
return rb;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_cl_rx_get_rb);
|
||||
|
@ -22,6 +22,25 @@
|
||||
#include "hbm.h"
|
||||
#include "client.h"
|
||||
|
||||
int ishtp_cl_get_tx_free_buffer_size(struct ishtp_cl *cl)
|
||||
{
|
||||
unsigned long tx_free_flags;
|
||||
int size;
|
||||
|
||||
spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags);
|
||||
size = cl->tx_ring_free_size * cl->device->fw_client->props.max_msg_length;
|
||||
spin_unlock_irqrestore(&cl->tx_free_list_spinlock, tx_free_flags);
|
||||
|
||||
return size;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_cl_get_tx_free_buffer_size);
|
||||
|
||||
int ishtp_cl_get_tx_free_rings(struct ishtp_cl *cl)
|
||||
{
|
||||
return cl->tx_ring_free_size;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_cl_get_tx_free_rings);
|
||||
|
||||
/**
|
||||
* ishtp_read_list_flush() - Flush read queue
|
||||
* @cl: ishtp client instance
|
||||
@ -90,6 +109,7 @@ static void ishtp_cl_init(struct ishtp_cl *cl, struct ishtp_device *dev)
|
||||
|
||||
cl->rx_ring_size = CL_DEF_RX_RING_SIZE;
|
||||
cl->tx_ring_size = CL_DEF_TX_RING_SIZE;
|
||||
cl->tx_ring_free_size = cl->tx_ring_size;
|
||||
|
||||
/* dma */
|
||||
cl->last_tx_path = CL_TX_PATH_IPC;
|
||||
@ -577,6 +597,8 @@ int ishtp_cl_send(struct ishtp_cl *cl, uint8_t *buf, size_t length)
|
||||
* max ISHTP message size per client
|
||||
*/
|
||||
list_del_init(&cl_msg->list);
|
||||
--cl->tx_ring_free_size;
|
||||
|
||||
spin_unlock_irqrestore(&cl->tx_free_list_spinlock, tx_free_flags);
|
||||
memcpy(cl_msg->send_buf.data, buf, length);
|
||||
cl_msg->send_buf.size = length;
|
||||
@ -685,6 +707,7 @@ static void ipc_tx_callback(void *prm)
|
||||
ishtp_write_message(dev, &ishtp_hdr, pmsg);
|
||||
spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags);
|
||||
list_add_tail(&cl_msg->list, &cl->tx_free_list.list);
|
||||
++cl->tx_ring_free_size;
|
||||
spin_unlock_irqrestore(&cl->tx_free_list_spinlock,
|
||||
tx_free_flags);
|
||||
} else {
|
||||
@ -778,6 +801,7 @@ static void ishtp_cl_send_msg_dma(struct ishtp_device *dev,
|
||||
ishtp_write_message(dev, &hdr, (unsigned char *)&dma_xfer);
|
||||
spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags);
|
||||
list_add_tail(&cl_msg->list, &cl->tx_free_list.list);
|
||||
++cl->tx_ring_free_size;
|
||||
spin_unlock_irqrestore(&cl->tx_free_list_spinlock, tx_free_flags);
|
||||
++cl->send_msg_cnt_dma;
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ struct ishtp_cl {
|
||||
/* Client Tx buffers list */
|
||||
unsigned int tx_ring_size;
|
||||
struct ishtp_cl_tx_ring tx_list, tx_free_list;
|
||||
int tx_ring_free_size;
|
||||
spinlock_t tx_list_spinlock;
|
||||
spinlock_t tx_free_list_spinlock;
|
||||
size_t tx_offs; /* Offset in buffer at head of 'tx_list' */
|
||||
@ -137,6 +138,8 @@ int ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl);
|
||||
int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl);
|
||||
void ishtp_cl_free_rx_ring(struct ishtp_cl *cl);
|
||||
void ishtp_cl_free_tx_ring(struct ishtp_cl *cl);
|
||||
int ishtp_cl_get_tx_free_buffer_size(struct ishtp_cl *cl);
|
||||
int ishtp_cl_get_tx_free_rings(struct ishtp_cl *cl);
|
||||
|
||||
/* DMA I/F functions */
|
||||
void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg,
|
||||
@ -178,5 +181,7 @@ int ishtp_cl_flush_queues(struct ishtp_cl *cl);
|
||||
|
||||
/* exported functions from ISHTP client buffer management scope */
|
||||
int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb);
|
||||
bool ishtp_cl_tx_empty(struct ishtp_cl *cl);
|
||||
struct ishtp_cl_rb *ishtp_cl_rx_get_rb(struct ishtp_cl *cl);
|
||||
|
||||
#endif /* _ISHTP_CLIENT_H_ */
|
||||
|
@ -207,7 +207,7 @@ struct ishtp_device {
|
||||
struct work_struct bh_hbm_work;
|
||||
|
||||
/* IPC write queue */
|
||||
struct wr_msg_ctl_info wr_processing_list_head, wr_free_list_head;
|
||||
struct list_head wr_processing_list, wr_free_list;
|
||||
/* For both processing list and free list */
|
||||
spinlock_t wr_processing_spinlock;
|
||||
|
||||
|
@ -3335,6 +3335,7 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
|
||||
|
||||
void wacom_setup_device_quirks(struct wacom *wacom)
|
||||
{
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct wacom_features *features = &wacom->wacom_wac.features;
|
||||
|
||||
/* The pen and pad share the same interface on most devices */
|
||||
@ -3464,6 +3465,24 @@ void wacom_setup_device_quirks(struct wacom *wacom)
|
||||
|
||||
if (features->type == REMOTE)
|
||||
features->device_type |= WACOM_DEVICETYPE_WL_MONITOR;
|
||||
|
||||
/* HID descriptor for DTK-2451 / DTH-2452 claims to report lots
|
||||
* of things it shouldn't. Lets fix up the damage...
|
||||
*/
|
||||
if (wacom->hdev->product == 0x382 || wacom->hdev->product == 0x37d) {
|
||||
features->quirks &= ~WACOM_QUIRK_TOOLSERIAL;
|
||||
__clear_bit(BTN_TOOL_BRUSH, wacom_wac->pen_input->keybit);
|
||||
__clear_bit(BTN_TOOL_PENCIL, wacom_wac->pen_input->keybit);
|
||||
__clear_bit(BTN_TOOL_AIRBRUSH, wacom_wac->pen_input->keybit);
|
||||
__clear_bit(ABS_Z, wacom_wac->pen_input->absbit);
|
||||
__clear_bit(ABS_DISTANCE, wacom_wac->pen_input->absbit);
|
||||
__clear_bit(ABS_TILT_X, wacom_wac->pen_input->absbit);
|
||||
__clear_bit(ABS_TILT_Y, wacom_wac->pen_input->absbit);
|
||||
__clear_bit(ABS_WHEEL, wacom_wac->pen_input->absbit);
|
||||
__clear_bit(ABS_MISC, wacom_wac->pen_input->absbit);
|
||||
__clear_bit(MSC_SERIAL, wacom_wac->pen_input->mscbit);
|
||||
__clear_bit(EV_MSC, wacom_wac->pen_input->evbit);
|
||||
}
|
||||
}
|
||||
|
||||
int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
|
||||
|
@ -1139,6 +1139,34 @@ static inline u32 hid_report_len(struct hid_report *report)
|
||||
int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
|
||||
int interrupt);
|
||||
|
||||
|
||||
/**
|
||||
* struct hid_scroll_counter - Utility class for processing high-resolution
|
||||
* scroll events.
|
||||
* @dev: the input device for which events should be reported.
|
||||
* @microns_per_hi_res_unit: the amount moved by the user's finger for each
|
||||
* high-resolution unit reported by the mouse, in
|
||||
* microns.
|
||||
* @resolution_multiplier: the wheel's resolution in high-resolution mode as a
|
||||
* multiple of its lower resolution. For example, if
|
||||
* moving the wheel by one "notch" would result in a
|
||||
* value of 1 in low-resolution mode but 8 in
|
||||
* high-resolution, the multiplier is 8.
|
||||
* @remainder: counts the number of high-resolution units moved since the last
|
||||
* low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should
|
||||
* only be used by class methods.
|
||||
*/
|
||||
struct hid_scroll_counter {
|
||||
struct input_dev *dev;
|
||||
int microns_per_hi_res_unit;
|
||||
int resolution_multiplier;
|
||||
|
||||
int remainder;
|
||||
};
|
||||
|
||||
void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
|
||||
int hi_res_value);
|
||||
|
||||
/* HID quirks API */
|
||||
unsigned long hid_lookup_quirk(const struct hid_device *hdev);
|
||||
int hid_quirks_init(char **quirks_param, __u16 bus, int count);
|
||||
|
@ -2132,6 +2132,7 @@ struct ec_response_get_next_event_v1 {
|
||||
/* Switches */
|
||||
#define EC_MKBP_LID_OPEN 0
|
||||
#define EC_MKBP_TABLET_MODE 1
|
||||
#define EC_MKBP_BASE_ATTACHED 2
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Temperature sensor commands */
|
||||
|
@ -708,6 +708,15 @@
|
||||
#define REL_DIAL 0x07
|
||||
#define REL_WHEEL 0x08
|
||||
#define REL_MISC 0x09
|
||||
/*
|
||||
* 0x0a is reserved and should not be used in input drivers.
|
||||
* It was used by HID as REL_MISC+1 and userspace needs to detect if
|
||||
* the next REL_* event is correct or is just REL_MISC + n.
|
||||
* We define here REL_RESERVED so userspace can rely on it and detect
|
||||
* the situation described above.
|
||||
*/
|
||||
#define REL_RESERVED 0x0a
|
||||
#define REL_WHEEL_HI_RES 0x0b
|
||||
#define REL_MAX 0x0f
|
||||
#define REL_CNT (REL_MAX+1)
|
||||
|
||||
@ -744,6 +753,15 @@
|
||||
|
||||
#define ABS_MISC 0x28
|
||||
|
||||
/*
|
||||
* 0x2e is reserved and should not be used in input drivers.
|
||||
* It was used by HID as ABS_MISC+6 and userspace needs to detect if
|
||||
* the next ABS_* event is correct or is just ABS_MISC + n.
|
||||
* We define here ABS_RESERVED so userspace can rely on it and detect
|
||||
* the situation described above.
|
||||
*/
|
||||
#define ABS_RESERVED 0x2e
|
||||
|
||||
#define ABS_MT_SLOT 0x2f /* MT slot being modified */
|
||||
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
|
||||
#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
|
||||
|
Loading…
Reference in New Issue
Block a user