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

Commit 4ea6e4ff authored by Nikolai Kondrashov's avatar Nikolai Kondrashov Committed by Jiri Kosina
Browse files

HID: add absolute axis resolution calculation



Add absolute axis resolution calculation to the core HID layer, according to HID
specification v1.11 6.2.2.7 Global Items. Only exponent 1 length units for
X/Y/Z/RX/RY/RZ axis are supported for now.

Signed-off-by: default avatarNikolai Kondrashov <spbnick@gmail.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent b6dc7992
Loading
Loading
Loading
Loading
+80 −0
Original line number Original line Diff line number Diff line
@@ -149,6 +149,83 @@ static int hidinput_setkeycode(struct input_dev *dev,
}
}




/**
 * hidinput_calc_abs_res - calculate an absolute axis resolution
 * @field: the HID report field to calculate resolution for
 * @code: axis code
 *
 * The formula is:
 *                         (logical_maximum - logical_minimum)
 * resolution = ----------------------------------------------------------
 *              (physical_maximum - physical_minimum) * 10 ^ unit_exponent
 *
 * as seen in the HID specification v1.11 6.2.2.7 Global Items.
 *
 * Only exponent 1 length units are processed. Centimeters are converted to
 * inches. Degrees are converted to radians.
 */
static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
{
	__s32 unit_exponent = field->unit_exponent;
	__s32 logical_extents = field->logical_maximum -
					field->logical_minimum;
	__s32 physical_extents = field->physical_maximum -
					field->physical_minimum;
	__s32 prev;

	/* Check if the extents are sane */
	if (logical_extents <= 0 || physical_extents <= 0)
		return 0;

	/*
	 * Verify and convert units.
	 * See HID specification v1.11 6.2.2.7 Global Items for unit decoding
	 */
	if (code == ABS_X || code == ABS_Y || code == ABS_Z) {
		if (field->unit == 0x11) {		/* If centimeters */
			/* Convert to inches */
			prev = logical_extents;
			logical_extents *= 254;
			if (logical_extents < prev)
				return 0;
			unit_exponent += 2;
		} else if (field->unit != 0x13) {	/* If not inches */
			return 0;
		}
	} else if (code == ABS_RX || code == ABS_RY || code == ABS_RZ) {
		if (field->unit == 0x14) {		/* If degrees */
			/* Convert to radians */
			prev = logical_extents;
			logical_extents *= 573;
			if (logical_extents < prev)
				return 0;
			unit_exponent += 1;
		} else if (field->unit != 0x12) {	/* If not radians */
			return 0;
		}
	} else {
		return 0;
	}

	/* Apply negative unit exponent */
	for (; unit_exponent < 0; unit_exponent++) {
		prev = logical_extents;
		logical_extents *= 10;
		if (logical_extents < prev)
			return 0;
	}
	/* Apply positive unit exponent */
	for (; unit_exponent > 0; unit_exponent--) {
		prev = physical_extents;
		physical_extents *= 10;
		if (physical_extents < prev)
			return 0;
	}

	/* Calculate resolution */
	return logical_extents / physical_extents;
}

static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
				     struct hid_usage *usage)
				     struct hid_usage *usage)
{
{
@@ -537,6 +614,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
			input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4);
			input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4);
		else	input_set_abs_params(input, usage->code, a, b, 0, 0);
		else	input_set_abs_params(input, usage->code, a, b, 0, 0);


		input_abs_set_res(input, usage->code,
				  hidinput_calc_abs_res(field, usage->code));

		/* use a larger default input buffer for MT devices */
		/* use a larger default input buffer for MT devices */
		if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0)
		if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0)
			input_set_events_per_packet(input, 60);
			input_set_events_per_packet(input, 60);