Commit 4ea6e4ffb4acdaaf9ba7687dbd84ae36e26cef9e
Committed by
Jiri Kosina
1 parent
b6dc79929f
Exists in
master
and in
7 other branches
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) |