[RFC][PATCH] new byteorder primitives - ..._{replace,get}_bits()

Al Viro
2017-12-11T15:54:25Z

A lot of drivers are open-coding the "replace these bits in __be32 with
the following value" kind of primitives. Let's add them to byteorder.h.

Primitives:
{be,le}{16,32,64}_replace_bits(old, v, bit, nbits)
{be,le}{16,32,64}_get_bits(val, bit, nbits)

Essentially, it gives helpers for work with bitfields in fixed-endian.
Suppose we have e.g. a little-endian 32bit value with fixed layout;
expressing that as a bitfield would go like
struct foo {
unsigned foo:4; /* bits 0..3 */
unsigned :2;
unsigned bar:12; /* bits 6..17 */
unsigned baz:14; /* bits 18..31 */
}
Even for host-endian it doesn't work all that well - you end up with
ifdefs in structure definition and generated code stinks. For fixed-endian
it gets really painful, and people tend to use explicit shift-and-mask
kind of macros for accessing the fields (and often enough get the
endianness conversions wrong, at that). With these primitives

struct foo v <=> __le32 v
v.foo = i ? 1 : 2 <=> v = le32_replace_bits(v, i ? 1 : 2, 0, 4)
f(4 + v.baz) <=> f(4 + le32_get_bits(v, 18, 14))

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
diff --git a/include/linux/byteorder/generic.h b/include/linux/byteorder/generic.h
index 451aaa0786ae..d8f169a7104a 100644
--- a/include/linux/byteorder/generic.h
+++ b/include/linux/byteorder/generic.h
@@ -187,4 +187,26 @@ static inline void be32_to_cpu_array(u32 *dst, const __be32 *src, size_t len)
dst[i] = be32_to_cpu(src[i]);
}

+#define ____MASK(bit, nbits) ((((1ULL << ((nbits) - 1)) << 1) - 1) << (bit))
+#define ____MAKE_OP(type,base) \
+static inline __##type type##_replace_bits(__##type old, \
+ base val, int bit, int nbits) \
+{ \
+ __##type mask = cpu_to_##type(____MASK(bit, nbits)); \
+ return (old & ~mask) | (cpu_to_##type(val << bit) & mask); \
+} \
+static inline base type##_get_bits(__##type val, int bit, int nbits) \
+{ \
+ return (type##_to_cpu(val) >> bit) & ____MASK(0, nbits); \
+}
+
+____MAKE_OP(le16,u16)
+____MAKE_OP(le32,u32)
+____MAKE_OP(le64,u64)
+____MAKE_OP(be16,u16)
+____MAKE_OP(be32,u32)
+____MAKE_OP(be64,u64)
+#undef ____MAKE_OP
+#undef ____MASK
+
#endif /* _LINUX_BYTEORDER_GENERIC_H */