Skip to content

Commit

Permalink
HID: add absolute axis resolution calculation
Browse files Browse the repository at this point in the history
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: Nikolai Kondrashov <spbnick@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
  • Loading branch information
Nikolai Kondrashov authored and Jiri Kosina committed Oct 15, 2010
1 parent b6dc799 commit 4ea6e4f
Showing 1 changed file with 80 additions and 0 deletions.
80 changes: 80 additions & 0 deletions drivers/hid/hid-input.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
struct hid_usage *usage)
{
Expand Down Expand Up @@ -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);
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 */
if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0)
input_set_events_per_packet(input, 60);
Expand Down

0 comments on commit 4ea6e4f

Please sign in to comment.