Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf26057a authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull HID updates from Jiri Kosina:

 - high-resolution scrolling support that gracefully handles differences
   between MS and Logitech implementations in HW, from Peter Hutterer
   and Harry Cutts

 - MSI IRQ support for intel-ish driver, from Song Hongyan

 - support for new hardware (Cougar 700K, Odys Winbook 13, ASUS FX503VD,
   ASUS T101HA) from Daniel M. Lambea, Hans de Goede and Aleix Roca
   Nonell

 - other small assorted fixups

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (22 commits)
  HID: i2c-hid: Add Odys Winbook 13 to descriptor override
  HID: lenovo: Add checks to fix of_led_classdev_register
  HID: intel-ish-hid: add MSI interrupt support
  HID: debug: Change to use DEFINE_SHOW_ATTRIBUTE macro
  HID: doc: fix wrong data structure reference for UHID_OUTPUT
  HID: intel-ish-hid: fixes incorrect error handling
  HID: asus: Add support for the ASUS T101HA keyboard dock
  HID: logitech: Use LDJ_DEVICE macro for existing Logitech mice
  HID: logitech: Enable high-resolution scrolling on Logitech mice
  HID: logitech: Add function to enable HID++ 1.0 "scrolling acceleration"
  HID: logitech-hidpp: fix typo, hiddpp to hidpp
  HID: input: use the Resolution Multiplier for high-resolution scrolling
  HID: core: process the Resolution Multiplier
  HID: core: store the collections as a basic tree
  Input: add `REL_WHEEL_HI_RES` and `REL_HWHEEL_HI_RES`
  HID: input: support Microsoft wireless radio control hotkey
  HID: use macros in IS_INPUT_APPLICATION
  HID: asus: Add support for the ASUS FX503VD laptop
  HID: asus: Add event handler to catch unmapped Asus Vendor UsagePage codes
  HID: cougar: Add support for Cougar 700K Gaming Keyboard
  ...
parents 1686cc1a bd8879fa
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -160,7 +160,7 @@ them but you should handle them according to your needs.
  UHID_OUTPUT:
  This is sent if the HID device driver wants to send raw data to the I/O
  device on the interrupt channel. You should read the payload and forward it to
  the device. The payload is of type "struct uhid_data_req".
  the device. The payload is of type "struct uhid_output_req".
  This may be received even though you haven't received UHID_OPEN, yet.

  UHID_GET_REPORT:
+20 −1
Original line number Diff line number Diff line
@@ -190,7 +190,26 @@ 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 detents moved on the wheel, the
    physical size of which varies by device. For high-resolution wheels
    this may be an approximation based on the high-resolution scroll events,
    see REL_WHEEL_HI_RES. These event codes are legacy codes and
    REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES should be preferred where
    available.

* REL_WHEEL_HI_RES, REL_HWHEEL_HI_RES:

  - High-resolution scroll wheel data. The accumulated value 120 represents
    movement by one detent. For devices that do not provide high-resolution
    scrolling, the value is always a multiple of 120. For devices with
    high-resolution scrolling, the value may be a fraction of 120.

    If a vertical scroll wheel supports high-resolution scrolling, this code
    will be emitted in addition to REL_WHEEL or REL_HWHEEL. The REL_WHEEL
    and REL_HWHEEL may be an approximation based on the high-resolution
    scroll events. There is no guarantee that the high-resolution data
    is a multiple of 120 at the time of an emulated REL_WHEEL or REL_HWHEEL
    event.

EV_ABS
------
+28 −0
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define QUIRK_T100_KEYBOARD		BIT(6)
#define QUIRK_T100CHI			BIT(7)
#define QUIRK_G752_KEYBOARD		BIT(8)
#define QUIRK_T101HA_DOCK		BIT(9)

#define I2C_KEYBOARD_QUIRKS			(QUIRK_FIX_NOTEBOOK_REPORT | \
						 QUIRK_NO_INIT_REPORTS | \
@@ -241,6 +242,18 @@ static int asus_report_input(struct asus_drvdata *drvdat, u8 *data, int size)
	return 1;
}

static int asus_event(struct hid_device *hdev, struct hid_field *field,
		      struct hid_usage *usage, __s32 value)
{
	if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 &&
	    (usage->hid & HID_USAGE) != 0x00 && !usage->type) {
		hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n",
			 usage->hid & HID_USAGE);
	}

	return 0;
}

static int asus_raw_event(struct hid_device *hdev,
		struct hid_report *report, u8 *data, int size)
{
@@ -510,6 +523,7 @@ static int asus_input_mapping(struct hid_device *hdev,
		case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP);		break;
		case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF);		break;
		case 0x6c: asus_map_key_clear(KEY_SLEEP);		break;
		case 0x7c: asus_map_key_clear(KEY_MICMUTE);		break;
		case 0x82: asus_map_key_clear(KEY_CAMERA);		break;
		case 0x88: asus_map_key_clear(KEY_RFKILL);			break;
		case 0xb5: asus_map_key_clear(KEY_CALC);			break;
@@ -528,6 +542,9 @@ static int asus_input_mapping(struct hid_device *hdev,
		/* Fn+Space Power4Gear Hybrid */
		case 0x5c: asus_map_key_clear(KEY_PROG3);		break;

		/* Fn+F5 "fan" symbol on FX503VD */
		case 0x99: asus_map_key_clear(KEY_PROG4);		break;

		default:
			/* ASUS lazily declares 256 usages, ignore the rest,
			 * as some make the keyboard appear as a pointer device. */
@@ -683,6 +700,11 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
		return ret;
	}

	/* use hid-multitouch for T101HA touchpad */
	if (id->driver_data & QUIRK_T101HA_DOCK &&
	    hdev->collection->usage == HID_GD_MOUSE)
		return -ENODEV;

	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
	if (ret) {
		hid_err(hdev, "Asus hw start failed: %d\n", ret);
@@ -805,12 +827,17 @@ static const struct hid_device_id asus_devices[] = {
		USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2), QUIRK_USE_KBD_BACKLIGHT },
	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
		USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3), QUIRK_G752_KEYBOARD },
	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
		USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD),
	  QUIRK_USE_KBD_BACKLIGHT },
	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
		USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD),
	  QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
		USB_DEVICE_ID_ASUSTEK_T100TAF_KEYBOARD),
	  QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
		USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD), QUIRK_T101HA_DOCK },
	{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_ASUS_AK1D) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_ASUS_MD_5110) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_ASUS_MD_5112) },
@@ -832,6 +859,7 @@ static struct hid_driver asus_driver = {
#ifdef CONFIG_PM
	.reset_resume           = asus_reset_resume,
#endif
	.event			= asus_event,
	.raw_event		= asus_raw_event
};
module_hid_driver(asus_driver);
+174 −0
Original line number Diff line number Diff line
@@ -172,6 +172,8 @@ static int open_collection(struct hid_parser *parser, unsigned type)
	collection->type = type;
	collection->usage = usage;
	collection->level = parser->collection_stack_ptr - 1;
	collection->parent = parser->active_collection;
	parser->active_collection = collection;

	if (type == HID_COLLECTION_APPLICATION)
		parser->device->maxapplication++;
@@ -190,6 +192,8 @@ static int close_collection(struct hid_parser *parser)
		return -EINVAL;
	}
	parser->collection_stack_ptr--;
	if (parser->active_collection)
		parser->active_collection = parser->active_collection->parent;
	return 0;
}

@@ -290,6 +294,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
		field->usage[i].collection_index =
			parser->local.collection_index[j];
		field->usage[i].usage_index = i;
		field->usage[i].resolution_multiplier = 1;
	}

	field->maxusage = usages;
@@ -943,6 +948,167 @@ struct hid_report *hid_validate_values(struct hid_device *hid,
}
EXPORT_SYMBOL_GPL(hid_validate_values);

static int hid_calculate_multiplier(struct hid_device *hid,
				     struct hid_field *multiplier)
{
	int m;
	__s32 v = *multiplier->value;
	__s32 lmin = multiplier->logical_minimum;
	__s32 lmax = multiplier->logical_maximum;
	__s32 pmin = multiplier->physical_minimum;
	__s32 pmax = multiplier->physical_maximum;

	/*
	 * "Because OS implementations will generally divide the control's
	 * reported count by the Effective Resolution Multiplier, designers
	 * should take care not to establish a potential Effective
	 * Resolution Multiplier of zero."
	 * HID Usage Table, v1.12, Section 4.3.1, p31
	 */
	if (lmax - lmin == 0)
		return 1;
	/*
	 * Handling the unit exponent is left as an exercise to whoever
	 * finds a device where that exponent is not 0.
	 */
	m = ((v - lmin)/(lmax - lmin) * (pmax - pmin) + pmin);
	if (unlikely(multiplier->unit_exponent != 0)) {
		hid_warn(hid,
			 "unsupported Resolution Multiplier unit exponent %d\n",
			 multiplier->unit_exponent);
	}

	/* There are no devices with an effective multiplier > 255 */
	if (unlikely(m == 0 || m > 255 || m < -255)) {
		hid_warn(hid, "unsupported Resolution Multiplier %d\n", m);
		m = 1;
	}

	return m;
}

static void hid_apply_multiplier_to_field(struct hid_device *hid,
					  struct hid_field *field,
					  struct hid_collection *multiplier_collection,
					  int effective_multiplier)
{
	struct hid_collection *collection;
	struct hid_usage *usage;
	int i;

	/*
	 * If multiplier_collection is NULL, the multiplier applies
	 * to all fields in the report.
	 * Otherwise, it is the Logical Collection the multiplier applies to
	 * but our field may be in a subcollection of that collection.
	 */
	for (i = 0; i < field->maxusage; i++) {
		usage = &field->usage[i];

		collection = &hid->collection[usage->collection_index];
		while (collection && collection != multiplier_collection)
			collection = collection->parent;

		if (collection || multiplier_collection == NULL)
			usage->resolution_multiplier = effective_multiplier;

	}
}

static void hid_apply_multiplier(struct hid_device *hid,
				 struct hid_field *multiplier)
{
	struct hid_report_enum *rep_enum;
	struct hid_report *rep;
	struct hid_field *field;
	struct hid_collection *multiplier_collection;
	int effective_multiplier;
	int i;

	/*
	 * "The Resolution Multiplier control must be contained in the same
	 * Logical Collection as the control(s) to which it is to be applied.
	 * If no Resolution Multiplier is defined, then the Resolution
	 * Multiplier defaults to 1.  If more than one control exists in a
	 * Logical Collection, the Resolution Multiplier is associated with
	 * all controls in the collection. If no Logical Collection is
	 * defined, the Resolution Multiplier is associated with all
	 * controls in the report."
	 * HID Usage Table, v1.12, Section 4.3.1, p30
	 *
	 * Thus, search from the current collection upwards until we find a
	 * logical collection. Then search all fields for that same parent
	 * collection. Those are the fields the multiplier applies to.
	 *
	 * If we have more than one multiplier, it will overwrite the
	 * applicable fields later.
	 */
	multiplier_collection = &hid->collection[multiplier->usage->collection_index];
	while (multiplier_collection &&
	       multiplier_collection->type != HID_COLLECTION_LOGICAL)
		multiplier_collection = multiplier_collection->parent;

	effective_multiplier = hid_calculate_multiplier(hid, multiplier);

	rep_enum = &hid->report_enum[HID_INPUT_REPORT];
	list_for_each_entry(rep, &rep_enum->report_list, list) {
		for (i = 0; i < rep->maxfield; i++) {
			field = rep->field[i];
			hid_apply_multiplier_to_field(hid, field,
						      multiplier_collection,
						      effective_multiplier);
		}
	}
}

/*
 * hid_setup_resolution_multiplier - set up all resolution multipliers
 *
 * @device: hid device
 *
 * Search for all Resolution Multiplier Feature Reports and apply their
 * value to all matching Input items. This only updates the internal struct
 * fields.
 *
 * The Resolution Multiplier is applied by the hardware. If the multiplier
 * is anything other than 1, the hardware will send pre-multiplied events
 * so that the same physical interaction generates an accumulated
 *	accumulated_value = value * * multiplier
 * This may be achieved by sending
 * - "value * multiplier" for each event, or
 * - "value" but "multiplier" times as frequently, or
 * - a combination of the above
 * The only guarantee is that the same physical interaction always generates
 * an accumulated 'value * multiplier'.
 *
 * This function must be called before any event processing and after
 * any SetRequest to the Resolution Multiplier.
 */
void hid_setup_resolution_multiplier(struct hid_device *hid)
{
	struct hid_report_enum *rep_enum;
	struct hid_report *rep;
	struct hid_usage *usage;
	int i, j;

	rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
	list_for_each_entry(rep, &rep_enum->report_list, list) {
		for (i = 0; i < rep->maxfield; i++) {
			/* Ignore if report count is out of bounds. */
			if (rep->field[i]->report_count < 1)
				continue;

			for (j = 0; j < rep->field[i]->maxusage; j++) {
				usage = &rep->field[i]->usage[j];
				if (usage->hid == HID_GD_RESOLUTION_MULTIPLIER)
					hid_apply_multiplier(hid,
							     rep->field[i]);
			}
		}
	}
}
EXPORT_SYMBOL_GPL(hid_setup_resolution_multiplier);

/**
 * hid_open_report - open a driver-specific device report
 *
@@ -1039,9 +1205,17 @@ int hid_open_report(struct hid_device *device)
				hid_err(device, "unbalanced delimiter at end of report description\n");
				goto err;
			}

			/*
			 * fetch initial values in case the device's
			 * default multiplier isn't the recommended 1
			 */
			hid_setup_resolution_multiplier(device);

			kfree(parser->collection_stack);
			vfree(parser);
			device->status |= HID_STAT_PARSED;

			return 0;
		}
	}
+2 −0
Original line number Diff line number Diff line
@@ -326,6 +326,8 @@ 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) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR,
			 USB_DEVICE_ID_COUGAR_700K_GAMING_KEYBOARD) },
	{}
};
MODULE_DEVICE_TABLE(hid, cougar_id_table);
Loading