1 /*
2  * Copyright (c) 2023 Florian Grandel, Zephyr Project.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief IEEE 802.15.4 MAC information element (IE) related types and helpers
10  *
11  * This is not to be included by the application. This file contains only those
12  * parts of the types required for IE support that need to be visible to IEEE
13  * 802.15.4 drivers and L2 at the same time, i.e. everything related to header
14  * IE representation, parsing and generation.
15  *
16  * All specification references in this file refer to IEEE 802.15.4-2020.
17  *
18  * @note All structs and attributes in this file that directly represent parts
19  * of IEEE 802.15.4 frames are in LITTLE ENDIAN, see section 4, especially
20  * section 4.3.
21  */
22 
23 #ifndef ZEPHYR_INCLUDE_NET_IEEE802154_IE_H_
24 #define ZEPHYR_INCLUDE_NET_IEEE802154_IE_H_
25 
26 #include <zephyr/net_buf.h>
27 #include <zephyr/sys/byteorder.h>
28 
29 /**
30  * @addtogroup ieee802154_driver
31  * @{
32  *
33  * @name IEEE 802.15.4, section 7.4.2: MAC header information elements
34  * @{
35  */
36 
37 /**
38  * @brief Information Element Types.
39  *
40  * @details See sections 7.4.2.1 and 7.4.3.1.
41  */
42 enum ieee802154_ie_type {
43 	IEEE802154_IE_TYPE_HEADER = 0x0, /**< Header type */
44 	IEEE802154_IE_TYPE_PAYLOAD,      /**< Payload type */
45 };
46 
47 /**
48  * @brief Header Information Element IDs.
49  *
50  * @details See section 7.4.2.1, table 7-7, partial list, only IEs actually used
51  * are implemented.
52  */
53 enum ieee802154_header_ie_element_id {
54 	IEEE802154_HEADER_IE_ELEMENT_ID_VENDOR_SPECIFIC_IE = 0x00,   /**< Vendor specific IE */
55 	IEEE802154_HEADER_IE_ELEMENT_ID_CSL_IE = 0x1a,	             /**< CSL IE */
56 	IEEE802154_HEADER_IE_ELEMENT_ID_RIT_IE = 0x1b,               /**< RIT IE */
57 	IEEE802154_HEADER_IE_ELEMENT_ID_RENDEZVOUS_TIME_IE = 0x1d,   /**< Rendezvous time IE */
58 	IEEE802154_HEADER_IE_ELEMENT_ID_TIME_CORRECTION_IE = 0x1e,   /**< Time correction IE */
59 	IEEE802154_HEADER_IE_ELEMENT_ID_HEADER_TERMINATION_1 = 0x7e, /**< Header termination 1 */
60 	IEEE802154_HEADER_IE_ELEMENT_ID_HEADER_TERMINATION_2 = 0x7f, /**< Header termination 2 */
61 	/* partial list, add additional ids as needed */
62 };
63 
64 /** @cond INTERNAL_HIDDEN */
65 #define IEEE802154_VENDOR_SPECIFIC_IE_OUI_LEN 3
66 /** INTERNAL_HIDDEN @endcond */
67 
68 /** @brief Vendor Specific Header IE, see section 7.4.2.3. */
69 struct ieee802154_header_ie_vendor_specific {
70 	/** Vendor OUI */
71 	uint8_t vendor_oui[IEEE802154_VENDOR_SPECIFIC_IE_OUI_LEN];
72 	/** Vendor specific information */
73 	uint8_t *vendor_specific_info;
74 } __packed;
75 
76 /** @brief Full CSL IE, see section 7.4.2.3. */
77 struct ieee802154_header_ie_csl_full {
78 	uint16_t csl_phase;           /**< CSL phase */
79 	uint16_t csl_period;          /**< CSL period */
80 	uint16_t csl_rendezvous_time; /**< Rendezvous time */
81 } __packed;
82 
83 /** @brief Reduced CSL IE, see section 7.4.2.3. */
84 struct ieee802154_header_ie_csl_reduced {
85 	uint16_t csl_phase;  /**< CSL phase */
86 	uint16_t csl_period; /**< CSL period */
87 } __packed;
88 
89 /** @brief Generic CSL IE, see section 7.4.2.3. */
90 struct ieee802154_header_ie_csl {
91 	union {
92 		/** CSL full information */
93 		struct ieee802154_header_ie_csl_full full;
94 		/** CSL reduced information */
95 		struct ieee802154_header_ie_csl_reduced reduced;
96 	};
97 } __packed;
98 
99 /** @brief RIT IE, see section 7.4.2.4. */
100 struct ieee802154_header_ie_rit {
101 	uint8_t time_to_first_listen;    /**< Time to First Listen */
102 	uint8_t number_of_repeat_listen; /**< Number of Repeat Listen */
103 	uint16_t repeat_listen_interval; /**< Repeat listen interval */
104 } __packed;
105 
106 /**
107  * @brief Full Rendezvous Time IE, see section 7.4.2.6
108  * (macCslInterval is nonzero).
109  */
110 struct ieee802154_header_ie_rendezvous_time_full {
111 	uint16_t rendezvous_time; /**< Rendezvous time */
112 	uint16_t wakeup_interval; /**< Wakeup interval */
113 } __packed;
114 
115 /**
116  * @brief Reduced Rendezvous Time IE, see section 7.4.2.6
117  * (macCslInterval is zero).
118  */
119 struct ieee802154_header_ie_rendezvous_time_reduced {
120 	uint16_t rendezvous_time; /**< Rendezvous time */
121 } __packed;
122 
123 /** @brief Rendezvous Time IE, see section 7.4.2.6. */
124 struct ieee802154_header_ie_rendezvous_time {
125 	union {
126 		/** Rendezvous time full information */
127 		struct ieee802154_header_ie_rendezvous_time_full full;
128 		/** Rendezvous time reduced information */
129 		struct ieee802154_header_ie_rendezvous_time_reduced reduced;
130 	};
131 } __packed;
132 
133 /** @brief Time Correction IE, see section 7.4.2.7. */
134 struct ieee802154_header_ie_time_correction {
135 	uint16_t time_sync_info;  /**< Time synchronization information */
136 } __packed;
137 
138 /** @cond INTERNAL_HIDDEN */
139 
140 /* @brief Generic Header IE, see section 7.4.2.1. */
141 struct ieee802154_header_ie {
142 #if CONFIG_LITTLE_ENDIAN
143 	uint16_t length : 7;
144 	uint16_t element_id_low : 1; /* see enum ieee802154_header_ie_element_id */
145 	uint16_t element_id_high : 7;
146 	uint16_t type : 1; /* always 0 */
147 #else
148 	uint16_t element_id_low : 1; /* see enum ieee802154_header_ie_element_id */
149 	uint16_t length : 7;
150 	uint16_t type : 1; /* always 0 */
151 	uint16_t element_id_high : 7;
152 #endif
153 	union {
154 		struct ieee802154_header_ie_vendor_specific vendor_specific;
155 		struct ieee802154_header_ie_csl csl;
156 		struct ieee802154_header_ie_rit rit;
157 		struct ieee802154_header_ie_rendezvous_time rendezvous_time;
158 		struct ieee802154_header_ie_time_correction time_correction;
159 		/* add additional supported header IEs here */
160 	} content;
161 } __packed;
162 
163 /** INTERNAL_HIDDEN @endcond */
164 
165 /** @brief The header IE's header length (2 bytes). */
166 #define IEEE802154_HEADER_IE_HEADER_LENGTH sizeof(uint16_t)
167 
168 
169 /** @cond INTERNAL_HIDDEN */
170 #define IEEE802154_DEFINE_HEADER_IE(_element_id, _length, _content, _content_type)                 \
171 	(struct ieee802154_header_ie) {                                                            \
172 		.length = (_length),                                                               \
173 		.element_id_high = (_element_id) >> 1U, .element_id_low = (_element_id) & 0x01,    \
174 		.type = IEEE802154_IE_TYPE_HEADER,                                                 \
175 		.content._content_type = _content,                                                 \
176 	}
177 
178 #define IEEE802154_DEFINE_HEADER_IE_VENDOR_SPECIFIC_CONTENT_LEN(_vendor_specific_info_len)         \
179 	(IEEE802154_VENDOR_SPECIFIC_IE_OUI_LEN + (_vendor_specific_info_len))
180 
181 #define IEEE802154_DEFINE_HEADER_IE_VENDOR_SPECIFIC_CONTENT(_vendor_oui, _vendor_specific_info)    \
182 	(struct ieee802154_header_ie_vendor_specific) {                                            \
183 		.vendor_oui = _vendor_oui, .vendor_specific_info = (_vendor_specific_info),        \
184 	}
185 
186 #define IEEE802154_DEFINE_HEADER_IE_CSL_REDUCED_CONTENT(_csl_phase, _csl_period)                   \
187 	(struct ieee802154_header_ie_csl_reduced) {                                                \
188 		.csl_phase = sys_cpu_to_le16(_csl_phase),                                          \
189 		.csl_period = sys_cpu_to_le16(_csl_period),                                        \
190 	}
191 
192 #define IEEE802154_DEFINE_HEADER_IE_CSL_FULL_CONTENT(_csl_phase, _csl_period,                      \
193 						     _csl_rendezvous_time)                         \
194 	(struct ieee802154_header_ie_csl_full) {                                                   \
195 		.csl_phase = sys_cpu_to_le16(_csl_phase),                                          \
196 		.csl_period = sys_cpu_to_le16(_csl_period),                                        \
197 		.csl_rendezvous_time = sys_cpu_to_le16(_csl_rendezvous_time),                      \
198 	}
199 
200 #define IEEE802154_HEADER_IE_TIME_CORRECTION_NACK          0x8000
201 #define IEEE802154_HEADER_IE_TIME_CORRECTION_MASK          0x0fff
202 #define IEEE802154_HEADER_IE_TIME_CORRECTION_SIGN_BIT_MASK 0x0800
203 
204 #define IEEE802154_DEFINE_HEADER_IE_TIME_CORRECTION_CONTENT(_ack, _time_correction_us)             \
205 	(struct ieee802154_header_ie_time_correction) {                                            \
206 		.time_sync_info = sys_cpu_to_le16(                                                 \
207 			(!(_ack) * IEEE802154_HEADER_IE_TIME_CORRECTION_NACK) |                    \
208 			((_time_correction_us) & IEEE802154_HEADER_IE_TIME_CORRECTION_MASK)),      \
209 	}
210 /** INTERNAL_HIDDEN @endcond */
211 
212 /**
213  * @brief Define a vendor specific header IE, see section 7.4.2.3.
214  *
215  * @details Example usage (all parameters in little endian):
216  *
217  * @code{.c}
218  *   uint8_t vendor_specific_info[] = {...some vendor specific IE content...};
219  *   struct ieee802154_header_ie header_ie = IEEE802154_DEFINE_HEADER_IE_VENDOR_SPECIFIC(
220  *       {0x9b, 0xb8, 0xea}, vendor_specific_info, sizeof(vendor_specific_info));
221  * @endcode
222  *
223  * @param _vendor_oui an initializer for a 3 byte vendor oui array in little
224  * endian
225  * @param _vendor_specific_info pointer to a variable length uint8_t array with
226  * the vendor specific IE content
227  * @param _vendor_specific_info_len the length of the vendor specific IE content
228  * (in bytes)
229  */
230 #define IEEE802154_DEFINE_HEADER_IE_VENDOR_SPECIFIC(_vendor_oui, _vendor_specific_info,            \
231 						    _vendor_specific_info_len)                     \
232 	IEEE802154_DEFINE_HEADER_IE(IEEE802154_HEADER_IE_ELEMENT_ID_VENDOR_SPECIFIC_IE,            \
233 				    IEEE802154_DEFINE_HEADER_IE_VENDOR_SPECIFIC_CONTENT_LEN(       \
234 					    _vendor_specific_info_len),                            \
235 				    IEEE802154_DEFINE_HEADER_IE_VENDOR_SPECIFIC_CONTENT(           \
236 					    _vendor_oui, _vendor_specific_info),                   \
237 				    vendor_specific)
238 
239 /**
240  * @brief Define a reduced CSL IE, see section 7.4.2.3.
241  *
242  * @details Example usage (all parameters in CPU byte order):
243  *
244  * @code{.c}
245  *   uint16_t csl_phase = ...;
246  *   uint16_t csl_period = ...;
247  *   struct ieee802154_header_ie header_ie =
248  *       IEEE802154_DEFINE_HEADER_IE_CSL_REDUCED(csl_phase, csl_period);
249  * @endcode
250  *
251  * @param _csl_phase CSL phase in CPU byte order
252  * @param _csl_period CSL period in CPU byte order
253  */
254 #define IEEE802154_DEFINE_HEADER_IE_CSL_REDUCED(_csl_phase, _csl_period)                           \
255 	IEEE802154_DEFINE_HEADER_IE(                                                               \
256 		IEEE802154_HEADER_IE_ELEMENT_ID_CSL_IE,                                            \
257 		sizeof(struct ieee802154_header_ie_csl_reduced),                                   \
258 		IEEE802154_DEFINE_HEADER_IE_CSL_REDUCED_CONTENT(_csl_phase, _csl_period),          \
259 		csl.reduced)
260 
261 /**
262  * @brief Define a full CSL IE, see section 7.4.2.3.
263  *
264  * @details Example usage (all parameters in CPU byte order):
265  *
266  * @code{.c}
267  *   uint16_t csl_phase = ...;
268  *   uint16_t csl_period = ...;
269  *   uint16_t csl_rendezvous_time = ...;
270  *   struct ieee802154_header_ie header_ie =
271  *       IEEE802154_DEFINE_HEADER_IE_CSL_REDUCED(csl_phase, csl_period, csl_rendezvous_time);
272  * @endcode
273  *
274  * @param _csl_phase CSL phase in CPU byte order
275  * @param _csl_period CSL period in CPU byte order
276  * @param _csl_rendezvous_time CSL rendezvous time in CPU byte order
277  */
278 #define IEEE802154_DEFINE_HEADER_IE_CSL_FULL(_csl_phase, _csl_period, _csl_rendezvous_time)        \
279 	IEEE802154_DEFINE_HEADER_IE(IEEE802154_HEADER_IE_ELEMENT_ID_CSL_IE,                        \
280 				    sizeof(struct ieee802154_header_ie_csl_full),                  \
281 				    IEEE802154_DEFINE_HEADER_IE_CSL_FULL_CONTENT(                  \
282 					    _csl_phase, _csl_period, _csl_rendezvous_time),        \
283 				    csl.full)
284 
285 /**
286  * @brief Define a Time Correction IE, see section 7.4.2.7.
287  *
288  * @details Example usage (parameter in CPU byte order):
289  *
290  * @code{.c}
291  *   uint16_t time_sync_info = ...;
292  *   struct ieee802154_header_ie header_ie =
293  *       IEEE802154_DEFINE_HEADER_IE_TIME_CORRECTION(true, time_sync_info);
294  * @endcode
295  *
296  * @param _ack whether or not the enhanced ACK frame that receives this IE is an
297  * ACK (true) or NACK (false)
298  * @param _time_correction_us the positive or negative deviation from expected
299  * RX time in microseconds
300  */
301 #define IEEE802154_DEFINE_HEADER_IE_TIME_CORRECTION(_ack, _time_correction_us)                     \
302 	IEEE802154_DEFINE_HEADER_IE(                                                               \
303 		IEEE802154_HEADER_IE_ELEMENT_ID_TIME_CORRECTION_IE,                                \
304 		sizeof(struct ieee802154_header_ie_time_correction),                               \
305 		IEEE802154_DEFINE_HEADER_IE_TIME_CORRECTION_CONTENT(_ack, _time_correction_us),    \
306 		time_correction)
307 
308 /**
309  * @brief Retrieve the time correction value in microseconds from a Time Correction IE,
310  * see section 7.4.2.7.
311  *
312  * @param[in] ie pointer to the Time Correction IE structure
313  *
314  * @return The time correction value in microseconds.
315  */
316 static inline int16_t
ieee802154_header_ie_get_time_correction_us(struct ieee802154_header_ie_time_correction * ie)317 ieee802154_header_ie_get_time_correction_us(struct ieee802154_header_ie_time_correction *ie)
318 {
319 	if (ie->time_sync_info & IEEE802154_HEADER_IE_TIME_CORRECTION_SIGN_BIT_MASK) {
320 		/* Negative integer */
321 		return (int16_t)ie->time_sync_info | ~IEEE802154_HEADER_IE_TIME_CORRECTION_MASK;
322 	}
323 
324 	/* Positive integer */
325 	return (int16_t)ie->time_sync_info & IEEE802154_HEADER_IE_TIME_CORRECTION_MASK;
326 }
327 
328 /**
329  * @brief Set the element ID of a header IE.
330  *
331  * @param[in] ie pointer to a header IE
332  * @param[in] element_id IE element id in CPU byte order
333  */
ieee802154_header_ie_set_element_id(struct ieee802154_header_ie * ie,uint8_t element_id)334 static inline void ieee802154_header_ie_set_element_id(struct ieee802154_header_ie *ie,
335 						       uint8_t element_id)
336 {
337 	ie->element_id_high = element_id >> 1U;
338 	ie->element_id_low = element_id & 0x01;
339 }
340 
341 /**
342  * @brief Get the element ID of a header IE.
343  *
344  * @param[in] ie pointer to a header IE
345  *
346  * @return header IE element id in CPU byte order
347  */
ieee802154_header_ie_get_element_id(struct ieee802154_header_ie * ie)348 static inline uint8_t ieee802154_header_ie_get_element_id(struct ieee802154_header_ie *ie)
349 {
350 	return (ie->element_id_high << 1U) | ie->element_id_low;
351 }
352 
353 /** @brief The length in bytes of a "Time Correction" header IE. */
354 #define IEEE802154_TIME_CORRECTION_HEADER_IE_LEN                                                   \
355 	(IEEE802154_HEADER_IE_HEADER_LENGTH + sizeof(struct ieee802154_header_ie_time_correction))
356 
357 /** @brief The length in bytes of a "Header Termination 1" header IE. */
358 #define IEEE802154_HEADER_TERMINATION_1_HEADER_IE_LEN IEEE802154_HEADER_IE_HEADER_LENGTH
359 
360 /**
361  * @}
362  *
363  * @}
364  */
365 
366 #endif /* ZEPHYR_INCLUDE_NET_IEEE802154_IE_H_ */
367