Commit 96f2f66a authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
  ...
parents 3f2dcb64 46011e97
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -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
------
+15 −1
Original line number Diff line number Diff line
@@ -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.

+1 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
// 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");
+1 −1
Original line number Diff line number Diff line
@@ -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;
Loading