1 /*
2  * Copyright (c) 2020 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdint.h>
8 #include <soc.h>
9 #include <zephyr/bluetooth/hci_types.h>
10 
11 #include "util/util.h"
12 #include "util/memq.h"
13 #include "util/mem.h"
14 #include "util/dbuf.h"
15 
16 #include "hal/cpu.h"
17 #include "hal/ccm.h"
18 #include "hal/radio_df.h"
19 
20 #include "pdu_df.h"
21 #include "pdu_vendor.h"
22 #include "pdu.h"
23 
24 #include "lll.h"
25 #include "lll_adv_types.h"
26 #include "lll_adv.h"
27 #include "lll_adv_pdu.h"
28 #include "lll_df_types.h"
29 #include "lll_sync.h"
30 #include "lll_df.h"
31 #include "lll_df_internal.h"
32 
33 #include <soc.h>
34 #include "hal/debug.h"
35 
36 /* Minimum number of antenna switch patterns required by Direction Finding Extension to be
37  * configured in SWTICHPATTER register. The value is set to 2, even though the radio peripheral
38  * specification requires 3.
39  * Radio always configures three antenna patterns. First pattern is set implicitly in
40  * radio_df_ant_switch_pattern_set. It is provided by DTS radio.dfe_pdu_antenna property.
41  * There is a need for two more patterns to be provided by an application.
42  * They are aimed for: reference period and switch-sample period.
43  */
44 #define DF_MIN_ANT_NUM_REQUIRED 2
45 
46 static int init_reset(void);
47 
48 /* @brief Function performs Direction Finding initialization
49  *
50  * @return	Zero in case of success, other value in case of failure.
51  */
lll_df_init(void)52 int lll_df_init(void)
53 {
54 #if defined(CONFIG_BT_CTLR_DF_INIT_ANT_SEL_GPIOS)
55 	radio_df_ant_switching_gpios_cfg();
56 #endif /* CONFIG_BT_CTLR_DF_INIT_ANT_SEL_GPIOS */
57 	return init_reset();
58 }
59 
60 /* @brief Function performs Direction Finding reset
61  *
62  * @return	Zero in case of success, other value in case of failure.
63  */
lll_df_reset(void)64 int lll_df_reset(void)
65 {
66 	return init_reset();
67 }
68 
69 /* @brief Function provides number of available antennas.
70  *
71  * The number of antenna is hardware defined and it is provided via devicetree.
72  *
73  * @return      Number of available antennas.
74  */
lll_df_ant_num_get(void)75 uint8_t lll_df_ant_num_get(void)
76 {
77 	return radio_df_ant_num_get();
78 }
79 
80 #if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
81 /* @brief Function enables transmission of Constant Tone Extension.
82  *
83  * @param lll_sync        Pointer to LLL sync. object associated with
84  *                        periodic advertising event.
85  * @param pdu             Pointer to PDU that will be transmitted.
86  * @param[out] cte_len_us Pointer to store actual CTE length in [us]
87  */
lll_df_cte_tx_enable(struct lll_adv_sync * lll_sync,const struct pdu_adv * pdu,uint32_t * cte_len_us)88 void lll_df_cte_tx_enable(struct lll_adv_sync *lll_sync, const struct pdu_adv *pdu,
89 			  uint32_t *cte_len_us)
90 {
91 	if (pdu->adv_ext_ind.ext_hdr_len) {
92 		const struct pdu_adv_ext_hdr *ext_hdr;
93 
94 		ext_hdr = &pdu->adv_ext_ind.ext_hdr;
95 
96 		/* Check if particular extended PDU has cte_info. It is possible that
97 		 * not all PDUs in a periodic advertising chain have CTE attached.
98 		 * Nevertheless whole periodic advertising chain has the same CTE
99 		 * configuration. Checking for existence of CTE configuration in lll_sync
100 		 * is not enough.
101 		 */
102 		if (ext_hdr->cte_info) {
103 			const struct lll_df_adv_cfg *df_cfg;
104 
105 			df_cfg = lll_adv_sync_extra_data_curr_get(lll_sync);
106 			LL_ASSERT(df_cfg);
107 
108 			lll_df_cte_tx_configure(df_cfg->cte_type, df_cfg->cte_length,
109 						df_cfg->ant_sw_len, df_cfg->ant_ids);
110 
111 			lll_sync->cte_started = 1U;
112 			*cte_len_us = CTE_LEN_US(df_cfg->cte_length);
113 		} else {
114 			if (lll_sync->cte_started) {
115 				lll_df_cte_tx_disable();
116 				lll_sync->cte_started = 0U;
117 			}
118 			*cte_len_us = 0U;
119 		}
120 	} else {
121 		if (lll_sync->cte_started) {
122 			lll_df_cte_tx_disable();
123 			lll_sync->cte_started = 0U;
124 		}
125 		*cte_len_us = 0U;
126 	}
127 }
128 #endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
129 
130 #if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX)
lll_df_sync_cfg_alloc(struct lll_df_sync * df_cfg,uint8_t * idx)131 struct lll_df_sync_cfg *lll_df_sync_cfg_alloc(struct lll_df_sync *df_cfg,
132 					      uint8_t *idx)
133 {
134 	uint8_t first, last;
135 
136 	/* TODO: Make this unique mechanism to update last element in double
137 	 *       buffer a reusable utility function.
138 	 */
139 	first = df_cfg->first;
140 	last = df_cfg->last;
141 	if (first == last) {
142 		/* Return the index of next free PDU in the double buffer */
143 		last++;
144 		if (last == DOUBLE_BUFFER_SIZE) {
145 			last = 0U;
146 		}
147 	} else {
148 		uint8_t first_latest;
149 
150 		/* LLL has not consumed the first PDU. Revert back the `last` so
151 		 * that LLL still consumes the first PDU while the caller of
152 		 * this function updates/modifies the latest PDU.
153 		 *
154 		 * Under race condition:
155 		 * 1. LLL runs before `pdu->last` is reverted, then `pdu->first`
156 		 *    has changed, hence restore `pdu->last` and return index of
157 		 *    next free PDU in the double buffer.
158 		 * 2. LLL runs after `pdu->last` is reverted, then `pdu->first`
159 		 *    will not change, return the saved `last` as the index of
160 		 *    the next free PDU in the double buffer.
161 		 */
162 		df_cfg->last = first;
163 		cpu_dmb();
164 		first_latest = df_cfg->first;
165 		if (first_latest != first) {
166 			df_cfg->last = last;
167 			last++;
168 			if (last == DOUBLE_BUFFER_SIZE) {
169 				last = 0U;
170 			}
171 		}
172 	}
173 
174 	*idx = last;
175 
176 	return &df_cfg->cfg[last];
177 }
178 
lll_df_sync_cfg_latest_get(struct lll_df_sync * df_cfg,uint8_t * is_modified)179 struct lll_df_sync_cfg *lll_df_sync_cfg_latest_get(struct lll_df_sync *df_cfg,
180 						   uint8_t *is_modified)
181 {
182 	uint8_t first;
183 
184 	first = df_cfg->first;
185 	if (first != df_cfg->last) {
186 		uint8_t cfg_idx;
187 
188 		cfg_idx = first;
189 
190 		first += 1U;
191 		if (first == DOUBLE_BUFFER_SIZE) {
192 			first = 0U;
193 		}
194 		df_cfg->first = first;
195 
196 		if (is_modified) {
197 			*is_modified = 1U;
198 		}
199 
200 		df_cfg->cfg[cfg_idx].is_enabled = 0U; /* mark as disabled - not used anymore */
201 	}
202 
203 	return &df_cfg->cfg[first];
204 }
205 #endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */
206 
207 #if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) || defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX)
208 /* @brief Function initializes reception of Constant Tone Extension.
209  *
210  * @param slot_duration     Switching and sampling slots duration (1us or 2us).
211  * @param ant_num           Number of antennas in switch pattern.
212  * @param ant_ids           Antenna identifiers that create switch pattern.
213  * @param chan_idx          Channel used to receive PDU with CTE
214  * @param cte_info_in_s1    Inform if CTEInfo is in S1 byte for conn. PDU or in extended advertising
215  *                          header of per. adv. PDU.
216  * @param phy               Current PHY
217  *
218  * In case of AoA mode ant_num and ant_ids parameters are not used.
219  */
lll_df_conf_cte_rx_enable(uint8_t slot_duration,uint8_t ant_num,const uint8_t * ant_ids,uint8_t chan_idx,bool cte_info_in_s1,uint8_t phy)220 int lll_df_conf_cte_rx_enable(uint8_t slot_duration, uint8_t ant_num, const uint8_t *ant_ids,
221 			      uint8_t chan_idx, bool cte_info_in_s1, uint8_t phy)
222 {
223 	struct node_rx_iq_report *node_rx;
224 
225 	/* ToDo change to appropriate HCI constant */
226 #if defined(CONFIG_BT_CTLR_DF_ANT_SWITCH_1US)
227 	if (slot_duration == 0x1) {
228 		radio_df_cte_rx_2us_switching(cte_info_in_s1, phy);
229 	} else
230 #endif /* CONFIG_BT_CTLR_DF_ANT_SWITCH_1US */
231 	{
232 		radio_df_cte_rx_4us_switching(cte_info_in_s1, phy);
233 	}
234 
235 #if defined(CONFIG_BT_CTLR_DF_ANT_SWITCH_RX)
236 	radio_df_ant_switching_pin_sel_cfg();
237 	radio_df_ant_switch_pattern_clear();
238 	radio_df_ant_switch_pattern_set(ant_ids, ant_num);
239 #endif /* CONFIG_BT_CTLR_DF_ANT_SWITCH_RX */
240 
241 	/* Could be moved up, if Radio setup is not needed if we are not going to report IQ data */
242 	node_rx = ull_df_iq_report_alloc_peek(1);
243 	if (!node_rx) {
244 		return -ENOMEM;
245 	}
246 
247 	radio_df_iq_data_packet_set(node_rx->pdu, IQ_SAMPLE_TOTAL_CNT);
248 	node_rx->chan_idx = chan_idx;
249 
250 	return 0;
251 }
252 #endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX || CONFIG_BT_CTLR_DF_CONN_CTE_RX */
253 
254 #if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX)
255 /**
256  * @brief Function allocates additional IQ report node for Host notification about
257  * insufficient resources to sample all CTE in a periodic synchronization event.
258  *
259  * @param sync_lll Pointer to periodic synchronization object
260  *
261  * @return -ENOMEM in case there is no free node for IQ Data report
262  * @return -ENOBUFS in case there are no free nodes for report of insufficient resources as well as
263  * IQ data report
264  * @return zero in case of success
265  */
lll_df_iq_report_no_resources_prepare(struct lll_sync * sync_lll)266 int lll_df_iq_report_no_resources_prepare(struct lll_sync *sync_lll)
267 {
268 	struct node_rx_iq_report *cte_incomplete;
269 	int err;
270 
271 	/* Allocate additional node for a sync context only once. This is an additional node to
272 	 * report there is no more memory to store IQ data, hence some of CTEs are not going
273 	 * to be sampled.
274 	 */
275 	if (!sync_lll->node_cte_incomplete && !sync_lll->is_cte_incomplete) {
276 		/* Check if there are free nodes for:
277 		 * - storage of IQ data collcted during a PDU reception
278 		 * - Host notification about insufficient resources for IQ data
279 		 */
280 		cte_incomplete = ull_df_iq_report_alloc_peek(2);
281 		if (!cte_incomplete) {
282 			/* Check if there is a free node to report insufficient resources only.
283 			 * There will be no IQ Data collection.
284 			 */
285 			cte_incomplete = ull_df_iq_report_alloc_peek(1);
286 			if (!cte_incomplete) {
287 				/* No free nodes at all */
288 				return -ENOBUFS;
289 			}
290 
291 			/* No memory for IQ data report */
292 			err = -ENOMEM;
293 		} else {
294 			err = 0;
295 		}
296 
297 		/* Do actual allocation and store the node for further processing after a PDU
298 		 * reception,
299 		 */
300 		ull_df_iq_report_alloc();
301 
302 		/* Store the node in lll_sync object. This is a place where the node may be stored
303 		 * until processing afte reception of a PDU to report no IQ data or hand over
304 		 * to aux objects for usage in ULL. If there is not enough memory for IQ data
305 		 * there is no node to use for temporary storage as it is done for PDUs.
306 		 */
307 		sync_lll->node_cte_incomplete = cte_incomplete;
308 
309 		/* Reset the state every time the prepare is called. IQ report node may be unchanged
310 		 * from former synchronization event.
311 		 */
312 		sync_lll->is_cte_incomplete = false;
313 	} else {
314 		err = 0;
315 	}
316 
317 	return err;
318 }
319 #endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX  */
320 
321 #if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX)
322 /**
323  * @brief Function initializes parsing of received PDU for CTEInfo.
324  *
325  * Parsing a PDU for CTEInfo is required to successfully receive a PDU that may include CTE.
326  * It makes possible to correctly interpret PDU content by Radio peripheral.
327  */
lll_df_conf_cte_info_parsing_enable(void)328 void lll_df_conf_cte_info_parsing_enable(void)
329 {
330 	/* Use of mandatory 2 us switching and sampling slots for CTEInfo parsing.
331 	 * The configuration here does not matter for actual IQ sampling.
332 	 * The collected data will not be reported to host.
333 	 * Also sampling offset does not matter so, provided PHY is legacy to setup
334 	 * the offset to default zero value.
335 	 */
336 	radio_df_cte_rx_4us_switching(true, PHY_LEGACY);
337 
338 #if defined(CONFIG_BT_CTLR_DF_ANT_SWITCH_RX)
339 	/* Use PDU_ANTENNA so no actual antenna change will be done. */
340 	static uint8_t ant_ids[DF_MIN_ANT_NUM_REQUIRED] = { PDU_ANTENNA, PDU_ANTENNA };
341 
342 	radio_df_ant_switching_pin_sel_cfg();
343 	radio_df_ant_switch_pattern_clear();
344 	radio_df_ant_switch_pattern_set(ant_ids, DF_MIN_ANT_NUM_REQUIRED);
345 #endif /* CONFIG_BT_CTLR_DF_ANT_SWITCH_RX */
346 
347 	/* Do not set storage for IQ samples, it is irrelevant for parsing of a PDU for CTEInfo. */
348 	radio_df_iq_data_packet_set(NULL, 0);
349 }
350 #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RX */
351 
352 /* @brief Function performs common steps for initialization and reset
353  * of Direction Finding LLL module.
354  *
355  * @return Zero in case of success, other value in case of failure.
356  */
init_reset(void)357 static int init_reset(void)
358 {
359 	return 0;
360 }
361 
362 #if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX) || defined(CONFIG_BT_CTLR_DF_CONN_CTE_TX)
363 /**
364  * @brief Function configures transmission of Constant Tone Extension.
365  *
366  * @param cte_type    Type of the CTE
367  * @param cte_length  Length of CTE in units of 8 us
368  * @param num_ant_ids Length of @p ant_ids
369  * @param ant_ids     Pointer to antenna identifiers array
370  *
371  * In case of AoA mode ant_sw_len and ant_ids members are not used.
372  */
lll_df_cte_tx_configure(uint8_t cte_type,uint8_t cte_length,uint8_t num_ant_ids,const uint8_t * ant_ids)373 void lll_df_cte_tx_configure(uint8_t cte_type, uint8_t cte_length, uint8_t num_ant_ids,
374 			     const uint8_t *ant_ids)
375 {
376 	if (cte_type == BT_HCI_LE_AOA_CTE) {
377 		radio_df_mode_set_aoa();
378 		radio_df_cte_tx_aoa_set(cte_length);
379 	}
380 #if defined(CONFIG_BT_CTLR_DF_ANT_SWITCH_TX)
381 	else {
382 		radio_df_mode_set_aod();
383 
384 		if (cte_type == BT_HCI_LE_AOD_CTE_1US) {
385 			radio_df_cte_tx_aod_2us_set(cte_length);
386 		} else {
387 			radio_df_cte_tx_aod_4us_set(cte_length);
388 		}
389 
390 		radio_df_ant_switching_pin_sel_cfg();
391 		radio_df_ant_switch_pattern_clear();
392 		radio_df_ant_switch_pattern_set(ant_ids, num_ant_ids);
393 	}
394 #endif /* CONFIG_BT_CTLR_DF_ANT_SWITCH_TX */
395 }
396 
lll_df_cte_tx_disable(void)397 void lll_df_cte_tx_disable(void)
398 {
399 	radio_df_reset();
400 }
401 #endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX || CONFIG_BT_CTLR_DF_CONN_CTE_TX */
402