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