diff options
| author | Benjamin Tissoires <benjamin.tissoires@gmail.com> | 2012-11-14 16:59:15 +0100 | 
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2012-11-15 10:07:55 +0100 | 
| commit | 774638386826621c984ab6994439f474709cac5e (patch) | |
| tree | 897fb8a15947768f7dc9521027c7c20fe830c1bf | |
| parent | ccdd699411b77c463022ebac311eb4a06ca56db4 (diff) | |
| download | olio-linux-3.10-774638386826621c984ab6994439f474709cac5e.tar.xz olio-linux-3.10-774638386826621c984ab6994439f474709cac5e.zip  | |
HID: fix unit exponent parsing
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>
| -rw-r--r-- | drivers/hid/hid-core.c | 16 | ||||
| -rw-r--r-- | drivers/hid/hid-input.c | 13 | ||||
| -rw-r--r-- | include/linux/hid.h | 1 | 
3 files changed, 25 insertions, 5 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f4109fd657f..f5004e2ca0c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -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.   */ diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 1b0adc3f780..007a9433bfa 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -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; diff --git a/include/linux/hid.h b/include/linux/hid.h index c6bef8f54a8..4161bf2c0b5 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -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  |