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:
Linus Torvalds 2018-10-25 06:23:07 -07:00
commit 96f2f66a98
32 changed files with 2230 additions and 250 deletions

View File

@ -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
------

View File

@ -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.

View File

@ -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
View 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");

View File

@ -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;

View File

@ -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) },

View File

@ -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;
}

View File

@ -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");

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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 },

View File

@ -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

View File

@ -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:

View 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;
}

View 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

View File

@ -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;

View File

@ -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;

View File

@ -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__,

View File

@ -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

View File

@ -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 */

View File

@ -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);

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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 */

View File

@ -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) */