HID: fix unit exponent parsing
authorBenjamin Tissoires <benjamin.tissoires@gmail.com>
Wed, 14 Nov 2012 15:59:15 +0000 (16:59 +0100)
committerJiri Kosina <jkosina@suse.cz>
Thu, 15 Nov 2012 09:07:55 +0000 (10:07 +0100)
HID spec details special values for the HID field unit exponent.
Basically, the range [0x8..0xf] correspond to [-8..-1], so this is
a standard two's complement on a half-byte.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
Acked-by: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/hid-core.c
drivers/hid/hid-input.c
include/linux/hid.h

index f4109fd..f5004e2 100644 (file)
@@ -315,6 +315,7 @@ static s32 item_sdata(struct hid_item *item)
 
 static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
 {
+       __u32 raw_value;
        switch (item->tag) {
        case HID_GLOBAL_ITEM_TAG_PUSH:
 
@@ -365,7 +366,14 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
                return 0;
 
        case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
-               parser->global.unit_exponent = item_sdata(item);
+               /* Units exponent negative numbers are given through a
+                * two's complement.
+                * See "6.2.2.7 Global Items" for more information. */
+               raw_value = item_udata(item);
+               if (!(raw_value & 0xfffffff0))
+                       parser->global.unit_exponent = hid_snto32(raw_value, 4);
+               else
+                       parser->global.unit_exponent = raw_value;
                return 0;
 
        case HID_GLOBAL_ITEM_TAG_UNIT:
@@ -865,6 +873,12 @@ static s32 snto32(__u32 value, unsigned n)
        return value & (1 << (n - 1)) ? value | (-1 << n) : value;
 }
 
+s32 hid_snto32(__u32 value, unsigned n)
+{
+       return snto32(value, n);
+}
+EXPORT_SYMBOL_GPL(hid_snto32);
+
 /*
  * Convert a signed 32-bit integer to a signed n-bit integer.
  */
index 1b0adc3..007a943 100644 (file)
@@ -192,7 +192,6 @@ static int hidinput_setkeycode(struct input_dev *dev,
        return -EINVAL;
 }
 
-
 /**
  * hidinput_calc_abs_res - calculate an absolute axis resolution
  * @field: the HID report field to calculate resolution for
@@ -235,17 +234,23 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
        case ABS_MT_TOOL_Y:
        case ABS_MT_TOUCH_MAJOR:
        case ABS_MT_TOUCH_MINOR:
-               if (field->unit == 0x11) {              /* If centimeters */
+               if (field->unit & 0xffffff00)           /* Not a length */
+                       return 0;
+               unit_exponent += hid_snto32(field->unit >> 4, 4) - 1;
+               switch (field->unit & 0xf) {
+               case 0x1:                               /* If centimeters */
                        /* Convert to millimeters */
                        unit_exponent += 1;
-               } else if (field->unit == 0x13) {       /* If inches */
+                       break;
+               case 0x3:                               /* If inches */
                        /* Convert to millimeters */
                        prev = physical_extents;
                        physical_extents *= 254;
                        if (physical_extents < prev)
                                return 0;
                        unit_exponent -= 1;
-               } else {
+                       break;
+               default:
                        return 0;
                }
                break;
index c6bef8f..4161bf2 100644 (file)
@@ -717,6 +717,7 @@ int hid_connect(struct hid_device *hid, unsigned int connect_mask);
 void hid_disconnect(struct hid_device *hid);
 const struct hid_device_id *hid_match_id(struct hid_device *hdev,
                                         const struct hid_device_id *id);
+s32 hid_snto32(__u32 value, unsigned n);
 
 /**
  * hid_map_usage - map usage input bits