1 /* -------------------------------------------------------------------------- */
2 /* Copyright 2021-2024 NXP */
3 /* All rights reserved. */
4 /* SPDX-License-Identifier: BSD-3-Clause */
5 /* -------------------------------------------------------------------------- */
6
7 /* -------------------------------------------------------------------------- */
8 /* Includes */
9 /* -------------------------------------------------------------------------- */
10
11 #include <stdint.h>
12 #include <stdbool.h>
13
14 #ifndef CONFIG_PRINTK /* Zephyr already has print support */
15 #include "fsl_debug_console.h"
16 #endif /* CONFIG_PRINTK */
17
18 #include "fsl_loader.h"
19 #include "fsl_power.h"
20 #include "fsl_adapter_rpmsg.h"
21 #include "fsl_adapter_rfimu.h"
22 #include "fsl_os_abstraction.h"
23
24 #include "fwk_config.h"
25 #include "fwk_platform_ble.h"
26 #include "fwk_platform_ot.h"
27 #include "fwk_platform_coex.h"
28
29 #ifdef SERIAL_BTSNOOP
30 #include "sbtsnoop.h"
31 #endif
32
33 /* -------------------------------------------------------------------------- */
34 /* Private macros */
35 /* -------------------------------------------------------------------------- */
36 /* By default, wait maximum 1s for the controller to wake up */
37 #ifndef PLATFORM_BLE_WAKE_UP_TIMEOUT_MS
38 #define PLATFORM_BLE_WAKE_UP_TIMEOUT_MS 1000U
39
40 #endif
41
42 #define HCI_COMMAND_PACKET 0x01U
43 #define HCI_EVENT_PACKET 0x04U
44 #define HCI_VENDOR_SPECIFIC_DEBUG_EVENT 0xFFU
45
46 #define HCI_CMD_PACKET_HEADER_LENGTH 3U
47 #define HCI_CMD_VENDOR_OCG 0x3FU
48
49 #define HCI_CMD_STORE_BT_CAL_DATA_OCF 0x61U
50 #define HCI_CMD_STORE_BT_CAL_DATA_PARAM_LENGTH 32U
51
52 #define HCI_CMD_STORE_BT_CAL_DATA_ANNEX100_OCF 0xFFU
53 #define HCI_CMD_STORE_BT_CAL_DATA_PARAM_ANNEX100_LENGTH 16U
54
55 #define HCI_CMD_SET_BT_SLEEP_MODE_OCF 0x23U
56 #define HCI_CMD_SET_BT_SLEEP_MODE_PARAM_LENGTH 3U
57
58 #define HCI_CMD_BT_HIU_HS_ENABLE_OCF 0x5A
59
60 #define HCI_CMD_BT_HOST_SLEEP_CONFIG_OCF 0x59U
61 #define HCI_CMD_BT_HOST_SLEEP_CONFIG_PARAM_LENGTH 2U
62
63 #define HCI_EVT_PS_SLEEP_OCF 0x20U
64
65 #define get_opcode(ocg, ocf) (((uint16_t)(ocg) & (uint16_t)0x3FU) << 10) | (uint16_t)((ocf)&0x3FFU)
66
67 /* The wake up done interrupt doesn't make any call to FreeRTOS API so it should
68 * be safe to make it higher priority than configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
69 * so it is not masked in FreeRTOS critical sections */
70 #define MCI_WAKEUP_DONE_PRIORITY (1)
71
72 #define BLE_POWER_STATUS() (SOCCTRL->BLE_POWER_STATUS & 0x3U)
73 #define BLE_POWER_ON (0U)
74 #define BLE_POWER_SLP (2U)
75 #define BLE_POWER_OFF (3U)
76
77 #define BLE_SMU_POWER_STATUS() (*((volatile uint32_t *)0x443C0000U))
78 #define BLE_SMU_POWER_OFF (0x3131312AU)
79
80 /* Can change the macro BLE_VENDOR_EVENT_HANDLE to false to let HOST handle the Vendor Event,
81 * like PowerSave Vendor Event */
82 #define BLE_VENDOR_EVENT_HANDLE (true)
83
84 #ifdef CONFIG_PRINTK
85
86 #ifndef PRINTF
87 #define PRINTF printk
88 #endif
89
90 #endif /* CONFIG_PRINTK */
91
92 #define BLE_PS_DBG(...) \
93 do \
94 { \
95 PRINTF("[BLE_PS] "); \
96 PRINTF(__VA_ARGS__); \
97 } while (false);
98
99 /*
100 * a.The following parameters are used in three cases,
101 * 1.For share antenna case or ant2 with external FEM(ble only case).
102 * 2.divesity case(enanble ant3)
103 * 3.divesity case(enanble ant4)
104 */
105 #if defined(gPlatformSetAntDiversity_d) && (gPlatformSetAntDiversity_d == 0)
106 // For share antenna case or ant2 with external FEM(ble only case)
107 #define BT_CAL_DATA_ANNEX_100_EPA_FEM_MASK_LOW_BYTE 0x02U
108 #define BT_CAL_DATA_ANNEX_100_LNA_FEM_MASK_LOW_BYTE 0x02U
109 #elif defined(gPlatformSetAntDiversity_d) && (gPlatformSetAntDiversity_d == 1)
110 // divesity case(enanble ant3)
111 #define BT_CAL_DATA_ANNEX_100_EPA_FEM_MASK_LOW_BYTE 0x0AU
112 #define BT_CAL_DATA_ANNEX_100_LNA_FEM_MASK_LOW_BYTE 0x0AU
113 #elif defined(gPlatformSetAntDiversity_d) && (gPlatformSetAntDiversity_d == 2)
114 // divesity case(enanble ant4)
115 #define BT_CAL_DATA_ANNEX_100_EPA_FEM_MASK_LOW_BYTE 0x06U
116 #define BT_CAL_DATA_ANNEX_100_LNA_FEM_MASK_LOW_BYTE 0x06U
117 #endif
118
119 /*
120 * The following parameters are used in two cases
121 */
122 #if defined(gPlatformDisableSetBtCalDataAnnex100_d) && (gPlatformDisableSetBtCalDataAnnex100_d == 1)
123 // For dual ant case
124 #define BT_CAL_DATA_ANNEX_FRONT_END_LOSS 0x02U
125 #elif defined(gPlatformDisableSetBtCalDataAnnex100_d) && (gPlatformDisableSetBtCalDataAnnex100_d == 0)
126 // For share antenna case or diversty case(ble only case)
127 #define BT_CAL_DATA_ANNEX_FRONT_END_LOSS 0x03U
128 #endif
129
130 /*
131 * After send annex55 to CPU2, CPU2 need reset,
132 *a delay of at least 20ms is required to continue sending annex100
133 */
134 #if defined(gPlatformDisableSetBtCalDataAnnex100_d) && (gPlatformDisableSetBtCalDataAnnex100_d == 0)
135 #define BLE_RESET_DELAY_MS 20U
136 #endif
137
138 /* -------------------------------------------------------------------------- */
139 /* Private types */
140 /* -------------------------------------------------------------------------- */
141
142 /*!
143 * \brief Controller power states enum
144 *
145 */
146 typedef enum
147 {
148 ble_awake_state,
149 ble_asleep_state
150 } ble_ps_t;
151
152 /*!
153 * \brief Controller power state HCI vendor events
154 *
155 */
156 typedef enum
157 {
158 ble_asleep_event = 0x1U,
159 ble_awake_event = 0x2U
160 } ble_ps_event_t;
161
162 /* -------------------------------------------------------------------------- */
163 /* Private prototypes */
164 /* -------------------------------------------------------------------------- */
165
166 /*!
167 * \brief Init HCI link with BLE controller
168 *
169 * \return int return status: >=0 for success, <0 for errors
170 */
171 static int PLATFORM_InitHciLink(void);
172
173 /*!
174 * \brief Terminate HCI link with BLE controller
175 *
176 * \return int return status: >=0 for success, <0 for errors
177 */
178 static int PLATFORM_TerminateHciLink(void);
179
180 /*!
181 * \brief Return HCI link status
182 *
183 * \return true Link is ready
184 * \return false Link is not ready yet
185 */
186 static bool PLATFORM_IsHciLinkReady(void);
187
188 /*!
189 * \brief Checks if the BLE controller is awake or asleep
190 *
191 * \return true BLE Controller is awake
192 * \return false BLE Controller is asleep
193 */
194 static bool PLATFORM_IsBleAwake(void);
195
196 /*!
197 * \brief RPMSG Rx callback used to receive HCI messages from Controller
198 *
199 * \param[in] param Usually NULL
200 * \param[in] data pointer to data buffer
201 * \param[in] len size of the data
202 * \return hal_rpmsg_return_status_t tells RPMSG to free or hold the buffer
203 */
204 static hal_rpmsg_return_status_t PLATFORM_HciRpmsgRxCallback(void *param, uint8_t *data, uint32_t len);
205
206 /*!
207 * \brief Set BT Cal Data to Controller
208 *
209 * \return int return status: >=0 for success, <0 for errors
210 */
211 static int PLATFORM_SetBtCalData(void);
212
213 #if !defined(gPlatformDisableSetBtCalDataAnnex100_d) || (gPlatformDisableSetBtCalDataAnnex100_d == 0)
214 /*!
215 * \brief Set BT Cal Data Annex100 to Controller
216 *
217 * \return int return status: >=0 for success, <0 for errors
218 */
219 static int PLATFORM_SetBtCalDataAnnex100(void);
220 #endif /* gPlatformDisableSetBtCalDataAnnex100_d */
221
222 /*!
223 * \brief Handles supported Vendor Specific events received from Controller
224 * If the received event is not supported, it will return false, so the
225 * called can send the packet to upper layers (host stack or application)
226 *
227 * \param[in] eventData Pointer to event data buffer
228 * \param[in] len size of the data
229 * \return true The event has been handled
230 * \return false The event has NOT been handled
231 */
232 static bool PLATFORM_HandleHciVendorEvent(uint8_t *eventData, uint32_t len);
233
234 /*!
235 * \brief Handle power state event from BLE Controller
236 *
237 * \param[in] psEvent event type received
238 * \return int return status: >=0 for success, <0 for errors
239 */
240 static int PLATFORM_HandleBlePowerStateEvent(ble_ps_event_t psEvent);
241
242 /*!
243 * \brief Send Host sleep config to Controller
244 *
245 * \return int return status: >=0 for success, <0 for errors
246 */
247 static int PLATFORM_BleSetHostSleepConfig(void);
248
249 void BLE_MCI_WAKEUP_DONE0_DriverIRQHandler(void);
250
251 static void PLATFORM_FillInHciCmdMsg(uint8_t *pbuf, uint16_t opcode, uint8_t msg_sz, const uint8_t *msg_payload);
252
253 /* -------------------------------------------------------------------------- */
254 /* Private memory */
255 /* -------------------------------------------------------------------------- */
256
257 static RPMSG_HANDLE_DEFINE(hci_rpmsg_handle);
258 static hal_rpmsg_config_t hci_rpmsg_config = {
259 .local_addr = 30,
260 .remote_addr = 40,
261 .imuLink = (uint8_t)kIMU_LinkCpu2Cpu3,
262 .callback = PLATFORM_HciRpmsgRxCallback,
263 .param = NULL,
264 };
265
266 static bool initialized = false;
267 static bool hciInitialized = false;
268 static volatile ble_ps_t blePowerState = ble_awake_state;
269
270 static OSA_EVENT_HANDLE_DEFINE(wakeUpEventGroup);
271 static OSA_MUTEX_HANDLE_DEFINE(bleMutexHandle);
272
273 static void (*hci_rx_callback)(uint8_t packetType, uint8_t *data, uint16_t len);
274
275 /* -------------------------------------------------------------------------- */
276 /* Public memory */
277 /* -------------------------------------------------------------------------- */
278
279 const uint8_t hci_cal_data_params[HCI_CMD_STORE_BT_CAL_DATA_PARAM_LENGTH] = {
280 0x00U, // Sequence Number : 0x00
281 0x00U, // Action : 0x00
282 0x01U, // Type : Not use CheckSum
283 0x1CU, // File Length : 0x1C
284 0x37U, // BT Annex Type : BT CFG
285 0x71U, // Checksum : 0x71
286 0x1CU, // Annex Length LSB: 0x001C
287 0x00U, // Annex Length MSB: 0x001C
288 0xFFU, // Pointer For Next Annex[0] : 0xFFFFFFFF
289 0xFFU, // Pointer For Next Annex[1] : 0xFFFFFFFF
290 0xFFU, // Pointer For Next Annex[2] : 0xFFFFFFFF
291 0xFFU, // Pointer For Next Annex[3] : 0xFFFFFFFF
292 0x01U, // Annex Version : 0x01
293 0x7CU, // External Xtal Calibration Value : 0x7C
294 0x04U, // Initial TX Power : 0x04
295 BT_CAL_DATA_ANNEX_FRONT_END_LOSS, // Front End Loss : 0x02 or 0x03
296 0x28U, // BT Options :
297 // BIT[0] Force Class 2 operation = 0
298 // BIT[1] Disable Pwr Control for class 2= 0
299 // BIT[2] MiscFlag(to indicagte external XTAL) = 0
300 // BIT[3] Used Internal Sleep Clock = 1
301 // BIT[4] BT AOA localtion support = 0
302 // BIT[5] Force Class 1 mode = 1
303 // BIT[7:6] Reserved
304 0x00U, // AOANumberOfAntennas: 0x00
305 0x00U, // RSSI Golden Low : 0
306 0x00U, // RSSI Golden High : 0
307 0xC0U, // UART Baud Rate[0] : 0x002DC6C0(3000000)
308 0xC6U, // UART Baud Rate[1] : 0x002DC6C0(3000000)
309 0x2DU, // UART Baud Rate[2] : 0x002DC6C0(3000000)
310 0x00U, // UART Baud Rate[3] : 0x002DC6C0(3000000)
311 0x00U, // BdAddress[0] : 0x000000000000
312 0x00U, // BdAddress[1] : 0x000000000000
313 0x00U, // BdAddress[2] : 0x000000000000
314 0x00U, // BdAddress[3] : 0x000000000000
315 0x00U, // BdAddress[4] : 0x000000000000
316 0x00U, // BdAddress[5] : 0x000000000000
317 0xF0U, // Encr_Key_Len[3:0]: MinEncrKeyLen = 0x0
318 // Encr_Key_Len[7:4]: MaxEncrKeyLen = 0xF
319 #if defined(gPlatformEnableTxPowerChangeWithCountry_d) && (gPlatformEnableTxPowerChangeWithCountry_d == 0)
320 0x00U, // RegionCode : 0x00
321 #else
322 0x00U, // Reserved : 0x00
323 #endif /* gPlatformEnableTxPowerChangeWithCountry_d */
324 };
325
326 #if !defined(gPlatformDisableSetBtCalDataAnnex100_d) || (gPlatformDisableSetBtCalDataAnnex100_d == 0)
327 /*
328 * a.The following parameters are used in three cases,
329 * 1.For share antenna case or ant2 with external FEM(ble only case).
330 * 2.diversity case(enable ant3)
331 * 3.diversity case(enable ant4)
332 */
333 const uint8_t hci_cal_data_annex100_params[HCI_CMD_STORE_BT_CAL_DATA_PARAM_ANNEX100_LENGTH] = {
334 /* BT_HW_INFO START */
335 0x64U, // Annex Type : 0x64
336 0x00U, // CheckSum: Annex100 ignores checksum
337 0x10U, // Length-In-Byte : 0x0010
338 0x00U, // Length-In-Byte : 0x0010
339 0xFFU, // Pointer for next annex structure : 0xFFFFFFFF
340 0xFFU, // Pointer for next annex structure : 0xFFFFFFFF
341 0xFFU, // Pointer for next annex structure : 0xFFFFFFFF
342 0xFFU, // Pointer for next annex structure : 0xFFFFFFFF
343 0x01U, // Ext_PA Gain : Bit[7:1] Ext_PA Present : Bit[0]
344 #if defined(gPlatformEnableTxPowerChangeWithCountry_d) && (gPlatformEnableTxPowerChangeWithCountry_d == 0)
345 0x00U, // Ext_Ant Gain : Bit[4:1] Ext_Ant Present : Bit[0]
346 #else
347 0x00U, // Reserved
348 #endif /* gPlatformEnableTxPowerChangeWithCountry_d */
349 BT_CAL_DATA_ANNEX_100_EPA_FEM_MASK_LOW_BYTE, // BT_HW_INFO_EPA_FEM_Mask
350 0x00U, // BT_HW_INFO_EPA_FEM_Mask
351 0x01U, // Ext_LNA Present : Bit[0] Ext_LNA Gain : Bit[7:1]
352 0x00U, // multipurpose mask
353 BT_CAL_DATA_ANNEX_100_LNA_FEM_MASK_LOW_BYTE, // BT / LE ext LNA FEM BITMASK
354 0x00U, // BT / LE ext LNA FEM BITMASK
355 /* BT_HW_INFO END */
356 };
357 #endif
358 /* -------------------------------------------------------------------------- */
359 /* Public functions */
360 /* -------------------------------------------------------------------------- */
361
PLATFORM_InitBle(void)362 int PLATFORM_InitBle(void)
363 {
364 int ret = 0;
365 osa_status_t status;
366
367 /* PLATFORM_InitBle can be called from OT or Ethermind context in multi mode applications
368 * The 'initialized' variable will be set to true only when the initialization is complete
369 * We have to protect the initialization flow with a mutex to make sure the first task completes the initialization
370 * before the second reads 'initialized' */
371 status = OSA_MutexCreate((osa_mutex_handle_t)bleMutexHandle);
372 assert(status == KOSA_StatusSuccess);
373 status = OSA_MutexLock((osa_mutex_handle_t)bleMutexHandle, osaWaitForever_c);
374 assert(status == KOSA_StatusSuccess);
375
376 do
377 {
378 if (initialized == true)
379 {
380 break;
381 }
382 status = OSA_EventCreate((osa_event_handle_t)wakeUpEventGroup, 0);
383 assert(status == KOSA_StatusSuccess);
384
385 /* Initialize BLE controller */
386 ret = PLATFORM_InitControllers(connBle_c);
387 if (ret != 0)
388 {
389 ret = -1;
390 break;
391 }
392
393 /* Initialize HCI link with BLE CPU */
394 ret = PLATFORM_InitHciLink();
395 if (ret != 0)
396 {
397 ret = -2;
398 break;
399 }
400
401 /* Configure BLE Wakeup done interrupt */
402 NVIC_SetPriority(BLE_MCI_WAKEUP_DONE0_IRQn, MCI_WAKEUP_DONE_PRIORITY);
403
404 initialized = true;
405 /* after re-init cpu2, Reset blePowerState to ble_awake_state. */
406 blePowerState = ble_awake_state;
407 } while (false);
408
409 status = OSA_MutexUnlock((osa_mutex_handle_t)bleMutexHandle);
410 assert(status == KOSA_StatusSuccess);
411 (void)status;
412
413 return ret;
414 }
415
PLATFORM_VendorSpecificInit(void)416 void PLATFORM_VendorSpecificInit(void)
417 {
418 #if !defined(gPlatformDisableSetBtCalData_d) || (gPlatformDisableSetBtCalData_d == 0)
419 /* Send the BT Cal Data to Controller */
420 (void)PLATFORM_SetBtCalData();
421 #if !defined(gPlatformDisableSetBtCalDataAnnex100_d) || (gPlatformDisableSetBtCalDataAnnex100_d == 0)
422 /* After send annex55 to CPU2, CPU2 need reset,
423 a delay of at least 20ms is required to continue sending annex100*/
424 OSA_TimeDelay(BLE_RESET_DELAY_MS);
425
426 /* Send the BT Cal Data annex100 to Controller */
427 (void)PLATFORM_SetBtCalDataAnnex100();
428 #endif
429 #endif
430
431 (void)PLATFORM_BleSetHostSleepConfig();
432
433 #if !defined(gPlatformDisableBleLowPower_d) || (gPlatformDisableBleLowPower_d == 0)
434 /* Allow Controller to enter low power */
435 (void)PLATFORM_EnableBleLowPower();
436 #endif
437 }
438
PLATFORM_TerminateBle(void)439 int PLATFORM_TerminateBle(void)
440 {
441 int ret = 0;
442
443 do
444 {
445 if (initialized == false)
446 {
447 break;
448 }
449
450 if (PLATFORM_TerminateHciLink() != 0)
451 {
452 ret = -1;
453 break;
454 }
455
456 if (PLATFORM_TerminateControllers((uint8_t)connBle_c) != 0) /* MISRA CID 26829044 */
457 {
458 ret = -2;
459 break;
460 }
461
462 if (OSA_EventDestroy((osa_event_handle_t)wakeUpEventGroup) != KOSA_StatusSuccess)
463 {
464 ret = -3;
465 break;
466 }
467
468 if (OSA_MutexDestroy((osa_mutex_handle_t)bleMutexHandle) != KOSA_StatusSuccess)
469 {
470 ret = -4;
471 break;
472 }
473
474 initialized = false;
475 /* after re-init cpu2, Reset hciInitialized to false. */
476 hciInitialized = false;
477 } while (false);
478
479 return ret;
480 }
481
PLATFORM_ResetBle(void)482 int PLATFORM_ResetBle(void)
483 {
484 int ret = 0;
485
486 do
487 {
488 if ((PLATFORM_GetRunningControllers() & conn802_15_4_c) != 0U)
489 {
490 /* Currently the CPU2 is running the combo firmware, so we should reset using this firmware */
491 PLATFORM_ResetOt();
492 }
493 else
494 {
495 if (PLATFORM_TerminateBle() != 0)
496 {
497 ret = -1;
498 break;
499 }
500
501 if (PLATFORM_InitBle() != 0)
502 {
503 ret = -2;
504 break;
505 }
506 }
507
508 } while (false);
509
510 return ret;
511 }
512
PLATFORM_StartHci(void)513 int PLATFORM_StartHci(void)
514 {
515 int ret = 0;
516
517 do
518 {
519 if (hciInitialized == true)
520 {
521 break;
522 }
523
524 while (PLATFORM_IsHciLinkReady() != true)
525 {
526 }
527
528 #if !defined(gPlatformDisableVendorSpecificInit) || (gPlatformDisableVendorSpecificInit == 0)
529 /* This function call uses HCI vendor commands to configure the controller,
530 * this can cause troubles with some BLE Host. A host can send the HCI commands
531 * using its own API and then expect the right response from the controller, if the
532 * commands are sent under the hood using the framework, the host may receive anexpected
533 * responses which may lead to issues. In this case enable gPlatformDisableVendorSpecificInit.
534 */
535 PLATFORM_VendorSpecificInit();
536 #endif
537 hciInitialized = true;
538 } while (false);
539
540 return ret;
541 }
542
PLATFORM_SetHciRxCallback(void (* callback)(uint8_t packetType,uint8_t * data,uint16_t len))543 int PLATFORM_SetHciRxCallback(void (*callback)(uint8_t packetType, uint8_t *data, uint16_t len))
544 {
545 int ret = 0;
546
547 hci_rx_callback = callback;
548
549 return ret;
550 }
551
PLATFORM_SendHciMessage(uint8_t * msg,uint32_t len)552 int PLATFORM_SendHciMessage(uint8_t *msg, uint32_t len)
553 {
554 int ret = 0;
555
556 #ifdef SERIAL_BTSNOOP
557 sbtsnoop_write_hci_pkt(msg[0U], 0U, &msg[1], (uint16_t)(len - 1U));
558 #endif
559
560 do
561 {
562 /* Before sending a HCI message, we have to make sure the Controller is
563 * awake */
564 ret = PLATFORM_RequestBleWakeUp();
565 if (ret != 0)
566 {
567 ret = -1;
568 break;
569 }
570
571 /* Send HCI Packet through RPMSG channel */
572 if (HAL_RpmsgSend(hci_rpmsg_handle, msg, len) != kStatus_HAL_RpmsgSuccess)
573 {
574 ret = -2;
575 break;
576 }
577
578 /* Release the wake up request now */
579 ret = PLATFORM_ReleaseBleWakeUp();
580 if (ret != 0)
581 {
582 ret = -3;
583 break;
584 }
585 } while (false);
586
587 return ret;
588 }
589
PLATFORM_EnableBleLowPower(void)590 int PLATFORM_EnableBleLowPower(void)
591 {
592 int ret = 0;
593 uint8_t buffer[1 + HCI_CMD_PACKET_HEADER_LENGTH + HCI_CMD_SET_BT_SLEEP_MODE_PARAM_LENGTH];
594 uint16_t opcode = get_opcode(HCI_CMD_VENDOR_OCG, HCI_CMD_SET_BT_SLEEP_MODE_OCF);
595 const uint8_t params[HCI_CMD_SET_BT_SLEEP_MODE_PARAM_LENGTH] = {
596 0x02U, // Auto sleep enable
597 0x00U, // Idle timeout LSB
598 0x00U // Idle timeout MSB
599 };
600
601 PLATFORM_FillInHciCmdMsg(&buffer[0], opcode, (uint8_t)sizeof(params), params);
602
603 ret = PLATFORM_SendHciMessage(buffer, sizeof(buffer));
604 if (ret != 0)
605 {
606 ret = -1;
607 }
608
609 return ret;
610 }
611
PLATFORM_DisableBleLowPower(void)612 int PLATFORM_DisableBleLowPower(void)
613 {
614 int ret = 0;
615 uint8_t buffer[1 + HCI_CMD_PACKET_HEADER_LENGTH + HCI_CMD_SET_BT_SLEEP_MODE_PARAM_LENGTH];
616 uint16_t opcode = get_opcode(HCI_CMD_VENDOR_OCG, HCI_CMD_SET_BT_SLEEP_MODE_OCF);
617 const uint8_t params[HCI_CMD_SET_BT_SLEEP_MODE_PARAM_LENGTH] = {
618 0x03U, // Auto sleep disable
619 0x00U, // Idle timeout LSB
620 0x00U // Idle timeout MSB
621 };
622
623 PLATFORM_FillInHciCmdMsg(buffer, opcode, (uint8_t)sizeof(params), params);
624
625 ret = PLATFORM_SendHciMessage(buffer, sizeof(buffer));
626 if (ret != 0)
627 {
628 ret = -1;
629 }
630
631 return ret;
632 }
633
PLATFORM_RequestBleWakeUp(void)634 int PLATFORM_RequestBleWakeUp(void)
635 {
636 int ret = 0;
637 osa_status_t status;
638 osa_event_flags_t events = 0;
639 /* The request can come from different tasks (BLE or OT), but only one request should be performed at the same
640 * time. The mutex ensures only one task is waking up the CPU2 at a time. */
641
642 if (OSA_MutexLock((osa_mutex_handle_t)bleMutexHandle, osaWaitForever_c) != KOSA_StatusSuccess)
643 {
644 /* shouldn't happen */
645 assert(0);
646 }
647
648 if (PLATFORM_IsBleAwake() == false)
649 {
650 /* After operate menu 130, there is a pending interrupt,
651 * before enableIRQ, clear pending interrupt */
652 NVIC_ClearPendingIRQ(BLE_MCI_WAKEUP_DONE0_IRQn);
653
654 /* Controller is in low power, we need to wake it up with PMU
655 * and wait for the wake up done interrupt to make sure it is
656 * completely awake and ready to receive a message */
657 NVIC_EnableIRQ(BLE_MCI_WAKEUP_DONE0_IRQn);
658
659 /* Wake up BLE core with PMU BLE_WAKEUP bit
660 * This bit is maintained until we receive a BLE_MCI_WAKEUP_DONE0
661 * interrupt */
662 PMU_EnableBleWakeup(0x1U);
663
664 /* Suspend the current task waiting for the Controller to be awake */
665 status = OSA_EventWait((osa_event_handle_t)wakeUpEventGroup, (uint32_t)ble_awake_event, 1,
666 PLATFORM_BLE_WAKE_UP_TIMEOUT_MS, &events);
667 if (((events & (uint32_t)ble_awake_event) == 0U) || (status != KOSA_StatusSuccess))
668 {
669 ret = -1;
670 }
671 }
672
673 if (OSA_MutexUnlock((osa_mutex_handle_t)bleMutexHandle) != KOSA_StatusSuccess)
674 {
675 ret = -1;
676 }
677
678 return ret;
679 }
680
PLATFORM_ReleaseBleWakeUp(void)681 int PLATFORM_ReleaseBleWakeUp(void)
682 {
683 int ret = 0;
684 /* Nothing to do, the BLE controller awakes with a one shot interrupt from PMU
685 * For now, there's no mechanism to force it active for a defined period of time
686 * The only concern is if the CPU2 has time to re-enter sleep while CPU3 still
687 * needs CPU2 power domain ressources such as IMU/SMU
688 * TODO: Use GPIO output from CPU2 to track sleep/active periods and measure
689 * the minimal time before CPU2 re-enters sleep after a wake up */
690 return ret;
691 }
692
BLE_MCI_WAKEUP_DONE0_DriverIRQHandler(void)693 void BLE_MCI_WAKEUP_DONE0_DriverIRQHandler(void)
694 {
695 /* The Controller is awake, we can clear BLE wake up interrupt */
696 PMU_DisableBleWakeup(0x1U);
697 NVIC_DisableIRQ(BLE_MCI_WAKEUP_DONE0_IRQn);
698 }
699
PLATFORM_HandleControllerPowerState(void)700 int PLATFORM_HandleControllerPowerState(void)
701 {
702 int ret = 0;
703
704 /* Controller can send data or event directly to Host without
705 * PS_AWAKE indication, the host needs to update the power state */
706 ret = PLATFORM_HandleBlePowerStateEvent(ble_awake_event);
707
708 /* Unblock any sending task waiting for wake up */
709 if (OSA_EventSet((osa_event_handle_t)wakeUpEventGroup, (uint32_t)ble_awake_event) != KOSA_StatusSuccess)
710 {
711 ret = -1;
712 }
713
714 return ret;
715 }
716
717 /* -------------------------------------------------------------------------- */
718 /* Private functions */
719 /* -------------------------------------------------------------------------- */
720
PLATFORM_InitHciLink(void)721 static int PLATFORM_InitHciLink(void)
722 {
723 int ret = 0;
724
725 do
726 {
727 /* Init RPMSG/IMU Channel */
728 if (HAL_RpmsgInit((hal_rpmsg_handle_t)hci_rpmsg_handle, &hci_rpmsg_config) != kStatus_HAL_RpmsgSuccess)
729 {
730 ret = -1;
731 break;
732 }
733 } while (false);
734
735 return ret;
736 }
737
PLATFORM_TerminateHciLink(void)738 static int PLATFORM_TerminateHciLink(void)
739 {
740 int ret = 0;
741
742 do
743 {
744 /* Force wake up CPU2 before send IMU_MSG_CONTROL_SHUTDOWN in HAL_ImuDeinit() */
745 PMU_EnableBleWakeup(0x1U);
746
747 /* Deinitialize IMU first
748 * Ignoring return value because kStatus_HAL_RpmsgError means it was already deinitialize */
749 (void)HAL_ImuDeinit(kIMU_LinkCpu2Cpu3, 0);
750
751 /* Clear CPU2 wake up bit after HAL_ImuDeinit() */
752 PMU_DisableBleWakeup(0x1U);
753
754 /* Deinitialize RPMSG first */
755 if (HAL_RpmsgDeinit(hci_rpmsg_handle) != kStatus_HAL_RpmsgSuccess)
756 {
757 ret = -2;
758 break;
759 }
760 } while (false);
761
762 return ret;
763 }
764
PLATFORM_IsHciLinkReady(void)765 static bool PLATFORM_IsHciLinkReady(void)
766 {
767 return (HAL_ImuLinkIsUp(hci_rpmsg_config.imuLink) == kStatus_HAL_RpmsgSuccess);
768 }
769
PLATFORM_IsBleAwake(void)770 static bool PLATFORM_IsBleAwake(void)
771 {
772 return (blePowerState != ble_asleep_state);
773 }
774
PLATFORM_HciRpmsgRxCallback(void * param,uint8_t * data,uint32_t len)775 static hal_rpmsg_return_status_t PLATFORM_HciRpmsgRxCallback(void *param, uint8_t *data, uint32_t len)
776 {
777 bool handled = false;
778 uint8_t packetType = data[0];
779
780 (void)param;
781
782 (void)PLATFORM_HandleControllerPowerState();
783
784 /* If the macro BLE_VENDOR_EVENT_HANDLE is set to true, PLATFORM module will check if it can handle Vendor Specific
785 * Events without going through Ethermind's HCI tasks If the packet is not handled, then it is sent to upper layers
786 * This is likely used to handle Controller low power state, so this is
787 * completely transparent to the application. If the macro BLE_VENDOR_EVENT_HANDLE is set false, the Ethermind's HCI
788 * tasks will handle vendor event */
789 if (packetType == HCI_EVENT_PACKET)
790 {
791 uint8_t eventType = data[1];
792
793 if (eventType == HCI_VENDOR_SPECIFIC_DEBUG_EVENT)
794 {
795 /* Received packet is a Vendor Specific event, check if PLATFORM
796 * can process it, if not, it will be sent to Ethermind */
797 handled = PLATFORM_HandleHciVendorEvent(&data[3], data[2]);
798 }
799 }
800
801 if ((handled == false) && (hci_rx_callback != NULL))
802 {
803 hci_rx_callback(packetType, &data[1], (uint16_t)(len - 1U));
804 }
805
806 #ifdef SERIAL_BTSNOOP
807 sbtsnoop_write_hci_pkt(data[0U], 1U, &data[1], (uint16_t)(len - 1U));
808 #endif
809
810 return kStatus_HAL_RL_RELEASE;
811 }
812
PLATFORM_SetBtCalData(void)813 static int PLATFORM_SetBtCalData(void)
814 {
815 int ret = 0;
816 uint8_t buffer[1 + HCI_CMD_PACKET_HEADER_LENGTH + HCI_CMD_STORE_BT_CAL_DATA_PARAM_LENGTH];
817 uint16_t opcode = get_opcode(HCI_CMD_VENDOR_OCG, HCI_CMD_STORE_BT_CAL_DATA_OCF);
818
819 PLATFORM_FillInHciCmdMsg(buffer, opcode, (uint8_t)sizeof(hci_cal_data_params), hci_cal_data_params);
820
821 ret = PLATFORM_SendHciMessage(buffer, sizeof(buffer));
822 if (ret != 0)
823 {
824 ret = -1;
825 }
826
827 return ret;
828 }
829
830 #if !defined(gPlatformDisableSetBtCalDataAnnex100_d) || (gPlatformDisableSetBtCalDataAnnex100_d == 0)
PLATFORM_SetBtCalDataAnnex100(void)831 static int PLATFORM_SetBtCalDataAnnex100(void)
832 {
833 int ret = 0;
834 uint8_t bufferAnnex100[1 + HCI_CMD_PACKET_HEADER_LENGTH + HCI_CMD_STORE_BT_CAL_DATA_PARAM_ANNEX100_LENGTH];
835 uint16_t opcodeAnnex100 = get_opcode(HCI_CMD_VENDOR_OCG, HCI_CMD_STORE_BT_CAL_DATA_ANNEX100_OCF);
836
837 PLATFORM_FillInHciCmdMsg(bufferAnnex100, opcodeAnnex100, (uint8_t)sizeof(hci_cal_data_annex100_params),
838 hci_cal_data_annex100_params);
839
840 ret = PLATFORM_SendHciMessage(bufferAnnex100, sizeof(bufferAnnex100));
841 if (ret != 0)
842 {
843 ret = -1;
844 }
845
846 return ret;
847 }
848 #endif /* gPlatformDisableSetBtCalDataAnnex100_d */
849
PLATFORM_HandleHciVendorEvent(uint8_t * eventData,uint32_t len)850 static bool PLATFORM_HandleHciVendorEvent(uint8_t *eventData, uint32_t len)
851 {
852 uint8_t vEventType = eventData[0];
853 bool handled = BLE_VENDOR_EVENT_HANDLE;
854
855 assert(eventData != NULL);
856 assert(len > 0U);
857
858 switch (vEventType)
859 {
860 case HCI_CMD_SET_BT_SLEEP_MODE_OCF:
861 break;
862
863 case HCI_EVT_PS_SLEEP_OCF:
864 (void)PLATFORM_HandleBlePowerStateEvent((ble_ps_event_t)eventData[1]);
865 break;
866
867 case HCI_CMD_BT_HIU_HS_ENABLE_OCF:
868 break;
869
870 default:
871 handled = false;
872 break;
873 }
874
875 return handled;
876 }
877
PLATFORM_HandleBlePowerStateEvent(ble_ps_event_t psEvent)878 static int PLATFORM_HandleBlePowerStateEvent(ble_ps_event_t psEvent)
879 {
880 int ret = 0;
881
882 switch (blePowerState)
883 {
884 case ble_awake_state:
885 {
886 switch (psEvent)
887 {
888 case ble_asleep_event:
889 blePowerState = ble_asleep_state;
890 /* Make sure to clear wake up event */
891 if (OSA_EventClear((osa_event_handle_t)wakeUpEventGroup, (uint32_t)ble_awake_event) !=
892 KOSA_StatusSuccess)
893 {
894 ret = -1;
895 }
896 break;
897
898 default:
899 break;
900 }
901 }
902 break;
903
904 case ble_asleep_state:
905 {
906 switch (psEvent)
907 {
908 case ble_awake_event:
909 blePowerState = ble_awake_state;
910 break;
911
912 default:
913 break;
914 }
915 }
916 break;
917
918 default:
919 ret = -1;
920 break;
921 }
922
923 return ret;
924 }
925
PLATFORM_BleSetHostSleepConfig(void)926 static int PLATFORM_BleSetHostSleepConfig(void)
927 {
928 int ret = 0;
929 /* This command must be sent before sending any power commands, likely
930 * after HCI init */
931 uint8_t buffer[1 + HCI_CMD_PACKET_HEADER_LENGTH + HCI_CMD_BT_HOST_SLEEP_CONFIG_PARAM_LENGTH];
932 uint16_t opcode = get_opcode(HCI_CMD_VENDOR_OCG, HCI_CMD_BT_HOST_SLEEP_CONFIG_OCF);
933 const uint8_t params[HCI_CMD_BT_HOST_SLEEP_CONFIG_PARAM_LENGTH] = {
934 0xFFU, // BT_HIU_WAKEUP_INBAND
935 0xFFU, // BT_HIU_WAKE_GAP_WAIT_FOR_IRQ
936 };
937
938 PLATFORM_FillInHciCmdMsg(buffer, opcode, (uint8_t)sizeof(params), params);
939
940 ret = PLATFORM_SendHciMessage(buffer, sizeof(buffer));
941 if (ret != 0)
942 {
943 ret = -1;
944 }
945
946 return ret;
947 }
948
PLATFORM_FillInHciCmdMsg(uint8_t * pbuf,uint16_t opcode,uint8_t msg_sz,const uint8_t * msg_payload)949 static void PLATFORM_FillInHciCmdMsg(uint8_t *pbuf, uint16_t opcode, uint8_t msg_sz, const uint8_t *msg_payload)
950 {
951 pbuf[0] = HCI_COMMAND_PACKET;
952 pbuf[1] = (uint8_t)(opcode & 0xff);
953 pbuf[2] = (uint8_t)((uint32_t)(opcode >> 8) & 0xff);
954 pbuf[3] = msg_sz;
955 (void)memcpy(&pbuf[4], msg_payload, msg_sz);
956 }
957