1 /*
2  * Copyright (c) 2017 Oticon A/S
3  * Copyright (c) 2023 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * RADIO - 2.4 GHz Radio
10  * https://infocenter.nordicsemi.com/topic/ps_nrf52833/radio.html?cp=5_1_0_5_17
11  * https://infocenter.nordicsemi.com/topic/ps_nrf52833/radio.html?cp=5_1_0_5_17
12  *
13  * Note: as of now, only 1&2Mbps BLE & 15.4 packet formats are supported, there is quite many notes around in the code
14  * where changes would be required to support other formats. PCNF1.STATLEN is always assumed 0
15  *
16  * Note3: Only logical address 0 (in Tx or Rx) is supported
17  *
18  * Note4: only default freq. map supported
19  *
20  * Note5: Only little endian hosts supported (x86 is little endian)
21  *
22  * Note6: RSSI is always sampled at the end of the address (and RSSIEND raised there)
23  *
24  * Note7: Whitening is always/never "used" (it is the phy who would use or not whitening), the radio model can assume it is always used (even that we ignore the initialization register)
25  *
26  * Note8: During idle nothing is sent to the air
27  *
28  * Note9: Double buffering of registers is not implemented. Changing the register during the packet Tx/Rx will cause trouble
29  *        It should be (at least): PACKETPTR @ START
30  *                                 MODE @ TXEN | RXEN
31  *                                 CRC config @ START
32  *
33  * Note10: Regarding MAXLEN:
34  *           if CRCINC==1, the CRC LEN is deducted from the length field, before MAXLEN is checked.
35  *           This seems to be also the real HW behavior
36  *         Bit errors in the length field reception, or even bit errors in the CI field reception (for Coded phy)
37  *         are not accounted for when handling the length field in this model. The model will always
38  *         act on the transmitted length field.
39  *
40  * Note11: Only the BLE & 15.4 CRC polynomials are supported
41  *         During reception we assume that CRCPOLY and CRCINIT are correct on both sides, and just rely on the phy bit error reporting to save processing time
42  *         On transmission we generate the correct CRC for correctness of the channel dump traces (and Ellisys traces)
43  * Note11b:The CRC configuration is directly deduced from the modulation, only BLE and 154 CRCs are supported so far
44  *
45  * Note12: * CCA or ED procedures cannot be performed while the RADIO is performing an actual packet reception (they are exclusive)
46  *         * In CCA Mode2 & 3, this model (due to the Phy) does not search for a SFD, or for a correlation peak
47  *           instead it searches for a compatible modulation of sufficient power (which is in line with what the 802.15.4
48  *           standard specifies)
49  *
50  * Note13: Nothing related to AoA/AoD features (CTE, DFE) is implemented
51  *
52  * Note14: Several 52833 radio state change events are not yet implemented
53  *         (EVENTS_MHRMATCH & EVENTS_CTEPRESENT)
54  *
55  * Note16: No antenna switching
56  *
57  * Note17: Interrupts are modeled as pulses to the NVIC, not level interrupts as they are in reality
58  *
59  * Note18: EVENTS_SYNC:
60  *         a) It is not generated at the exact correct time:
61  *         In this model it is generated at the end of the address (or SFD)
62  *         while according to the infocenter spec this should come at the end of the preamble,
63  *         and only if in coded and 15.4 mode.
64  *         In reality it seems to come somewhere during the preamble for 15.4 and coded phy,
65  *         and somewhere during the address for 1&2Mbps BLE.
66  *         In any case this seems to be a debug signal, and a quite imprecise one,
67  *         so the assumption is that nobody uses it for anything timing critical.
68  *         b) it is only generated when there is a full address match. While in real HW this is not required
69  *         (so false positives happen in real HW)
70  *
71  * Note19: EVENTS_PHYEND
72  *         It is not generated at the exact correct time. In the model it is generated at the
73  *         exact same time as END. While according to the spec, it should be generated with the last
74  *         bit on *air* (That is the Tx chain delay later for Tx, and RxChainDelay earlier for Rx)
75  *
76  * Note20: The LQI value is based on a single measurement at the end of the SFD.
77  *         While the real HW samples it in 3 places during the payload, and the middle one selected.
78  *
79  * Note21: Timings:
80  *          * Radio ramp down time for 2Mbps BLE is 2 microseconds too long.
81  *          * Many timings are simplified, and some events which take slightly different amounts of time to occur
82  *            are produced at the same time as others, or so. Check NRF_RADIO_timings.c for some more notes.
83  *
84  * Note21.b: CodedPhy timings:
85  *          * For CodedPhy the spec lacks radio timings, so the values used are mostly rounded
86  *            versions of the ones the Zephyr controller used
87  *          * At this point (for simplicity) The Rx chain delay for S=2 and S=8 are modeled as being equal.
88  *          * It is quite unclear if the CRCOK/ERROR and PAYLOAD events are delayed by the equivalent
89  *            of the conv. decoder taking 2 extra input bits or not, and if the ADDRESS event has an
90  *            equivalent delay or not.
91  *            At this point the model does not add a different delay to these, so this events
92  *            timings should be considered a rough guess. CRCOK/ERROR is generated at the same time
93  *            and the END event based on the end of TERM2.
94  *
95  * Note22: EVENTS_FRAMESTART
96  *          * It is generated for all modulation types, this seems to be how the HW behaves even if the spec
97  *            seems to mildly imply it is only for 15.4
98  *          * In Tx: The spec seems unclear about the FRAMESTART being generated or not (after SHR).
99  *            Drawings imply it is, the text that it does not. The HW does. The model does generate it after SHR.
100  *          * In Rx: In the model it is generated at the SHR/SFD end (not PHR), meaning, at the same time as the ADDRESS EVENT
101  *            The spec seems to contradict itself here. But seems in real HW it is generated at the end of the PHR.
102  *
103  * Note23: For nRF52/53: Powering off/on is not properly modeled (it is mostly ignored)
104  *         (In the real setting POWER to 0, resets most registers. That is not the case in the models)
105  *
106  * Note24: Only PCNF1.ENDIAN == Little is supported (What BT and 802.15.4 use)
107  *
108  * Note25: The RATEBOOST event in real HW seems to be generated: only for Rx, only if the FEC2 block has S=2,
109  *         and roughly at the end of TERM1. The models generates it at that point and only in that case.
110  *         (It's timing is probably a bit off compared to real HW)
111  *
112  * Note26: TASK_SOFTRESET (for nRF54 targets) is not yet implemented
113  *
114  *
115  * Implementation Specification:
116  *   A diagram of the main state machine can be found in docs/RADIO_states.svg
117  *   That main state machine is driven by a timer (Timer_RADIO) which results in calls to nhw_radio_timer_triggered()
118  *   and the tasks which cause transitions and/or the timer to be set to a new value.
119  *
120  *   Apart from this main state machine there is a small state machine for handling the automatic TIFS re-enabling.
121  *   See TIFS_state, Timer_TIFS, nhw_RADIO_fake_task_TRXEN_TIFS, and maybe_prepare_TIFS()
122  *   This TIFS machine piggybacks on the main machine and its timer.
123  *
124  *   And apart from this, there is an "abort" state machine, which is used to handle SW or another peripheral
125  *   triggering a TASK which requires us to stop a transaction with the Phy midway.
126  *   The idea here, is that when we start a transaction with the Phy (say a Tx), we do not know at the start if something
127  *   will want to stop it midway. So we tell the Phy when we start, when we expect to end, but also, when we
128  *   want the Phy to recheck with us if the transaction needs to be aborted midway.
129  *   This recheck time is set to the time anything may decide to stop. Which for simplicity is whenever *anything* may run.
130  *   That is, whenever any timer is scheduled. As this includes other peripherals which may trigger tasks thru the PPI,
131  *   or SW doing so after an interrupt.
132  *   If at any point, a TASK that stops a transaction comes while that transaction is ongoing, the abort state machine will flag it,
133  *   and the next time we need to respond to the Phy we will tell that we are stopping.
134  *
135  *   Apart from these, there is the interaction with the Phy (check ext_2G4_libPhyComv1/docs):
136  *   There is 3 different procedures for this (Tx, Rx & CCA) which fundamentally work in the same way.
137  *   At start_Rx/Tx/CCA_ED() (which is called at the micros when the actual Tx/Rx/CCA/ED measurement starts),
138  *   the Phy is told we want to start, and immediately we block until we get a response from the Phy.
139  *   (1) Here the response may be that:
140  *     * The Phy finished the procedure, in which case we just pre-program the main state machine timer,
141  *       and set registers and other state accordingly. (as we are done interacting with the Phy for this operation)
142  *       OR
143  *     * The Phy asks us to reevaluate if we want to abort. In this case, we hold responding to the Phy
144  *       and instead set the Timer_RADIO_abort_reeval to the time in which we need to respond to the Phy
145  *       and let time pass until that microsecond is ended.
146  *       At that point in time:
147  *          * If SW (or whatever else may trigger a TASK) has caused the procedure to end, we tell the Phy
148  *            we are aborting right now
149  *          * If nothing stopped it yet, we respond with a new abort reevaluation time in the future to the Phy,
150  *            and continue from (1).
151  *       The idea is that Timer_RADIO_abort_reeval is a separate timer that runs in parallel to the main Timer_RADIO and any other
152  *       HW event timer. And as Timer_RADIO_abort_reeval is the last timer scheduled by the HW_model_top in a given time, we will know if
153  *       anything else has affected the RADIO state in a way that requires us to stop the interaction with the Phy or not.
154  *   When we receive the CCA end from the Phy, we will also check the result, set registers accordingly, and pre-set the cca_status so
155  *   as to raise or not the CCABUSY/IDLE signals.
156  *   For an Rx it is marginally more complex, as we not only receive the end (either no sync, or crcok/failed), but also an intermediate
157  *   notification when the address has been received. At this point (address end) we pre-check some of the packet content (address for BLE adv,
158  *   length, etc) and already set some status registers and make some decisions about if we should proceed with the packet or not.
159  *
160  *   The CCA and ED procedures are so similar that they are handled with the same CCA_ED state in the main state machine,
161  *   most of the same CCA_ED code, and the same CCA procedure to the Phy.
162  *
163  *   For BLE Coded Phy, transmissions and receptions are actually handled as 2 piggybacking ones:
164  *   One for the FEC1 block and another for the FEC2.
165  *   * In transmission, during start_Tx() both FEC1 and FEC2 tx_req and tx_req_fec1 structures are
166  *   filled in, and the transmission for the FEC1 is started. When the FEC2 transmission start time
167  *   has been reached (the micros after the FEC1 has ended), the main state state machine will call
168  *   start_Tx_FEC2() which will update the FEC2 tx_req, and start it. From there it will continue
169  *   like a normal transmission until the CRC end.
170  *   * In receptions similarly start_Rx() will prefill both rx_req structures. But: the 2nd rx_req
171  *   will be updated after the FEC1 CI is received at start_Rx_FEC2(), and when the FEC1 ends.
172  *   Unlike a Tx, a reception can take several code paths depending on the possibility of sync'ing,
173  *   having an erroneous FEC1 or FEC2 packet. See docs/Rx_Phy_paths.svg for more info.
174  *
175  *   The main state machine has a few conditions to transition differently for Coded Phy and normal
176  *   packets.
177  */
178 
179 #include <string.h>
180 #include <stdbool.h>
181 #include <stdint.h>
182 #include "bs_types.h"
183 #include "bs_tracing.h"
184 #include "bs_utils.h"
185 #include "bs_pc_2G4.h"
186 #include "bs_pc_2G4_utils.h"
187 #include "bs_rand.h"
188 #include "NHW_common_types.h"
189 #include "NHW_config.h"
190 #include "NHW_peri_types.h"
191 #include "NHW_RADIO.h"
192 #include "NHW_RADIO_signals.h"
193 #include "NHW_RADIO_utils.h"
194 #include "NHW_RADIO_timings.h"
195 #include "NHW_RADIO_bitcounter.h"
196 #include "NHW_RADIO_priv.h"
197 #include "nsi_hw_scheduler.h"
198 #include "NHW_AES_CCM.h"
199 #include "irq_ctrl.h"
200 #include "NRF_HWLowL.h"
201 #include "crc.h"
202 #include "nsi_tasks.h"
203 #include "nsi_hws_models_if.h"
204 #include "weak_stubs.h"
205 
206 #if NHW_RADIO_TOTAL_INST > 1
207 #error "This model only supports 1 instance so far"
208 #endif
209 
210 NRF_RADIO_Type NRF_RADIO_regs;
211 
212 static bs_time_t Timer_RADIO = TIME_NEVER; //main radio timer
213 static bs_time_t Timer_RADIO_abort_reeval = TIME_NEVER; //Abort reevaluation response timer, this timer must have the lowest priority of all events (which may cause an abort)
214 
215 static TIFS_state_t TIFS_state = TIFS_DISABLE;
216 static bool TIFS_ToTxNotRx = false; //Are we in a TIFS automatically starting a Tx from a Rx (true), or Rx from Tx (false)
217 static bs_time_t Timer_TIFS = TIME_NEVER;
218 static bool from_hw_tifs = false; /* Unfortunate hack due to the SW racing the HW to clear SHORTS*/
219 
220 static RADIO_Rx_status_t rx_status;
221 static RADIO_Tx_status_t tx_status;
222 static RADIO_CCA_status_t cca_status;
223 
224 static double bits_per_us; //Bits per us for the ongoing Tx or Rx
225 
226 static bs_time_t next_recheck_time; // when we asked the phy to recheck (in our own time) next time
227 static abort_state_t abort_fsm_state = No_pending_abort_reeval; //This variable shall be set to Tx/Rx_Abort_reeval when the phy is waiting for an abort response (and in no other circumstance)
228 static int aborting_set = 0; //If set, we will abort the current Tx/Rx/CCA at the next abort reevaluation
229 
230 static nrfra_state_t radio_state;
231 static nrfra_sub_state_t radio_sub_state;
232 
233 static uint8_t tx_buf[_NRF_MAX_PACKET_SIZE]; //starting from the header, and including CRC
234 static uint8_t rx_buf[_NRF_MAX_PACKET_SIZE]; // "
235 static uint8_t *rx_pkt_buffer_ptr = (uint8_t*)&rx_buf;
236 
237 #if !NHW_RADIO_IS_54
238 static bool radio_POWER = false;
239 #endif
240 
241 static bool rssi_sampling_on = false;
242 
243 static double cheat_rx_power_offset;
244 
245 static void start_Tx(void);
246 static void start_Tx_FEC2(void);
247 static void start_Rx(void);
248 static void start_Rx_FEC2(void);
249 static void start_CCA_ED(bool CCA_not_ED);
250 static void Rx_Addr_received(void);
251 static void Tx_abort_eval_respond(void);
252 static void Rx_abort_eval_respond(void);
253 static void CCA_abort_eval_respond(void);
254 static void nhw_radio_device_address_match(uint8_t rx_buf[]);
255 
radio_reset(void)256 static void radio_reset(void) {
257   memset(&NRF_RADIO_regs, 0, sizeof(NRF_RADIO_regs));
258   radio_state = RAD_DISABLED;
259   radio_sub_state = SUB_STATE_INVALID;
260   Timer_RADIO = TIME_NEVER;
261   rssi_sampling_on = false;
262 
263   TIFS_state = TIFS_DISABLE;
264   TIFS_ToTxNotRx = false;
265   Timer_TIFS = TIME_NEVER;
266 
267   //Registers' reset values:
268   NRF_RADIO_regs.FREQUENCY = 0x00000002;
269   NRF_RADIO_regs.SFD = 0xA7;
270   NRF_RADIO_regs.CCACTRL = 0x052D0000;
271   NRF_RADIO_regs.CTEINLINECONF = 0x00002800;
272   NRF_RADIO_regs.DFECTRL1 = 0x00023282;
273   for (int i = 0; i < 8; i++)
274     NRF_RADIO_regs.PSEL.DFEGPIO[i] = 0xFFFFFFFF;
275   NRF_RADIO_regs.DFEPACKET.MAXCNT = 0x00001000;
276 
277 #if defined(NRF54L15) && !defined(RADIO_DATAWHITEIV_DATAWHITEIV_Msk)
278   NRF_RADIO_regs.DATAWHITE = 0x00890040;
279 #else
280   NRF_RADIO_regs.DATAWHITEIV = 0x00000040;
281 #endif
282 
283 #if !NHW_RADIO_IS_54
284   NRF_RADIO_regs.MODECNF0 = 0x00000200;
285   NRF_RADIO_regs.POWER = 1;
286 #else
287   NRF_RADIO_regs.EDCTRL = 0x20000000;
288   NRF_RADIO_regs.TXPOWER = 0x00000013;
289 #endif
290 
291   nhwra_signalif_reset();
292 }
293 
nhw_radio_init(void)294 static void nhw_radio_init(void) {
295   nrfra_timings_init();
296   radio_reset();
297 #if !NHW_RADIO_IS_54
298   radio_POWER = false;
299 #endif
300   bits_per_us = 1;
301 }
302 
303 NSI_TASK(nhw_radio_init, HW_INIT, 100);
304 
nhw_radio_get_bpus(void)305 double nhw_radio_get_bpus(void) {
306   return bits_per_us;
307 }
308 
nhwra_set_Timer_RADIO(bs_time_t t)309 static inline void nhwra_set_Timer_RADIO(bs_time_t t){
310   Timer_RADIO = t;
311   nsi_hws_find_next_event();
312 }
313 
nhwra_set_Timer_abort_reeval(bs_time_t t)314 static inline void nhwra_set_Timer_abort_reeval(bs_time_t t){
315   Timer_RADIO_abort_reeval = t;
316   nsi_hws_find_next_event();
317 }
318 
nhw_RADIO_TASK_TXEN(void)319 void nhw_RADIO_TASK_TXEN(void) {
320   if ( ( radio_state != RAD_DISABLED )
321       && ( radio_state != RAD_TXIDLE )
322       && ( radio_state != RAD_RXIDLE ) ){
323     bs_trace_warning_line_time(
324         "NRF_RADIO: TXEN received when the radio was not DISABLED or TX/RXIDLE but in state %i. It will be ignored. Expect problems\n",
325         radio_state);
326     return;
327   }
328   radio_state = RAD_TXRU;
329   NRF_RADIO_regs.STATE = RAD_TXRU;
330 
331   nhwra_set_Timer_RADIO(nsi_hws_get_time() + nhwra_timings_get_rampup_time(1, from_hw_tifs));
332 }
333 
nhw_RADIO_TASK_RXEN(void)334 void nhw_RADIO_TASK_RXEN(void) {
335   if ( ( radio_state != RAD_DISABLED )
336       && ( radio_state != RAD_TXIDLE )
337       && ( radio_state != RAD_RXIDLE ) ){
338     bs_trace_warning_line_time(
339         "NRF_RADIO: RXEN received when the radio was not DISABLED or TX/RXIDLE but in state %i. It will be ignored. Expect problems\n",
340         radio_state);
341     return;
342   }
343   TIFS_state = TIFS_DISABLE;
344   radio_state = RAD_RXRU;
345   NRF_RADIO_regs.STATE = RAD_RXRU;
346   nhwra_set_Timer_RADIO(nsi_hws_get_time() + nhwra_timings_get_rampup_time(0, from_hw_tifs));
347 }
348 
abort_if_needed(void)349 static void abort_if_needed(void) {
350   if ( ( abort_fsm_state == Tx_Abort_reeval )
351       || ( abort_fsm_state == Rx_Abort_reeval )
352       || ( abort_fsm_state == CCA_Abort_reeval ) ){
353     //If the phy is waiting for a response from us, we need to tell it, that we are aborting whatever it was doing
354     aborting_set = 1;
355   }
356   /* Note: In Rx, we may be
357    *   waiting to respond to the Phy to an abort reevaluation request abort_fsm_state == Rx_Abort_reeval
358    *   or waiting to reach the address end time to respond to the Phy if we accepted the packet or not
359    * but not both
360    */
361   if ( radio_sub_state == RX_WAIT_FOR_ADDRESS_END ){
362     //we answer immediately to the phy rejecting the packet
363     p2G4_dev_rxv2_cont_after_addr_nc_b(false, NULL);
364     radio_sub_state = SUB_STATE_INVALID;
365   }
366 }
367 
nhw_RADIO_TASK_START(void)368 void nhw_RADIO_TASK_START(void) {
369   if ( radio_state == RAD_TXIDLE ) {
370     bs_time_t Tx_start_time = nsi_hws_get_time() + nhwra_timings_get_TX_chain_delay();
371     radio_state = RAD_TXSTARTING;
372     NRF_RADIO_regs.STATE = RAD_TX;
373     nhwra_set_Timer_RADIO(Tx_start_time);
374   } else if ( radio_state == RAD_RXIDLE ) {
375     start_Rx();
376   } else {
377     bs_trace_warning_line_time(
378         "NRF_RADIO: TASK_START received while the radio was not in either TXIDLE or RXIDLE but in state %i. It will be ignored => expect problems\n",
379         radio_state);
380   }
381 }
382 
nhw_RADIO_TASK_SOFTRESET(void)383 void nhw_RADIO_TASK_SOFTRESET(void) {
384   if (radio_state != RAD_DISABLED) {
385     bs_trace_warning_line_time(
386         "NRF_RADIO: TASK_SOFTRESET should only be used in disabled state."
387         "Current state %i\n", radio_state);
388   }
389   bs_trace_warning_line_time(
390       "NRF_RADIO: SOFTRESET not yet supported. It will be ignored.\n");
391 }
392 
nhw_RADIO_TASK_CCASTART(void)393 void nhw_RADIO_TASK_CCASTART(void) {
394   if ((radio_state != RAD_RXIDLE)){
395     bs_trace_warning_line_time(
396         "NRF_RADIO: CCASTART received when the radio was not RXIDLE but in state %i. "
397         "It will be ignored. Expect problems\n",
398         radio_state);
399     return;
400   }
401   start_CCA_ED(1);
402 }
403 
nhw_RADIO_TASK_CCASTOP(void)404 void nhw_RADIO_TASK_CCASTOP(void) {
405   if (( radio_state == RAD_CCA_ED ) && ( cca_status.CCA_notED )) {
406     abort_if_needed();
407     radio_state = RAD_RXIDLE;
408     NRF_RADIO_regs.STATE = RAD_RXIDLE;
409     nhwra_set_Timer_RADIO(TIME_NEVER);
410     nhw_RADIO_signal_EVENTS_CCASTOPPED(0);
411   } else {
412     bs_trace_info_line_time(3,
413         "NRF_RADIO: TASK_CCASTOP received while the radio was not on a CCA procedure (was %i, %i). "
414         "It will be ignored\n",
415         radio_state, cca_status.CCA_notED);
416   }
417 }
418 
nhw_RADIO_TASK_EDSTART(void)419 void nhw_RADIO_TASK_EDSTART(void) {
420   if ((radio_state != RAD_RXIDLE)){
421     bs_trace_warning_line_time(
422         "NRF_RADIO: EDSTART received when the radio was not RXIDLE but in state %i. "
423         "It will be ignored. Expect problems\n",
424         radio_state);
425     return;
426   }
427   start_CCA_ED(0);
428 }
429 
nhw_RADIO_TASK_EDSTOP(void)430 void nhw_RADIO_TASK_EDSTOP(void) {
431   if (( radio_state == RAD_CCA_ED ) && ( cca_status.CCA_notED == 0)) {
432     abort_if_needed();
433     radio_state = RAD_RXIDLE;
434     NRF_RADIO_regs.STATE = RAD_RXIDLE;
435     nhwra_set_Timer_RADIO(TIME_NEVER);
436     nhw_RADIO_signal_EVENTS_EDSTOPPED(0);
437   } else {
438     bs_trace_info_line_time(3,
439         "NRF_RADIO: TASK_EDSTOP received while the radio was not on a ED procedure (was %i, %i). "
440         "It will be ignored\n",
441         radio_state, cca_status.CCA_notED);
442   }
443 }
444 
nhw_RADIO_TASK_STOP(void)445 void nhw_RADIO_TASK_STOP(void) {
446   nhw_radio_stop_bit_counter();
447 
448   if ((radio_state == RAD_TX) || (radio_state == RAD_TXSTARTING)) {
449     if (radio_state == RAD_TX) {
450       abort_if_needed();
451     }
452     radio_state = RAD_TXIDLE;
453     NRF_RADIO_regs.STATE = RAD_TXIDLE;
454     nhwra_set_Timer_RADIO(TIME_NEVER);
455   } else if ( radio_state == RAD_RX ){
456     abort_if_needed();
457     radio_state = RAD_RXIDLE;
458     NRF_RADIO_regs.STATE = RAD_RXIDLE;
459     nhwra_set_Timer_RADIO(TIME_NEVER);
460   } else if ( radio_state == RAD_CCA_ED ){
461     //The documentation is not clear about what happens if we get a STOP during a CCA or ED procedure,
462     //but it seems for CCA it can cause a bit of a mess depending on CCA mode.
463     //the behavior here is that we stop just like if it was an active Rx, and do *not* trigger a CCASTOPPED or EDSTOPPED event
464     bs_trace_warning_line_time(
465         "NRF_RADIO: TASK_STOP received while the radio was performing a CCA or ED procedure. "
466         "In this models we stop the procedure, but this can cause a mess in real HW\n");
467     abort_if_needed();
468     radio_state = RAD_RXIDLE;
469     NRF_RADIO_regs.STATE = RAD_RXIDLE;
470     nhwra_set_Timer_RADIO(TIME_NEVER);
471   } else {
472     bs_trace_warning_line_time(
473         "NRF_RADIO: TASK_STOP received while the radio was not on either TX or RX but in state %i. "
474         "It will be ignored\n",
475         radio_state);
476   }
477 }
478 
nhw_RADIO_TASK_DISABLE(void)479 void nhw_RADIO_TASK_DISABLE(void) {
480   nhw_radio_stop_bit_counter();
481 
482   if ((radio_state == RAD_TX) || (radio_state == RAD_TXSTARTING)) {
483     if (radio_state == RAD_TX) {
484       abort_if_needed();
485     }
486     radio_state = RAD_TXIDLE; //Momentary (will be changed in the if below)
487     NRF_RADIO_regs.STATE = RAD_TXIDLE;
488   } else if ( radio_state == RAD_RX ){
489     abort_if_needed();
490     radio_state = RAD_RXIDLE; //Momentary (will be changed in the if below)
491     NRF_RADIO_regs.STATE = RAD_RXIDLE;
492   } else if ( radio_state == RAD_CCA_ED ){
493     //The documentation is not clear about what happens if we get a disable during a CCA  or ED procedure,
494     //the assumption here is that we stop just like if it was an active Rx, but do not trigger a CCASTOPPED or EDSTOPPED event
495     abort_if_needed();
496     radio_state = RAD_RXIDLE; //Momentary (will be changed in the if below)
497     NRF_RADIO_regs.STATE = RAD_RXIDLE;
498   }
499 
500   if (TIFS_state != TIFS_DISABLE) {
501     TIFS_state = TIFS_DISABLE;
502     nhwra_set_Timer_RADIO(TIME_NEVER);
503     Timer_TIFS = TIME_NEVER;
504   }
505 
506   if ( ( radio_state == RAD_TXRU ) || ( radio_state == RAD_TXIDLE ) ) {
507     radio_state = RAD_TXDISABLE;
508     NRF_RADIO_regs.STATE = RAD_TXDISABLE;
509     TIFS_state = TIFS_DISABLE;
510     nhwra_set_Timer_RADIO(nsi_hws_get_time() + nhwra_timings_get_TX_rampdown_time());
511   } else if ( ( radio_state == RAD_RXRU ) || ( radio_state == RAD_RXIDLE ) ) {
512     radio_state = RAD_RXDISABLE;
513     NRF_RADIO_regs.STATE = RAD_RXDISABLE;
514     TIFS_state = TIFS_DISABLE;
515     nhwra_set_Timer_RADIO(nsi_hws_get_time() + nhwra_timings_get_RX_rampdown_time());
516   } else if ( radio_state == RAD_DISABLED ) {
517     //It seems the radio will also signal a DISABLED event even if it was already disabled
518     nhw_radio_stop_bit_counter();
519     nhw_RADIO_signal_EVENTS_DISABLED(0);
520   }
521 }
522 
nhw_RADIO_TASK_RSSISTART(void)523 void nhw_RADIO_TASK_RSSISTART(void) {
524   rssi_sampling_on = true;
525 }
526 
nhw_RADIO_TASK_RSSISTOP(void)527 void nhw_RADIO_TASK_RSSISTOP(void) {
528   rssi_sampling_on = false;
529 }
530 
531 #if !NHW_RADIO_IS_54
nhw_RADIO_regw_sideeffects_POWER(void)532 void nhw_RADIO_regw_sideeffects_POWER(void) {
533   if ( NRF_RADIO_regs.POWER == 0 ){
534     radio_POWER = false;
535   } else {
536     if ( radio_POWER == false ){
537       radio_POWER = true;
538       abort_if_needed();
539       radio_reset();
540       nsi_hws_find_next_event();
541     }
542   }
543 }
544 #endif
545 
546 /**
547  * This is a fake task meant to start a HW timer for the TX->RX or RX->TX TIFS
548  */
nhw_RADIO_fake_task_TRXEN_TIFS(void)549 void nhw_RADIO_fake_task_TRXEN_TIFS(void) {
550   if ( TIFS_state == TIFS_WAITING_FOR_DISABLE ) {
551     TIFS_state = TIFS_TRIGGERING_TRX_EN;
552     nhwra_set_Timer_RADIO(Timer_TIFS);
553     if ( Timer_RADIO < nsi_hws_get_time() ){
554       bs_trace_warning_line_time("NRF_RADIO: TIFS Ups: The Ramp down from Rx/Tx into a Tx/Rx takes more than the programmed TIFS time\n");
555     }
556   }
557 }
558 
559 /**
560  * If the HW automatic TIFS switch is enabled, prepare the internals for an automatic switch
561  * (when a fake_task_TRXEN_TIFS is automatically triggered after a disable due to a shortcut)
562  * otherwise do nothing
563  *
564  * Input Tx_Not_Rx: Are we finishing a Tx (true) or an Rx (false)
565  */
maybe_prepare_TIFS(bool Tx_Not_Rx)566 void maybe_prepare_TIFS(bool Tx_Not_Rx){
567   bs_time_t delta;
568   if ( !nhwra_is_HW_TIFS_enabled() ) {
569     TIFS_state = TIFS_DISABLE;
570     return;
571   }
572   if ( NRF_RADIO_regs.SHORTS & RADIO_SHORTS_DISABLED_TXEN_Msk ){
573     TIFS_ToTxNotRx = true;
574   } else {
575     TIFS_ToTxNotRx = false;
576   }
577 
578   if ( Tx_Not_Rx ){ //End of Tx
579     delta = NRF_RADIO_regs.TIFS + nhwra_timings_get_TX_chain_delay() - nhwra_timings_get_rampup_time(0, 1) - 3; /*open slightly earlier to have jitter margin*/
580   } else { //End of Rx
581     delta = NRF_RADIO_regs.TIFS - nhwra_timings_get_Rx_chain_delay() - nhwra_timings_get_TX_chain_delay() - nhwra_timings_get_rampup_time(1, 1) + 1;
582   }
583   Timer_TIFS = nsi_hws_get_time() + delta;
584   TIFS_state = TIFS_WAITING_FOR_DISABLE; /* In Timer_TIFS we will trigger a TxEN or RxEN */
585 }
586 
maybe_signal_event_RATEBOOST(void)587 static void maybe_signal_event_RATEBOOST(void) {
588   if (rx_status.CI == 1) {
589     nhw_RADIO_signal_EVENTS_RATEBOOST(0);
590   }
591 }
592 
593 /**
594  * The main radio timer (Timer_RADIO) has just triggered,
595  * continue whatever activity we are on
596  * (typically do something at the end/start of a state, set the new state
597  * and schedule further the next state change)
598  */
nhw_radio_timer_triggered(void)599 static void nhw_radio_timer_triggered(void) {
600   if ( radio_state == RAD_TXRU ){
601     radio_state = RAD_TXIDLE;
602     NRF_RADIO_regs.STATE = RAD_TXIDLE;
603     nhwra_set_Timer_RADIO(TIME_NEVER);
604     nhw_RADIO_signal_EVENTS_READY(0);
605     nhw_RADIO_signal_EVENTS_TXREADY(0);
606   } else if ( radio_state == RAD_RXRU ){
607     radio_state = RAD_RXIDLE;
608     NRF_RADIO_regs.STATE = RAD_RXIDLE;
609     nhwra_set_Timer_RADIO(TIME_NEVER);
610     nhw_RADIO_signal_EVENTS_READY(0);
611     nhw_RADIO_signal_EVENTS_RXREADY(0);
612   } else if ( radio_state == RAD_TXSTARTING ){
613     nhwra_set_Timer_RADIO(TIME_NEVER);
614     start_Tx();
615   } else if ( radio_state == RAD_TX ){
616     if ( radio_sub_state == TX_WAIT_FOR_ADDRESS_END ){
617       if (tx_status.codedphy) {
618         radio_sub_state = TX_WAIT_FOR_FEC1_END;
619         nhwra_set_Timer_RADIO(tx_status.FEC2_start_time);
620       } else {
621         radio_sub_state = TX_WAIT_FOR_PAYLOAD_END;
622         nhwra_set_Timer_RADIO(tx_status.PAYLOAD_end_time);
623       }
624       nhw_RADIO_signal_EVENTS_ADDRESS(0);
625       nhw_RADIO_signal_EVENTS_FRAMESTART(0); //See note on FRAMESTART
626     } else if ( radio_sub_state == TX_WAIT_FOR_FEC1_END ) {
627       start_Tx_FEC2();
628       radio_sub_state = TX_WAIT_FOR_PAYLOAD_END;
629       nhwra_set_Timer_RADIO(tx_status.PAYLOAD_end_time);
630     } else if ( radio_sub_state == TX_WAIT_FOR_PAYLOAD_END ) {
631       radio_sub_state = TX_WAIT_FOR_CRC_END;
632       nhwra_set_Timer_RADIO(tx_status.CRC_end_time);
633       nhw_RADIO_signal_EVENTS_PAYLOAD(0);
634     } else if ( radio_sub_state == TX_WAIT_FOR_CRC_END ) {
635       radio_sub_state = SUB_STATE_INVALID;
636       radio_state = RAD_TXIDLE;
637       NRF_RADIO_regs.STATE = RAD_TXIDLE;
638       nhwra_set_Timer_RADIO(TIME_NEVER);
639       nhw_radio_stop_bit_counter();
640       nhw_RADIO_signal_EVENTS_END(0);
641       nhw_RADIO_signal_EVENTS_PHYEND(0); //See note on EVENTS_PHYEND
642       maybe_prepare_TIFS(true);
643     }  else { //SUB_STATE_INVALID
644       bs_trace_error_time_line("programming error\n");
645     }
646   } else if ( radio_state == RAD_RX ){
647     if ( radio_sub_state == RX_WAIT_FOR_ADDRESS_END ) {
648       nhw_RADIO_signal_EVENTS_SYNC(0); //See note on EVENTS_SYNC
649       nhw_RADIO_signal_EVENTS_ADDRESS(0);
650       nhw_RADIO_signal_EVENTS_FRAMESTART(0); //See note on FRAMESTART
651       nhwra_set_Timer_RADIO(TIME_NEVER); //Provisionally clear the RADIO timer for the Rx cont.
652       Rx_Addr_received();
653       if (rx_status.codedphy) {
654         radio_sub_state = RX_WAIT_FOR_FEC1_END;
655         // The timer will be set once we get the Phy FEC1 end response
656       } else {
657         radio_sub_state = RX_WAIT_FOR_PAYLOAD_END;
658         nhwra_set_Timer_RADIO(rx_status.PAYLOAD_End_Time);
659       }
660     } else if ( radio_sub_state == RX_WAIT_FOR_FEC1_END ) {
661       maybe_signal_event_RATEBOOST();
662       /* The next state transition will be programmed when we get the Phy response for the FEC2 */
663       nhwra_set_Timer_RADIO(TIME_NEVER);
664       start_Rx_FEC2();
665     } else if ( radio_sub_state == RX_WAIT_FOR_PAYLOAD_END ) {
666       radio_sub_state = RX_WAIT_FOR_CRC_END;
667       nhwra_set_Timer_RADIO(rx_status.CRC_End_Time);
668       nhw_RADIO_signal_EVENTS_PAYLOAD(0);
669     } else if ( radio_sub_state == RX_WAIT_FOR_CRC_END ) {
670 #if !NHW_RADIO_IS_54
671       //TODO Reconnect as soon as the CCM model is in
672       nhw_ccm_radio_received_packet(!rx_status.CRC_OK);
673 #endif
674       radio_sub_state = SUB_STATE_INVALID;
675       radio_state = RAD_RXIDLE;
676       NRF_RADIO_regs.STATE = RAD_RXIDLE;
677       nhwra_set_Timer_RADIO(TIME_NEVER);
678       if ( rx_status.CRC_OK ) {
679         nhw_RADIO_signal_EVENTS_CRCOK(0);
680       } else {
681         nhw_RADIO_signal_EVENTS_CRCERROR(0);
682       }
683       nhw_radio_stop_bit_counter();
684       nhw_RADIO_signal_EVENTS_PHYEND(0); //See note on EVENTS_PHYEND
685       nhw_RADIO_signal_EVENTS_END(0);
686       maybe_prepare_TIFS(false);
687     } else { //SUB_STATE_INVALID
688       bs_trace_error_time_line("programming error\n");
689     }
690   } else if ( radio_state == RAD_CCA_ED ){
691     radio_state = RAD_RXIDLE;
692     NRF_RADIO_regs.STATE = RAD_RXIDLE;
693     nhwra_set_Timer_RADIO(TIME_NEVER);
694     if (cca_status.CCA_notED) { //CCA procedure ended
695       if (cca_status.is_busy) {
696         nhw_RADIO_signal_EVENTS_CCABUSY(0);
697       } else {
698         nhw_RADIO_signal_EVENTS_CCAIDLE(0);
699       }
700     } else { //ED procedure ended
701       nhw_RADIO_signal_EVENTS_EDEND(0);
702     }
703   } else if ( radio_state == RAD_TXDISABLE ){
704     radio_state = RAD_DISABLED;
705     NRF_RADIO_regs.STATE = RAD_DISABLED;
706     nhwra_set_Timer_RADIO(TIME_NEVER);
707     nhw_radio_stop_bit_counter();
708     nhw_RADIO_signal_EVENTS_DISABLED(0);
709   } else if ( radio_state == RAD_RXDISABLE ){
710     radio_state = RAD_DISABLED;
711     NRF_RADIO_regs.STATE = RAD_DISABLED;
712     nhwra_set_Timer_RADIO(TIME_NEVER);
713     nhw_radio_stop_bit_counter();
714     nhw_RADIO_signal_EVENTS_DISABLED(0);
715   } else {
716     if ( ( radio_state == RAD_DISABLED ) && ( TIFS_state == TIFS_TRIGGERING_TRX_EN ) ) {
717       if ( Timer_RADIO != Timer_TIFS ){
718         bs_trace_warning_line_time("NRF_RADIO: TIFS Ups 3\n");
719       }
720       TIFS_state = TIFS_DISABLE;
721       nhwra_set_Timer_RADIO(TIME_NEVER);
722       from_hw_tifs = true;
723       if ( TIFS_ToTxNotRx ) {
724         nhw_RADIO_TASK_TXEN();
725       } else {
726         nhw_RADIO_TASK_RXEN();
727       }
728       from_hw_tifs = false;
729     } else {
730       bs_trace_error_line_time(
731           "NRF_RADIO: this should not have happened (radio_state =%i)\n",
732           radio_state);
733     }
734   }
735 }
736 
737 NSI_HW_EVENT(Timer_RADIO, nhw_radio_timer_triggered, 990 /*We want the radio to be one of the very last to avoid unnecessary abort re-evaluations to the Phy*/);
738 
739 /**
740  * The abort reevaluation timer has just triggered,
741  * => we can now respond to the Phy with our abort decision
742  */
nhw_radio_timer_abort_reeval_triggered(void)743 static void nhw_radio_timer_abort_reeval_triggered(void) {
744   nhwra_set_Timer_abort_reeval(TIME_NEVER);
745 
746   if ( abort_fsm_state == Tx_Abort_reeval ){
747     abort_fsm_state = No_pending_abort_reeval;
748     Tx_abort_eval_respond();
749   } else if ( abort_fsm_state == Rx_Abort_reeval ) {
750     abort_fsm_state = No_pending_abort_reeval;
751     Rx_abort_eval_respond();
752   } else if ( abort_fsm_state == CCA_Abort_reeval ) {
753     abort_fsm_state = No_pending_abort_reeval;
754     CCA_abort_eval_respond();
755   } else {
756     bs_trace_error_line("The abort timer was left running.. somebody forgot to cleanup..\n");
757   }
758 }
759 
760 NSI_HW_EVENT(Timer_RADIO_abort_reeval, nhw_radio_timer_abort_reeval_triggered, 999 /* Purposely the last (all other events must have been evaluated before) */);
761 
762 /**
763  * Handle all possible responses to a Tx request from the Phy
764  */
handle_Tx_response(int ret)765 static void handle_Tx_response(int ret){
766   if (ret == -1){
767     bs_trace_raw_manual_time(3, nsi_hws_get_time(),"The phy disconnected us during a Tx\n");
768     hwll_disconnect_phy_and_exit();
769   } else if (ret == P2G4_MSG_TX_END) {
770     bs_time_t end_time = hwll_dev_time_from_phy(tx_status.tx_resp.end_time);
771     phy_sync_ctrl_set_last_phy_sync_time(end_time);
772     //The main machine was already pre-programmed at the Tx Start, no need to do anything else now
773   } else if ( ret == P2G4_MSG_ABORTREEVAL ) {
774     phy_sync_ctrl_set_last_phy_sync_time( next_recheck_time );
775     abort_fsm_state = Tx_Abort_reeval;
776     nhwra_set_Timer_abort_reeval(next_recheck_time);
777   }
778 }
779 
780 /**
781  * Set the Phy abort structure to the next time we will want to either abort or have a recheck
782  * And store in next_recheck_time the next recheck time
783  */
update_abort_struct(p2G4_abort_t * abort,bs_time_t * next_recheck_time)784 static void update_abort_struct(p2G4_abort_t *abort, bs_time_t *next_recheck_time){
785   //We will want to recheck next time anything may decide to stop the radio, that can be SW or HW
786   //The only logical way to do so is to set it to the next timer whatever it may be as many can trigger SW interrupts
787   *next_recheck_time = nsi_hws_get_next_event_time();
788   abort->recheck_time = hwll_phy_time_from_dev(*next_recheck_time);
789 
790   //We either have decided already we want to abort so we do it right now
791   //or we have not decided yet
792   if ( aborting_set == 1 ) {
793     aborting_set = 0; //By returning nsi_hws_get_time(), we are aborting right now
794     abort->abort_time = hwll_phy_time_from_dev(nsi_hws_get_time());
795   } else {
796     abort->abort_time = TIME_NEVER;
797   }
798 }
799 
800 /**
801  * We have reached the time in which we wanted to reevaluate if we would abort or not
802  * so we answer to the Phy with our decision
803  */
Tx_abort_eval_respond(void)804 static void Tx_abort_eval_respond(void) {
805   //The abort must have been evaluated by now so we can respond to the waiting phy
806   p2G4_abort_t *abort = &tx_status.tx_req.abort;
807 
808   update_abort_struct(abort, &next_recheck_time);
809 
810   int ret = p2G4_dev_provide_new_tx_abort_nc_b(abort);
811 
812   handle_Tx_response(ret);
813 }
814 
815 /*
816  * Actually start the Tx in this microsecond (+ the Tx chain delay in the Phy)
817  * (For coded phy, starts the FEC1 Tx itself, and prepares the FEC2 to be started later by start_Tx_FEC2() )
818  */
start_Tx(void)819 static void start_Tx(void) {
820 
821   radio_state = RAD_TX;
822   NRF_RADIO_regs.STATE = RAD_TX;
823 
824   nhwra_check_packet_conf();
825 
826   //TOLOW: Add support for other packet formats and bitrates
827   uint8_t preamble_len = 0;
828   uint8_t address_len = 0;
829   uint8_t header_len = 0;
830   uint payload_len = 0;
831   uint8_t crc_len = nhwra_get_crc_length();
832   uint8_t CI = 0;
833   uint8_t main_packet_coding_rate = 0;
834 
835   tx_status.codedphy = false;
836 
837   if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_1Mbit) {
838     preamble_len = 1; //1 byte
839     address_len = 4;
840     header_len  = 2;
841     bits_per_us = 1;
842   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_2Mbit) {
843     preamble_len = 2; //2 bytes
844     address_len = 4;
845     header_len  = 2;
846     bits_per_us = 2;
847   } else if ((NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_LR125Kbit)
848             || (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_LR500Kbit)) {
849     tx_status.codedphy = true;
850     address_len = 4;
851     header_len  = 2;
852     if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_LR125Kbit) {
853       bits_per_us = 0.125;
854       CI = 0; //0b00
855       main_packet_coding_rate = 8;
856     } else { /* RADIO_MODE_MODE_Ble_LR500Kbit */
857       bits_per_us = 0.5;
858       CI = 1; //0b01
859       main_packet_coding_rate = 2;
860     }
861   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
862     preamble_len = 4;
863     address_len = 1;
864     header_len  = 1;
865     bits_per_us = 0.25;
866   }
867 
868   payload_len = nhwra_tx_copy_payload(tx_buf);
869 
870   /* This code should be generalized to support any CRC configuration (CRCCNF, CRCINIT AND CRCPOLY)
871    * When doing so, we should still calculate the ble and 154 crc's with their optimized table implementations
872    * Here we just assume the CRC is configured as it should given the modulation */
873   uint32_t crc_init = NRF_RADIO_regs.CRCINIT & RADIO_CRCINIT_CRCINIT_Msk;
874   if (nhwra_is_ble_mode(NRF_RADIO_regs.MODE)) {
875     append_crc_ble(tx_buf, header_len + payload_len, crc_init);
876   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
877     //15.4 does not CRC the length (header) field
878     append_crc_154(&tx_buf[header_len], payload_len, crc_init);
879   }
880 
881   uint main_packet_size; //Main "packet" size (the payload sent thru the phy)
882   bs_time_t packet_duration = 0; //Main packet duration (from preamble to CRC except for codedPhy which is just the FEC2)
883 
884   if (!tx_status.codedphy) {
885     packet_duration = preamble_len*8 + address_len*8;
886   } else {
887     packet_duration = 3; //TERM2
888   }
889   packet_duration += header_len*8 + payload_len*8 + crc_len*8;
890   packet_duration /= bits_per_us;
891   main_packet_size = header_len + payload_len + crc_len;
892 
893   bs_time_t payload_start_time;
894   bs_time_t main_packet_start_time;
895 
896   if (tx_status.codedphy) {
897     tx_status.ADDRESS_end_time = nsi_hws_get_time() + (bs_time_t)(80 + 256 - nhwra_timings_get_TX_chain_delay());
898     payload_start_time = tx_status.ADDRESS_end_time + 16 + 24;/* CI = 16us; TERM1= 24us */
899 
900     bs_time_t fec1_duration = 80 + 256 + 16 + 24;
901 
902     nhwra_prep_tx_request(&tx_status.tx_req_fec1, 1, fec1_duration, hwll_phy_time_from_dev(nsi_hws_get_time()), 8);
903     update_abort_struct(&tx_status.tx_req_fec1.abort, &next_recheck_time);
904     main_packet_start_time = tx_status.tx_req_fec1.end_tx_time + 1;
905     tx_status.FEC2_start_time = main_packet_start_time; /* in air */
906   } else {
907     tx_status.ADDRESS_end_time = nsi_hws_get_time() + (bs_time_t)((preamble_len*8 + address_len*8)/bits_per_us) - nhwra_timings_get_TX_chain_delay();
908     payload_start_time = tx_status.ADDRESS_end_time;
909     main_packet_start_time = hwll_phy_time_from_dev(nsi_hws_get_time());
910   }
911   tx_status.PAYLOAD_end_time = payload_start_time + (bs_time_t)(8*(header_len + payload_len)/bits_per_us);
912   tx_status.CRC_end_time = tx_status.PAYLOAD_end_time + (bs_time_t)(crc_len*8/bits_per_us);
913 
914   nhwra_prep_tx_request(&tx_status.tx_req, main_packet_size, packet_duration,
915                         main_packet_start_time, main_packet_coding_rate);
916   update_abort_struct(&tx_status.tx_req.abort, &next_recheck_time);
917 
918   int ret;
919   if (tx_status.codedphy) {
920     //Request the FEC1 Tx from the Phy:
921     ret = p2G4_dev_req_txv2_nc_b(&tx_status.tx_req_fec1, &CI, &tx_status.tx_resp);
922   } else { /* not codedphy */
923     //Request the Tx from the Phy:
924     ret = p2G4_dev_req_txv2_nc_b(&tx_status.tx_req, tx_buf, &tx_status.tx_resp);
925   }
926   handle_Tx_response(ret);
927 
928   radio_sub_state = TX_WAIT_FOR_ADDRESS_END;
929   nhwra_set_Timer_RADIO(tx_status.ADDRESS_end_time);
930 }
931 
start_Tx_FEC2(void)932 static void start_Tx_FEC2(void) {
933   int ret;
934   update_abort_struct(&tx_status.tx_req.abort, &next_recheck_time);
935   tx_status.tx_req.phy_address = 0; /* An invalid address */
936   ret = p2G4_dev_req_txv2_nc_b(&tx_status.tx_req, tx_buf, &tx_status.tx_resp);
937   handle_Tx_response(ret);
938 }
939 
Rx_handle_CI_reception(void)940 static void Rx_handle_CI_reception(void) {
941   rx_status.CI = rx_buf[0] & 0x3;
942 
943   if ((rx_status.rx_resp.packet_size < 1) || (rx_status.CI > 1)) {
944     bs_trace_warning_time_line("%s: Received supposed BLE CodedPhy FEC1 without CI or corrupted CI (%i, %i)\n",
945         __func__, rx_status.rx_resp.packet_size, rx_status.CI);
946   }
947 
948   NRF_RADIO_regs.PDUSTAT |= (rx_status.CI << RADIO_PDUSTAT_CISTAT_Pos) & RADIO_PDUSTAT_CISTAT_Msk;
949 
950   if (rx_status.rx_resp.status != P2G4_RXSTATUS_OK) {
951     /* Error during CI decoding, we don't know how many bits, and if it would have recovered.
952      * So, we just do a 50% drop of having each CI bit corrupted or not */
953     int error;
954     error = bs_random_Bern(RAND_PROB_1/2);
955     NRF_RADIO_regs.PDUSTAT ^= error << (RADIO_PDUSTAT_CISTAT_Pos + 1); /* The don't-care bit in CI */
956 
957     error = bs_random_Bern(RAND_PROB_1/2);
958     if (error) {
959       NRF_RADIO_regs.PDUSTAT ^= 1 << (RADIO_PDUSTAT_CISTAT_Pos);
960       rx_status.CI ^= 1;
961       rx_status.CI_error = true;
962     }
963   }
964 }
965 
Rx_handle_end_response(bs_time_t end_time)966 static void Rx_handle_end_response(bs_time_t end_time) {
967 
968   if (rx_status.inFEC1 == true) { /* End of CodedPhy packet FEC1 */
969     Rx_handle_CI_reception();
970 
971   } else { //Normal packet or end of FEC2
972     if (rx_status.rx_resp.status != P2G4_RXSTATUS_HEADER_ERROR) {
973         rx_status.CRC_End_Time = end_time + nhwra_timings_get_Rx_chain_delay();
974     } //Otherwise we do not really now how the Nordic RADIO behaves depending on
975     //where the biterrors are and so forth. So let's always behave like if the
976     //packet lenght was received correctly, and just report a CRC error at the
977     //end of the CRC
978 
979     if ( rx_status.rx_resp.status == P2G4_RXSTATUS_OK ){
980       NRF_RADIO_regs.RXCRC = nhwra_get_rx_crc_value(rx_buf, rx_status.rx_resp.packet_size);
981       rx_status.CRC_OK = 1;
982       NRF_RADIO_regs.CRCSTATUS = 1;
983     }
984   }
985 }
986 
987 
Rx_handle_address_end_response(bs_time_t address_time)988 static void Rx_handle_address_end_response(bs_time_t address_time) {
989 
990   rx_status.ADDRESS_End_Time = address_time + nhwra_timings_get_Rx_chain_delay();
991 
992   if ((rx_status.codedphy == true) && (rx_status.inFEC1)) {
993     rx_status.FEC2_start_time = address_time + 16 + 24 + 1; /* It will be updated on the FEC1 Rx end */
994     rx_status.packet_rejected = false; //We always accept the FEC1 part
995 
996     //Let's set a very provisional packet end time, in case the Tx aborts between FEC1 and FEC2:
997     rx_status.PAYLOAD_End_Time = rx_status.FEC2_start_time + 2*8/bits_per_us
998                                 + nhwra_timings_get_Rx_chain_delay(); /* An empty packet */
999     rx_status.CRC_End_Time = rx_status.PAYLOAD_End_Time + rx_status.CRC_duration;
1000     return;
1001   }
1002   //Otherwise, FEC2 or not Coded Phy
1003 
1004   uint length = nhwra_get_payload_length(rx_buf);
1005   uint max_length = nhwra_get_MAXLEN();
1006 
1007   if (length > max_length) {
1008     // We reject the packet right away, setting the CRC error, and timers as expected
1009     bs_trace_warning_time_line("NRF_RADIO: received a packet longer than the configured MAXLEN (%i>%i). Truncating it\n", length, max_length);
1010     length  = max_length;
1011     NRF_RADIO_regs.PDUSTAT |= RADIO_PDUSTAT_PDUSTAT_Msk;
1012     rx_status.packet_rejected = true;
1013   } else {
1014     rx_status.packet_rejected = false;
1015   }
1016   if (rx_status.CI_error) {
1017     /* Let's just stop the Phy reception here, as continuing does not give us anything anymore */
1018     rx_status.packet_rejected = true;
1019   }
1020 
1021   //TODO: Discard Ieee802154_250Kbit frames with length == 0
1022 
1023   bs_time_t payload_end = 0;
1024 
1025   if (nhwra_is_ble_mode(NRF_RADIO_regs.MODE)) {
1026     payload_end = rx_status.rx_resp.rx_time_stamp + (bs_time_t)((2+length)*8/bits_per_us);
1027   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
1028     payload_end = rx_status.rx_resp.rx_time_stamp + (bs_time_t)((1+length)*8/bits_per_us);
1029   } //Eventually this should be generalized with the packet configuration
1030 
1031   rx_status.PAYLOAD_End_Time = nhwra_timings_get_Rx_chain_delay() +
1032                                hwll_dev_time_from_phy(payload_end);
1033 
1034   int TERM2_duration = 0; /* See Note21.b */
1035   if (rx_status.codedphy) {
1036     if (rx_status.CI == 1) {
1037       TERM2_duration = 6; //S=2 * 3bits
1038     } else {
1039       TERM2_duration = 24;//S=8 * 3bits
1040     }
1041   }
1042 
1043   rx_status.CRC_End_Time = rx_status.PAYLOAD_End_Time + rx_status.CRC_duration + TERM2_duration; //Provisional value (if we are accepting the packet)
1044 
1045   //Copy the whole packet (S0, lenght, S1 & payload) excluding the CRC.
1046   if (nhwra_is_ble_mode(NRF_RADIO_regs.MODE)) {
1047     if (rx_status.rx_resp.packet_size >= 5) { /*At least the header and CRC, otherwise better to not try to copy it*/
1048       ((uint8_t*)NRF_RADIO_regs.PACKETPTR)[0] = rx_buf[0];
1049       ((uint8_t*)NRF_RADIO_regs.PACKETPTR)[1] = rx_buf[1];
1050       /* We cheat a bit and copy the whole packet already (The AAR block will look in Adv packets after 64 bits)*/
1051       memcpy(&((uint8_t*)NRF_RADIO_regs.PACKETPTR)[2 + rx_status.S1Offset],
1052           &rx_buf[2] , length);
1053     }
1054   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
1055     if (rx_status.rx_resp.packet_size >= 3) { /*At least the header and CRC, otherwise better to not try to copy it*/
1056             ((uint8_t*)NRF_RADIO_regs.PACKETPTR)[0] = rx_buf[0];
1057             memcpy(&((uint8_t*)NRF_RADIO_regs.PACKETPTR)[1 + rx_status.S1Offset],
1058                 &rx_buf[1] , length);
1059           }
1060   } //Eventually this should be generalized with the packet configuration
1061 
1062   if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
1063     //The real HW only copies the LQI value after the payload in this mode
1064     //Note that doing it this early is a cheat
1065     double RSSI = p2G4_RSSI_value_to_dBm(rx_status.rx_resp.rssi.RSSI) + cheat_rx_power_offset;
1066     uint8_t LQI = nhwra_dBm_to_modem_LQIformat(RSSI);
1067     //Eventually this should be generalized with the packet configuration:
1068     ((uint8_t*)NRF_RADIO_regs.PACKETPTR)[1 + rx_status.S1Offset + length] = LQI;
1069   }
1070 
1071 }
1072 
1073 /**
1074  * Handle all possible responses from the phy to a Rx request
1075  */
handle_Rx_response(int ret)1076 static void handle_Rx_response(int ret){
1077   if (ret == -1) {
1078     bs_trace_raw_manual_time(3,nsi_hws_get_time(),"Communication with the phy closed during Rx\n");
1079     hwll_disconnect_phy_and_exit();
1080 
1081   } else if (ret == P2G4_MSG_ABORTREEVAL) {
1082 
1083     phy_sync_ctrl_set_last_phy_sync_time( next_recheck_time );
1084     abort_fsm_state = Rx_Abort_reeval;
1085     nhwra_set_Timer_abort_reeval( BS_MAX(next_recheck_time,nsi_hws_get_time()) );
1086 
1087   } else  if ((ret == P2G4_MSG_RXV2_ADDRESSFOUND) && (radio_state == RAD_RX /*if we havent aborted*/)) {
1088 
1089     bs_time_t addres_time = hwll_dev_time_from_phy(rx_status.rx_resp.rx_time_stamp); //this is the end of the sync word in air time
1090     phy_sync_ctrl_set_last_phy_sync_time(addres_time);
1091     Rx_handle_address_end_response(addres_time);
1092 
1093     if ((rx_status.codedphy == false) || (rx_status.inFEC1)) {
1094       radio_sub_state = RX_WAIT_FOR_ADDRESS_END;
1095       nhwra_set_Timer_RADIO(rx_status.ADDRESS_End_Time);
1096     } else { //FEC2
1097       radio_sub_state = RX_WAIT_FOR_PAYLOAD_END;
1098       nhwra_set_Timer_RADIO(TIME_NEVER);
1099       Rx_Addr_received();
1100       nhwra_set_Timer_RADIO(rx_status.PAYLOAD_End_Time);
1101     }
1102   } else if ((ret == P2G4_MSG_RXV2_END) && (radio_state == RAD_RX /*if we havent aborted*/)) {
1103 
1104     bs_time_t end_time = hwll_dev_time_from_phy(rx_status.rx_resp.end_time);
1105     phy_sync_ctrl_set_last_phy_sync_time(end_time);
1106 
1107     /* P2G4_RXSTATUS_NOSYNC during a simple packet or CodedPhy FEC1 cannot really happen
1108      * As that would mean we have run out of the "infinite" scan time.
1109      * It can happen though at the start of the CodedPhy FEC2, if the transmitter aborted
1110      * before starting the FEC2 */
1111     if (rx_status.rx_resp.status == P2G4_RXSTATUS_NOSYNC) {
1112       if ((rx_status.codedphy == false) || (rx_status.inFEC1 == true)) {
1113         bs_trace_error_time_line("Unexpected not-handled path\n");
1114       }
1115       /* Otherwise we just wait for the RX_WAIT_FOR_PAYLOAD_END handling in the main RADIO FSM
1116        * A provisional PAYLOAD_End_Time was set earlier */
1117       radio_sub_state = RX_WAIT_FOR_PAYLOAD_END;
1118       nhwra_set_Timer_RADIO(rx_status.PAYLOAD_End_Time);
1119       return;
1120     } else {
1121       Rx_handle_end_response(end_time);
1122 
1123       if (rx_status.inFEC1 == true) {
1124         /* To avoid issues with possible rounding errors in the phy<->dev timing conversion,
1125          * we ensure the FEC2 Rx will start in the next us in Phy time */
1126         rx_status.rx_req.start_time = rx_status.rx_resp.end_time + 1;
1127         nhwra_set_Timer_RADIO(rx_status.FEC2_start_time);
1128       }
1129     }
1130   }
1131 }
1132 
1133 /**
1134  * We have reached the time in which we wanted to reevaluate if we would abort or not
1135  * so we answer to the phy with our decision
1136  */
Rx_abort_eval_respond(void)1137 static void Rx_abort_eval_respond(void) {
1138   //The abort must have been evaluated by now so we can respond to the waiting phy
1139   p2G4_abort_t *abort = &rx_status.rx_req.abort;
1140   update_abort_struct(abort, &next_recheck_time);
1141 
1142   int ret = p2G4_dev_provide_new_rxv2_abort_nc_b(abort);
1143 
1144   handle_Rx_response(ret);
1145 }
1146 
1147 /*
1148  * Actually start the Rx in this microsecond
1149  */
start_Rx(void)1150 static void start_Rx(void) {
1151   #define RX_N_ADDR 8 /* How many addresses we can search in parallel */
1152   p2G4_address_t rx_addresses[RX_N_ADDR];
1153 
1154   nhwra_check_packet_conf();
1155 
1156   radio_state = RAD_RX;
1157   NRF_RADIO_regs.STATE = RAD_RX;
1158   NRF_RADIO_regs.CRCSTATUS = 0;
1159   NRF_RADIO_regs.PDUSTAT = 0;
1160 
1161   if ( NRF_RADIO_regs.PCNF0 & ( RADIO_PCNF0_S1INCL_Include << RADIO_PCNF0_S1INCL_Pos ) ){
1162     rx_status.S1Offset = 1; /*1 byte offset in RAM (S1 length > 8 not supported)*/
1163   } else {
1164     rx_status.S1Offset = 0;
1165   }
1166 
1167   rx_status.codedphy = false;
1168   rx_status.inFEC1 = false;
1169   rx_status.CI_error = false;
1170   rx_status.CI = 0;
1171 
1172   if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_1Mbit) {
1173     bits_per_us = 1;
1174   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_2Mbit) {
1175     bits_per_us = 2;
1176   } else if ((NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_LR125Kbit)
1177       || (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_LR500Kbit)) {
1178     bits_per_us = 0.125; /* For FEC1 part */
1179     rx_status.codedphy = true;
1180     rx_status.inFEC1 = true;
1181   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
1182     bits_per_us = 0.25;
1183   }
1184   rx_status.CRC_duration = nhwra_get_crc_length()*8/bits_per_us;
1185   rx_status.CRC_OK = false;
1186   rx_status.rx_resp.status = P2G4_RXSTATUS_NOSYNC;
1187 
1188   if (rx_status.codedphy) {
1189     nhwra_prep_rx_request_FEC1(&rx_status.rx_req_fec1, rx_addresses);
1190     update_abort_struct(&rx_status.rx_req_fec1.abort, &next_recheck_time);
1191   }
1192   nhwra_prep_rx_request(&rx_status.rx_req, rx_addresses);
1193   update_abort_struct(&rx_status.rx_req.abort, &next_recheck_time);
1194 
1195   //attempt to receive
1196   int ret;
1197   if (rx_status.codedphy) {
1198     ret = p2G4_dev_req_rxv2_nc_b(&rx_status.rx_req_fec1, rx_addresses,
1199                                  &rx_status.rx_resp, &rx_pkt_buffer_ptr,
1200                                  _NRF_MAX_PACKET_SIZE);
1201   } else {
1202     ret = p2G4_dev_req_rxv2_nc_b(&rx_status.rx_req, rx_addresses,
1203                                  &rx_status.rx_resp,&rx_pkt_buffer_ptr,
1204                                  _NRF_MAX_PACKET_SIZE);
1205   }
1206 
1207   radio_sub_state = SUB_STATE_INVALID;
1208   nhwra_set_Timer_RADIO(TIME_NEVER);
1209 
1210   handle_Rx_response(ret);
1211 }
1212 
1213 /*
1214  * Start the Rx for a CodedPhy FEC2 packet part in this microsecond
1215  */
start_Rx_FEC2(void)1216 static void start_Rx_FEC2(void) {
1217 
1218   rx_status.inFEC1 = false;
1219 
1220   if (rx_status.CI == 0) {
1221     rx_status.rx_req.coding_rate = 8;
1222     //error_calc_rate & header_duration preset in nhwra_prep_rx_request() are already correct
1223   } else { //0b01
1224     bits_per_us = 0.5;
1225     rx_status.rx_req.coding_rate = 2;
1226     rx_status.rx_req.error_calc_rate = 500000;
1227     rx_status.rx_req.header_duration = 2*8*2 /* 2 bytes at 500kbps */;
1228   }
1229   /* rx_req.start_time was set based on the FEC1 end */
1230   rx_status.rx_req.pream_and_addr_duration = 0;
1231   rx_status.rx_req.n_addr = 0;
1232   rx_status.rx_req.scan_duration = 1;
1233   rx_status.rx_req.prelocked_tx = true;
1234 
1235   rx_status.CRC_duration = nhwra_get_crc_length()*8/bits_per_us;
1236 
1237   update_abort_struct(&rx_status.rx_req.abort, &next_recheck_time);
1238 
1239   int ret;
1240 
1241   ret = p2G4_dev_req_rxv2_nc_b(&rx_status.rx_req, NULL,
1242                                &rx_status.rx_resp, &rx_pkt_buffer_ptr,
1243                                _NRF_MAX_PACKET_SIZE);
1244 
1245   handle_Rx_response(ret);
1246 }
1247 
1248 /**
1249  * This function is called at the time when the Packet address* would have been
1250  * completely received for simple packets, AND at the beginning of the FEC2
1251  * for CodedPhy packets.
1252  * (* at the time of the end of the last bit of the packet address)
1253  * To continue processing the reception (the Phy was left waiting for a response)
1254  *
1255  * Note that libPhyCom has already copied the whole packet into the input buffer
1256  */
Rx_Addr_received(void)1257 static void Rx_Addr_received(void) {
1258 
1259   bool accept_packet = !rx_status.packet_rejected;
1260 
1261   if (rx_status.codedphy == false || rx_status.inFEC1 == true) {
1262     if ( rssi_sampling_on ){
1263       NRF_RADIO_regs.RSSISAMPLE = nhwra_RSSI_value_to_modem_format(
1264                                     p2G4_RSSI_value_to_dBm(rx_status.rx_resp.rssi.RSSI)
1265                                     + cheat_rx_power_offset
1266                                   );
1267 #if !NHW_RADIO_IS_54
1268       nhw_RADIO_signal_EVENTS_RSSIEND(0);
1269 #endif
1270     }
1271   }
1272 
1273   if (rx_status.codedphy == false || rx_status.inFEC1 == false) {
1274     NRF_RADIO_regs.RXMATCH = 0; //The only we support so far
1275 
1276     if (NRF_RADIO_regs.DACNF & 0xFF) { /*If any of the addresses for device address match is enabled*/
1277       /*
1278        * NOTE: we cheat and we already check the advertisement addresses and
1279        * raise the event, even though we should wait for 16 + 48 bits more
1280        *
1281        * If this is a problem, add a new timer and Rx state and delay raising the event
1282        * until then
1283        */
1284       nhw_radio_device_address_match(rx_buf);
1285     }
1286   }
1287 
1288   update_abort_struct(&rx_status.rx_req.abort, &next_recheck_time);
1289   int ret = p2G4_dev_rxv2_cont_after_addr_nc_b(accept_packet, &rx_status.rx_req.abort);
1290 
1291   if ( accept_packet ){ /* Always true for CodedPhy FEC1 */
1292     handle_Rx_response(ret);
1293   } else {
1294     //We said we don't want to continue => there will be no response (ret==0 always). We just close the reception like if the phy finished on its own even though we finished it
1295 
1296     //We do what would correspond to Rx_handle_end_response() as it won't get called
1297     NRF_RADIO_regs.RXCRC = nhwra_get_rx_crc_value(rx_buf, rx_status.rx_resp.packet_size);
1298 #if !NHW_RADIO_IS_54
1299       //TODO Reconnect as soon as the CCM model is in
1300     nhw_ccm_radio_received_packet(!rx_status.CRC_OK);
1301 #endif
1302   }
1303 }
1304 
1305 /**
1306  * Check if the address in the received (advertisement) packet
1307  * matches one configured in the DAP/DAB registers as set by DACNF
1308  *
1309  * If it does, it sets appropriately the DAI register,
1310  * in any case, it generates the DEVMATCH and DEVMISS signals accordingly
1311  *
1312  * Note that, as specified in the infocenter documentation,
1313  * the address is assumed to be the first 48 bits after the 2 byte header
1314  * and the TxAddr bit to be 7th bit in 1st header byte as per the BT Core spec.
1315  */
nhw_radio_device_address_match(uint8_t rx_buf[])1316 static void nhw_radio_device_address_match(uint8_t rx_buf[]) {
1317   bool match_found = false;
1318   bool nomatch;
1319   int TxAdd;
1320 
1321   for (int i = 0 ; i < 8; i++) {
1322     if (((NRF_RADIO_regs.DACNF >> i) & 1) == 0 ) {
1323       continue;
1324     }
1325 
1326     TxAdd = (NRF_RADIO_regs.DACNF >> (i + 8)) & 1;
1327 
1328     if (TxAdd != ((rx_buf[0] >> 6) & 1) ) {
1329       continue;
1330     }
1331 
1332     nomatch = (*(uint32_t *)(rx_buf + 2) != NRF_RADIO_regs.DAB[i]);
1333     uint32_t DAP = NRF_RADIO_regs.DAP[i] & UINT16_MAX;
1334     nomatch |= (*(uint16_t *)(rx_buf + 6) != DAP);
1335 
1336     if (nomatch) {
1337       continue;
1338     }
1339 
1340     match_found = true;
1341     NRF_RADIO_regs.DAI = i;
1342     break;
1343   }
1344 
1345   if (match_found) {
1346     nhw_RADIO_signal_EVENTS_DEVMATCH(0);
1347   } else {
1348     nhw_RADIO_signal_EVENTS_DEVMISS(0);
1349   }
1350 }
1351 
CCA_handle_end_response(void)1352 static void CCA_handle_end_response(void) {
1353   //Depending on mode, set status and registers
1354   //raising CCAIDLE, CCABUSY or EDEND will happen in the correct time in the main machine
1355 
1356   if (cca_status.CCA_notED) { //End a CCA procedure
1357     uint CCAMode = (NRF_RADIO_regs.CCACTRL & RADIO_CCACTRL_CCAMODE_Msk) >> RADIO_CCACTRL_CCAMODE_Pos;
1358 
1359     if ((CCAMode == RADIO_CCACTRL_CCAMODE_EdMode)
1360         || (CCAMode == RADIO_CCACTRL_CCAMODE_EdModeTest1)) {
1361       cca_status.is_busy = cca_status.cca_resp.rssi_overthreshold;
1362     } else if (CCAMode == RADIO_CCACTRL_CCAMODE_CarrierMode) {
1363       cca_status.is_busy = cca_status.cca_resp.mod_found;
1364     } else if (CCAMode == RADIO_CCACTRL_CCAMODE_CarrierAndEdMode) {
1365       cca_status.is_busy = cca_status.cca_resp.mod_found
1366           && cca_status.cca_resp.rssi_overthreshold;
1367     } else if (CCAMode == RADIO_CCACTRL_CCAMODE_CarrierOrEdMode) {
1368       cca_status.is_busy = cca_status.cca_resp.mod_found
1369           || cca_status.cca_resp.rssi_overthreshold;
1370     } else {
1371       bs_trace_error_time_line("%s, CCAMODE=%i suppport not yet implemented\n",
1372           __func__, CCAMode);
1373     }
1374   } else { // Ending an ED procedure
1375     double RSSI = p2G4_RSSI_value_to_dBm(cca_status.cca_resp.RSSI_max) + cheat_rx_power_offset;
1376     NRF_RADIO_regs.EDSAMPLE = nhwra_dBm_to_modem_LQIformat(RSSI);
1377   }
1378 }
1379 
1380 /**
1381  * Handle all possible responses to a CCA request from the Phy
1382  */
handle_CCA_response(int ret)1383 static void handle_CCA_response(int ret){
1384   if (ret == -1){
1385     bs_trace_raw_manual_time(3,nsi_hws_get_time(),"The Phy disconnected us during a CCA procedure\n");
1386     hwll_disconnect_phy_and_exit();
1387   } else if ( ret == P2G4_MSG_CCA_END  ) {
1388     bs_time_t end_time = hwll_dev_time_from_phy(cca_status.cca_resp.end_time);
1389     phy_sync_ctrl_set_last_phy_sync_time( end_time );
1390     cca_status.CCA_end_time = end_time;
1391     if (radio_state == RAD_CCA_ED) { /*if we haven't aborted*/
1392       nhwra_set_Timer_RADIO(cca_status.CCA_end_time);
1393     }
1394     CCA_handle_end_response();
1395   } else if ( ret == P2G4_MSG_ABORTREEVAL ) {
1396     phy_sync_ctrl_set_last_phy_sync_time( next_recheck_time );
1397     abort_fsm_state = CCA_Abort_reeval;
1398     nhwra_set_Timer_abort_reeval(next_recheck_time);
1399   }
1400 }
1401 
1402 /**
1403  * We have reached the time in which we wanted to reevaluate if we would abort or not
1404  * so we answer to the Phy with our decision
1405  */
CCA_abort_eval_respond(void)1406 static void CCA_abort_eval_respond(void) {
1407   //The abort must have been evaluated by now so we can respond to the waiting phy
1408   p2G4_abort_t *abort = &cca_status.cca_req.abort;
1409 
1410   update_abort_struct(abort, &next_recheck_time);
1411 
1412   int ret = p2G4_dev_provide_new_cca_abort_nc_b(abort);
1413 
1414   handle_CCA_response(ret);
1415 }
1416 
1417 /**
1418  * Start CCA or ED procedure right now.
1419  * input: CCA_not_ED = 1 for CCA, 0 for ED
1420  */
start_CCA_ED(bool CCA_not_ED)1421 static void start_CCA_ED(bool CCA_not_ED){
1422 
1423   radio_state = RAD_CCA_ED;
1424 
1425   cca_status.CCA_notED = CCA_not_ED;
1426   cca_status.is_busy = false;
1427 
1428   nhwra_prep_cca_request(&cca_status.cca_req, CCA_not_ED, cheat_rx_power_offset);
1429 
1430   update_abort_struct(&cca_status.cca_req.abort, &next_recheck_time);
1431 
1432   //Expected end time; note that it may be shorter if detect over threshold is set
1433   cca_status.CCA_end_time = nsi_hws_get_time() + cca_status.cca_req.scan_duration;
1434   nhwra_set_Timer_RADIO(cca_status.CCA_end_time);
1435 
1436   //Request the CCA from the Phy:
1437   int ret = p2G4_dev_req_cca_nc_b(&cca_status.cca_req, &cca_status.cca_resp);
1438   handle_CCA_response(ret);
1439 }
1440 
hw_radio_testcheat_set_rx_power_gain(double power_offset)1441 void hw_radio_testcheat_set_rx_power_gain(double power_offset){
1442   cheat_rx_power_offset = power_offset;
1443 }
1444