aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorGrant Grundler <grundler@parisc-linux.org>2006-10-19 18:09:51 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-11-03 14:57:18 -0500
commit68717950e11eab8ff754b2721d23e9cb3a47b56f (patch)
treea64053d0288df2151769b9a038ede7ca5bf66b64 /drivers/usb
parentbc724b98c5e782c2d6781428ed87768daa34921d (diff)
hid-core: big-endian fix fix
Adam Kropelin had posted 32-bit fix in June 2005 about two weeks after I originally had posted my fixes for big endian support. Adam has a UPS device which reports LINEV using 32-bits. Added comments to describe the limitations of the code. extract() is the same version I posted earlier and tested in user space. Made similar changes to implement() routine. I've written (and will shortly post) a test for implement(). Code tested on C3600 (parisc) with USB keyboard/mouse attached. I've dropped test_implement.c and a few other user space test programs on http://iou.parisc-linux.org/~grundler/tests/ -rw-r--r-- 1 grundler grundler 1750 Oct 18 09:13 test_extract.c -rw-r--r-- 1 grundler grundler 561 Jan 25 2006 test_ffs.c -rw-r--r-- 1 grundler users 7175 Apr 8 2005 test_fls.c -rw-r--r-- 1 grundler grundler 206 Sep 1 15:52 test_gettimeofday.c -rw-r--r-- 1 grundler grundler 1886 Oct 19 09:20 test_implement.c -rw-r--r-- 1 grundler users 2707 Jun 4 2005 test_unaligned.c I would appreciate if someone else would look at the output of test_implement.c to make it does The Right Thing. Signed-off-by: Grant Grundler <grundler@parisc-linux.org> Cc: Matthew Wilcox <matthew@wil.cx> Cc: Dmitry Torokhov <dtor@mail.ru> Acked-By: Adam Kropelin <akropel1@rochester.rr.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/input/hid-core.c59
1 files changed, 42 insertions, 17 deletions
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
index 45f44fe33bfe..6d42036c906c 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
270 * Read data value from item. 270 * Read data value from item.
271 */ 271 */
272 272
273static __inline__ __u32 item_udata(struct hid_item *item) 273static u32 item_udata(struct hid_item *item)
274{ 274{
275 switch (item->size) { 275 switch (item->size) {
276 case 1: return item->data.u8; 276 case 1: return item->data.u8;
@@ -280,7 +280,7 @@ static __inline__ __u32 item_udata(struct hid_item *item)
280 return 0; 280 return 0;
281} 281}
282 282
283static __inline__ __s32 item_sdata(struct hid_item *item) 283static s32 item_sdata(struct hid_item *item)
284{ 284{
285 switch (item->size) { 285 switch (item->size) {
286 case 1: return item->data.s8; 286 case 1: return item->data.s8;
@@ -727,7 +727,7 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
727 * done by hand. 727 * done by hand.
728 */ 728 */
729 729
730static __inline__ __s32 snto32(__u32 value, unsigned n) 730static s32 snto32(__u32 value, unsigned n)
731{ 731{
732 switch (n) { 732 switch (n) {
733 case 8: return ((__s8)value); 733 case 8: return ((__s8)value);
@@ -741,9 +741,9 @@ static __inline__ __s32 snto32(__u32 value, unsigned n)
741 * Convert a signed 32-bit integer to a signed n-bit integer. 741 * Convert a signed 32-bit integer to a signed n-bit integer.
742 */ 742 */
743 743
744static __inline__ __u32 s32ton(__s32 value, unsigned n) 744static u32 s32ton(__s32 value, unsigned n)
745{ 745{
746 __s32 a = value >> (n - 1); 746 s32 a = value >> (n - 1);
747 if (a && a != -1) 747 if (a && a != -1)
748 return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1; 748 return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1;
749 return value & ((1 << n) - 1); 749 return value & ((1 << n) - 1);
@@ -751,30 +751,55 @@ static __inline__ __u32 s32ton(__s32 value, unsigned n)
751 751
752/* 752/*
753 * Extract/implement a data field from/to a little endian report (bit array). 753 * Extract/implement a data field from/to a little endian report (bit array).
754 *
755 * Code sort-of follows HID spec:
756 * http://www.usb.org/developers/devclass_docs/HID1_11.pdf
757 *
758 * While the USB HID spec allows unlimited length bit fields in "report
759 * descriptors", most devices never use more than 16 bits.
760 * One model of UPS is claimed to report "LINEV" as a 32-bit field.
761 * Search linux-kernel and linux-usb-devel archives for "hid-core extract".
754 */ 762 */
755 763
756static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n) 764static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n)
757{ 765{
758 u32 x; 766 u64 x;
767
768 WARN_ON(n > 32);
759 769
760 report += offset >> 3; /* adjust byte index */ 770 report += offset >> 3; /* adjust byte index */
761 offset &= 8 - 1; 771 offset &= 7; /* now only need bit offset into one byte */
762 x = get_unaligned((u32 *) report); 772 x = get_unaligned((u64 *) report);
763 x = le32_to_cpu(x); 773 x = le64_to_cpu(x);
764 x = (x >> offset) & ((1 << n) - 1); 774 x = (x >> offset) & ((1ULL << n) - 1); /* extract bit field */
765 return x; 775 return (u32) x;
766} 776}
767 777
778/*
779 * "implement" : set bits in a little endian bit stream.
780 * Same concepts as "extract" (see comments above).
781 * The data mangled in the bit stream remains in little endian
782 * order the whole time. It make more sense to talk about
783 * endianness of register values by considering a register
784 * a "cached" copy of the little endiad bit stream.
785 */
768static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value) 786static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value)
769{ 787{
770 u32 x; 788 u64 x;
789 u64 m = (1ULL << n) - 1;
790
791 WARN_ON(n > 32);
792
793 WARN_ON(value > m);
794 value &= m;
771 795
772 report += offset >> 3; 796 report += offset >> 3;
773 offset &= 8 - 1; 797 offset &= 7;
774 x = get_unaligned((u32 *)report); 798
775 x &= cpu_to_le32(~((((__u32) 1 << n) - 1) << offset)); 799 x = get_unaligned((u64 *)report);
776 x |= cpu_to_le32(value << offset); 800 x &= cpu_to_le64(~(m << offset));
777 put_unaligned(x,(u32 *)report); 801 x |= cpu_to_le64(((u64) value) << offset);
802 put_unaligned(x, (u64 *) report);
778} 803}
779 804
780/* 805/*