@@ -1075,28 +1075,45 @@ static u32 s32ton(__s32 value, unsigned n)
1075
1075
* Extract/implement a data field from/to a little endian report (bit array).
1076
1076
*
1077
1077
* Code sort-of follows HID spec:
1078
- * http://www.usb.org/developers/devclass_docs /HID1_11.pdf
1078
+ * http://www.usb.org/developers/hidpage /HID1_11.pdf
1079
1079
*
1080
1080
* While the USB HID spec allows unlimited length bit fields in "report
1081
1081
* descriptors", most devices never use more than 16 bits.
1082
1082
* One model of UPS is claimed to report "LINEV" as a 32-bit field.
1083
1083
* Search linux-kernel and linux-usb-devel archives for "hid-core extract".
1084
1084
*/
1085
1085
1086
- __u32 hid_field_extract (const struct hid_device * hid , __u8 * report ,
1087
- unsigned offset , unsigned n )
1088
- {
1089
- u64 x ;
1086
+ static u32 __extract (u8 * report , unsigned offset , int n )
1087
+ {
1088
+ unsigned int idx = offset / 8 ;
1089
+ unsigned int bit_nr = 0 ;
1090
+ unsigned int bit_shift = offset % 8 ;
1091
+ int bits_to_copy = 8 - bit_shift ;
1092
+ u32 value = 0 ;
1093
+ u32 mask = n < 32 ? (1U << n ) - 1 : ~0U ;
1094
+
1095
+ while (n > 0 ) {
1096
+ value |= ((u32 )report [idx ] >> bit_shift ) << bit_nr ;
1097
+ n -= bits_to_copy ;
1098
+ bit_nr += bits_to_copy ;
1099
+ bits_to_copy = 8 ;
1100
+ bit_shift = 0 ;
1101
+ idx ++ ;
1102
+ }
1103
+
1104
+ return value & mask ;
1105
+ }
1090
1106
1091
- if (n > 32 )
1107
+ u32 hid_field_extract (const struct hid_device * hid , u8 * report ,
1108
+ unsigned offset , unsigned n )
1109
+ {
1110
+ if (n > 32 ) {
1092
1111
hid_warn (hid , "hid_field_extract() called with n (%d) > 32! (%s)\n" ,
1093
1112
n , current -> comm );
1113
+ n = 32 ;
1114
+ }
1094
1115
1095
- report += offset >> 3 ; /* adjust byte index */
1096
- offset &= 7 ; /* now only need bit offset into one byte */
1097
- x = get_unaligned_le64 (report );
1098
- x = (x >> offset ) & ((1ULL << n ) - 1 ); /* extract bit field */
1099
- return (u32 ) x ;
1116
+ return __extract (report , offset , n );
1100
1117
}
1101
1118
EXPORT_SYMBOL_GPL (hid_field_extract );
1102
1119
@@ -1106,31 +1123,56 @@ EXPORT_SYMBOL_GPL(hid_field_extract);
1106
1123
* The data mangled in the bit stream remains in little endian
1107
1124
* order the whole time. It make more sense to talk about
1108
1125
* endianness of register values by considering a register
1109
- * a "cached" copy of the little endiad bit stream.
1126
+ * a "cached" copy of the little endian bit stream.
1110
1127
*/
1111
- static void implement (const struct hid_device * hid , __u8 * report ,
1112
- unsigned offset , unsigned n , __u32 value )
1128
+
1129
+ static void __implement (u8 * report , unsigned offset , int n , u32 value )
1130
+ {
1131
+ unsigned int idx = offset / 8 ;
1132
+ unsigned int size = offset + n ;
1133
+ unsigned int bit_shift = offset % 8 ;
1134
+ int bits_to_set = 8 - bit_shift ;
1135
+ u8 bit_mask = 0xff << bit_shift ;
1136
+
1137
+ while (n - bits_to_set >= 0 ) {
1138
+ report [idx ] &= ~bit_mask ;
1139
+ report [idx ] |= value << bit_shift ;
1140
+ value >>= bits_to_set ;
1141
+ n -= bits_to_set ;
1142
+ bits_to_set = 8 ;
1143
+ bit_mask = 0xff ;
1144
+ bit_shift = 0 ;
1145
+ idx ++ ;
1146
+ }
1147
+
1148
+ /* last nibble */
1149
+ if (n ) {
1150
+ if (size % 8 )
1151
+ bit_mask &= (1U << (size % 8 )) - 1 ;
1152
+ report [idx ] &= ~bit_mask ;
1153
+ report [idx ] |= (value << bit_shift ) & bit_mask ;
1154
+ }
1155
+ }
1156
+
1157
+ static void implement (const struct hid_device * hid , u8 * report ,
1158
+ unsigned offset , unsigned n , u32 value )
1113
1159
{
1114
- u64 x ;
1115
- u64 m = (1ULL << n ) - 1 ;
1160
+ u64 m ;
1116
1161
1117
- if (n > 32 )
1162
+ if (n > 32 ) {
1118
1163
hid_warn (hid , "%s() called with n (%d) > 32! (%s)\n" ,
1119
1164
__func__ , n , current -> comm );
1165
+ n = 32 ;
1166
+ }
1120
1167
1168
+ m = (1ULL << n ) - 1 ;
1121
1169
if (value > m )
1122
1170
hid_warn (hid , "%s() called with too large value %d! (%s)\n" ,
1123
1171
__func__ , value , current -> comm );
1124
1172
WARN_ON (value > m );
1125
1173
value &= m ;
1126
1174
1127
- report += offset >> 3 ;
1128
- offset &= 7 ;
1129
-
1130
- x = get_unaligned_le64 (report );
1131
- x &= ~(m << offset );
1132
- x |= ((u64 )value ) << offset ;
1133
- put_unaligned_le64 (x , report );
1175
+ __implement (report , offset , n , value );
1134
1176
}
1135
1177
1136
1178
/*
0 commit comments