Commit 4ea6e4ffb4acdaaf9ba7687dbd84ae36e26cef9e

Authored by Nikolai Kondrashov
Committed by Jiri Kosina
1 parent b6dc79929f

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: Nikolai Kondrashov <spbnick@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

Showing 1 changed file with 80 additions and 0 deletions Side-by-side Diff

drivers/hid/hid-input.c
... ... @@ -149,6 +149,83 @@
149 149 }
150 150  
151 151  
  152 +/**
  153 + * hidinput_calc_abs_res - calculate an absolute axis resolution
  154 + * @field: the HID report field to calculate resolution for
  155 + * @code: axis code
  156 + *
  157 + * The formula is:
  158 + * (logical_maximum - logical_minimum)
  159 + * resolution = ----------------------------------------------------------
  160 + * (physical_maximum - physical_minimum) * 10 ^ unit_exponent
  161 + *
  162 + * as seen in the HID specification v1.11 6.2.2.7 Global Items.
  163 + *
  164 + * Only exponent 1 length units are processed. Centimeters are converted to
  165 + * inches. Degrees are converted to radians.
  166 + */
  167 +static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
  168 +{
  169 + __s32 unit_exponent = field->unit_exponent;
  170 + __s32 logical_extents = field->logical_maximum -
  171 + field->logical_minimum;
  172 + __s32 physical_extents = field->physical_maximum -
  173 + field->physical_minimum;
  174 + __s32 prev;
  175 +
  176 + /* Check if the extents are sane */
  177 + if (logical_extents <= 0 || physical_extents <= 0)
  178 + return 0;
  179 +
  180 + /*
  181 + * Verify and convert units.
  182 + * See HID specification v1.11 6.2.2.7 Global Items for unit decoding
  183 + */
  184 + if (code == ABS_X || code == ABS_Y || code == ABS_Z) {
  185 + if (field->unit == 0x11) { /* If centimeters */
  186 + /* Convert to inches */
  187 + prev = logical_extents;
  188 + logical_extents *= 254;
  189 + if (logical_extents < prev)
  190 + return 0;
  191 + unit_exponent += 2;
  192 + } else if (field->unit != 0x13) { /* If not inches */
  193 + return 0;
  194 + }
  195 + } else if (code == ABS_RX || code == ABS_RY || code == ABS_RZ) {
  196 + if (field->unit == 0x14) { /* If degrees */
  197 + /* Convert to radians */
  198 + prev = logical_extents;
  199 + logical_extents *= 573;
  200 + if (logical_extents < prev)
  201 + return 0;
  202 + unit_exponent += 1;
  203 + } else if (field->unit != 0x12) { /* If not radians */
  204 + return 0;
  205 + }
  206 + } else {
  207 + return 0;
  208 + }
  209 +
  210 + /* Apply negative unit exponent */
  211 + for (; unit_exponent < 0; unit_exponent++) {
  212 + prev = logical_extents;
  213 + logical_extents *= 10;
  214 + if (logical_extents < prev)
  215 + return 0;
  216 + }
  217 + /* Apply positive unit exponent */
  218 + for (; unit_exponent > 0; unit_exponent--) {
  219 + prev = physical_extents;
  220 + physical_extents *= 10;
  221 + if (physical_extents < prev)
  222 + return 0;
  223 + }
  224 +
  225 + /* Calculate resolution */
  226 + return logical_extents / physical_extents;
  227 +}
  228 +
152 229 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
153 230 struct hid_usage *usage)
154 231 {
... ... @@ -536,6 +613,9 @@
536 613 if (field->application == HID_GD_GAMEPAD || field->application == HID_GD_JOYSTICK)
537 614 input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4);
538 615 else input_set_abs_params(input, usage->code, a, b, 0, 0);
  616 +
  617 + input_abs_set_res(input, usage->code,
  618 + hidinput_calc_abs_res(field, usage->code));
539 619  
540 620 /* use a larger default input buffer for MT devices */
541 621 if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0)