1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef _LINUX_UNALIGNED_GENERIC_H
3#define _LINUX_UNALIGNED_GENERIC_H
4
5#include <linux/types.h>
6
7/*
8 * Cause a link-time error if we try an unaligned access other than
9 * 1,2,4 or 8 bytes long
10 */
11extern void __bad_unaligned_access_size(void);
12
13#define __get_unaligned_le(ptr) ((__force typeof(*(ptr)))({			\
14	__builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr),			\
15	__builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)),	\
16	__builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)),	\
17	__builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)),	\
18	__bad_unaligned_access_size()))));					\
19	}))
20
21#define __get_unaligned_be(ptr) ((__force typeof(*(ptr)))({			\
22	__builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr),			\
23	__builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_be16((ptr)),	\
24	__builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_be32((ptr)),	\
25	__builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_be64((ptr)),	\
26	__bad_unaligned_access_size()))));					\
27	}))
28
29#define __put_unaligned_le(val, ptr) ({					\
30	void *__gu_p = (ptr);						\
31	switch (sizeof(*(ptr))) {					\
32	case 1:								\
33		*(u8 *)__gu_p = (__force u8)(val);			\
34		break;							\
35	case 2:								\
36		put_unaligned_le16((__force u16)(val), __gu_p);		\
37		break;							\
38	case 4:								\
39		put_unaligned_le32((__force u32)(val), __gu_p);		\
40		break;							\
41	case 8:								\
42		put_unaligned_le64((__force u64)(val), __gu_p);		\
43		break;							\
44	default:							\
45		__bad_unaligned_access_size();				\
46		break;							\
47	}								\
48	(void)0; })
49
50#define __put_unaligned_be(val, ptr) ({					\
51	void *__gu_p = (ptr);						\
52	switch (sizeof(*(ptr))) {					\
53	case 1:								\
54		*(u8 *)__gu_p = (__force u8)(val);			\
55		break;							\
56	case 2:								\
57		put_unaligned_be16((__force u16)(val), __gu_p);		\
58		break;							\
59	case 4:								\
60		put_unaligned_be32((__force u32)(val), __gu_p);		\
61		break;							\
62	case 8:								\
63		put_unaligned_be64((__force u64)(val), __gu_p);		\
64		break;							\
65	default:							\
66		__bad_unaligned_access_size();				\
67		break;							\
68	}								\
69	(void)0; })
70
71static inline u32 __get_unaligned_be24(const u8 *p)
72{
73	return p[0] << 16 | p[1] << 8 | p[2];
74}
75
76static inline u32 get_unaligned_be24(const void *p)
77{
78	return __get_unaligned_be24(p);
79}
80
81static inline u32 __get_unaligned_le24(const u8 *p)
82{
83	return p[0] | p[1] << 8 | p[2] << 16;
84}
85
86static inline u32 get_unaligned_le24(const void *p)
87{
88	return __get_unaligned_le24(p);
89}
90
91static inline void __put_unaligned_be24(const u32 val, u8 *p)
92{
93	*p++ = val >> 16;
94	*p++ = val >> 8;
95	*p++ = val;
96}
97
98static inline void put_unaligned_be24(const u32 val, void *p)
99{
100	__put_unaligned_be24(val, p);
101}
102
103static inline void __put_unaligned_le24(const u32 val, u8 *p)
104{
105	*p++ = val;
106	*p++ = val >> 8;
107	*p++ = val >> 16;
108}
109
110static inline void put_unaligned_le24(const u32 val, void *p)
111{
112	__put_unaligned_le24(val, p);
113}
114
115#endif /* _LINUX_UNALIGNED_GENERIC_H */
116