diff options
Diffstat (limited to 'drivers/usb/input/hid-core.c')
| -rw-r--r-- | drivers/usb/input/hid-core.c | 59 | 
1 files changed, 42 insertions, 17 deletions
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 45f44fe33bf..6d42036c906 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -270,7 +270,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign   * Read data value from item.   */ -static __inline__ __u32 item_udata(struct hid_item *item) +static u32 item_udata(struct hid_item *item)  {  	switch (item->size) {  		case 1: return item->data.u8; @@ -280,7 +280,7 @@ static __inline__ __u32 item_udata(struct hid_item *item)  	return 0;  } -static __inline__ __s32 item_sdata(struct hid_item *item) +static s32 item_sdata(struct hid_item *item)  {  	switch (item->size) {  		case 1: return item->data.s8; @@ -727,7 +727,7 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)   * done by hand.   */ -static __inline__ __s32 snto32(__u32 value, unsigned n) +static s32 snto32(__u32 value, unsigned n)  {  	switch (n) {  		case 8:  return ((__s8)value); @@ -741,9 +741,9 @@ static __inline__ __s32 snto32(__u32 value, unsigned n)   * Convert a signed 32-bit integer to a signed n-bit integer.   */ -static __inline__ __u32 s32ton(__s32 value, unsigned n) +static u32 s32ton(__s32 value, unsigned n)  { -	__s32 a = value >> (n - 1); +	s32 a = value >> (n - 1);  	if (a && a != -1)  		return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1;  	return value & ((1 << n) - 1); @@ -751,30 +751,55 @@ static __inline__ __u32 s32ton(__s32 value, unsigned n)  /*   * Extract/implement a data field from/to a little endian report (bit array). + * + * Code sort-of follows HID spec: + *     http://www.usb.org/developers/devclass_docs/HID1_11.pdf + * + * While the USB HID spec allows unlimited length bit fields in "report + * descriptors", most devices never use more than 16 bits. + * One model of UPS is claimed to report "LINEV" as a 32-bit field. + * Search linux-kernel and linux-usb-devel archives for "hid-core extract".   */  static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n)  { -	u32 x; +	u64 x; + +	WARN_ON(n > 32);  	report += offset >> 3;  /* adjust byte index */ -	offset &= 8 - 1; -	x = get_unaligned((u32 *) report); -	x = le32_to_cpu(x); -	x = (x >> offset) & ((1 << n) - 1); -	return x; +	offset &= 7;		/* now only need bit offset into one byte */ +	x = get_unaligned((u64 *) report); +	x = le64_to_cpu(x); +	x = (x >> offset) & ((1ULL << n) - 1);	/* extract bit field */ +	return (u32) x;  } +/* + * "implement" : set bits in a little endian bit stream. + * Same concepts as "extract" (see comments above). + * The data mangled in the bit stream remains in little endian + * order the whole time. It make more sense to talk about + * endianness of register values by considering a register + * a "cached" copy of the little endiad bit stream. + */  static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value)  { -	u32 x; +	u64 x; +	u64 m = (1ULL << n) - 1; + +	WARN_ON(n > 32); + +	WARN_ON(value > m); +	value &= m;  	report += offset >> 3; -	offset &= 8 - 1; -	x = get_unaligned((u32 *)report); -	x &= cpu_to_le32(~((((__u32) 1 << n) - 1) << offset)); -	x |= cpu_to_le32(value << offset); -	put_unaligned(x,(u32 *)report); +	offset &= 7; + +	x = get_unaligned((u64 *)report); +	x &= cpu_to_le64(~(m << offset)); +	x |= cpu_to_le64(((u64) value) << offset); +	put_unaligned(x, (u64 *) report);  }  /*  | 
