From 9585af8bf198986cc8d952dee08fbc930ca7b424 Mon Sep 17 00:00:00 2001 From: Dariel Zhang Date: Fri, 24 Jul 2026 19:17:37 +0800 Subject: [PATCH 1/5] drivers: ppt: add sync source and configurable log level Add ppt sync source files for rtl87x2g alongside the existing prebuilt libs. Source builds let CONFIG_PPT_DRIVER_LOG_LEVEL take effect at compile time, which is impossible with a .a. - Add src/ tree: ppt_driver, ppt_simple, ppt_sync (master+slave) - Add libppt_sync_slave.a as prebuilt fallback - Add REALTEK_USING_PPT_SYNC_SLAVE Kconfig option - Add PPT_DRIVER_LOG_LEVEL Kconfig (0=none..4=dbg, default 0) - Use zephyr_library_named(ppt_sync) to keep CMake object paths within Windows MAX_PATH limits - Compile master and slave objects unconditionally to satisfy ppt_sync.c references regardless of SYNC_ROLE_*_SUPPORT flags --- bee/drivers/ppt/CMakeLists.txt | 4 +- bee/drivers/ppt/Kconfig | 21 +- bee/drivers/ppt/sync/CMakeLists.txt | 22 +- .../ppt/sync/lib/rtl87x2g/CMakeLists.txt | 8 +- .../ppt/sync/lib/rtl87x2g/libppt_sync_slave.a | Bin 0 -> 868964 bytes .../ppt/sync/src/driver/common/DataType.h | 172 + .../ppt/sync/src/driver/common/bzdma.h | 223 + .../ppt/sync/src/driver/common/ll_common.h | 501 +++ .../ppt/sync/src/driver/common/ppt_driver.c | 1877 ++++++++ .../ppt/sync/src/driver/common/ppt_driver.h | 1215 +++++ .../ppt/sync/src/driver/common/ppt_hw_reg.h | 3896 +++++++++++++++++ .../src/driver/common/rtl87x2g/debug_port.h | 81 + .../src/driver/common/rtl87x2g/platform_rtc.h | 83 + .../common/rtl87x2g/power_manager_interface.h | 187 + .../common/rtl87x2g/power_manager_slave.h | 208 + .../rtl87x2g/power_manager_unit_platform.h | 200 + .../sync/src/driver/common/rtl87x2g/ppt_pf.h | 57 + .../sync/src/driver/common/rtl87x2g/ppt_pm.h | 59 + .../ppt/sync/src/driver/simple/ppt_simple.c | 504 +++ .../ppt/sync/src/driver/simple/ppt_simple.h | 330 ++ bee/drivers/ppt/sync/src/sync/inc/ppt_sync.h | 585 +++ .../ppt/sync/src/sync/inc/ppt_sync_master.h | 184 + .../ppt/sync/src/sync/inc/ppt_sync_slave.h | 116 + .../ppt/sync/src/sync/src/platform_list.c | 105 + .../ppt/sync/src/sync/src/platform_list.h | 90 + .../ppt/sync/src/sync/src/platform_macros.h | 165 + .../ppt/sync/src/sync/src/platform_types.h | 55 + bee/drivers/ppt/sync/src/sync/src/ppt_sync.c | 1643 +++++++ .../ppt/sync/src/sync/src/ppt_sync_int.h | 462 ++ .../ppt/sync/src/sync/src/ppt_sync_master.c | 2886 ++++++++++++ .../ppt/sync/src/sync/src/ppt_sync_pendcall.c | 41 + .../ppt/sync/src/sync/src/ppt_sync_pendcall.h | 67 + .../ppt/sync/src/sync/src/ppt_sync_slave.c | 1937 ++++++++ 33 files changed, 17980 insertions(+), 4 deletions(-) create mode 100644 bee/drivers/ppt/sync/lib/rtl87x2g/libppt_sync_slave.a create mode 100644 bee/drivers/ppt/sync/src/driver/common/DataType.h create mode 100644 bee/drivers/ppt/sync/src/driver/common/bzdma.h create mode 100644 bee/drivers/ppt/sync/src/driver/common/ll_common.h create mode 100644 bee/drivers/ppt/sync/src/driver/common/ppt_driver.c create mode 100644 bee/drivers/ppt/sync/src/driver/common/ppt_driver.h create mode 100644 bee/drivers/ppt/sync/src/driver/common/ppt_hw_reg.h create mode 100644 bee/drivers/ppt/sync/src/driver/common/rtl87x2g/debug_port.h create mode 100644 bee/drivers/ppt/sync/src/driver/common/rtl87x2g/platform_rtc.h create mode 100644 bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_interface.h create mode 100644 bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_slave.h create mode 100644 bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_unit_platform.h create mode 100644 bee/drivers/ppt/sync/src/driver/common/rtl87x2g/ppt_pf.h create mode 100644 bee/drivers/ppt/sync/src/driver/common/rtl87x2g/ppt_pm.h create mode 100644 bee/drivers/ppt/sync/src/driver/simple/ppt_simple.c create mode 100644 bee/drivers/ppt/sync/src/driver/simple/ppt_simple.h create mode 100644 bee/drivers/ppt/sync/src/sync/inc/ppt_sync.h create mode 100644 bee/drivers/ppt/sync/src/sync/inc/ppt_sync_master.h create mode 100644 bee/drivers/ppt/sync/src/sync/inc/ppt_sync_slave.h create mode 100644 bee/drivers/ppt/sync/src/sync/src/platform_list.c create mode 100644 bee/drivers/ppt/sync/src/sync/src/platform_list.h create mode 100644 bee/drivers/ppt/sync/src/sync/src/platform_macros.h create mode 100644 bee/drivers/ppt/sync/src/sync/src/platform_types.h create mode 100644 bee/drivers/ppt/sync/src/sync/src/ppt_sync.c create mode 100644 bee/drivers/ppt/sync/src/sync/src/ppt_sync_int.h create mode 100644 bee/drivers/ppt/sync/src/sync/src/ppt_sync_master.c create mode 100644 bee/drivers/ppt/sync/src/sync/src/ppt_sync_pendcall.c create mode 100644 bee/drivers/ppt/sync/src/sync/src/ppt_sync_pendcall.h create mode 100644 bee/drivers/ppt/sync/src/sync/src/ppt_sync_slave.c diff --git a/bee/drivers/ppt/CMakeLists.txt b/bee/drivers/ppt/CMakeLists.txt index c05cb37b..7dd2f50c 100644 --- a/bee/drivers/ppt/CMakeLists.txt +++ b/bee/drivers/ppt/CMakeLists.txt @@ -1,4 +1,6 @@ # Copyright (c) 2024 Realtek Semiconductor Corp. # SPDX-License-Identifier: Apache-2.0 -add_subdirectory_ifdef(CONFIG_REALTEK_USING_PPT_SYNC_MASTER sync) +if(CONFIG_REALTEK_USING_PPT_SYNC_MASTER OR CONFIG_REALTEK_USING_PPT_SYNC_SLAVE) + add_subdirectory(sync) +endif() diff --git a/bee/drivers/ppt/Kconfig b/bee/drivers/ppt/Kconfig index e8d43944..14a54cde 100644 --- a/bee/drivers/ppt/Kconfig +++ b/bee/drivers/ppt/Kconfig @@ -7,6 +7,25 @@ config REALTEK_PPT config REALTEK_USING_PPT_SYNC_MASTER bool "RealTek ppt master library" - default y if REALTEK_PPT + default y if REALTEK_PPT && !REALTEK_USING_PPT_SYNC_SLAVE + select USE_HAL_REALTEK_ENHTIM help This option enables the ppt master library for REALTEK MCU. + +config REALTEK_USING_PPT_SYNC_SLAVE + bool "RealTek ppt slave library" + default n + select USE_HAL_REALTEK_ENHTIM + help + This option enables the ppt slave library for REALTEK MCU. + Requires libppt_sync_slave.a placed in + modules/hal/realtek/bee/drivers/ppt/sync/lib/rtl87x2g/. + +config PPT_DRIVER_LOG_LEVEL + int "PPT 2.4G driver log level" + range 0 4 + default 0 + depends on REALTEK_PPT + help + Compile-time log level for PPT 2.4G driver modules. + 0=none 1=err 2=wrn 3=inf 4=dbg. Set in prj.conf. diff --git a/bee/drivers/ppt/sync/CMakeLists.txt b/bee/drivers/ppt/sync/CMakeLists.txt index 9dd82302..ba1380a0 100644 --- a/bee/drivers/ppt/sync/CMakeLists.txt +++ b/bee/drivers/ppt/sync/CMakeLists.txt @@ -1,4 +1,24 @@ # Copyright (c) 2024 Realtek Semiconductor Corp. # SPDX-License-Identifier: Apache-2.0 -add_subdirectory(lib) + +# add_subdirectory(lib) +# Compile from source so CONFIG_PPT_DRIVER_LOG_LEVEL takes effect at build time. +set(PPT_SYNC_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src) + +zephyr_library_named(ppt_sync) + +zephyr_library_sources(${PPT_SYNC_SRC}/driver/common/ppt_driver.c) +zephyr_library_sources(${PPT_SYNC_SRC}/driver/simple/ppt_simple.c) +zephyr_library_sources(${PPT_SYNC_SRC}/sync/src/platform_list.c) +zephyr_library_sources(${PPT_SYNC_SRC}/sync/src/ppt_sync.c) +zephyr_library_sources(${PPT_SYNC_SRC}/sync/src/ppt_sync_pendcall.c) +zephyr_library_sources(${PPT_SYNC_SRC}/sync/src/ppt_sync_master.c) +zephyr_library_sources(${PPT_SYNC_SRC}/sync/src/ppt_sync_slave.c) + zephyr_include_directories(inc) +zephyr_include_directories(${PPT_SYNC_SRC}) +zephyr_include_directories(${PPT_SYNC_SRC}/driver/common) +zephyr_include_directories(${PPT_SYNC_SRC}/driver/common/rtl87x2g) +zephyr_include_directories(${PPT_SYNC_SRC}/driver/simple) +zephyr_include_directories(${PPT_SYNC_SRC}/sync/inc) +zephyr_include_directories(${PPT_SYNC_SRC}/sync/src) diff --git a/bee/drivers/ppt/sync/lib/rtl87x2g/CMakeLists.txt b/bee/drivers/ppt/sync/lib/rtl87x2g/CMakeLists.txt index 3780c673..a9a2abea 100644 --- a/bee/drivers/ppt/sync/lib/rtl87x2g/CMakeLists.txt +++ b/bee/drivers/ppt/sync/lib/rtl87x2g/CMakeLists.txt @@ -1,4 +1,10 @@ # Copyright (c) 2024 Realtek Semiconductor Corp. # SPDX-License-Identifier: Apache-2.0 -zephyr_link_libraries(${CMAKE_CURRENT_SOURCE_DIR}/libppt_sync_master.a) +if(CONFIG_REALTEK_USING_PPT_SYNC_MASTER) + zephyr_link_libraries(${CMAKE_CURRENT_SOURCE_DIR}/libppt_sync_master.a) +endif() + +if(CONFIG_REALTEK_USING_PPT_SYNC_SLAVE) + zephyr_link_libraries(${CMAKE_CURRENT_SOURCE_DIR}/libppt_sync_slave.a) +endif() diff --git a/bee/drivers/ppt/sync/lib/rtl87x2g/libppt_sync_slave.a b/bee/drivers/ppt/sync/lib/rtl87x2g/libppt_sync_slave.a new file mode 100644 index 0000000000000000000000000000000000000000..fb8b45aae17fa0aca599120ea3704be8eef18137 GIT binary patch literal 868964 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%0DNV^!1eia3D;{}W0yr#NBWdyINyeiS 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:LdAFQ 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 + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +typedef unsigned char BOOLEAN; +typedef void VOID; +typedef unsigned char UCHAR; +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT32; +typedef unsigned long long UINT64; +typedef signed char CHAR; +typedef signed char INT8; +typedef signed short INT16; +typedef signed int INT32; +typedef signed long long INT64; + +/** @brief The upper 8 bits of a 16 bit value */ +#ifndef MSB +#define MSB(a) (((a) & 0xFF00) >> 8) +#endif + +/** @brief The lower 8 bits of a 16 bit value */ +#ifndef LSB +#define LSB(a) ((a) & 0xFF) +#endif + +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef CEIL +#define CEIL(a,b) (((a)+(b)-1)/(b)) +#endif + +#define KSEG1 0 +#define NON_DCATCH(ptr) ((UINT32)((UINT32)(ptr) | KSEG1 | BUFFER_RAM_START_ADDR)) +#define NON_DCATCH_PTR(ptr) ((UINT8*)NON_DCATCH(ptr)) + +#define RD_32BIT_IO(base, offset) \ + (*(volatile UINT32*)((base) + (offset))) +#define RD_16BIT_IO(base, offset) \ + (*((volatile UINT16*)((base) + (offset)))) +#define RD_8BIT_IO(base, offset) \ + (*((volatile UINT8*)((base) + (offset)))) +#define WR_32BIT_IO(base, offset, val) \ + (*((volatile UINT32*)((base) + (offset))) = (val)) +#define WR_16BIT_IO(base, offset, val) \ + (*((volatile UINT16*)((base) + (offset))) = (val)) +#define WR_8BIT_IO(base, offset, val) \ + (*((volatile UINT8*)((base) + (offset))) = (val)) + +#define RD_REG_MMIO(type, addr) \ + (*((volatile type*) (addr))) +#define WR_REG_MMIO(type, addr, val) \ + (*((volatile type*) (addr)) = (val)) + + +typedef union UINT32_S_ +{ + UINT32 u4Byte; + UINT16 u2Byte[2]; + UINT8 u1Byte[4]; +} UINT32_S; + +typedef union INT32_S_ +{ + INT32 s4Byte; + INT16 s2Byte[2]; + INT8 s1Byte[4]; +} INT32_S; + +typedef struct UINT32_BM_S_ +{ + UINT32 bit0: 1; + UINT32 bit1: 1; + UINT32 bit2: 1; + UINT32 bit3: 1; + UINT32 bit4: 1; + UINT32 bit5: 1; + UINT32 bit6: 1; + UINT32 bit7: 1; + UINT32 bit8: 1; + UINT32 bit9: 1; + UINT32 bit10: 1; + UINT32 bit11: 1; + UINT32 bit12: 1; + UINT32 bit13: 1; + UINT32 bit14: 1; + UINT32 bit15: 1; + UINT32 bit16: 1; + UINT32 bit17: 1; + UINT32 bit18: 1; + UINT32 bit19: 1; + UINT32 bit20: 1; + UINT32 bit21: 1; + UINT32 bit22: 1; + UINT32 bit23: 1; + UINT32 bit24: 1; + UINT32 bit25: 1; + UINT32 bit26: 1; + UINT32 bit27: 1; + UINT32 bit28: 1; + UINT32 bit29: 1; + UINT32 bit30: 1; + UINT32 bit31: 1; +} UINT32_BM_S; + +typedef struct UINT16_BM_S_ +{ + UINT16 bit0: 1; + UINT16 bit1: 1; + UINT16 bit2: 1; + UINT16 bit3: 1; + UINT16 bit4: 1; + UINT16 bit5: 1; + UINT16 bit6: 1; + UINT16 bit7: 1; + UINT16 bit8: 1; + UINT16 bit9: 1; + UINT16 bit10: 1; + UINT16 bit11: 1; + UINT16 bit12: 1; + UINT16 bit13: 1; + UINT16 bit14: 1; + UINT16 bit15: 1; +} UINT16_BM_S; + +typedef struct UINT8_BM_S_ +{ + UINT8 bit0: 1; + UINT8 bit1: 1; + UINT8 bit2: 1; + UINT8 bit3: 1; + UINT8 bit4: 1; + UINT8 bit5: 1; + UINT8 bit6: 1; + UINT8 bit7: 1; +} UINT8_BM_S; + +#define PLATFORM_WAKEUP_TIME_INFINITE 0xffffffff + +typedef enum +{ + ROUND = 0, + ROUNDUP = 1, + ROUNDDOWN = 2, +} RoundType; + +uint32_t us_to_rtc_clk(uint32_t us, RoundType round_type); + +uint32_t rtc_clk_to_us(uint32_t rtc_clk, RoundType round_type); + +uint64_t clk_cnt_diff(uint64_t clk_begin, uint64_t clk_end, uint64_t bit_mask); + +#endif // _DATATYPE_H_ + diff --git a/bee/drivers/ppt/sync/src/driver/common/bzdma.h b/bee/drivers/ppt/sync/src/driver/common/bzdma.h new file mode 100644 index 00000000..92b8733a --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/common/bzdma.h @@ -0,0 +1,223 @@ +/*************************************************************************** + Copyright (C) Realtek + This module is a confidential and proprietary property of Realtek and + a possession or use of this module requires written permission of Realtek. + ***************************************************************************/ + +/** + * \file + * BZDMA interface. + * + * \author + * austin , (C) 2010 + */ + +/** \addtogroup bb_driver_bzdma Baseband Driver Module + * @{ */ +#ifndef __BB_BZDMA_H__ +#define __BB_BZDMA_H__ + +#include +#include +#include "utils.h" + +#ifndef FALSE +#define FALSE false +#endif + +#ifndef TRUE +#define TRUE true +#endif + +typedef bool BOOLEAN; +typedef unsigned char UCHAR; +typedef uint8_t UINT8; +typedef uint16_t UINT16; +typedef uint32_t UINT32; +typedef uint64_t UINT64; +typedef signed char CHAR; +typedef int8_t INT8; +typedef int16_t INT16; +typedef int32_t INT32; +typedef int64_t INT64; + +//#include "DataType.h" +//#include "bt_fw_os.h" +//#include "platform.h" + +#define BZDMA_TX_DESC_ENTRY_NUM 13 +#define BZDMA_LEONLY_TX_MAX_ENTRY_NUM 11 + +/* ============= BZ DMA Register Structure (End) =================== */ +/* The structure of one segment of TX Descriptor */ +typedef struct BZDMA_TX_DESC_SEGMENT_ +{ + union + { + struct + { +#ifdef _NEW_BZDMA_FROM_V10_FROM_160223_ + UINT32 start_addr: 18; /* bit[17:0], the start address of SRAM */ +#else + UINT32 start_addr: 16; /* bit[15:0], the start address of SRAM */ + UINT32 rsvd0: 2; /* bit[17:16], reserved */ +#endif + UINT32 len: 11; /* bit[28:18], the copied length*/ + UINT32 rsvd1: 3; /* bit[31:29], reserved */ + }; + UINT32 DWord0; + }; + union + { + struct + { + UINT32 rsvd2: 29; /* bit[60:32], reserved */ + UINT32 llid: 2; /* bit[62:61], the link layer id */ + UINT32 isLast: 1; /* bit[63], is last segment */ + }; + UINT32 DWord1; + }; +} BZDMA_TX_DESC_SEGMENT, *PBZDMA_TX_DESC_SEGMENT; + + +typedef union BZDMA_BLEONLY_TX_DESC_SEGMENT_ +{ + struct + { + UINT32 start_addr: 16; /* bit[15:0], the start address of buffer SRAM + (note: it's 8 byte units in 18 bits mode) */ + UINT32 len: 11; /* bit[26:16], the copied length*/ +#if defined (_NEW_BZDMA_FROM_V10_) || defined (_BT5_0_LE_LONG_RANGE_SUPPORT_) + UINT32 le_ci: 2; /* bit[28:27], code indicator for LE LR */ +#else + UINT32 rsvd: 2; /* bit[28:27], reserved */ +#endif + UINT32 llid: 2; /* bit[30:29], the link layer id (only valid for data) */ + UINT32 isLast: 1; /* bit[31], is last segment (only valid for adv) + (170524) after _BT5_1_LE_CONNECTION_CTE_RECEIVE_TRANSMIT_PROCEDURE_ + this bit is used to indicate the sp flag in the header of + data channel pkt. */ + }; + UINT32 DWord; +} BZDMA_BLEONLY_TX_DESC_SEGMENT, *PBZDMA_BLEONLY_TX_DESC_SEGMENT; + +/* The structure of one fragment of TX Descriptor for BLE only */ +typedef union BZDMA_BLEONLY_TX_DESC_FRAGMENT_ +{ + struct + { + UINT32 start_addr: 16; /* bit[15:0], the start address of buffer SRAM */ + UINT32 len: 11; /* bit[26:16], the copied length*/ + UINT32 rsvd: 4; /* bit[30:27], reserved */ + UINT32 isLast: 1; /* bit[31], is last segment */ + }; + struct + { + UINT32 start_addr_ex: 18; /* bit[17:0], the start address of buffer SRAM */ + UINT32 len_ex: 11; /* bit[28:18], the copied length*/ + UINT32 rsvd_ex: 2; /* bit[30:29], reserved */ + UINT32 isLast_ex: 1; /* bit[31], is last segment */ + }; + UINT32 DWord; +} BZDMA_BLEONLY_TX_DESC_FRAGMENT, *PBZDMA_BLEONLY_TX_DESC_FRAGMENT; + +/* The structure of TX descriptor entry status of BZDMA */ +typedef struct BZDMA_TX_DESC_ENTRY_STATUS_ +{ + BZDMA_TX_DESC_SEGMENT *pTxDesc; /* The pointer of multiple segments */ + UINT32 entry_idx: 4; /* my entry index*/ + UINT32 pkt_id: 2; /* current packet Id */ + UINT32 ch_id: 2; /* current channel Id */ + UINT32 used: 1; /* the entry is used ? */ + UINT32 piconet_id: 2; /* current piconet id */ + UINT32 total_len: 11; /* the total data length of all segments */ + UINT32 total_segs: 4; /* the total segements */ + UINT32 is_sco_lt: 1; /* the logic link is sco ? */ +#ifndef _NEW_HCI_DMA_DESIGN_FOR_ACL_SNIFF_SCHEDULE_ + UINT32 rsvd1: 5; /* reserved */ +#else + UINT32 is_in_sniff: 1; /* the command is used in sniff ? */ + UINT32 rsvd1: 4; /* reserved */ +#endif + + UINT32 le_mode_en: 1; /* Enable Txdma in LE mode */ + UINT32 le_pdu_cmd: 2; /* The Txdma PDU Command Type in LE */ + UINT32 le_conn_entry: 6; /* Connection Entry in LE */ + UINT32 lt_addr: 3; /* my lt_addr */ + UINT32 rsvd2: 20; /* reserved */ +} BZDMA_TX_DESC_ENTRY_STATUS, *PBZDMA_TX_DESC_ENTRY_STATUS; + +/* Fragment memory layout: + * adv_id=0 adv_id=1 adv_id=2 + * +----------------+----------------+----------------+ + * seg_id=0 | MAX_FRAGS_ADV0 | MAX_FRAGS_ADV1 | MAX_FRAGS_ADV2 | + * +----------------+----------------+----------------+ + * seg_id=1 | MAX_FRAGS_ADV0 | MAX_FRAGS_ADV1 | MAX_FRAGS_ADV2 | + * +----------------+----------------+----------------+ + */ +/* The structure of Ble Advertising TX descriptor entry status of BZDMA */ +typedef struct BZDMA_BLEONLY_ADV_TX_DESC_ENTRY_STATUS_ +{ + BZDMA_BLEONLY_TX_DESC_SEGMENT *pTxSegDesc; /* The start pointer of multiple segments */ + BZDMA_BLEONLY_TX_DESC_FRAGMENT *pTxFragDesc; /* The start pointer of multiple fragments */ + UINT32 max_segs: 2; /* maximum segments in the tx descriptor */ + UINT32 max_frags: 4; /* maximum fragments in one segment */ + UINT32 le_pdu_cmd: 2; /* The Txdma PDU Command Type in LE */ +#ifdef BT5_0_LE_ADV_EXT_SUPPORT_NEW_BZDMA_FOR_LR_AUX_SCAN_RSP + UINT32 frag_row_num: 8; /* Number of fragments in a row */ + UINT32 rsvd1: 16; /* reserved */ +#else + UINT32 rsvd1: 24; /* reserved */ +#endif + UINT8 TxSegDescOffset[3]; /* the start offset array of Tx segment descriptor for every advertising channels */ + UINT8 TxFragDescOffset[3]; /* the start offset array of Tx fragment descriptor for every advertising channels */ +} BZDMA_BLEONLY_ADV_TX_DESC_ENTRY_STATUS, *PBZDMA_BLEONLY_ADV_TX_DESC_ENTRY_STATUS; + +/* The structure of Ble Data TX descriptor entry status of BZDMA */ +typedef struct BZDMA_BLEONLY_DATA_TX_DESC_ENTRY_STATUS_ +{ + BZDMA_BLEONLY_TX_DESC_SEGMENT *pTxSegDesc; /* The pointer of multiple segments */ + BZDMA_BLEONLY_TX_DESC_FRAGMENT *pTxFragDesc; /* The pointer of multiple fragment */ + UINT32 seg_wptr: 4; /* write pointer of segment table */ + UINT32 seg_rptr: 4; /* read pointer of segment table */ + UINT32 free_segs: 5; /* free packet counts of segment table */ + UINT32 rsvd1: 3; /* reserved */ + UINT32 bm_used_segs: 16; /* index bitmap of used segments */ +} BZDMA_BLEONLY_DATA_TX_DESC_ENTRY_STATUS, *PBZDMA_BLEONLY_DATA_TX_DESC_ENTRY_STATUS; + +#ifdef _NEW_BZDMA_FROM_V20_200512_ +#ifdef _BT5_0_LE_ADV_EXT_PA_HW_ +/* The structure of Ble PA Data TX descriptor entry status of BZDMA */ +typedef struct BZDMA_PA_TX_DESC_ENTRY_STATUS_ +{ + BZDMA_BLEPA_TX_DESC_SEGMENT *pTxSegDesc; /* The pointer of multiple segments */ + BZDMA_BLEPA_TX_DESC_FRAGMENT *pTxFragDesc; /* The pointer of multiple fragment */ + UINT32 seg_wptr: 4; /* write pointer of segment table (0~15) */ + UINT32 seg_rptr: 4; /* read pointer of segment table (0~15) */ + UINT32 free_segs: 5; /* free packet counts of segment table (max ?) */ + UINT32 rsvd1: 19; /* reserved */ +} BZDMA_PA_TX_DESC_ENTRY_STATUS, *PBZDMA_PA_TX_DESC_ENTRY_STATUS; +#endif +#endif /* end of _NEW_BZDMA_FROM_V20_200512_ */ + +/* The structure of management of BZDMA */ +typedef struct BZDMA_MANAGE_ +{ + BZDMA_TX_DESC_ENTRY_STATUS TxEntSta[BZDMA_TX_DESC_ENTRY_NUM]; + UINT16 bmFreeTxEnt; /* free Tx command entry bitmap */ + + + BZDMA_BLEONLY_ADV_TX_DESC_ENTRY_STATUS BleAdvTxEntSta; + BZDMA_BLEONLY_DATA_TX_DESC_ENTRY_STATUS BleTxEntSta[BZDMA_LEONLY_TX_MAX_ENTRY_NUM]; + UINT16 bmFreeBleTxEnt; /* free Tx command entry bitmap */ + +#ifdef _BT5_0_LE_ADV_EXT_PA_HW_ + BZDMA_PA_TX_DESC_ENTRY_STATUS BlePATxEntSta[BZDMA_LEONLY_PA_TX_MAX_ENTRY_NUM]; +#endif +} BZDMA_MANAGE, *PBZDMA_MANAGE; + + +extern BZDMA_MANAGE Bzdma_Manager; + +#endif /* __BB_BZDMA_H__ */ + diff --git a/bee/drivers/ppt/sync/src/driver/common/ll_common.h b/bee/drivers/ppt/sync/src/driver/common/ll_common.h new file mode 100644 index 00000000..d0bcd47c --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/common/ll_common.h @@ -0,0 +1,501 @@ +#ifndef __LL_COMMON_H__ +#define __LL_COMMON_H__ + +//#include "DataType.h" +#include "rtl876x.h" +#include "bzdma.h" + +#define _NEW_BZDMA_FROM_V7_ +#define _NEW_BZDMA_FROM_V8_ +#define _NEW_BZDMA_FROM_V10_FROM_160223_ +#define _NEW_BZDMA_FROM_V10_ +#define _BT5_0_LE_LONG_RANGE_SUPPORT_ +#define _VENDOR_REG_MUST_UPDATE_FROM_RTL_SVN_VER_17316_ +#define _RTL8821A_ +#define _NEW_MODEM_PI_ACCESS_ +#define _NEW_MODEM_PI_ACCESS_BY_VENDOR_REG_ + +#define BT_SOC_VENDOR_BASE_ADDR VENDOR_REG_BASE//0x40006000 +#define BT_SOC_VENDOR_ADDR(offs) (BT_SOC_VENDOR_BASE_ADDR + (offs)) +#define BTMAC_ISR_STATUS_REG_ADDR BT_SOC_VENDOR_ADDR(0x10) + +/* The Base of LE Register */ +#define LE_REG_BASE 0x40051000 +#define PPT_REG_ADDR_BASE (BLUEWIZ_REG_BASE + 0x1c00) + +/* =========================== vendor section ===========================*/ +/* note: vender reg can be access by 1, 2, 4 byte */ +/* note: should be 1-byte aligned, 2-byte aligned, 4-byte aligned */ +#define VENDOR_BASE_ADDRESS 0x40058000 +#define BT_SYS_CTRL_REG 0x00 + +#define VENDOR_REG_ADDR(offset) (VENDOR_BASE_ADDRESS + (offset)) +#define VENDOR_READ(Vendor_offset) \ + ((uint32_t)*((volatile uint32_t*)(VENDOR_BASE_ADDRESS+(Vendor_offset)))) +#define VENDOR_WRITE(Vendor_offset, Value) \ + ((*((volatile uint32_t*)(VENDOR_BASE_ADDRESS + (Vendor_offset)))) = (Value)) +#define VENDOR_U16_READ(Vendor_offset) \ + ((uint16_t)*((volatile uint16_t*)(VENDOR_BASE_ADDRESS+(Vendor_offset)))) +#define VENDOR_U16_WRITE(Vendor_offset, Value) \ + ((*((volatile uint16_t*)(VENDOR_BASE_ADDRESS + (Vendor_offset)))) = (Value)) +#define VENDOR_BYTE_READ(Vendor_offset) \ + (*((volatile uint8_t*)(VENDOR_BASE_ADDRESS+(Vendor_offset)))) +#define VENDOR_BYTE_WRITE(Vendor_offset, Value) \ + ((*((volatile uint8_t*)(VENDOR_BASE_ADDRESS + (Vendor_offset)))) = (Value)) +#define VENDOR_U32_REG_UPDATE(Vendor_offset, Mask, Value) \ + {uint32_t temp; \ + temp = VENDOR_READ(Vendor_offset) & ~(Mask);\ + VENDOR_WRITE(Vendor_offset, (temp | ((Value) & (Mask))));} +#define VENDOR_U16_REG_UPDATE(Vendor_offset, Mask, Value) \ + {uint16_t temp; \ + temp = VENDOR_U16_READ(Vendor_offset) & ~(Mask);\ + VENDOR_U16_WRITE(Vendor_offset, (temp | ((Value) & (Mask))));} +#define VENDOR_U8_REG_UPDATE(Vendor_offset, Mask, Value) \ + {uint8_t temp; \ + temp = VENDOR_BYTE_READ(Vendor_offset) & ~(Mask);\ + VENDOR_BYTE_WRITE(Vendor_offset, (temp | ((Value) & (Mask))));} + +#define RD_32BIT_IO(base, offset) \ + (*(volatile UINT32*)((base) + (offset))) +#define RD_16BIT_IO(base, offset) \ + (*((volatile UINT16*)((base) + (offset)))) +#define RD_8BIT_IO(base, offset) \ + (*((volatile UINT8*)((base) + (offset)))) +#define WR_32BIT_IO(base, offset, val) \ + (*((volatile UINT32*)((base) + (offset))) = (val)) +#define WR_16BIT_IO(base, offset, val) \ + (*((volatile UINT16*)((base) + (offset))) = (val)) +#define WR_8BIT_IO(base, offset, val) \ + (*((volatile UINT8*)((base) + (offset))) = (val)) + +#define RD_U32_BZDMA_REG(offset) HAL_READ32(BZDMA_REG_BASE, offset)//RD_32BIT_IO(BZDMA_REG_BASE, offset) +#define RD_U16_BZDMA_REG(offset) HAL_READ16(BZDMA_REG_BASE, offset)//RD_16BIT_IO(BZDMA_REG_BASE, offset) +#define RD_U8_BZDMA_REG(offset) HAL_READ8(BZDMA_REG_BASE, offset)//RD_8BIT_IO(BZDMA_REG_BASE, offset) +#define WR_U32_BZDMA_REG(offset, val) HAL_WRITE32(BZDMA_REG_BASE, offset, val)//WR_32BIT_IO(BZDMA_REG_BASE, offset, val) +#define WR_U16_BZDMA_REG(offset, val) HAL_WRITE16(BZDMA_REG_BASE, offset, val)//WR_16BIT_IO(BZDMA_REG_BASE, offset, val) +#define WR_U8_BZDMA_REG(offset, val) HAL_WRITE8(BZDMA_REG_BASE, offset, val)//WR_8BIT_IO(BZDMA_REG_BASE, offset, val) + +#ifdef _NEW_BZDMA_FROM_V10_ +#define BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(num) ((((num)&BIT3)?0x464:0x440)+(((num)&0x7) << 2)) +#else +#define BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(num) (0x440 + ((num) << 2)) +#endif + +/* BZ DMA register list */ +#define BZDMA_REG_BASE 0x40058000 +#define BZDMA_REG_ACL_RXFIFO_PTR 0x33C + +#define BZDMA_ACL_RXFIFO_PTR_LEN 11 +#define BZDMA_ACL_RXFIFO_PTR_MASK 0x7FF +#define BZDMA_ACL_RXFIFO_WPTR(reg_value) ((reg_value) & BZDMA_ACL_RXFIFO_PTR_MASK) +#define BZDMA_ACL_RXFIFO_RPTR(reg_value) (((reg_value) >> 16) & BZDMA_ACL_RXFIFO_PTR_MASK) +#define BZDMA_ACL_RXFIFO_GET_LEFT(reg) (((reg.wr_ptr >= reg.rd_ptr ? reg.wr_ptr - reg.rd_ptr : reg.wr_ptr + (1 << BZDMA_ACL_RXFIFO_PTR_LEN) - reg.rd_ptr) + reg.rdbuffer_cnt) << 1) + +/* Structure of Vendor Register 0x33C(BZDMA_REG_ACL_RXFIFO_PTR)*/ +typedef union ACL_RX_FIFO_S_ +{ + uint32_t d32; + struct + { + UINT32 wr_ptr: 11; /* [10:0] write pointer (unit: word)*/ + UINT32 rsvd1: 5; /* [15:11] reserved */ + UINT32 rd_ptr: 11; /* [26:16] read pointer (unit: word) */ + UINT32 rdbuffer_cnt: 2; /* [28:27] read_buffer_cnt (unit: word)(Range:0~2)*/ +UINT32 rxfifo_busy: + 1; /* [29] Rx fifo busy indicator. 1: HW is pushing data into acl rx fifo or sco fifo. */ + UINT32 rsvd2: 2; /* [31:30] reserved 2 */ + }; +} ACL_RX_FIFO_S, *PACL_RX_FIFO_S; + +/* Baseband Control */ +#define INSTRUCTION_REGISTER 0x00 +#define CONNECTOR_REGISTER 0x02 + +#define BB_BASE_ADDR 0x40050000 +#define BB_REG_ADDR(offset) (BB_BASE_ADDR + (offset)) + +/* BASEBAND CONTROLLER OPCODES (Please Sort the OpCode) */ +#define BB_NOP 0X00 +#define BB_INQUIRY 0X01 +#define BB_INQUIRY_SCAN_FIRST_ID 0X02 +#define BB_PAGE 0X03 +#define BB_PAGE_SCAN 0X04 +#define BB_EXECUTE 0X05 +#define BB_HOLD 0X06 +#define BB_SNIFF 0X07 +#define BB_START_BEACON 0X08 +#define BB_UNPARK_REQUEST 0X09 +#define BB_INQUIRY_SCAN_SECOND_ID 0X0A +#define BB_START_SLEEP 0X0B +#ifdef _RTL8821A_ +#define BB_INQUIRY_SCAN_3DD 0X0B +#endif +#define BB_FLUSH 0X0C +#define BB_SEND_PACKET 0X0D +#define BB_MASTER_SLAVE_SWITCH 0X0E +#define BB_LOAD_OFFSET 0X0F + +#define BB_SWITCH_MASTER_PICONET 0X10 +#define BB_PRIORITY_SEND_PACKET 0x12 +#define BB_KILL_SEND_PACKET 0x13 +#ifdef _RTL8821A_ +#define BB_START_SYNC_3DD 0x14 +#define BB_START_BEACON_3DD 0x15 +#endif +#ifdef _SUPPORT_CSB_RECEIVER_ +#define BB_START_SYNC_SCAN_3DD 0x16 +#define BB_START_CSB_SCAN_3DD 0x17 +#endif +#define BB_SWITCH_SLAVE_PICONET 0x18 + +#define BB_SEQN_INIT 0x21 +#define BB_SEND_FLOW_ZERO 0x23 +#define BB_SEND_FLOW_GO 0x24 + +#define BB_START_TPOLL 0x30 +#define BB_STOP_TPOLL 0x31 +#ifdef _NEW_MODEM_PSD_SCAN_ +#define BB_SET_PSD_EN 0x40 +#define BB_CLR_PSD_EN 0x41 +#endif + +#define BB_RESET 0X80 +#define BB_KILL_INQUIRY 0X81 +#define BB_KILL_PAGE 0X82 +#define BB_KILL_HOLD 0X83 +#define BB_KILL_SNIFF 0x84 +#define BB_KILL_SLEEP 0x85 +#define BB_EXIT_BEACON 0x86 +#define BB_KILL_BEACON 0x87 +#define BB_ENTER_SNIFF_TRAN_MODE_DURING_EXIT_SNIFF_MASTER 0x89 +#define BB_ENTER_SNIFF_TRAN_MODE 0x89 +#define BB_EXIT_SNIFF_TRAN_MODE 0x8c +#ifdef _RTL8821A_ +#ifdef _DAPE_TEST_NEW_HW_INSTRUCTION +#define BB_KILL_INQUIRY_SCAN 0X8E +#define BB_KILL_PAGE_SCAN 0X8F +#endif +#define BB_KILL_SYNC_3DD 0x90 +#define BB_KILL_BEACON_3DD 0x91 +#endif +#ifdef _SUPPORT_CSB_RECEIVER_ +#define BB_KILL_SYNC_SCAN_3DD 0x92 +#define BB_KILL_CSB_SCAN_3DD 0x93 +#endif + +extern uint16_t (*rfc_reg_read)(uint8_t addr); +extern void (*rfc_reg_write)(uint8_t addr, uint16_t wdata); +extern void (*rfc_reg_update)(uint8_t addr, uint16_t mask, uint16_t wdata); + +#define RTK_READ_RFC_REG_PI(addr) rfc_reg_read(addr) +#define RTK_WRITE_RFC_REG_PI(addr, wdata) rfc_reg_write(addr, wdata) +#define RTK_UPDATE_RFC_REG_PI(addr, mask, wdata) rfc_reg_update(addr, mask, wdata) + +#ifdef _NEW_MODEM_PI_ACCESS_ +extern uint16_t (*modem_pi_read)(uint8_t, uint8_t); +extern void (*modem_pi_write)(uint8_t, uint8_t, uint16_t); +extern void (*modem_pi_update)(uint8_t, uint8_t, uint16_t, uint16_t); + +#define RTK_READ_MODEM_REG_PI(modem_page, waddr) modem_pi_read(modem_page, waddr) +#define RTK_WRITE_MODEM_REG_PI(modem_page, waddr, value) modem_pi_write(modem_page, waddr, value) +#define RTK_UPDATE_MODEM_REG_PI(modem_page, waddr, bm_mask, value) modem_pi_update(modem_page, waddr, bm_mask, value) +#endif + +#ifdef _NEW_MODEM_PI_ACCESS_ +#ifdef _NEW_MODEM_PI_ACCESS_BY_VENDOR_REG_ +#define REG_BTPHY_FPI_IF 0x354 +#define PI_READ_MODEM_REG 0 +#define PI_WRITE_MODEM_REG 1 +/* Read wiki page for more information about modem PI access: * + * https://wiki.realtek.com/pages/viewpage.action?pageId=20234492 */ +typedef union +{ + struct + { + UINT32 pi_wrdata : 16; //[15: 0] rw + UINT32 pi_modem_addr : 6; //[21:16] rw + UINT32 pi_modem_page : 6; //[27:22] rw + UINT32 pi_wr : 1; //[28] w, 0: read modem, 1: write modem + UINT32 bt_phy_pi_en : 1; //[29] r + UINT32 pi_rising_edge_time: 2; //[31:30] rw, 4*(1 + pi_rising_edge_time)T must >= 100ns + }; + UINT32 u32; +} MODEM_PI_VENDOR_REG_S; +#else +extern UINT8 g_efuse_modem_pi_enable; +#endif +extern UINT8 g_modem_init_completed; +#define MODEM_PI_PAGE_0 0 +#define MODEM_PI_PAGE_1 1 +#define MODEM_PI_PAGE_2 2 +#define MODEM_PI_PAGE_3 3 +#define MODEM_PI_PAGE_4 4 +#define MODEM_PI_PAGE_5 5 +#define MODEM_PI_PAGE_6 6 +#define MODEM_PI_PAGE_7 7 +#define MODEM_PI_PAGE_NEW_AGC_TAB0 8 +#define MODEM_PI_PAGE_NEW_AGC_TAB1 9 +#define MODEM_PI_PAGE_NEW_AGC_TAB2 10 +#define MODEM_PI_PAGE_NEW_AGC_TAB3 11 +#define MODEM_PI_PAGE_OLD_AGC_TAB0 12 +#define MODEM_PI_PAGE_W_ADDR0_START MODEM_PI_PAGE_NEW_AGC_TAB0 +#define MODEM_PI_PAGE_W_ADDR0_END MODEM_PI_PAGE_OLD_AGC_TAB0 +#endif +#define TRANS_MODEM_REG(x) (x) /* this is used to translate byte address to word address*/ + +typedef union BTMAC_ISR_STATUS_REG_ +{ + struct + { + __I uint32_t timer_intr1_intr0_r: 1; + __I uint32_t bluewiz_intr_r: 1; + __I uint32_t RESERVED_1: 1; + __I uint32_t bluewiz_dma_intr_r: 1; + __I uint32_t pro_intr_r: 1; + __I uint32_t RESERVED_0: 27; + }; + UINT32 d32; +} BTMAC_ISR_STATUS_REG; + +///* The structure of one fragment of TX Descriptor for BLE only */ +//typedef union BZDMA_BLEONLY_TX_DESC_FRAGMENT_ +//{ +// struct +// { +// UINT32 start_addr: 16; /* bit[15:0], the start address of buffer SRAM */ +// UINT32 len: 11; /* bit[26:16], the copied length*/ +// UINT32 rsvd: 4; /* bit[30:27], reserved */ +// UINT32 isLast: 1; /* bit[31], is last segment */ +// }; +//#ifdef _NEW_BZDMA_FROM_V10_FROM_160223_ +// struct +// { +// UINT32 start_addr_ex: 18; /* bit[17:0], the start address of buffer SRAM */ +// UINT32 len_ex: 11; /* bit[28:18], the copied length*/ +// UINT32 rsvd_ex: 2; /* bit[30:29], reserved */ +// UINT32 isLast_ex: 1; /* bit[31], is last segment */ +// }; +//#endif +// UINT32 DWord; +//} BZDMA_BLEONLY_TX_DESC_FRAGMENT, *PBZDMA_BLEONLY_TX_DESC_FRAGMENT; + +/* The structure of one segment of RX Descriptor (only use for FW) */ +typedef union BZDMA_RX_DESC_SEGMENT_ +{ + struct + { +#ifdef _NEW_BZDMA_FROM_V10_FROM_160223_ + UINT32 addr: 18; /* the address of destination address */ +#else + UINT32 addr: 16; /* the address of destination address */ +#endif + UINT32 len: 11; /* the copied byte length */ + UINT32 flush: 1; /* flush data from rx fifo */ +#ifdef _NEW_BZDMA_FROM_V10_FROM_160223_ + UINT32 rsvd: 2; /* reserved */ +#else + UINT32 rsvd: 4; /* reserved */ +#endif + }; + UINT32 DWord; +} BZDMA_RX_DESC_SEGMENT, *PBZDMA_RX_DESC_SEGMENT; + +/*====================================================================*/ +/* Table 2.3 LLID Defination */ +/*====================================================================*/ +enum LL_LLID_SET +{ + LL_LLID_RSVD = 0, + LL_LLID_DATA_PDU_CONT = 1, + LL_LLID_EMPTY_PDU = 1, + LL_LLID_DATA_PDU_START = 2, + LL_LLID_CTRL_PDU = 3 +}; + +/* The HW Definition of Tx/Rx PID */ +enum BZDMA_PID_ +{ + BZDMA_TX_PID_ACL = 0, +#ifdef _SUPPORT_CSB_TRANSMITTER_ + BZDMA_TX_PID_CSB = 1, +#endif + BZDMA_TX_PID_BCST = 2, + BZDMA_TX_PID_SCO = 3, +#ifdef _NEW_BZDMA_FROM_V7_ + BZDMA_TX_PID_NEW_SCO = 4, +#endif + BZDMA_TX_PID_TOTAL = 3, + BZDMA_RX_PID_ACL = 0, + BZDMA_RX_PID_SCO = 1, + BZDMA_RX_PID_TOTAL = (BZDMA_RX_PID_SCO + 1), +}; + +/* + * bit + * 0:1: System clock: + * 00b: 5MHz; 01b:10MHz; 10b:20MHz; 11b:40MHz + * 2: Bluewiz reset: + * defaut: 0,reset state + * 1: disable reset + * 3:4: + * 5: Modem reset: + * defaut: 0,reset state + * 1: disable reset + * 6:7: + * 8: The extern clock gat of bluewiz: + * defaut: 0,gating state + * 1: disable gating + * 9: CPU clock 40MHz switch: + * defaut: 0, 32K mode + * 1: enable 40MHz mode + * 10: Exist bluewiz seep sleep mode: + * defaut: 0 + * 1: force leaving deep sleep mode + * 11:31: +**/ +typedef union BT_SYS_CTRL_REG_S +{ + UINT32 d32; + struct + { + UINT32 sys_clk : 2; //bit0~1 + UINT32 bluewiz_rst : 1; //bit2 + UINT32 reserved_3_4 : 2; //bit3~4 + UINT32 modem_rst : 1; //bit5 + UINT32 pon_rst_n : 1; //bit6 + UINT32 pro_rst_n : 1; //bit7 + UINT32 external_bluewiz_clk_gat : 1; //bit8 + UINT32 cpu_40mhz_en : 1; //bit9 + UINT32 exist_deep_sleep_mode : 1; //bit10 +#ifdef _VENDOR_REG_MUST_UPDATE_FROM_RTL_SVN_VER_17316_ + UINT32 reserved_11_15 : 5; //bit 11~15 + UINT32 rfc_ret_rst_n : 1; //bit16, RFC retention register reset + UINT32 rfc_rst_n : 1; //bit17, RFC off domain register reset + UINT32 bb_ret_rst_n : 1; //bit18, modem retention FF reset + UINT32 modem_clk_force_on : 1; //bit19, force modem clock always on + UINT32 dp_bb_ret_rst_n : 1; //bit20 + UINT32 reserved_21_24 : 5; //bit21~24 + UINT32 zb_ret_rst_n : 1; //bit25 + UINT32 pro_clk_en : 1; //bit26 + UINT32 rfc_clk_force_on : 1; //bit27 + UINT32 bt_le_clk_en : 1; //bit28, le clock enable + UINT32 bt_br_edr_clk_en : 1; //bit29, BR EDR clock enable + UINT32 force_adda_iso_off : 1; //bit30, force ADDA isolation release. + UINT32 force_adda_pwron : 1; //bit31, force ADDA LDO on +#else + UINT32 reserved_11_31 : 21; //bit 11~31 +#endif + } b; +} BT_SYS_CTRL_REG_S_TYPE; + +typedef union MODEM_PSD_REPORT_ELEMENT_S +{ + UINT32 d32_array[4]; + struct // RTL8703B, RTL8822B, RTL8821B-MP + { + UINT32 psd_avg_dc: 16; //[15:0] + UINT32 psd_avg_pos: 16; ///[31:16] + UINT32 psd_avg_neg: 16; //[47:32] + UINT32 mp_gain_idx: 6; //[53:48] + UINT32 gnt_bt_hold: 1; //[54] + UINT32 gnt_bt_final: 1; //[55] + + UINT32 psd_u28_scale: 3; //[58:56] + UINT32 gnt_wl_hold: 1; //[59] + UINT32 gnt_wl_final: 1; //[60] + UINT32 wifi_txon_hold: 1; //[61] + UINT32 wifi_txon_final: 1; //[62] + UINT32 rsvd1: 1; //[63] + + UINT32 psd_avg_dfir_lsb: 16; //[79:64] + UINT32 psd_avg_dfir_msb: 4; //[83:80] + UINT32 psd_clip_ratio_start: 6; //[89:84] + UINT32 psd_clip_ratio_end: 6; //[95:90] + + UINT32 agc_ste: 4; //[99:96] + UINT32 lna_idx_use: 3; //[102:100] + UINT32 bpf_mode_use: 1; //[103] + UINT32 gain_idx_use: 5; //[108:104] + UINT32 rf_pd_en_use: 1; //[109] + UINT32 signature_bit: 2; //[111:110] //default 2'b0 + UINT32 rsvd3: 16; //[127:112] + }; +} MODEM_PSD_REPORT_ELEMENT_S_TYPE; + +typedef UINT8(*PF_ROM_CODE_PATCH_FUNC)(void *buf, ...); +extern PF_ROM_CODE_PATCH_FUNC rcp_BTMAC_Handler; +extern PF_ROM_CODE_PATCH_FUNC rcp_BB_handle_psd_end_intr_func; + +extern UINT8 bzdma_supported_le_max_seg_num; +extern MODEM_PSD_REPORT_ELEMENT_S_TYPE *g_modem_psd_report_array; +extern UINT16 g_modem_psd_report_entry_num; +extern uint16_t g_efuse_modem_psd_setting_1; +#define PSD_CHANN_NUM 128 +#define PSD_TIMEOUT_DEFAULT 0x3ff +#define PPT_RF_MODE 0 +extern int8_t (*calculate_log_from_rssi)(uint16_t, uint8_t, uint8_t); + +#define RD_REG_MMIO(type, addr) \ + (*((volatile type*) (addr))) +#define WR_REG_MMIO(type, addr, val) \ + (*((volatile type*) (addr)) = (val)) + +__STATIC_INLINE BTMAC_ISR_STATUS_REG btmac_isr_status_reg_read(void) +{ + return RD_REG_MMIO(BTMAC_ISR_STATUS_REG, BTMAC_ISR_STATUS_REG_ADDR); +} + +#if defined (_NEW_BZDMA_FROM_V10_) || defined (_BT5_0_LE_LONG_RANGE_SUPPORT_) +extern UINT8(*bzdma_send_packet_to_ble_data_ring_fifo)(UINT8 conn_entry, UINT8 llid, + BZDMA_BLEONLY_TX_DESC_FRAGMENT *pfrag, UINT8 Nfrag, + UINT8 codeind); +#else +UINT8 bzdma_send_packet_to_ble_data_ring_fifo(UINT8 conn_entry, UINT8 llid, + BZDMA_BLEONLY_TX_DESC_FRAGMENT *pfrag, UINT8 Nfrag); + +#endif +UINT8 bzdma_update_fw_rptr_of_ble_data_ring_fifo(UINT8 conn_entry, + UINT8 free_cnts, UINT8 check_cam); + + +void bzdma_send_burst_rxcmd_and_wait_complete(BZDMA_RX_DESC_SEGMENT *pRxDesc, + UINT8 cmd_count, UINT8 fifo_type, UINT8 sco_ch_id, UINT8 wait_complete); +void bzdma_flush_ble_data_ring_fifo(UINT8 conn_entry); +void bzdma_reset_ble_data_ring_fifo(UINT8 conn_entry); +void bzdma_ble_set_segment_valid_bitmap(UINT8 entry, UINT16 bitmap); +void bzdma_generic_ble_seg_cmd_clear(UINT8 entry, BOOLEAN clear_all); + +/* Prototypes of Baseband Driver Functions exported to other modules */ +UINT16 BB_read_baseband_register(UINT16 bb_reg_offset); +void BB_write_baseband_register(UINT16 bb_reg_offset, UINT16 val); +uint16_t BB_read_native_counter(void); +uint32_t BB_read_native_clock(void); + +#ifdef _NEW_MODEM_PI_ACCESS_ +UCHAR lc_read_modem_pi_page(void); +void lc_write_modem_pi_page(UCHAR modem_page); +extern UINT32(*rtk_read_modem_radio_reg_pi)(UCHAR modem_page, UCHAR addr, UCHAR type); +extern void (*rtk_write_modem_radio_reg_pi)(UCHAR modem_page, UCHAR addr, UCHAR type, UINT16 val); +#endif + +void phy_init(UINT8 dlps_flow); +extern void (*ll_hw_init)(UINT8); + +extern uint32_t (*platform_rtc_get_counter)(void); + +extern uint32_t (*lowerstack_SystemCall)(uint32_t, uint32_t, uint32_t, + uint32_t); + +#ifndef COUNT_ARG +#define ARG_T(t) t +#define ARG_N(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,N,...) N +#define ARG_N_HELPER(...) ARG_T(ARG_N(__VA_ARGS__)) +#define COUNT_ARG1(...) ARG_N_HELPER(__VA_ARGS__,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0) +#define COUNT_ARG(...) ((COUNT_ARG1(1, ##__VA_ARGS__) - 1)%21) +#endif + +#ifndef UNUSED +#define UNUSED(expr) do { (void)(expr); } while (0) +#endif + +#endif diff --git a/bee/drivers/ppt/sync/src/driver/common/ppt_driver.c b/bee/drivers/ppt/sync/src/driver/common/ppt_driver.c new file mode 100644 index 00000000..c8cc2a33 --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/common/ppt_driver.c @@ -0,0 +1,1877 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_driver.c + * @brief Source file for 2.4G module common driver. + * @details + * @author bill + * @date 2022-09-21 + * @version v0.1 + * ************************************************************************************* + */ + +#include +#include +#include +//#include "trace.h" +#include "rtl876x.h" +#include "rtl_pinmux.h" +#include "utils.h" +#include "os_sync.h" +#include "os_mem.h" +#include "ppt_driver.h" +#include "ppt_pm.h" +#include "ppt_pf.h" + +LOG_MODULE_REGISTER(ppt_driver, PPT_DRIVER_LOG_LEVEL); + +#define PPT_LOG_METHOD 0 //!< 0: no log, 1: buffer log, 2: direct log +#define PPT_REG_ACCESS_TIM_DBG (TIME_DEBUG && 0) +#define PPT_WR_WI_RD_BACK 0 + +#define PPT_IS_ASIC 1 +#define PATCH_BTMAC_ISR 1 + +void bt_retention_domain_reset(void); + +bool ppt_enable = false; +volatile bool ppt_dlps_mac_idle = true; +bool (*ppt_dlps_mac_check_cb)(uint32_t *wakeup_time_diff); +void (*ppt_dlps_mac_enter_cb)(void); +void (*ppt_dlps_mac_exit_cb)(void); +void (*ppt_dlps_pf_enter_cb)(void); +void (*ppt_dlps_pf_exit_cb)(void); +bool ppt_tx_fixed_rptr[PPT_ENTRY_NUM]; + +struct +{ + uint32_t modem_acc[PPT_ENTRY_RX_NUM]; + uint8_t modem_acc_enable; + uint8_t acc_byte3_flag; //!< 0: not set, 1: set for 1M, 2: set for 2M + bool acc_similar_flag; + bool mod_index_2m_flag; +} ppt_fw_ret; + +#if PATCH_BTMAC_ISR +void *btmac_handler_temp; +#endif +void *bb_psd_handler_temp; + +static void (*ppt_isr_handler)(void); +static void (*ppt_psd_isr_handler)(void); + +#if TIME_DEBUG +uint8_t test_pin[] = {TEST_PIN}; +uint32_t gpio_test_pin[sizeof(test_pin)]; +bool level_test_pin[sizeof(test_pin)]; +#endif + +static PPT_DAT_SECTION ppt_reg_field_t ppt_lut_prefix[PPT_ENTRY_RX_NUM] = +{ + {PRO_PREFIX0_1, 0}, {PRO_PREFIX0_1, 8}, + {PRO_PREFIX2_3, 0}, {PRO_PREFIX2_3, 8}, + {PRO_PREFIX4_5, 0}, {PRO_PREFIX4_5, 8}, + {PRO_PREFIX6_7, 0}, {PRO_TX_ADDRESS_UPPER, 8} +}; + +static PPT_DAT_SECTION uint16_t ppt_lut_tx_addr[PPT_ENTRY_RX_NUM] = +{ + PRO_TX_ADDRESS_LOWER, PRO_TX_ADDRESS1_LOWER, + PRO_TX_ADDRESS2_LOWER, PRO_TX_ADDRESS3_LOWER, + PRO_TX_ADDRESS4_LOWER, PRO_TX_ADDRESS5_LOWER, + PRO_TX_ADDRESS6_LOWER, PRO_TX_ADDRESS7_LOWER, +}; + +PPT_API_SECTION uint8_t ppt_swap_bits8(uint8_t inp) +{ + uint8_t retval = 0; + for (uint8_t i = 0; i < 8; i++) + { + retval |= ((inp >> i) & 0x01) << (7 - i); + } + return retval; +} + +PPT_API_SECTION uint16_t ppt_swap_bits16(uint16_t inp) +{ + uint16_t retval = 0; + for (uint8_t i = 0; i < 16; i++) + { + retval |= ((inp >> i) & 0x01) << (15 - i); + } + return retval; +} + +PPT_API_SECTION uint32_t ppt_swap_bits24(uint32_t inp) +{ + uint32_t retval = 0; + for (uint8_t i = 0; i < 24; i++) + { + retval |= ((inp >> i) & 0x01) << (23 - i); + } + return retval; +} + +PPT_API_SECTION uint32_t ppt_swap_bits32(uint32_t inp) +{ + uint32_t retval = 0; + for (uint8_t i = 0; i < 32; i++) + { + retval |= ((inp >> i) & 0x01) << (31 - i); + } + return retval; +} + +bool btmac_pm_check_inactive(void); +bool btmac_pm_check_active(void); +void btmac_pm_initiate_wakeup(void); +bool ppt_dlps_check_active(void); +bool ppt_dlps_check_inactive(void); +void ppt_dlps_initiate_wakeup(void); + +PPT_API_SECTION uint16_t ppt_read_reg_dlps(uint16_t offset) +{ + uint16_t reg; + + /* wake up btmac */ + if (btmac_pm_check_inactive()) + { + btmac_pm_initiate_wakeup(); + } + /* wake up ppt */ + if (ppt_dlps_check_inactive()) + { + ppt_dlps_initiate_wakeup(); + } + +#if PPT_REG_ACCESS_TIM_DBG + time_debug_level_high(TIME_DEBUG_CHAN_THREAD); +#endif + reg = HAL_READ16(PPT_REG_ADDR_BASE, offset); +#if PPT_REG_ACCESS_TIM_DBG + time_debug_level_low(TIME_DEBUG_CHAN_THREAD); +#endif + +#if PPT_LOG_METHOD == 2 + DBG_DIRECT("PPTR %04x=%04x", offset, reg); +#elif PPT_LOG_METHOD == 1 + APP_PRINT_INFO2("ppt_read_reg: offset 0x%04x, value 0x%04x", offset, reg); +#endif + return reg; +} + +PPT_API_SECTION uint16_t ppt_read_reg_active(uint16_t offset) +{ + return HAL_READ16(PPT_REG_ADDR_BASE, offset); +} + +PPT_API_SECTION void ppt_write_reg_dlps(uint16_t offset, uint16_t data) +{ + /* wake up btmac */ + if (btmac_pm_check_inactive()) + { + btmac_pm_initiate_wakeup(); + } + /* wake up ppt */ + if (ppt_dlps_check_inactive()) + { + ppt_dlps_initiate_wakeup(); + } + +#if PPT_REG_ACCESS_TIM_DBG + time_debug_level_high(TIME_DEBUG_CHAN_THREAD); +#endif + HAL_WRITE16(PPT_REG_ADDR_BASE, offset, data); +#if PPT_REG_ACCESS_TIM_DBG + time_debug_level_low(TIME_DEBUG_CHAN_THREAD); +#endif + +#if PPT_LOG_METHOD + uint16_t read_data = 0; +#if PPT_WR_WI_RD_BACK + read_data = ppt_read_reg(offset); +#endif +#endif +#if PPT_LOG_METHOD == 2 + DBG_DIRECT("PPTW %04x=%04x(r%04x)", offset, data, read_data); +#elif PPT_LOG_METHOD == 1 + APP_PRINT_INFO3("ppt_write_reg: offset 0x%04x, value 0x%04x(r%04x)", offset, data, read_data); +#endif +} + +PPT_API_SECTION void ppt_write_reg_active(uint16_t offset, uint16_t data) +{ + HAL_WRITE16(PPT_REG_ADDR_BASE, offset, data); +} + +uint16_t ppt_read_reg_def(uint16_t offset) +{ + ppt_read_reg = ppt_read_reg_active; + ppt_write_reg = ppt_write_reg_active; + return ppt_read_reg_dlps(offset); +} + +void ppt_write_reg_def(uint16_t offset, uint16_t data) +{ + ppt_read_reg = ppt_read_reg_active; + ppt_write_reg = ppt_write_reg_active; + ppt_write_reg_dlps(offset, data); +} + +uint16_t (*ppt_read_reg)(uint16_t offset) = ppt_read_reg_def; +void (*ppt_write_reg)(uint16_t offset, uint16_t data) = ppt_write_reg_def; + +PPT_API_SECTION void ppt_reset_core_domain(bool ppt_mode) +{ + { + /* proprietary reset */ + BT_SYS_CTRL_REG_S_TYPE bt_sys_reg; + bt_sys_reg.d32 = VENDOR_READ(BT_SYS_CTRL_REG); + bt_sys_reg.b.pro_rst_n = 0; + VENDOR_WRITE(BT_SYS_CTRL_REG, bt_sys_reg.d32); + + bt_sys_reg.b.pro_rst_n = 1; + VENDOR_WRITE(BT_SYS_CTRL_REG, bt_sys_reg.d32); + } + + { + /* clock enable */ + BT_SYS_CTRL_REG_S_TYPE bt_sys_reg; + bt_sys_reg.d32 = VENDOR_READ(BT_SYS_CTRL_REG); + bt_sys_reg.b.pro_clk_en = ppt_mode; + VENDOR_WRITE(BT_SYS_CTRL_REG, bt_sys_reg.d32); + platform_delay_us(2); + } + ppt_enable = ppt_mode; +} + +PPT_API_SECTION void ppt_lock(uint8_t entry, bool lock) +{ + assert_param(entry < PPT_ENTRY_NUM); + + if (entry <= 1) + { + PRO_TX_CONTROL_TYPE tx_ctrl = {.d16 = RD_PPT_REG(PRO_TX_CONTROL)}; + if (entry == 0) + { + tx_ctrl.lock0 = lock; + } + else if (entry == 1) + { + tx_ctrl.lock1 = lock; + } + WR_PPT_REG(PRO_TX_CONTROL, tx_ctrl.d16); + } + else if (entry == 2) + { + PRO_GPIO_CONTROL_TYPE gpio_ctrl = {.d16 = RD_PPT_REG(PRO_GPIO_CONTROL)}; + gpio_ctrl.lock2 = lock; + WR_PPT_REG(PRO_GPIO_CONTROL, gpio_ctrl.d16); + } + else + { + uint16_t offset = PRO_WHITE_FIELD_LENGTH_0 + ((entry - PPT_ENTRY_NUM_ORI) << 1); + PRO_WHITE_FIELD_LENGTH_0_TYPE len0 = {.d16 = RD_PPT_REG(offset)}; + len0.lock3 = lock; + WR_PPT_REG(offset, len0.d16); + } +} + +void ppt_set_auto_no_tx(uint8_t entry, bool enable) +{ + assert_param(entry < PPT_ENTRY_NUM); + uint16_t pro_reg; + if (entry == 0) + { + pro_reg = RD_PPT_REG(PRO_TX_CONTROL); + ((PRO_TX_CONTROL_TYPE *)(&pro_reg))->auto_no_tx_option0 = enable; + WR_PPT_REG(PRO_TX_CONTROL, pro_reg); + } + else if (entry == 1) + { + pro_reg = RD_PPT_REG(PRO_TRX_CONTROL); + ((PRO_TRX_CONTROL_TYPE *)(&pro_reg))->auto_no_tx_option1 = enable; + WR_PPT_REG(PRO_TRX_CONTROL, pro_reg); + } + else if (entry == 2) + { + pro_reg = RD_PPT_REG(PRO_GPIO_CONTROL); + ((PRO_GPIO_CONTROL_TYPE *)(&pro_reg))->auto_no_tx_option2 = enable; + WR_PPT_REG(PRO_GPIO_CONTROL, pro_reg); + } + else + { + uint16_t offset = PRO_WHITE_FIELD_LENGTH_0 + ((entry - PPT_ENTRY_NUM_ORI) << 1); + PRO_WHITE_FIELD_LENGTH_0_TYPE len0 = {.d16 = RD_PPT_REG(offset)}; + len0.auto_no_tx_option3 = enable; + WR_PPT_REG(offset, len0.d16); + } +} + +void ppt_clear_hw_no_tx(uint8_t entry) +{ + assert_param(entry < PPT_ENTRY_NUM); + if (entry <= 1) + { + PRO_TX_CONTROL_TYPE tx_ctrl = {.d16 = RD_PPT_REG(PRO_TX_CONTROL)}; + if (entry == 0) + { + tx_ctrl.clear_hw_no_tx0 = 1; + } + else if (entry == 1) + { + tx_ctrl.clear_hw_no_tx1 = 1; + } + WR_PPT_REG(PRO_TX_CONTROL, tx_ctrl.d16); + } + else if (entry == 2) + { + PRO_GPIO_CONTROL_TYPE gpio_ctrl = {.d16 = RD_PPT_REG(PRO_GPIO_CONTROL)}; + gpio_ctrl.clear_hw_no_tx2 = 1; + WR_PPT_REG(PRO_GPIO_CONTROL, gpio_ctrl.d16); + } + else + { + uint16_t offset = PRO_WHITE_FIELD_LENGTH_0 + ((entry - PPT_ENTRY_NUM_ORI) << 1); + PRO_WHITE_FIELD_LENGTH_0_TYPE len0 = {.d16 = RD_PPT_REG(offset)}; + len0.clear_hw_no_tx3 = 1; + WR_PPT_REG(offset, len0.d16); + } +} + +void ppt_set_auto_ack(uint8_t entry, bool enable) +{ + assert_param(entry < PPT_ENTRY_NUM); + if (entry == 0) + { + WR_PPT_REG_FIELD(PRO_FIELD_LENGTH, auto_no_rdptr_option0, enable); + } + else if (entry == 1) + { + WR_PPT_REG_FIELD(PRO_TRX_CONTROL, auto_no_rdptr_option1, enable); + } + else if (entry == 2) + { + WR_PPT_REG_FIELD(PRO_GPIO_CONTROL, auto_no_rdptr_option2, enable); + } + else + { + uint16_t offset = PRO_FIELD_LENGTH3 + ((entry - PPT_ENTRY_NUM_ORI) << 1); + PRO_FIELD_LENGTH3_TYPE len3 = {.d16 = RD_PPT_REG(offset)}; + len3.auto_no_rdptr_option3 = enable; + WR_PPT_REG(offset, len3.d16); + } +} + +PPT_API_SECTION uint8_t ppt_trigger_fw_ack_lock(uint8_t entry, bool lock) +{ + assert_param(entry < PPT_ENTRY_NUM); + if (ppt_tx_fixed_rptr[entry]) + { + ppt_tx_fixed_rptr[entry] = false; + if (entry < PPT_ENTRY_NUM_ORI) + { + PRO_PACKET_RELATED_CONTROL0_TYPE pkt_related_ctrl = {.d16 = RD_PPT_REG(PRO_ENTRY_FIELD_BASE(entry) + PRO_PACKET_RELATED_CONTROL0)}; + pkt_related_ctrl.auto_add_rptr_option0 = 0; + WR_PPT_REG(PRO_ENTRY_FIELD_BASE(entry) + PRO_PACKET_RELATED_CONTROL0, + pkt_related_ctrl.d16); + } + else + { + uint16_t offset = ((entry - PPT_ENTRY_NUM_ORI) << 1) + PRO_FIELD_LENGTH3; + PRO_FIELD_LENGTH3_TYPE field_len = {.d16 = RD_PPT_REG(offset)}; + field_len.auto_add_rptr_option3 = 0; + WR_PPT_REG(offset, field_len.d16); + } + } + /* Fw trigger rptr ++ */ +#if PPT_LOG_METHOD == 1 + UINT32 cur_bm = RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(entry)); +#endif + if (lock) + { + ppt_lock(entry, true); + } + if (entry < PPT_ENTRY_NUM_ORI) + { + PRO_PACKET_RELATED_CONTROL0_TYPE pkt_related_ctrl = {.d16 = RD_PPT_REG(PRO_ENTRY_FIELD_BASE(entry) + PRO_PACKET_RELATED_CONTROL0)}; + pkt_related_ctrl.fw_ack0 = TRUE; // w1 ack, hw auto pull down in next T + WR_PPT_REG(PRO_ENTRY_FIELD_BASE(entry) + PRO_PACKET_RELATED_CONTROL0, pkt_related_ctrl.d16); + } + else + { + uint16_t offset = ((entry - PPT_ENTRY_NUM_ORI) << 1) + PRO_WHITE_FIELD_LENGTH_0; + PRO_WHITE_FIELD_LENGTH_0_TYPE white_field_len = {.d16 = RD_PPT_REG(offset)}; + white_field_len.fw_ack3 = 0; + WR_PPT_REG(offset, white_field_len.d16); + } + //bzdma_update_fw_rptr_of_ble_data_ring_fifo(entry, 1, FALSE); + uint8_t rd_ptr = Bzdma_Manager.BleTxEntSta[entry].seg_rptr; + rd_ptr = (rd_ptr + 1) & (bzdma_supported_le_max_seg_num - 1); + Bzdma_Manager.BleTxEntSta[entry].seg_rptr = rd_ptr; + Bzdma_Manager.BleTxEntSta[entry].free_segs += 1; + if (lock) + { + ppt_lock(entry, false); + } +#if PPT_LOG_METHOD == 1 + APP_PRINT_INFO4("ppt_trigger_fw_ack: BZDMA bitmap 0x%x -> 0x%x, entry %d, rd ptr %d", cur_bm, + RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(entry)), entry, + rd_ptr); +#endif + return rd_ptr; +} + +PPT_API_SECTION uint8_t ppt_force_fw_ack(uint8_t entry) +{ + assert_param(entry < PPT_ENTRY_NUM); + uint8_t rd_ptr = Bzdma_Manager.BleTxEntSta[entry].seg_rptr; + uint32_t cur_bm = RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(entry)); + uint32_t cur_bm_n = cur_bm & (~(1 << rd_ptr)); + WR_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(entry), cur_bm_n); + + rd_ptr = (rd_ptr + 1) & (bzdma_supported_le_max_seg_num - 1); + if (entry < PPT_ENTRY_NUM_ORI) + { + PRO_PACKET_RELATED_CONTROL0_TYPE pkt_related_ctrl = {.d16 = RD_PPT_REG(PRO_ENTRY_FIELD_BASE(entry) + PRO_PACKET_RELATED_CONTROL0)}; + pkt_related_ctrl.auto_add_rptr_option0 = 1; + pkt_related_ctrl.fw_rd_ptr0 = rd_ptr; + WR_PPT_REG(PRO_ENTRY_FIELD_BASE(entry) + PRO_PACKET_RELATED_CONTROL0, pkt_related_ctrl.d16); + } + else + { + uint16_t offset = ((entry - PPT_ENTRY_NUM_ORI) << 1) + PRO_FIELD_LENGTH3; + PRO_FIELD_LENGTH3_TYPE field_len = {.d16 = RD_PPT_REG(offset)}; + field_len.auto_add_rptr_option3 = 1; + field_len.fw_rd_ptr3 = rd_ptr; + WR_PPT_REG(offset, field_len.d16); + } + + Bzdma_Manager.BleTxEntSta[entry].seg_rptr = rd_ptr; + Bzdma_Manager.BleTxEntSta[entry].free_segs += 1; + + ppt_tx_fixed_rptr[entry] = true; +#if PPT_LOG_METHOD == 1 + APP_PRINT_INFO4("ppt_force_fw_ack: BZDMA bitmap 0x%x -> 0x%x, entry %d, rd ptr %d", cur_bm, + RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(entry)), entry, + rd_ptr); +#endif + //pkt_related_ctrl.auto_add_rptr_option0 = 0; + //WR_PPT_REG(PRO_PACKET_RELATED_CONTROL0, pkt_related_ctrl.d16); + //bzdma_update_fw_rptr_of_ble_data_ring_fifo(entry, 1, FALSE); + return rd_ptr; +} + +void ppt_flush_rx_fifo(void) +{ + BT_SYS_CTRL_REG_S_TYPE bt_sys_reg; + bt_sys_reg.d32 = VENDOR_READ(BT_SYS_CTRL_REG); + bt_sys_reg.b.bt_br_edr_clk_en = 1; + VENDOR_WRITE(BT_SYS_CTRL_REG, bt_sys_reg.d32); + BB_write_baseband_register(INSTRUCTION_REGISTER, BB_FLUSH); + bt_sys_reg.b.bt_br_edr_clk_en = 0; + VENDOR_WRITE(BT_SYS_CTRL_REG, bt_sys_reg.d32); +} + +void ppt_flush_trx_int(void) +{ + while (1) + { + PRO_MISR_TYPE reg_misr; + reg_misr.d16 = RD_PPT_REG(PRO_MISR); + if (reg_misr.tx_int) + { + PRO_TX_LENGTH_TYPE length = {.d16 = RD_PPT_REG(PRO_TX_LENGTH)}; // Read 0x6a to pop stack + } + else if (reg_misr.rx_int) + { + PRO_RX_HS_UPPER_TYPE rx_hs_upper = {.d16 = RD_PPT_REG(PRO_RX_HS_UPPER)}; //read 0x62 to pop rx stack + } + else + { + break; + } + } +} + +void ppt_reset_trx_pkt_num(void) +{ + PRO_CBK_CTRL_TYPE cbk_ctrl = {.d16 = RD_PPT_REG(PRO_CBK_CTRL)}; + cbk_ctrl.reset_rx_pktnum0 = 1; + cbk_ctrl.reset_rx_pktnum1 = 1; + cbk_ctrl.reset_tx_pktnum0 = 1; + cbk_ctrl.reset_tx_pktnum1 = 1; + WR_PPT_REG(PRO_CBK_CTRL, cbk_ctrl.d16); + cbk_ctrl.reset_rx_pktnum0 = 0; + cbk_ctrl.reset_rx_pktnum1 = 0; + cbk_ctrl.reset_tx_pktnum0 = 0; + cbk_ctrl.reset_tx_pktnum1 = 0; + WR_PPT_REG(PRO_CBK_CTRL, cbk_ctrl.d16); + + PRO_WHITE_FIELD_LENGTH_0_TYPE white0 = {.d16 = RD_PPT_REG(PRO_WHITE_FIELD_LENGTH_0)}; + white0.reset_rx_pktnum2 = 1; + WR_PPT_REG(PRO_WHITE_FIELD_LENGTH_0, white0.d16); + white0.reset_rx_pktnum2 = 0; + WR_PPT_REG(PRO_WHITE_FIELD_LENGTH_0, white0.d16); + + for (uint8_t entry = PPT_ENTRY_NUM_ORI; entry < PPT_ENTRY_NUM; entry++) + { + uint8_t offset = PRO_WHITE_FIELD_LENGTH_0 + ((entry - PPT_ENTRY_NUM_ORI) << 1); + PRO_WHITE_FIELD_LENGTH_0_TYPE white0 = {.d16 = RD_PPT_REG(offset)}; + white0.reset_rx_pktnum3 = 1; + WR_PPT_REG(offset, white0.d16); + white0.reset_rx_pktnum3 = 0; + WR_PPT_REG(offset, white0.d16); + } + + PRO_GPIO_CONTROL_TYPE gpio_ctrl = {.d16 = RD_PPT_REG(PRO_GPIO_CONTROL)}; + gpio_ctrl.reset_tx_pktnum2 = 1; + WR_PPT_REG(PRO_GPIO_CONTROL, gpio_ctrl.d16); + gpio_ctrl.reset_tx_pktnum2 = 0; + WR_PPT_REG(PRO_GPIO_CONTROL, gpio_ctrl.d16); + + for (uint8_t entry = PPT_ENTRY_NUM_ORI; entry < PPT_ENTRY_NUM; entry++) + { + uint8_t offset = PRO_FIELD_LENGTH3 + ((entry - PPT_ENTRY_NUM_ORI) << 1); + PRO_FIELD_LENGTH3_TYPE len3 = {.d16 = RD_PPT_REG(offset)}; + len3.reset_tx_pktnum3 = 1; + WR_PPT_REG(offset, len3.d16); + len3.reset_tx_pktnum3 = 0; + WR_PPT_REG(offset, len3.d16); + } +} + +PPT_API_SECTION void ppt_execute_instruction(uint8_t instruction_code) +{ + if (instruction_code >= PPT_HW_INSTRUCTION_MAX) + { +#if PPT_LOG_METHOD == 1 + APP_PRINT_ERROR1("ppt_execute_instruction: unknown instruction %d", instruction_code); +#endif + return; + } + +#if PPT_LOG_METHOD == 1 + APP_PRINT_INFO1("ppt_execute_instruction: execute %d", instruction_code); +#endif + PRO_INSTRUCTION_TYPE inst;// = {.d16 = RD_PPT_REG(PRO_INSTRUCTION)}; + inst.instruction = instruction_code; + WR_PPT_REG(PRO_INSTRUCTION, inst.d16); +} + +void ppt_set_crc_param(ppt_crc_param_t *param) +{ + PRO_PACKET_CONTROL_TYPE pkt_ctrl = {.d16 = RD_PPT_REG(PRO_PACKET_CONTROL)}; + pkt_ctrl.crc_field_length_2_0 = param->len; + /* not support feature, shall not be set to true */ + //pkt_ctrl.crc_include_addr_enable = param->include_addr; + WR_PPT_REG(PRO_PACKET_CONTROL, pkt_ctrl.d16); +} + +void ppt_set_crc_entry_param(uint8_t entry, ppt_crc_entry_param_t *param) +{ + assert_param(entry < PPT_ENTRY_NUM); + + /* poly */ + uint16_t crc_poly_lower = param->value.poly & 0xffff; + uint16_t crc_poly_upper = (param->value.poly >> 16) & 0xffff; + uint16_t reg_offset_poly = PPT_REG_OFFSET(entry, PRO_CRC_POLYNOMIAL_REG0_LOWER, + PRO_ONE_ENTRY_DW_SIZE, PRO_CRC_POLYNOMIAL_REG3_LOWER, 4); + WR_PPT_REG(reg_offset_poly, crc_poly_lower); + WR_PPT_REG(reg_offset_poly + 2, crc_poly_upper); + + /* init tx */ + uint16_t crc_init_lower = param->value.init & 0xffff; + uint16_t crc_init_upper = (param->value.init >> 16) & 0xffff; + uint16_t reg_offset_init = PPT_REG_OFFSET(entry, PRO_CRC_INIT0_LOWER, PRO_ONE_ENTRY_DW_SIZE, + PRO_CRC_INIT3_LOWER, 4); + WR_PPT_REG(reg_offset_init, crc_init_lower); + WR_PPT_REG(reg_offset_init + 2, crc_init_upper); + + /* init rx */ + uint16_t reg_offset_init_rx = PPT_REG_OFFSET(entry, PRO_RX_CRC_INIT0_LOWER, 4, + PRO_RX_CRC_INIT3_LOWER, 4); + WR_PPT_REG(reg_offset_init_rx, crc_init_lower); + WR_PPT_REG(reg_offset_init_rx + 2, crc_init_upper); +} + +void ppt_set_white_param(ppt_white_param_t *param) +{ + PRO_PACKET_CONTROL_TYPE packet_ctrl = {.d16 = RD_PPT_REG(PRO_PACKET_CONTROL)}; + packet_ctrl.white_dis = !param->enable; + WR_PPT_REG(PRO_PACKET_CONTROL, packet_ctrl.d16); +} + +void ppt_set_white_entry_param(uint8_t entry, ppt_white_entry_param_t *param) +{ + assert_param(entry < PPT_ENTRY_NUM); + + if (entry < PPT_ENTRY_NUM_ORI) + { + PRO_PACKET_RELATED_CONTROL0_TYPE pkt_related_ctrl = {.d16 = RD_PPT_REG(PRO_ENTRY_FIELD_BASE(entry) + PRO_PACKET_RELATED_CONTROL0)}; + pkt_related_ctrl.white_field_length0 = param->len; + WR_PPT_REG(PRO_ENTRY_FIELD_BASE(entry) + PRO_PACKET_RELATED_CONTROL0, pkt_related_ctrl.d16); + } + else + { + uint16_t offset = ((entry - PPT_ENTRY_NUM_ORI) << 1) + PRO_WHITE_FIELD_LENGTH_0; + PRO_WHITE_FIELD_LENGTH_0_TYPE white0 = {.d16 = RD_PPT_REG(offset)}; + white0.white_field_length3 = param->len; + WR_PPT_REG(offset, white0.d16); + } + + uint16_t white_poly_l = param->value.poly & 0xffff; + uint16_t white_poly_u = (param->value.poly >> 16) & 0xffff; + uint16_t reg_offset_poly = PPT_REG_OFFSET(entry, PRO_WHITE_POLYNOMIAL_REG0_LOWER, + PRO_ONE_ENTRY_DW_SIZE, PRO_WHITE_POLYNOMIAL_REG3_LOWER, 4); + WR_PPT_REG(reg_offset_poly, white_poly_l); + WR_PPT_REG(reg_offset_poly + 2, white_poly_u); + + uint16_t white_ini_l = param->value.init & 0xffff; + uint16_t white_ini_u = (param->value.init >> 16) & 0xffff; + uint16_t reg_offset_init = PPT_REG_OFFSET(entry, PRO_WHITE_INIT0_LOWER, PRO_ONE_ENTRY_DW_SIZE, + PRO_WHITE_INIT3_LOWER, 4); + WR_PPT_REG(reg_offset_init, white_ini_l); + WR_PPT_REG(reg_offset_init + 2, white_ini_u); +} + +void ppt_set_phy_rx_type(ppt_phy_type_t rx_phy) +{ + PRO_RX_CONTROL_TYPE rx_ctrl = {.d16 = RD_PPT_REG(PRO_RX_CONTROL)}; + rx_ctrl.rx_phy = rx_phy; + WR_PPT_REG(PRO_RX_CONTROL, rx_ctrl.d16); +} + +void ppt_set_phy_tx_type(uint8_t entry, ppt_phy_type_t tx_phy) +{ + assert_param(entry < PPT_ENTRY_NUM); + + if (entry < PPT_ENTRY_NUM_ORI) + { + PRO_PACKET_RELATED_CONTROL0_TYPE pkt_related_ctrl = {.d16 = RD_PPT_REG(PRO_ENTRY_FIELD_BASE(entry) + PRO_PACKET_RELATED_CONTROL0)}; + pkt_related_ctrl.tx_phy0 = tx_phy; + WR_PPT_REG(PRO_ENTRY_FIELD_BASE(entry) + PRO_PACKET_RELATED_CONTROL0, pkt_related_ctrl.d16); + } + else + { + PRO_WHITE_FIELD_LENGTH_0_TYPE white_field_len = {.d16 = RD_PPT_REG(2 * (entry - PPT_ENTRY_NUM_ORI) + PRO_WHITE_FIELD_LENGTH_0)}; + white_field_len.tx_phy3 = tx_phy; + WR_PPT_REG(2 * (entry - PPT_ENTRY_NUM_ORI) + PRO_WHITE_FIELD_LENGTH_0, white_field_len.d16); + } +} + +void ppt_set_phy_mod_index_2m(bool change) +{ + if (ppt_fw_ret.mod_index_2m_flag != change) + { + if (change) + { + RTK_WRITE_MODEM_REG_PI(MODEM_PI_PAGE_7, TRANS_MODEM_REG(0x14), 0xfb34); + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_7, TRANS_MODEM_REG(0x16), 0x3, 0x1); + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_2, TRANS_MODEM_REG(0x14), 0x3f, 0x28); + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_6, TRANS_MODEM_REG(0x2c), 0x3fe, 0x50); + } + else + { + RTK_WRITE_MODEM_REG_PI(MODEM_PI_PAGE_7, TRANS_MODEM_REG(0x14), 0xffd3); + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_7, TRANS_MODEM_REG(0x16), 0x3, 0x2); + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_2, TRANS_MODEM_REG(0x14), 0x3f, 0x1a); + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_6, TRANS_MODEM_REG(0x2c), 0x3fe, 0x84); + } + ppt_fw_ret.mod_index_2m_flag = change; + } +} + +PPT_API_SECTION void ppt_set_phy_bank(uint8_t bank_idx) +{ + /* deprecated */ +} + +PPT_API_SECTION bool ppt_set_phy_channel(uint16_t freq_mhz) +{ + uint8_t bank = 0, channel = 0; + if (false == ppt_get_bank_channel(freq_mhz, &bank, &channel)) + { + return false; + } + PRO_CHANNEL_BANK_TYPE ch_bank; + ch_bank.bank = bank; + ch_bank.pro_channel = channel; + WR_PPT_REG(PRO_CHANNEL_BANK, ch_bank.d16); + /* update phy parameters along with rf bank */ + ppt_set_phy_bank(bank); + return true; +} + +void ppt_set_phy_param(ppt_phy_param_t *param) +{ + PRO_CHANNEL_BANK_TYPE ch_bank = {.d16 = RD_PPT_REG(PRO_CHANNEL_BANK)}; + ch_bank.bank = param->bank; + ch_bank.pro_channel = param->channel; + WR_PPT_REG(PRO_CHANNEL_BANK, ch_bank.d16); + /* update phy parameters along with rf bank */ + ppt_set_phy_bank(param->bank); + + ppt_set_phy_rx_type(param->rx_phy); +} + +void ppt_set_phy_entry_param(uint8_t entry, ppt_phy_entry_param_t *param) +{ + assert_param(entry < PPT_ENTRY_NUM); + + ppt_set_phy_tx_type(entry, param->tx_phy); + ppt_set_preamble_len(entry, param->preamble_len); +} + +void ppt_set_preamble_len(uint8_t entry, uint8_t preamble_len) +{ + assert_param(entry < PPT_ENTRY_NUM); + + if (entry == 0) + { + PRO_FIELD_LENGTH_TYPE field_len = {.d16 = RD_PPT_REG(PRO_FIELD_LENGTH)}; + field_len.preamble_field_length0_2_0 = preamble_len; + WR_PPT_REG(PRO_FIELD_LENGTH, field_len.d16); + } + else if (entry == 1) + { + PRO_FIELD_LENGTH2_TYPE field_len2 = {.d16 = RD_PPT_REG(PRO_FIELD_LENGTH2)}; + field_len2.preamble_field_length1_2_0 = preamble_len; + WR_PPT_REG(PRO_FIELD_LENGTH2, field_len2.d16); + } + else if (entry == 2) + { + PRO_GPIO_CONTROL_TYPE gpio_ctrl = {.d16 = RD_PPT_REG(PRO_GPIO_CONTROL)}; + gpio_ctrl.preamble_field_length2 = preamble_len; + WR_PPT_REG(PRO_GPIO_CONTROL, gpio_ctrl.d16); + } + else + { + PRO_FIELD_LENGTH3_TYPE field_len = {.d16 = RD_PPT_REG(2 * (entry - PPT_ENTRY_NUM_ORI) + PRO_FIELD_LENGTH3)}; + field_len.preamble_field_length3_2_0 = preamble_len; + WR_PPT_REG(2 * (entry - PPT_ENTRY_NUM_ORI) + PRO_FIELD_LENGTH3, field_len.d16); + } +} + +void ppt_set_pkt_format(ppt_pkt_format_t *param) +{ + PRO_RX_MATCH_CONTROL_TYPE rx_match_ctrl = {.d16 = RD_PPT_REG(PRO_RX_MATCH_CONTROL)}; + rx_match_ctrl.compare_prefix_option = param->addr_len == 5; + WR_PPT_REG(PRO_RX_MATCH_CONTROL, rx_match_ctrl.d16); + + PRO_FIELD_LENGTH_TYPE field_len = {.d16 = RD_PPT_REG(PRO_FIELD_LENGTH)}; + field_len.address_field_length_2_0 = param->addr_len; + field_len.hp_field_length_3_0 = param->hp_len; + field_len.length_field_length_3_0 = param->length_len; + WR_PPT_REG(PRO_FIELD_LENGTH, field_len.d16); + + PRO_HS_LENGTH_TYPE hs_len = {.d16 = RD_PPT_REG(PRO_HS_LENGTH)}; + hs_len.hs_field_length_4_0 = param->hs_len; + WR_PPT_REG(PRO_HS_LENGTH, hs_len.d16); + + PRO_ORDER_CONTROL_TYPE order_ctrl = {.d16 = RD_PPT_REG(PRO_ORDER_CONTROL)}; + order_ctrl.hp_order = param->header_order; + order_ctrl.hp_order_first = param->header_order; + order_ctrl.length_order = param->header_order; + order_ctrl.length_order_first = param->header_order; + order_ctrl.hs_order = param->header_order; + order_ctrl.hs_order_first = param->header_order; + WR_PPT_REG(PRO_ORDER_CONTROL, order_ctrl.d16); + WR_PPT_REG_FIELD(PRO_MAX_LENGTH, txfifo_out_split, + param->payload_order == PPT_FIELD_BIT_ORDER_MSB ? 1 : 0); + WR_PPT_REG_FIELD(PRO_MAX_LENGTH, rxfifo_in_split, + param->payload_order == PPT_FIELD_BIT_ORDER_MSB ? 1 : 0); + + PRO_PREFIX6_7_TYPE prefix6_7 = {.d16 = RD_PPT_REG(PRO_PREFIX6_7)}; + prefix6_7.hp_order_first_rx = param->header_order; + prefix6_7.length_order_first_rx = param->header_order; + prefix6_7.hs_order_first_rx = param->header_order; + WR_PPT_REG(PRO_PREFIX6_7, prefix6_7.d16); +} + +void ppt_set_addon_len(uint8_t entry, ppt_addon_length_t *param) +{ + assert_param(entry < PPT_ENTRY_NUM); + + if (entry < PPT_ENTRY_NUM_ORI) + { + PRO_ADD_ON_LENGTH0_TYPE add_on_length = {.d16 = RD_PPT_REG(PRO_ENTRY_FIELD_BASE(entry) + PRO_ADD_ON_LENGTH0)}; + add_on_length.add_on_length0_7_0 = param->len; + WR_PPT_REG(PRO_ENTRY_FIELD_BASE(entry) + PRO_ADD_ON_LENGTH0, add_on_length.d16); + } + else + { + uint16_t offset = ((entry - PPT_ENTRY_NUM_ORI) & (~BIT0)) + PRO_ADD_ON_LENGTH3; + uint8_t field_offset = ((entry - PPT_ENTRY_NUM_ORI) & BIT0) << 3; + OV_PPT_REG_FIELD(offset, field_offset, 8, param->len); + } +} + +void ppt_set_tx_power(uint8_t tx_gain) +{ + PRO_GAIN_TYPE gain = {.d16 = RD_PPT_REG(PRO_GAIN)}; + gain.rif_tx_gain = tx_gain; + WR_PPT_REG(PRO_GAIN, gain.d16); +} + +extern bool (*get_valid_txgain_index_from_tx_power)(int8_t, uint8_t, uint8_t, bool, uint8_t *, + int8_t *); +#define TXGAIN_LE_1M 0 +#define TPM_MODE 0 +void ppt_set_tx_power_dbm(float tx_power_dbm) +{ + uint8_t tx_gain; + int8_t tx_power = tx_power_dbm; + int8_t tx_power_act; + //todo: set according to phy + get_valid_txgain_index_from_tx_power(tx_power * 2, TXGAIN_LE_1M, TPM_MODE, TRUE, &tx_gain, + &tx_power_act); + ppt_set_tx_power(tx_gain); +} + +void ppt_set_tx_addr(uint8_t entry, ppt_tx_addr_t *param) +{ + assert_param(entry < PPT_ENTRY_NUM); + + uint16_t tx_addr; + tx_addr = param->tx_addr[0] | (param->tx_addr[1] << 8); + WR_PPT_REG(ppt_lut_tx_addr[entry], tx_addr); + + tx_addr = param->tx_addr[2] | (param->tx_addr[3] << 8); + WR_PPT_REG(ppt_lut_tx_addr[entry] + 2, tx_addr); + + OV_PPT_REG_FIELD(ppt_lut_tx_addr[entry] + 4, 0, 8, param->tx_addr[4]); +} + +void ppt_check_rx_addr(uint8_t bit_th) +{ + PRO_RX_CONTROL_TYPE rx_ctrl = {.d16 = RD_PPT_REG(PRO_RX_CONTROL)}; + PRO_RX_MATCH_CONTROL_TYPE rx_match_ctrl = {.d16 = RD_PPT_REG(PRO_RX_MATCH_CONTROL)}; + uint32_t rx_addr0, rx_addr1; + bool byte3_flag = false; + bool similar_flag = false; + if ((rx_match_ctrl.rx_match_enable_bit_7_0 & 0x1) == 0x1) + { + rx_addr0 = RD_PPT_REG(PRO_BASE0_LOWER) + (RD_PPT_REG(PRO_BASE0_UPPER) << 16); + if ((rx_addr0 >> 24) == 0xff) + { + byte3_flag = true; + } + } + if ((rx_match_ctrl.rx_match_enable_bit_7_0 & 0x2) == 0x2) + { + rx_addr1 = RD_PPT_REG(PRO_BASE1_LOWER) + (RD_PPT_REG(PRO_BASE1_UPPER) << 16); + if ((rx_addr1 >> 24) == 0xff) + { + byte3_flag = true; + } + } + + uint8_t acc_byte3_flag = byte3_flag ? (rx_ctrl.rx_phy == PPT_PHY_TYPE_BLE_2M ? 2 : 1) : 0; + if (acc_byte3_flag != ppt_fw_ret.acc_byte3_flag) + { + /* default value */ + if (ppt_fw_ret.acc_byte3_flag == 2) /* 2M PHY */ + { + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x10), 0x1 << 3, 0x1 << 3); + } + else if (ppt_fw_ret.acc_byte3_flag == 1)/* 1M PHY */ + { + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_2, TRANS_MODEM_REG(0x68), 0x7 << 9, 0x4 << 9); + } + + /* modified value */ + if (acc_byte3_flag == 2)/* 2M PHY */ + { + /* need restore after dlps */ + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x10), 0x1 << 3, 0x0 << 3); + } + else if (acc_byte3_flag == 1)/* 1M PHY */ + { + /* need restore after dlps */ + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_2, TRANS_MODEM_REG(0x68), 0x7 << 9, 0x1 << 9); + } + + ppt_fw_ret.acc_byte3_flag = acc_byte3_flag; + } + + if ((rx_match_ctrl.rx_match_enable_bit_7_0 & 0x3) == 0x3) + { + uint32_t addr_diff = rx_addr0 ^ rx_addr1; + uint8_t diff_count = 0; + while (addr_diff) + { + diff_count += 1; + addr_diff &= addr_diff - 1; + } + if (diff_count <= bit_th) + { + similar_flag = true; + } + } + + if (similar_flag != ppt_fw_ret.acc_similar_flag) + { + ppt_fw_ret.acc_similar_flag = similar_flag; + if (similar_flag) /* modified value */ + { + /* need restore after dlps */ + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_6, TRANS_MODEM_REG(0x4a), 0xff << 0, 0x33 << 0); + /* 1M PHY */ + { + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x3C), 0x1 << 11, 0x1 << 11); + /* need restore after dlps */ + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x2C), 0x7 << 13, 0x0 << 13); + } + /* 2M PHY */ + { + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_6, TRANS_MODEM_REG(0x2a), 0x1 << 3, 0x1 << 3); + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_6, TRANS_MODEM_REG(0x2a), 0x7 << 4, 0x0 << 4); + } + } + else /* default value */ + { + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_6, TRANS_MODEM_REG(0x4a), 0xff << 0, 0xcc << 0); + /* 1M PHY */ + { + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x3C), 0x1 << 11, 0x0 << 11); + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x2C), 0x7 << 13, 0x2 << 13); + } + /* 2M PHY */ + { + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_6, TRANS_MODEM_REG(0x2a), 0x1 << 3, 0x0 << 3); + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_6, TRANS_MODEM_REG(0x2a), 0x7 << 4, 0x6 << 4); + } + } + } +} + +#define ppt_set_prefix(entry, prefix) OV_PPT_REG_FIELD(ppt_lut_prefix[entry].reg_offset, ppt_lut_prefix[entry].field_offset, 8, prefix) + +void ppt_set_rx_addr(uint8_t entry, ppt_rx_addr_t *param) +{ + assert_param(entry < PPT_ENTRY_RX_NUM); + PRO_RX_MATCH_CONTROL_TYPE rx_match_ctrl = {.d16 = RD_PPT_REG(PRO_RX_MATCH_CONTROL)}; + if (param->enable) + { + rx_match_ctrl.rx_match_enable_bit_7_0 |= BIT(entry); + ppt_fw_ret.modem_acc_enable |= BIT(entry); + uint16_t addr = param->rx_addr[0] + (param->rx_addr[1] << 8); + uint16_t addr1 = param->rx_addr[2] + (param->rx_addr[3] << 8); + uint8_t prefix = param->rx_addr[4]; + uint32_t base_for_modem = ppt_swap_bits32(addr + (addr1 << 16)); + ppt_fw_ret.modem_acc[entry] = base_for_modem; + if (entry == 0) + { + WR_PPT_REG(PRO_BASE0_LOWER, addr); + WR_PPT_REG(PRO_BASE0_UPPER, addr1); + RTK_WRITE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x1c), base_for_modem); + RTK_WRITE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x1e), base_for_modem >> 16); + } + else + { + WR_PPT_REG(PRO_BASE1_LOWER, addr); + WR_PPT_REG(PRO_BASE1_UPPER, addr1); + RTK_WRITE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x24), base_for_modem); + RTK_WRITE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x26), base_for_modem >> 16); + // Set Bit0 to 1 if use 2 rx match filter + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x14), BIT0, 1); + WR_PPT_REG_FIELD(PRO_TX_PKT_NUM2, modem_compare2_addr, 1); + // disable acc false alarm protect scheme + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_6, TRANS_MODEM_REG(0x56), 0x3, 0); + } + ppt_set_prefix(entry, prefix); + } + else + { + rx_match_ctrl.rx_match_enable_bit_7_0 &= ~BIT(entry); + ppt_fw_ret.modem_acc_enable &= ~BIT(entry); + if (entry >= 1 && (ppt_fw_ret.modem_acc_enable & 0xfe) == 0) + { + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x14), BIT0, 0); + WR_PPT_REG_FIELD(PRO_TX_PKT_NUM2, modem_compare2_addr, 0); + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_6, TRANS_MODEM_REG(0x56), 0x3, 0x3); + } + } + WR_PPT_REG(PRO_RX_MATCH_CONTROL, rx_match_ctrl.d16); +} + +PPT_API_SECTION void ppt_set_hp(uint8_t entry, uint8_t hp) +{ + assert_param(entry < PPT_ENTRY_NUM); + + if (entry < PPT_ENTRY_NUM_ORI) + { + PRO_HP_0_TYPE hp0 = {.d16 = RD_PPT_REG(PRO_HP_0 + PRO_ENTRY_FIELD_BASE(entry))}; + hp0.hp_0_7_0 = hp; + WR_PPT_REG(PRO_HP_0 + PRO_ENTRY_FIELD_BASE(entry), hp0.d16); + } + else + { + uint16_t reg_offet = PRO_HP_3 + (((entry - PPT_ENTRY_NUM_ORI) >> 1) << 1); + uint8_t field_offset = ((entry - PPT_ENTRY_NUM_ORI) & 0x1) << 3; + OV_PPT_REG_FIELD(reg_offet, field_offset, 8, hp); + } +} + +PPT_API_SECTION void ppt_set_length(uint8_t entry, uint16_t length) +{ + assert_param(entry < PPT_ENTRY_NUM); + + if (entry < PPT_ENTRY_NUM_ORI) + { + PRO_LENGTH_0_TYPE len = {.d16 = RD_PPT_REG(PRO_LENGTH_0 + PRO_ENTRY_FIELD_BASE(entry))}; + len.length_0_14_0 = length; + WR_PPT_REG(PRO_LENGTH_0 + PRO_ENTRY_FIELD_BASE(entry), len.d16); + } + else + { + uint16_t offset = ((entry - PPT_ENTRY_NUM_ORI) << 1) + PRO_LENGTH_3; + PRO_LENGTH_3_TYPE len = {.d16 = RD_PPT_REG(offset)}; + len.length_3_14_0 = length; + WR_PPT_REG(offset, len.d16); + } +} + +PPT_API_SECTION void ppt_set_hs(uint8_t entry, uint32_t hs) +{ + assert_param(entry < PPT_ENTRY_NUM); + + uint16_t reg_offet_l = PPT_REG_OFFSET(entry, PRO_HS_0_LOWER, PRO_ONE_ENTRY_DW_SIZE, PRO_HS_3_LOWER, + 4); + WR_PPT_REG(reg_offet_l, hs & 0xffff); + OV_PPT_REG_FIELD(reg_offet_l + 2, 1, 15, (hs >> 16) & 0xffff); +} + +void ppt_set_tx_header(uint8_t entry, ppt_header_t *param) +{ + assert_param(entry < PPT_ENTRY_NUM); + + ppt_set_hp(entry, param->hp); + /* length not used if dma_length_mode is enabled */ + ppt_set_length(entry, param->length); + ppt_set_hs(entry, param->hs); +} + +void ppt_set_tifs(uint8_t tifs_us) +{ + PRO_TX_TURN_AROUND_DELAY_TYPE tx_delay = {.d16 = RD_PPT_REG(PRO_TX_TURN_AROUND_DELAY)}; + tx_delay.pro_tx2rx_delay_base = tifs_us - 22; + WR_PPT_REG(PRO_TX_TURN_AROUND_DELAY, tx_delay.d16); + PRO_RX_TURN_AROUND_DELAY_TYPE rx_delay = {.d16 = RD_PPT_REG(PRO_RX_TURN_AROUND_DELAY)}; + rx_delay.pro_rx2tx_delay_base = tifs_us - 20; + WR_PPT_REG(PRO_RX_TURN_AROUND_DELAY, rx_delay.d16); +} + +void ppt_set_timing_latch(ppt_timing_latch_t *param) +{ + PRO_CBK_CTRL_TYPE cbk_ctrl = {.d16 = RD_PPT_REG(PRO_CBK_CTRL)}; + cbk_ctrl.tx_time_enable = param->tx_on; + WR_PPT_REG(PRO_CBK_CTRL, cbk_ctrl.d16); + + PRO_RX_CONTROL_TYPE rx_ctrl = {.d16 = RD_PPT_REG(PRO_RX_CONTROL)}; + rx_ctrl.acchit_time_enable = param->acc_hit; + WR_PPT_REG(PRO_RX_CONTROL, rx_ctrl.d16); +} + +void ppt_set_gpio_param(ppt_gpio_param_t *param) +{ + PRO_RESET_CTRL_REG_TYPE reset_ctrl = {.d16 = RD_PPT_REG(PRO_RESET_CTRL_REG)}; + reset_ctrl.gpio_tx_enable = param->enable; + reset_ctrl.gpio_in_enable = param->int_enable; + WR_PPT_REG(PRO_RESET_CTRL_REG, reset_ctrl.d16); +} + +void ppt_set_gpio_entry_param(uint8_t entry, ppt_gpio_entry_param_t *param) +{ + assert_param(entry < PPT_ENTRY_NUM); + + if (entry == 0) + { + PRO_GPIO_TX_DELAY_TYPE gpio_tx_delay = {.d16 = RD_PPT_REG(PRO_GPIO_TX_DELAY)}; + gpio_tx_delay.gpio_tx_delay_15_0 = param->gpio_delay; + WR_PPT_REG(PRO_GPIO_TX_DELAY, gpio_tx_delay.d16); + } + else if (entry == 1) + { + PRO_GPIO_TX_DELAY1_TYPE gpio_tx_delay1 = {.d16 = RD_PPT_REG(PRO_GPIO_TX_DELAY1)}; + gpio_tx_delay1.gpio_tx_delay1_15_0 = param->gpio_delay; + WR_PPT_REG(PRO_GPIO_TX_DELAY1, gpio_tx_delay1.d16); + } + else if (entry == 2) + { + PRO_GPIO_TX_DELAY2_TYPE gpio_tx_delay2 = {.d16 = RD_PPT_REG(PRO_GPIO_TX_DELAY2)}; + gpio_tx_delay2.gpio_tx_delay2_15_0 = param->gpio_delay; + WR_PPT_REG(PRO_GPIO_TX_DELAY2, gpio_tx_delay2.d16); + } + else + { + /* no setting here */ + } +} + +void ppt_set_ptx_mode(ppt_ptx_mode_t *param) +{ + PRO_TX_CONTROL_TYPE tx_ctrl = {.d16 = RD_PPT_REG(PRO_TX_CONTROL)}; + tx_ctrl.periodic_enable = param->periodic_mode; + WR_PPT_REG(PRO_TX_CONTROL, tx_ctrl.d16); + + PRO_TRX_CONTROL_TYPE trx_ctrl = {.d16 = RD_PPT_REG(PRO_TRX_CONTROL)}; + trx_ctrl.ack_enable = param->ack_mode; + WR_PPT_REG(PRO_TRX_CONTROL, trx_ctrl.d16); + + if (param->periodic_mode) + { + PRO_PERIODIC_INTERVAL_TYPE periodic_interval; + periodic_interval.d16 = param->periodic_interval; + WR_PPT_REG(PRO_PERIODIC_INTERVAL, periodic_interval.d16); + PRO_TRX_CONTROL_TYPE trx_ctrl = {.d16 = RD_PPT_REG(PRO_TRX_CONTROL)}; + trx_ctrl.periodic_interval_19_16 = param->periodic_interval >> 16; + WR_PPT_REG(PRO_TRX_CONTROL, trx_ctrl.d16); + } +} + +void ppt_set_prx_mode(ppt_prx_mode_t *param) +{ + PRO_RX_CONTROL_TYPE rx_ctrl = {.d16 = RD_PPT_REG(PRO_RX_CONTROL)}; + rx_ctrl.continuous_rx = param->continuous_mode; + WR_PPT_REG(PRO_RX_CONTROL, rx_ctrl.d16); + + PRO_TRX_CONTROL_TYPE trx_ctrl = {.d16 = RD_PPT_REG(PRO_TRX_CONTROL)}; + trx_ctrl.ack_enable = param->ack_mode; + WR_PPT_REG(PRO_TRX_CONTROL, trx_ctrl.d16); +} + +void ppt_set_psd_mode(ppt_psd_mode_t *param) +{ + assert_param(param->chann_start <= param->chann_stop); + assert_param(param->chann_stop < PSD_CHANN_NUM); + assert_param(param->chann_step < PSD_CHANN_NUM); + + PRO_PSD_RANGE0_TYPE psd_range0 = {.d16 = RD_PPT_REG(PRO_PSD_RANGE0)}; + psd_range0.pro_psd_start = param->chann_start; + psd_range0.pro_psd_end = param->chann_stop; + WR_PPT_REG(PRO_PSD_RANGE0, psd_range0.d16); + + PRO_PSD_RANGE1_TYPE psd_range1 = {.d16 = RD_PPT_REG(PRO_PSD_RANGE1)}; + psd_range1.pro_psd_step = param->chann_step; + psd_range1.pro_psd_mode = param->mode; + WR_PPT_REG(PRO_PSD_RANGE1, psd_range1.d16); + + PRO_PSD_TIMEOUT_TYPE psd_timeout = {.d16 = RD_PPT_REG(PRO_PSD_TIMEOUT)}; + psd_timeout.pro_psd_timeout = param->timeout; + WR_PPT_REG(PRO_PSD_TIMEOUT, psd_timeout.d16); +} + +void ppt_dbg_port_open(uint16_t bt_port, uint16_t ppt_port, uint16_t ppt_port2) +{ + /* baseband port */ + VENDOR_WRITE(0x30, (VENDOR_READ(0x30)&BIT31) | 0x40); + WR_16BIT_IO(BB_BASE_ADDR, 0x1FE, bt_port); + + /* 2.4g port */ + WR_PPT_REG(PRO_DEBUG_SEL, ppt_port); + WR_PPT_REG(PRO_DEBUG_SEL2, ppt_port2); + + /* sys */ + debug_port_open(DIGI_DBG_BT); +} + +void ppt_dbg_port_set_pin(uint8_t pin_group, uint32_t dbg_bitmap) +{ +#if PPT_IS_ASIC + debug_port_set_pin_bit_map(pin_group, dbg_bitmap); +#endif +} + +void ppt_dbg_port(uint16_t bt_port, uint16_t ppt_port, uint16_t ppt_port2, uint32_t dbg_bitmap) +{ + ppt_dbg_port_set_pin(DBG_PIN_GROUP_A, dbg_bitmap); + ppt_dbg_port_open(bt_port, ppt_port, ppt_port2); +} + +void ppt_dump_nonzero_reg(void) +{ + DBG_DIRECT("ppt_dump_nonzero_reg: start"); + uint16_t offset = 0; + while (offset < PRO_REG_TOTAL_SIZE) + { + uint16_t value = RD_PPT_REG(offset); + if (value != 0) + { + DBG_DIRECT("ppt_dump_nonzero_reg: 0x%04x = 0x%04x", offset, value); + } + offset += 2; + } + DBG_DIRECT("ppt_dump_nonzero_reg: end"); +} + +bool ppt_get_bank_channel(uint16_t frequency, uint8_t *bank, uint8_t *channel) +{ + if (frequency > 2530) + { + return false; + } + else if (frequency > 2500) + { + *bank = 2; + *channel = frequency - 2452; + } + else if (frequency >= 2402) + { + *bank = 0; + *channel = frequency - 2402; + } + else if (frequency >= 2348) + { + *bank = 1; + *channel = frequency - 2348; + } + else + { + return false; + } + return true; +} + +static const uint8_t le_logic_chan[40] = +{ + 37, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 38, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 39 +}; + +int8_t ppt_get_ble_logic_chan(uint8_t bank, uint8_t channel) +{ + if (bank == 0 && (channel & 0x1) == 0 && channel <= 78) + { + return le_logic_chan[channel / 2]; + } + else + { + return -1; + } +} + +int8_t ppt_get_ble_logic_chan_via_freq(uint16_t frequency) +{ + if (frequency >= 2402 && frequency <= 2480 && ((frequency & 0x1) == 0)) + { + return le_logic_chan[(frequency - 2402) / 2]; + } + else + { + return -1; + } +} + +uint32_t ppt_get_ble_white_init(uint8_t le_logic_chan) +{ + uint32_t white_init = 1; + le_logic_chan = ppt_swap_bits8(le_logic_chan); + white_init |= le_logic_chan >> 1; + return white_init; +} + +#if PATCH_BTMAC_ISR +PPT_ISR_SECTION static uint8_t btmac_isr_handler(void *pbuf) +#else +PPT_ISR_SECTION static void btmac_isr_handler(void) +#endif +{ + BTMAC_ISR_STATUS_REG reg = btmac_isr_status_reg_read(); + //APP_PRINT_INFO1("btmac_isr_handler 0x%08x!", reg.d32); + if (reg.pro_intr_r) + { + if (ppt_isr_handler) + { + ppt_isr_handler(); + } + else + { + DBG_DIRECT("ppt: fail, ISR isn't registered!"); + } + } +#if PATCH_BTMAC_ISR + return false; +#endif +} + +PPT_ISR_SECTION static uint8_t btmac_psd_isr_handler(void *buf) +{ + ppt_clear_radio(); + if (ppt_psd_isr_handler) + { + ppt_psd_isr_handler(); + } + + return true; +} + +PPT_API_SECTION bool ppt_push_tx_fifo(uint8_t entry, uint8_t *payload, uint16_t payload_len) +{ + bool ret; + assert_param(entry < PPT_ENTRY_NUM); + uint32_t s = os_lock(); +#if PPT_LOG_METHOD == 1 + UINT32 cur_bm = RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(entry)); +#endif + /* config bzdma */ + BZDMA_BLEONLY_TX_DESC_FRAGMENT TxFragCache = { .DWord = 0 }; + TxFragCache.start_addr_ex = (UINT32)payload; + TxFragCache.len_ex = payload_len; + TxFragCache.isLast = TRUE; + // Pad_Config(P3_0, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, + // PAD_OUT_LOW); + + ret = bzdma_send_packet_to_ble_data_ring_fifo(entry, LL_LLID_DATA_PDU_START, + &TxFragCache, 1, 0); + // Pad_Config(P3_0, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, + // PAD_OUT_LOW); + + ret = true; +#if PPT_LOG_METHOD == 1 + APP_PRINT_INFO5("ppt_push_tx_fifo: BZDMA bitmap 0x%x -> 0x%x, entry %d, wr ptr %d, len %d", cur_bm, + RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(entry)), entry, + Bzdma_Manager.BleTxEntSta[entry].seg_wptr, payload_len); +#endif + os_unlock(s); + return ret; +} + +PPT_API_SECTION static UINT8 bzdma_update_packet_to_ble_data_ring_fifo(UINT8 conn_entry, UINT8 llid, + BZDMA_BLEONLY_TX_DESC_FRAGMENT *pfrag, UINT8 Nfrag, + UINT8 codeind) +{ + BZDMA_BLEONLY_DATA_TX_DESC_ENTRY_STATUS *pTxEnt; + BZDMA_BLEONLY_TX_DESC_SEGMENT *pTxSeg; + BZDMA_BLEONLY_TX_DESC_FRAGMENT *pTxFrag; + UINT8 cur_wptr; + UINT8 i; + UINT8 PktSize = 0; + + pTxEnt = &Bzdma_Manager.BleTxEntSta[conn_entry]; + if (pTxEnt->free_segs == bzdma_supported_le_max_seg_num) + { + /* fifo is empty */ + return FALSE; + } + + cur_wptr = (pTxEnt->seg_wptr + bzdma_supported_le_max_seg_num - 1) & (bzdma_supported_le_max_seg_num + - 1); + pTxSeg = &pTxEnt->pTxSegDesc[cur_wptr]; + pTxFrag = &pTxEnt->pTxFragDesc[cur_wptr * 2]; + + /* fill all fragments */ + for (i = 0; i < Nfrag; i++) + { + pTxFrag[i].DWord = pfrag[i].DWord; + UINT16 local_len; + local_len = pfrag[i].len_ex; + PktSize += local_len; + if (local_len == 0) + { + /* hw can not handle zero length !! */ + return FALSE; + } + } + + /* fill full packet sie to tx segment */ + pTxSeg->len = PktSize; + pTxSeg->llid = llid; + return TRUE; +} + +PPT_API_SECTION bool ppt_update_tx_fifo(uint8_t entry, uint8_t *payload, uint16_t payload_len) +{ + bool ret; + assert_param(entry < PPT_ENTRY_NUM); + uint32_t s = os_lock(); +#if PPT_LOG_METHOD == 1 + UINT32 cur_bm = RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(entry)); +#endif + /* config bzdma */ + BZDMA_BLEONLY_TX_DESC_FRAGMENT TxFragCache = { .DWord = 0 }; + TxFragCache.start_addr_ex = (UINT32)payload; + TxFragCache.len_ex = payload_len; + TxFragCache.isLast = TRUE; + ret = bzdma_update_packet_to_ble_data_ring_fifo(entry, LL_LLID_DATA_PDU_START, + &TxFragCache, 1, 0); + ret = true; +#if PPT_LOG_METHOD == 1 + APP_PRINT_INFO5("ppt_update_tx_fifo: BZDMA bitmap 0x%x -> 0x%x, entry %d, wr ptr %d, len %d", + cur_bm, + RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(entry)), entry, + Bzdma_Manager.BleTxEntSta[entry].seg_wptr, payload_len); +#endif + os_unlock(s); + return ret; +} + +PPT_API_SECTION bool ppt_pop_rx_fifo(uint8_t entry, uint8_t *pdu, uint16_t pdu_len) +{ + assert_param(entry < PPT_ENTRY_NUM); + + /* config bzdma */ + BZDMA_RX_DESC_SEGMENT rxdesc = { .DWord = 0 }; + rxdesc.addr = (uint32_t)pdu; + rxdesc.len = pdu_len; + rxdesc.flush = FALSE; + bzdma_send_burst_rxcmd_and_wait_complete(&rxdesc, 1, + BZDMA_RX_PID_ACL, entry, TRUE); + return true; +} + +uint8_t ppt_flush_tx_fifo(uint8_t entry) +{ + assert_param(entry < PPT_ENTRY_NUM); + + Bzdma_Manager.BleTxEntSta[entry].seg_wptr = Bzdma_Manager.BleTxEntSta[entry].seg_rptr; + Bzdma_Manager.BleTxEntSta[entry].free_segs = bzdma_supported_le_max_seg_num; + bzdma_ble_set_segment_valid_bitmap(entry, 0); + return Bzdma_Manager.BleTxEntSta[entry].seg_rptr; +} + +PPT_API_SECTION uint8_t ppt_get_tx_fifo_rdptr(uint8_t entry) +{ + assert_param(entry < PPT_ENTRY_NUM); + return Bzdma_Manager.BleTxEntSta[entry].seg_rptr; +} + +PPT_API_SECTION void ppt_set_tx_fifo_rdptr(uint8_t entry, uint8_t ptr) +{ + assert_param(entry < PPT_ENTRY_NUM); + Bzdma_Manager.BleTxEntSta[entry].seg_rptr = ptr; +} + +PPT_API_SECTION uint8_t ppt_get_tx_fifo_wrptr(uint8_t entry) +{ + assert_param(entry < PPT_ENTRY_NUM); + return Bzdma_Manager.BleTxEntSta[entry].seg_wptr; +} + +PPT_API_SECTION void ppt_reset_tx_fifo(uint8_t entry) +{ + ppt_set_tx_fifo_rdptr(entry, PPT_TX_FIFO_SIZE - 1); + ppt_force_fw_ack(entry); + ppt_flush_tx_fifo(entry); +} + +PPT_API_SECTION int8_t ppt_get_rssi(uint16_t rssi_raw) +{ + return calculate_log_from_rssi(rssi_raw, 0xff, PPT_RF_MODE); +} + +void ppt_clear_dma_tx_done(void) +{ + /* This is a workaround for reset when bzdma start which results in dma carry error later. */ + /* BIT17: dma clock gating disable flag */ + VENDOR_WRITE(0x328, (VENDOR_READ(0x328) & (~BIT17)) | BIT17); + platform_delay_us(1); + VENDOR_WRITE(0x328, VENDOR_READ(0x328) & (~BIT17)); +} + +void ppt_init_dma(void) +{ + bzdma_supported_le_max_seg_num = PPT_TX_FIFO_SIZE; + for (uint8_t loop = 0; loop < PPT_ENTRY_NUM; loop++) + { + Bzdma_Manager.BleTxEntSta[loop].seg_rptr = 0; + Bzdma_Manager.BleTxEntSta[loop].seg_wptr = 0; + Bzdma_Manager.BleTxEntSta[loop].free_segs = bzdma_supported_le_max_seg_num; + bzdma_ble_set_segment_valid_bitmap(loop, 0); + } +} + +void ppt_deinit_dma(void) +{ + bzdma_supported_le_max_seg_num = 16; + for (uint8_t loop = 0; loop < PPT_ENTRY_NUM; loop++) + { + Bzdma_Manager.BleTxEntSta[loop].seg_rptr = 0; + Bzdma_Manager.BleTxEntSta[loop].seg_wptr = 0; + Bzdma_Manager.BleTxEntSta[loop].free_segs = bzdma_supported_le_max_seg_num; + bzdma_ble_set_segment_valid_bitmap(loop, 0); + } +} + +void ppt_notify_btmac(bool ppt_mode) +{ + /* notify 2.4g state to btmac */ + lowerstack_SystemCall(6, ppt_mode, 0, 0); +} + +void ppt_init_misc(void) +{ +#if PATCH_BTMAC_ISR + btmac_handler_temp = rcp_BTMAC_Handler; + rcp_BTMAC_Handler = (PF_ROM_CODE_PATCH_FUNC)btmac_isr_handler; +#else + RamVectorTableUpdate(BTMAC_VECTORn, btmac_isr_handler); +#endif + bb_psd_handler_temp = rcp_BB_handle_psd_end_intr_func; + rcp_BB_handle_psd_end_intr_func = (PF_ROM_CODE_PATCH_FUNC)btmac_psd_isr_handler; + + /* modem init 1: proprietary mode, 0: normal mode */ + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x66), BIT15, BIT15); + /* force tx gain */ + //RTK_WRITE_MODEM_REG_PI(MODEM_PI_PAGE_3, TRANS_MODEM_REG(0x5c), 0x7840); + + /* workaround for rx empty pkt */ + //uint16_t reg = ((uint16_t) * ((volatile uint16_t *)(0x40050000 + 0x18e))); + //TODO: BB_write_baseband_register(0x18e, reg | BIT15); +} + +void ppt_deinit_misc(void) +{ + //uint16_t reg = ((uint16_t) * ((volatile uint16_t *)(0x40050000 + 0x18e))); + //TODO: BB_write_baseband_register(0x18e, reg & (~BIT15)); + + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x66), BIT15, 0); + //TODO:ll_hw_init(false); + +#if PATCH_BTMAC_ISR + rcp_BTMAC_Handler = (PF_ROM_CODE_PATCH_FUNC)btmac_handler_temp; +#else + RamVectorTableUpdate(BTMAC_VECTORn, BTMAC_Handler); +#endif + rcp_BB_handle_psd_end_intr_func = (PF_ROM_CODE_PATCH_FUNC)bb_psd_handler_temp; +} + +PPT_API_SECTION uint32_t ppt_crc_eng_cal(ppt_crc_eng_param_t *param, uint8_t *data, + uint32_t data_len) +{ + uint32_t crc_hw = 0; + uint32_t init = param->init; + + WR_PPT_REG(PRO_CRC_ENGINE_POLY, param->poly & 0xffff); + WR_PPT_REG(PRO_CRC_ENGINE_POLY1, (param->poly >> 16) & 0xffff); + WR_PPT_REG_FIELD(PRO_CRC_ENGINE_EN, crc_engine_field_length, param->length >> 3); + WR_PPT_REG_FIELD(PRO_CRC_ENGINE_EN, crc_engine_data_length_in_bit, param->bit_mode); + WR_PPT_REG_FIELD(PRO_CRC_ENGINE_EN, crc_engine_input_endian, param->input_endian); + WR_PPT_REG_FIELD(PRO_CRC_ENGINE_EN, crc_engine_output_endian, 0); + + uint8_t step_len = param->bit_mode ? 40 : 5; + for (uint32_t loop = 0; loop < data_len; loop += step_len) + { + if (param->output_endian && loop + step_len >= data_len) + { + WR_PPT_REG_FIELD(PRO_CRC_ENGINE_EN, crc_engine_output_endian, 1); + } + + WR_PPT_REG(PRO_CRC_ENGINE_INIT, init & 0xffff); + WR_PPT_REG(PRO_CRC_ENGINE_INIT1, (init >> 16) & 0xffff); + + uint32_t data_idx = param->bit_mode ? (loop >> 3) : loop; + WR_PPT_REG(PRO_CRC_ENGINE_IN, data[data_idx + 0] + (data[data_idx + 1] << 8)); + WR_PPT_REG(PRO_CRC_ENGINE_IN1, data[data_idx + 2] + (data[data_idx + 3] << 8)); + WR_PPT_REG_FIELD(PRO_CRC_ENGINE_IN2, crc_engine_input_data_39_32, data[data_idx + 4]); + + uint8_t len = data_len - loop; + if (len > step_len) + { + len = step_len; + } + WR_PPT_REG_FIELD(PRO_CRC_ENGINE_EN, crc_engine_data_length, param->bit_mode ? len : len - 1); + + WR_PPT_REG_FIELD(PRO_CRC_ENGINE_EN, crc_engine_en, 1); + while (1) + { + bool run = RD_PPT_REG_FIELD(PRO_CRC_ENGINE_EN, crc_engine_en); + if (run == false) + { + break; + } + } + + uint16_t crc_l = RD_PPT_REG(PRO_CRC_ENGINE_OUT); + uint16_t crc_h = RD_PPT_REG(PRO_CRC_ENGINE_OUT1); + crc_hw = (crc_h << 16) + crc_l; + init = crc_hw; + } + + if (param->output_endian == 1) + { + crc_hw >>= 32 - param->length; + } + return crc_hw; +} + +void ppt_reg_handler(void (*handler)(void)) +{ + ppt_isr_handler = handler; +} + +void ppt_reg_psd_handler(void (*handler)(void)) +{ + ppt_psd_isr_handler = handler; +} + +#if TIME_DEBUG +void ppt_init_time_debug(void) +{ + RCC_PeriphClockCmd(APBPeriph_GPIOA, APBPeriph_GPIOA_CLOCK, (FunctionalState)ENABLE); + for (uint8_t loop = 0; loop < sizeof(test_pin) / sizeof(test_pin[0]); loop++) + { + Pad_Config(test_pin[loop], PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, + PAD_OUT_HIGH); + Pinmux_Config(test_pin[loop], DWGPIO); + gpio_test_pin[loop] = GPIO_GetPin(test_pin[loop]); + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_StructInit(&GPIO_InitStruct); + GPIO_InitStruct.GPIO_Pin = gpio_test_pin[loop]; + GPIO_InitStruct.GPIO_Mode = GPIO_MODE_OUT; + GPIO_Init(GPIOA, &GPIO_InitStruct); + GPIOA->GPIO_DR |= gpio_test_pin[loop]; + GPIOA->GPIO_DR &= ~gpio_test_pin[loop]; + } +} + +PPT_API_SECTION void ppt_store_time_debug(void) +{ + for (uint8_t loop = 0; loop < sizeof(test_pin) / sizeof(test_pin[0]); loop++) + { + Pad_Config(test_pin[loop], PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, + level_test_pin[loop] ? PAD_OUT_HIGH : PAD_OUT_LOW); + } +} + +PPT_API_SECTION void ppt_restore_time_debug(void) +{ + for (uint8_t loop = 0; loop < sizeof(test_pin) / sizeof(test_pin[0]); loop++) + { + Pad_Config(test_pin[loop], PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, + PAD_OUT_HIGH); + } +} +#endif + +PPT_API_SECTION static PMCheckResult ppt_dlps_mac_check(uint32_t cur_clk, + uint32_t pre_sys_lv_wakeup_time_diff, + uint32_t *unit_wakeup_time_diff) +{ + if (ppt_enable == false) + { + return PM_CHECK_PASS; + } + //DBG_DIRECT("ppt mac check"); + //DBG_DIRECT("ppt mac check %04x, %04x", RD_PPT_REG(PRO_DSM_READ), RD_PPT_REG(PRO_DSM_READ1)); + + BT_SYS_CTRL_REG_S_TYPE bt_sys_reg; + bt_sys_reg.d32 = VENDOR_READ(BT_SYS_CTRL_REG); + if (bt_sys_reg.b.pro_rst_n != 0) + { + uint16_t pro_dsm = RD_PPT_REG(PRO_DSM_READ); + if ((pro_dsm & (BIT6 + BIT11 + BIT12)) != (BIT6 + BIT11 + BIT12)) + { + return PM_CHECK_FAIL; + } + uint16_t pro_dsm1 = RD_PPT_REG(PRO_DSM_READ1); + if ((pro_dsm1 & (BIT0 + BIT7 + BIT13)) != (BIT0 + BIT7 + BIT13)) + { + return PM_CHECK_FAIL; + } + if ((pro_dsm1 & (BIT1 + BIT2)) != 0) + { + return PM_CHECK_FAIL; + } + uint16_t isr_sta = RD_PPT_REG(PRO_MISR) & PPT_ISR_VALID_BITS_MASK; + uint16_t isr_msk = RD_PPT_REG(PRO_IMR) & PPT_ISR_VALID_BITS_MASK; + if (isr_sta & (~isr_msk)) + { + return PM_CHECK_FAIL; + } + } + + if (ppt_dlps_mac_idle == false) + { + return PM_CHECK_FAIL; + } + if (ppt_dlps_mac_check_cb != NULL) + { + if (ppt_dlps_mac_check_cb(unit_wakeup_time_diff) == false) + { + return PM_CHECK_FAIL; + } + } + return PM_CHECK_PASS; +} + +PPT_API_SECTION static void ppt_dlps_mac_store(void) +{ + if (ppt_enable == false) + { + return; + } + + if (ppt_dlps_mac_enter_cb) + { + ppt_dlps_mac_enter_cb(); + } + + ppt_read_reg = ppt_read_reg_dlps; + ppt_write_reg = ppt_write_reg_dlps; +} + +PPT_API_SECTION static void ppt_dlps_mac_restore(void) +{ + if (ppt_enable == false) + { + return; + } + //DBG_DIRECT("ppt mac restore"); + + ppt_reset_core_domain(true); + ppt_read_reg = ppt_read_reg_active; + ppt_write_reg = ppt_write_reg_active; + + for (uint8_t loop = 0; loop < PPT_ENTRY_RX_NUM; loop++) + { + if (ppt_fw_ret.modem_acc_enable & BIT(loop)) + { + uint32_t base_for_modem = ppt_fw_ret.modem_acc[loop]; + if (loop == 0) + { + RTK_WRITE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x1c), base_for_modem); + RTK_WRITE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x1e), base_for_modem >> 16); + } + else if (loop == 1) + { + RTK_WRITE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x24), base_for_modem); + RTK_WRITE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x26), base_for_modem >> 16); + } + } + } + + if (ppt_fw_ret.acc_byte3_flag == 2)/* 2M PHY */ + { + /* need restore after dlps */ + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x10), 0x1 << 3, 0x0 << 3); + } + else if (ppt_fw_ret.acc_byte3_flag == 1)/* 1M PHY */ + { + /* need restore after dlps */ + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_2, TRANS_MODEM_REG(0x68), 0x7 << 9, 0x1 << 9); + } + + if (ppt_fw_ret.acc_similar_flag) + { + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_6, TRANS_MODEM_REG(0x4a), 0xff << 0, 0x33 << 0); + /* 1M PHY */ + { + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x2C), 0x7 << 13, 0x0 << 13); + } + } + + if (ppt_fw_ret.mod_index_2m_flag) + { + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_2, TRANS_MODEM_REG(0x14), 0x3f, 0x28); + RTK_UPDATE_MODEM_REG_PI(MODEM_PI_PAGE_6, TRANS_MODEM_REG(0x2c), 0x3fe, 0x50); + } + + if (ppt_dlps_mac_exit_cb) + { + ppt_dlps_mac_exit_cb(); + } +} + +PPT_API_SECTION static void ppt_dlps_pf_enter(void) +{ + if (ppt_enable == false) + { + return; + } + //DBG_DIRECT("ppt pf enter"); + if (ppt_dlps_pf_enter_cb) + { + ppt_dlps_pf_enter_cb(); + } +#if TIME_DEBUG + ppt_store_time_debug(); +#endif +} + +PPT_API_SECTION static void ppt_dlps_pf_exit(void) +{ + if (ppt_enable == false) + { + return; + } + //DBG_DIRECT("ppt pf exit"); +#if TIME_DEBUG + ppt_restore_time_debug(); +#endif + if (ppt_dlps_pf_exit_cb) + { + ppt_dlps_pf_exit_cb(); + } +} + +void ppt_dlps_init(void) +{ + /* no way to unregister callback, so only register once */ + static bool ppt_dlps_pf_cb_reg = false; + if (ppt_dlps_pf_cb_reg == false) + { + ppt_dlps_pf_cb_reg = true; + platform_pm_register_callback_func(ppt_dlps_pf_enter, PLATFORM_PM_STORE); + platform_pm_register_callback_func(ppt_dlps_pf_exit, PLATFORM_PM_PEND); + + PowerManagerSlaveUnit *ppt_pm = (PowerManagerSlaveUnit *)os_mem_zalloc(RAM_TYPE_DATA_ON, + sizeof(PowerManagerSlaveUnit)); + ppt_pm->check_func = ppt_dlps_mac_check; + ppt_pm->enter_func = ppt_dlps_mac_store; + ppt_pm->exit_func = ppt_dlps_mac_restore; + ppt_pm->unit_id = PM_UNIT_PROPRIETARY; + power_manager_slave_register_unit(PM_UNIT_PROPRIETARY, ppt_pm); + } +} + +void ppt_dlps_deinit(void) +{ + +} + +PPT_API_SECTION bool ppt_dlps_check_active(void) +{ + return (power_manager_interface_get_unit_status(PM_SLAVE_PROPRIETARY, + PM_UNIT_PROPRIETARY) == PM_UNIT_ACTIVE); +} + +PPT_API_SECTION bool ppt_dlps_check_inactive(void) +{ + return (power_manager_interface_get_unit_status(PM_SLAVE_PROPRIETARY, + PM_UNIT_PROPRIETARY) == PM_UNIT_INACTIVE); +} + +PPT_API_SECTION void ppt_dlps_initiate_wakeup(void) +{ + power_manager_master_initiate_wakeup(PM_SLAVE_PROPRIETARY, ((PM_UNIT_PROPRIETARY >> 2) & 0x3), + PM_UNIT_PROPRIETARY); + while (ppt_dlps_check_inactive()); +} + +void ppt_dlps_reg_mac_check_cb(bool (*check_cb)(uint32_t *wakeup_time_diff)) +{ + ppt_dlps_mac_check_cb = check_cb; +} + +void ppt_dlps_reg_mac_enter_cb(void (*enter_cb)(void)) +{ + ppt_dlps_mac_enter_cb = enter_cb; +} + +void ppt_dlps_reg_mac_exit_cb(void (*exit_cb)(void)) +{ + ppt_dlps_mac_exit_cb = exit_cb; +} + +void ppt_dlps_reg_pf_enter_cb(void (*enter_cb)(void)) +{ + ppt_dlps_pf_enter_cb = enter_cb; +} + +void ppt_dlps_reg_pf_exit_cb(void (*exit_cb)(void)) +{ + ppt_dlps_pf_exit_cb = exit_cb; +} diff --git a/bee/drivers/ppt/sync/src/driver/common/ppt_driver.h b/bee/drivers/ppt/sync/src/driver/common/ppt_driver.h new file mode 100644 index 00000000..7ecd1737 --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/common/ppt_driver.h @@ -0,0 +1,1215 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_driver.h + * @brief Head file for 2.4G module common driver. + * @details data structs and external functions declaration. + * @author bill + * @date 2020-12-03 + * @version v0.1 + * ************************************************************************************* + */ + +/* Define to prevent recursive inclusion */ +#ifndef _PPT_DRIVER_H_ +#define _PPT_DRIVER_H_ + +/* Add Includes here */ +#include +#include +//#include "app_section.h" +//#include "DataType.h" +#include "ppt_hw_reg.h" +#include "ll_common.h" + +/* Undefine RTK SDK macros that conflict with Zephyr sys/util.h */ +#ifdef hex2char +#undef hex2char +#endif +#ifdef ARRAY_SIZE +#undef ARRAY_SIZE +#endif +#ifdef SAME_TYPE +#undef SAME_TYPE +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup PPT_Driver + * @{ + */ + +/** @defgroup PPT_Driver_Exported_Macros Exported Macros + * @brief + * @{ + */ +#include "zephyr/logging/log.h" + +#if 1 +#define PPT_DRIVER_LOG_LEVEL CONFIG_PPT_DRIVER_LOG_LEVEL + +#define APP_PRINT_INFO0(...) LOG_INF(__VA_ARGS__) +#define APP_PRINT_INFO1(...) LOG_INF(__VA_ARGS__) +#define APP_PRINT_INFO2(...) LOG_INF(__VA_ARGS__) +#define APP_PRINT_INFO3(...) LOG_INF(__VA_ARGS__) +#define APP_PRINT_INFO4(...) LOG_INF(__VA_ARGS__) +#define APP_PRINT_INFO5(...) LOG_INF(__VA_ARGS__) +#define APP_PRINT_INFO6(...) LOG_INF(__VA_ARGS__) +#define APP_PRINT_INFO7(...) LOG_INF(__VA_ARGS__) +#define APP_PRINT_INFO8(...) LOG_INF(__VA_ARGS__) +#define APP_PRINT_WARN0(...) LOG_WRN(__VA_ARGS__) +#define APP_PRINT_WARN1(...) LOG_WRN(__VA_ARGS__) +#define APP_PRINT_WARN2(...) LOG_WRN(__VA_ARGS__) +#define APP_PRINT_WARN3(...) LOG_WRN(__VA_ARGS__) +#define APP_PRINT_WARN4(...) LOG_WRN(__VA_ARGS__) +#define APP_PRINT_WARN5(...) LOG_WRN(__VA_ARGS__) +#define APP_PRINT_WARN6(...) LOG_WRN(__VA_ARGS__) +#define APP_PRINT_WARN7(...) LOG_WRN(__VA_ARGS__) +#define APP_PRINT_WARN8(...) LOG_WRN(__VA_ARGS__) +#define APP_PRINT_ERROR0(...) LOG_ERR(__VA_ARGS__) +#define APP_PRINT_ERROR1(...) LOG_ERR(__VA_ARGS__) +#define APP_PRINT_ERROR2(...) LOG_ERR(__VA_ARGS__) +#define APP_PRINT_ERROR3(...) LOG_ERR(__VA_ARGS__) +#define APP_PRINT_ERROR4(...) LOG_ERR(__VA_ARGS__) +#define APP_PRINT_ERROR5(...) LOG_ERR(__VA_ARGS__) +#define APP_PRINT_ERROR6(...) LOG_ERR(__VA_ARGS__) +#define APP_PRINT_ERROR7(...) LOG_ERR(__VA_ARGS__) +#define APP_PRINT_ERROR8(...) LOG_ERR(__VA_ARGS__) +#define DBG_DIRECT(...) LOG_INF(__VA_ARGS__) +#define TRACE_BINARY(len, pointer) (len) +#else +#define APP_PRINT_INFO0 LOG_INF +#define APP_PRINT_INFO1 LOG_INF +#define APP_PRINT_INFO2 LOG_INF +#define APP_PRINT_INFO3 LOG_INF +#define APP_PRINT_INFO4 LOG_INF +#define APP_PRINT_INFO5 LOG_INF +#define APP_PRINT_INFO6 LOG_INF +#define APP_PRINT_INFO7 LOG_INF +#define APP_PRINT_INFO8 LOG_INF +#define APP_PRINT_WARN0 LOG_INF +#define APP_PRINT_WARN1 LOG_INF +#define APP_PRINT_WARN2 LOG_INF +#define APP_PRINT_WARN3 LOG_INF +#define APP_PRINT_WARN4 LOG_INF +#define APP_PRINT_WARN5 LOG_INF +#define APP_PRINT_WARN6 LOG_INF +#define APP_PRINT_WARN7 LOG_INF +#define APP_PRINT_WARN8 LOG_INF +#define APP_PRINT_ERROR0 LOG_INF +#define APP_PRINT_ERROR1 LOG_INF +#define APP_PRINT_ERROR2 LOG_INF +#define APP_PRINT_ERROR3 LOG_INF +#define APP_PRINT_ERROR4 LOG_INF +#define APP_PRINT_ERROR5 LOG_INF +#define APP_PRINT_ERROR6 LOG_INF +#define APP_PRINT_ERROR7 LOG_INF +#define APP_PRINT_ERROR8 LOG_INF +#define DBG_DIRECT LOG_INF +#define TRACE_BINARY(len, pointer) (len) +#endif + +/** @brief Control the memory location of the function. + * The processing can speed up if the code is in RAM region. + * @{ + */ +#define PPT_ISR_SECTION //RAM_FUNCTION +#define PPT_API_SECTION //RAM_FUNCTION +#define PPT_DAT_SECTION //const +/** @} */ + +/** @brief Hardware trx channel related macros. + * @{ + */ +/* Total channels number */ +#define PPT_ENTRY_NUM 8 +#define PPT_ENTRY_NUM_ORI 3 +/* The maximum rx channels */ +#define PPT_ENTRY_RX_NUM 8 +#define PPT_ENTRY_RX_NUM_ORI 2 +/** @} */ + +/** @brief Hardware feature definition. + * @{ + */ +#define PPT_ADDR_LEN_MAX 5 +#define PPT_TX_FIFO_SIZE 8 + +#define PRO_ONE_ENTRY_DW_SIZE 0x20 +#define PRO_ENTRY_FIELD_BASE(entry) ((entry) * PRO_ONE_ENTRY_DW_SIZE) +#define PRO_ONE_PREFIX_DW_SIZE 0x2 +#define PRO_PREFIX_BASE(entry) ((entry) * PRO_ONE_PREFIX_DW_SIZE) + +#define PPT_ISR_NUMBER 7 +#define PPT_ISR_VALID_BITS_MASK 0x7F +/** @} */ + +/** @} End of PPT_Driver_Exported_Macros */ + +/** @defgroup PPT_Driver_Exported_Types Exported Types + * @brief + * @{ + */ + +/** @brief hw register field descriptor */ +typedef struct +{ + uint16_t reg_offset; + uint8_t field_offset; +} ppt_reg_field_t; + +/** @brief hw supported instructions */ +typedef enum +{ + PPT_HW_INSTRUCTION_PTX_ENABLE = 0, + PPT_HW_INSTRUCTION_PRX_ENABLE = 1, + PPT_HW_INSTRUCTION_PTX_DISABLE = 2, + PPT_HW_INSTRUCTION_PRX_DISABLE = 3, + PPT_HW_INSTRUCTION_PSD_ENABLE = 4, + PPT_HW_INSTRUCTION_PSD_DISABLE = 5, + PPT_HW_INSTRUCTION_MAX +} ppt_hw_instruction_t; + +/** @brief hw supported states */ +typedef enum +{ + PPT_FSM_STANDBY, + PPT_FSM_PTX, + PPT_FSM_PRX, + PPT_FSM_PSD +} ppt_fsm_t; + +/** @brief the bit endianess */ +typedef enum +{ + PPT_FIELD_BIT_ORDER_LSB, + PPT_FIELD_BIT_ORDER_MSB +} ppt_field_bit_order_t; + +/** @brief the packet format control parameters */ +typedef struct +{ + uint8_t addr_len; //!< byte + uint8_t hp_len; //!< bit + uint8_t length_len; //!< bit + uint8_t hs_len; //!< bit + ppt_field_bit_order_t header_order; + ppt_field_bit_order_t payload_order; +} ppt_pkt_format_t; + +/** @brief the addon field length parameters */ +typedef struct +{ + uint8_t len; +} ppt_addon_length_t; + +/** @brief the crc formula parameters */ +typedef struct +{ + uint32_t poly; + uint32_t init; +} ppt_crc_value_t; + +/** @brief the crc length parameters */ +typedef struct +{ + uint8_t len; //!< byte + bool include_addr; //!< not support feature +} ppt_crc_param_t; + +/** @brief crc parameters of each channel */ +typedef struct +{ + ppt_crc_value_t value; +} ppt_crc_entry_param_t; + +/** @brief the whitening formula parameters */ +typedef struct +{ + uint32_t poly; + uint32_t init; +} ppt_white_value_t; + +/** @brief the whitening switch */ +typedef struct +{ + bool enable; +} ppt_white_param_t; + +/** @brief the whitening parameters of each channel */ +typedef struct +{ + uint8_t len; //!< bit + ppt_white_value_t value; +} ppt_white_entry_param_t; + +/** @brief the supported PHY */ +typedef enum +{ + PPT_PHY_TYPE_BLE_1M, + PPT_PHY_TYPE_BLE_2M +} ppt_phy_type_t; + +/** @brief the PHY common parameters */ +typedef struct +{ + uint8_t bank; + uint8_t channel; + ppt_phy_type_t rx_phy; +} ppt_phy_param_t; + +/** @brief the PHY special parameters of each channel */ +typedef struct +{ + uint8_t preamble_len; + ppt_phy_type_t tx_phy; +} ppt_phy_entry_param_t; + +/** @brief rf tx gain */ +typedef enum +{ + PPT_TX_GAIN_8DBM = 0xe0, + PPT_TX_GAIN_7P5DBM = 0xe0, + PPT_TX_GAIN_4DBM = 0xb0, + PPT_TX_GAIN_3DBM = 0xa0, + PPT_TX_GAIN_0DBM = 0x70, + PPT_TX_GAIN_N4DBM = 0x50, + PPT_TX_GAIN_N8DBM = 0x40, + PPT_TX_GAIN_N10DBM = 0x30, + PPT_TX_GAIN_N20DBM = 0x10 +} ppt_tx_gain_t; + +/** @brief the tx access address of each channel */ +typedef struct +{ + uint8_t tx_addr[PPT_ADDR_LEN_MAX]; +} ppt_tx_addr_t; + +/** @brief the rx access address of each channel */ +typedef struct +{ + bool enable; + uint8_t rx_addr[PPT_ADDR_LEN_MAX]; +} ppt_rx_addr_t; + +/** @brief the header fields of frame of each channel*/ +typedef struct +{ + uint8_t hp; + uint16_t length; //!< this field set by DMA automatically + uint32_t hs; +} ppt_header_t; + +/** @brief the ptx mode parameters */ +typedef struct +{ + bool ack_mode; + bool periodic_mode; + uint32_t periodic_interval; //!< 20 valid bits, units of 125us, (n+1)*125us +} ppt_ptx_mode_t; + +/** @brief the prx mode parameters */ +typedef struct +{ + bool ack_mode; + bool continuous_mode; +} ppt_prx_mode_t; + +/** @brief the psd mode parameters */ +typedef struct +{ + uint8_t chann_start; + uint8_t chann_stop; + uint8_t chann_step; + uint8_t mode; //!< useless + uint16_t timeout; //!< value @ref PSD_TIMEOUT_DEFAULT is recommended +} ppt_psd_mode_t; + +/** @brief the timing latch parameters */ +typedef struct +{ + bool tx_on; + bool acc_hit; +} ppt_timing_latch_t; + +/** @brief the GPIO trigger parameters of each channel */ +typedef struct +{ + uint16_t gpio_delay; +} ppt_gpio_entry_param_t; + +/** @brief the GPIO trigger common parameters */ +typedef struct +{ + bool enable; + bool int_enable; +} ppt_gpio_param_t; + +/** @brief the crc engine common parameters */ +typedef struct +{ + uint32_t poly; + uint32_t init; + uint8_t length; //!< length in bits + uint8_t input_endian : 1; //!< 0: lsb, 1: msb + uint8_t output_endian : 1; //!< 0: lsb, 1: msb + uint8_t bit_mode : 1; //!< 0: byte, 1: bit +} ppt_crc_eng_param_t; + +/** @brief flag to tell driver whether it can sleep */ +extern volatile bool ppt_dlps_mac_idle; + +/** @} End of PPT_Driver_Exported_Types */ + +/** @defgroup PPT_Driver_Exported_Functions Exported Functions + * @brief + * @{ + */ + +/** + * @brief swap bit order of one byte data + * @param[in] inp: the data to be swapped + * @return the new value after swapped bit order + */ +uint8_t ppt_swap_bits8(uint8_t inp); + +/** + * @brief swap bit order of two bytes data + * @param[in] inp: the data to be swapped + * @return the new value after swapped bit order + */ +uint16_t ppt_swap_bits16(uint16_t inp); + +/** + * @brief swap bit order of three bytes data + * @param[in] inp: the data to be swapped + * @return the new value after swapped bit order + */ +uint32_t ppt_swap_bits24(uint32_t inp); + +/** + * @brief swap bit order of four bytes data + * @param[in] inp: the data to be swapped + * @return the new value after swapped bit order + */ +uint32_t ppt_swap_bits32(uint32_t inp); + +/** + * @brief read radio register + * @param[in] offset: the register address + * @return the register value + */ +extern uint16_t (*ppt_read_reg)(uint16_t offset); +#define RD_PPT_REG(offset) ppt_read_reg(offset) + +/** + * @brief write radio register + * @param[in] offset: the register address + * @param[in] data: the register value + * @return none + */ +extern void (*ppt_write_reg)(uint16_t offset, uint16_t data); +#define WR_PPT_REG(offset, data) ppt_write_reg(offset, data) + +/** + * @brief update radio register + * @param[in] offset: the register address + * @param[in] mask: the register mask + * @param[in] data: the register value + * @return none + */ +#define UP_PPT_REG(offset, mask, data) do\ + {\ + uint16_t reg = RD_PPT_REG(offset);\ + reg &= ~(mask);\ + reg |= (data) & (mask);\ + WR_PPT_REG(offset, reg);\ + } while(0) + +/** + * @brief read radio register by specific field + * @param[in] name: the register name + * @param[in] field: the specific field + * @return the field value + */ +#define RD_PPT_REG_FIELD(name, field) ({name##_TYPE reg = {.d16 = RD_PPT_REG(name)}; reg.field;}) + +/** + * @brief write radio register by specific field + * @param[in] name: the register name + * @param[in] field: the specific field + * @param[in] data: the field value + * @return the field value + */ +#define WR_PPT_REG_FIELD(name, field, data) do\ + {\ + name##_TYPE reg = {.d16 = RD_PPT_REG(name)};\ + reg.field = data;\ + WR_PPT_REG(name, reg.d16);\ + } while (0) + +/** + * @brief update radio register by specific field + * @param[in] name: the register address + * @param[in] field: the specific field + * @param[in] mask: the field mask + * @param[in] data: the field value + * @return none + */ +#define UP_PPT_REG_FIELD(name, field, mask, data) do\ + {\ + name##_TYPE reg = {.d16 = RD_PPT_REG(name)};\ + reg.field &= ~(mask);\ + reg.field |= (data) & (mask);\ + WR_PPT_REG(name, reg.d16);\ + } while(0) + +/** + * @brief overwrite radio field value + * @param[in] offset: the register address + * @param[in] field_offset: the field bit offset + * @param[in] field_len: the field bit length + * @param[in] data: the field value + * @return none + */ +#define OV_PPT_REG_FIELD(offset, field_offset, field_len, data) do\ + {\ + uint16_t reg = RD_PPT_REG(offset);\ + reg &= ~(((0xffff >> (16 - (field_len)))) << (field_offset));\ + reg |= (data) << (field_offset);\ + WR_PPT_REG(offset, reg);\ + } while(0) + +/** + * @brief obtain the register offset of the specific entry + * @param[in] entry: the entry of parameter + * @param[in] offset0: the register offset0 + * @param[in] size0: the entry step size0 + * @param[in] offset1: the register offset1 + * @param[in] size0: the entry step size1 + * @return register offset + */ +#define PPT_REG_OFFSET(entry, offset0, size0, offset1, size1) (((entry) < PPT_ENTRY_NUM_ORI) ? ((size0)*(entry) + (offset0)) : ((size1) * ((entry) - PPT_ENTRY_NUM_ORI) + (offset1))) + +/** + * @brief obtain the tx entry in the tx stack information + * @param[in] tx_stack: the @ref PRO_TX_STACK_TYPE register + * @param[in] tx_len: the @ref PRO_TX_LENGTH_TYPE register + */ +#define PPT_TX_STACK_ENTRY(tx_stack, tx_len) (tx_stack.tx_entry_1_0 + (tx_len.tx_entry_2 << 2)) + +/** + * @brief obtain the rx entry in the rx stack information + * @param[in] rx_stack: the @ref PRO_RX_STACK_TYPE register + */ +#define PPT_RX_STACK_ENTRY(rx_stack) (rx_stack.entry_0 + (rx_stack.entry_2_1 << 1)) + +/** + * @brief obtain the header size pushed to the Rx FIFO + * + * The Rx FIFO is filled with the header and the payload of a packet. + * The header contains the header prefix, length and header suffix. + * The total size will be aligned to 16 bits when filling to the Rx FIFO. + * @param[in] hp_size: header prefix size in unit of bit + * @param[in] length_size: length size in unit of bit + * @param[in] hp_size: header suffix size in unit of bit + * @return header size in unit of byte + */ +#define PPT_RX_FIFO_HEADER_SIZE(hp_size, length_size, hs_size) ((((hp_size) + (length_size) + (hs_size) + 15) >> 4) << 1) + +/** + * @brief obtain the header size pushed to the Rx FIFO + * + * The Rx FIFO is filled with the header and the payload of a packet. + * The header contains the header prefix, length and header suffix. + * The total size will be aligned to 16 bits when filling to the Rx FIFO. + * @param[in] hp_size: header prefix size in unit of byte + * @param[in] length_size: length size in unit of byte + * @param[in] hp_size: header suffix size in unit of byte + * @return header size in unit of byte + */ +#define PPT_RX_FIFO_HEADER_SIZE_BYTE(hp_size, length_size, hs_size) PPT_RX_FIFO_HEADER_SIZE(hp_size << 3, length_size << 3, hs_size << 3) + +/** + * @brief enable radio interrupt + * @return none + */ +#define PPT_ISR_ENABLE() NVIC_EnableIRQ(BTMAC_IRQn) + +/** + * @brief disable radio interrupt + * @return none + */ +#define PPT_ISR_DISABLE() NVIC_DisableIRQ(BTMAC_IRQn) + +/** + * @brief initialize the DMA of radio + * @return none + */ +void ppt_init_dma(void); + +/** + * @brief workaround for reset when bzdma start which results in dma carry error later. + * @return none + */ +void ppt_clear_dma_tx_done(void); + +/** + * @brief deinitialize the DMA of radio + * @return none + */ +void ppt_deinit_dma(void); + +/** + * @brief initial the miscellaneous functions + * @return none + */ +void ppt_init_misc(void); + +/** + * @brief deinitial the miscellaneous functions + * @return none + */ +void ppt_deinit_misc(void); + +/** + * @brief reset trx packet number counter of hardware + * @return none + */ +void ppt_reset_trx_pkt_num(void); + +/** + * @brief enable the clock of radio + * @param[in] ppt_mode: true for 2.4G, false for ble + * @return none + */ +void ppt_reset_core_domain(bool ppt_mode); + +/** + * @brief notify 2.4G state to the btmac which may optimize the performance + * @param[in] ppt_mode: true for 2.4G enable, false for 2.4G disable + * @return none + */ +void ppt_notify_btmac(bool ppt_mode); + +/** + * @brief execute instruction to switch the radio state + * @param[in] instruction_code: instruction opcode @ref ppt_hw_instruction_t + * @return none + */ +void ppt_execute_instruction(uint8_t instruction_code); + +/** + * @brief clear radio state + * @deprecated no used + * @return none + */ +#define ppt_clear_radio() +#define ppt_clear_radio_quick(...) + +/** + * @brief lock/unlock the header fields value + * + * Since the the header has multiple fields, they can be written in the atomic way after lock. + * @param[in] entry: channel index + * @param[in] lock: true for lock, false for unlock + * @return none + */ +void ppt_lock(uint8_t entry, bool lock); + +/** + * @brief set whether to holding tx automatically + * @param[in] entry: channel index + * @param[in] enable: feature value + * @return none + */ +void ppt_set_auto_no_tx(uint8_t entry, bool enable); + +/** + * @brief clear hardware holding tx once + * @param[in] entry: channel index + * @return none + */ +void ppt_clear_hw_no_tx(uint8_t entry); + +/** + * @brief set the feature of hardware auto ack the transmitted packet + * @param[in] entry: channel index + * @param[in] enable: feature value + * @return none + */ +void ppt_set_auto_ack(uint8_t entry, bool enable); + +/** + * @brief firmware ack the transmitted packet once with lock flag + * @param[in] entry: channel index + * @param[in] lock: lock the header and tx fifo + * @return tx fifo read pointer + */ +uint8_t ppt_trigger_fw_ack_lock(uint8_t entry, bool lock); + +/** + * @brief firmware ack the transmitted packet once + * @param[in] entry: channel index + * @return tx fifo read pointer + */ +#define ppt_trigger_fw_ack(entry) ppt_trigger_fw_ack_lock(entry, true) + +/** + * @brief firmware force ack the first packet + * + * PRX role may use this api to quit data pushed previously. + * @param[in] entry: channel index + * @return tx fifo read pointer + */ +uint8_t ppt_force_fw_ack(uint8_t entry); + +/** + * @brief register the interrupt handler + * @param[in] handler: the function pointer of ISR + * @return none + */ +void ppt_reg_handler(void (*handler)(void)); + +/** + * @brief register the psd interrupt handler + * @param[in] handler: the function pointer of ISR + * @return none + */ +void ppt_reg_psd_handler(void (*handler)(void)); + +/** + * @brief set crc common parameters + * + * @attention crc including address field is not supported. + * It can be as a workaround to replace the crc init by a new value which is calculated by crc check the address with the crc init. + * @param[in] param: parameters + * @return none + */ +void ppt_set_crc_param(ppt_crc_param_t *param); + +/** + * @brief set crc channel specific parameters + * @param[in] entry: channel index + * @param[in] param: parameters + * @return none + */ +void ppt_set_crc_entry_param(uint8_t entry, ppt_crc_entry_param_t *param); + +/** + * @brief set whitening common parameters + * @param[in] param: parameters + * @return none + */ +void ppt_set_white_param(ppt_white_param_t *param); + +/** + * @brief set whitening channel specific parameters + * @param[in] entry: channel index + * @param[in] param: parameters + * @return none + */ +void ppt_set_white_entry_param(uint8_t entry, ppt_white_entry_param_t *param); + +/** + * @brief set rx phy + * @param[in] rx_phy: phy type + * @return none + */ +void ppt_set_phy_rx_type(ppt_phy_type_t rx_phy); + +/** + * @brief set tx phy + * @param[in] entry: channel index + * @param[in] tx_phy: phy type + * @return none + */ +void ppt_set_phy_tx_type(uint8_t entry, ppt_phy_type_t tx_phy); + +/** + * @brief set phy common parameters + * @param[in] param: parameters + * @return none + */ +void ppt_set_phy_param(ppt_phy_param_t *param); + +/** + * @brief set modulation index of 2M phy + * @param[in] change: change default value + * @return none + */ +void ppt_set_phy_mod_index_2m(bool change); + +/** + * @brief set phy parameters for different rf bank + * + * Currently modulation tx parameters are updated by this function, so only tx needs these settings. + * + * @param[in] bank_idx: 0 for 2402~2480MHz, 1 for 2348~2426MHz, 2 for 2452~2530MHz + * @return none + */ +void ppt_set_phy_bank(uint8_t bank_idx); + +/** + * @brief set phy channel + * + * It will call ppt_set_phy_bank to update phy parameters at the same time. + * + * @param[in] freq_mhz: 2348~2530MHz + * @return set result + */ +bool ppt_set_phy_channel(uint16_t freq_mhz); + +/** + * @brief set phy channel specific parameters + * @param[in] entry: channel index + * @param[in] param: parameters + * @return none + */ +void ppt_set_phy_entry_param(uint8_t entry, ppt_phy_entry_param_t *param); + +/** + * @brief set packet format parameters + * @param[in] param: parameters + * @return none + */ +void ppt_set_pkt_format(ppt_pkt_format_t *param); + +/** + * @brief set preamble length + * @param[in] entry: channel index + * @param[in] preamble_len: length in unit of byte + * @return none + */ +void ppt_set_preamble_len(uint8_t entry, uint8_t preamble_len); + +/** + * @brief set addon field parameters + * @param[in] entry: channel index + * @param[in] param: parameters + * @return none + */ +void ppt_set_addon_len(uint8_t entry, ppt_addon_length_t *param); + +/** + * @brief set rf tx power + * @param[in] tx_gain: tx gain value @ref ppt_tx_gain_t + * @return none + */ +void ppt_set_tx_power(uint8_t tx_gain); + +/** + * @brief set rf tx power + * @param[in] tx_power_dbm: tx power in unit of dbm + * @return none + */ +void ppt_set_tx_power_dbm(float tx_power_dbm); + +/** + * @brief set tx access address + * @param[in] entry: channel index + * @param[in] param: parameters + * @return none + */ +void ppt_set_tx_addr(uint8_t entry, ppt_tx_addr_t *param); + +/** + * @brief set rx access address + * + * The caller shall check whether any address value is 0xff or two rx addresses values are similar. + * The access address shall be chotic as the definition in BT specification. + * If not, the radio parameter need be changed to receive the packets successfully. + * But this will decrease the rx sensitivity. + * + * The radio parameter can be updated by @ref ppt_check_rx_addr. + * @param[in] entry: channel index + * @param[in] param: parameters + * @return none + */ +void ppt_set_rx_addr(uint8_t entry, ppt_rx_addr_t *param); + +/** + * @brief check rx access address and set radio paramters + * + * If any address value is 0xff or two rx addresses values are similar, this function will set the radio parameters. + * Since the parameters are set according to the choosen rx PHY, this function shall be called + * after both the address and rx PHY are set. + * @param[in] bit_th: bit difference threshold, recommend to use 5 + * @return none + */ +void ppt_check_rx_addr(uint8_t bit_th); + +/** + * @brief set ptx modes + * @param[in] param: parameters + * @return none + */ +void ppt_set_ptx_mode(ppt_ptx_mode_t *param); + +/** + * @brief set prx modes + * @param[in] param: parameters + * @return none + */ +void ppt_set_prx_mode(ppt_prx_mode_t *param); + +/** + * @brief set psd modes + * @param[in] param: psd parameters + * @return none + */ +void ppt_set_psd_mode(ppt_psd_mode_t *param); + +/** + * @brief set tx header prefix field + * @param[in] entry: channel index + * @param[in] hp: header prefix + * @return none + */ +void ppt_set_hp(uint8_t entry, uint8_t hp); + +/** + * @brief set tx header length field + * + * The value of the length field indicates the octet length of the payload. + * But this value is useless if dma_length_mode is enabled (which is default on) which use dma length instead. + * @param[in] entry: channel index + * @param[in] length: payload length + * @return none + */ +void ppt_set_length(uint8_t entry, uint16_t length); + +/** + * @brief set tx header suffix field + * @param[in] entry: channel index + * @param[in] hs: header suffix + * @return none + */ +void ppt_set_hs(uint8_t entry, uint32_t hs); + +/** + * @brief set tx header field value + * @param[in] entry: channel index + * @param[in] param: parameters + * @return none + */ +void ppt_set_tx_header(uint8_t entry, ppt_header_t *param); + +/** + * @brief set rf turn around time + * + * If not set, the default value is 150us. + * @param[in] tifs_us: tifs value in microsecond + * @return none + */ +void ppt_set_tifs(uint8_t tifs_us); + +/** + * @brief set timing latch parameters + * @param[in] param: parameters + * @return none + */ +void ppt_set_timing_latch(ppt_timing_latch_t *param); + +/** + * @brief set gpio trigger common parameters + * @param[in] param: parameters + * @return none + */ +void ppt_set_gpio_param(ppt_gpio_param_t *param); + +/** + * @brief set gpio trigger channel specific parameters + * @param[in] entry: channel index + * @param[in] param: parameters + * @return none + */ +void ppt_set_gpio_entry_param(uint8_t entry, ppt_gpio_entry_param_t *param); + +/** + * @brief push the payload field to the tx fifo + * + * The hardware won't copy the data, so the data need be valid until it is acknoledged. + * The other fields will be filled to the transmitted frame automatically by the hardware. + * @param[in] entry: channel index + * @param[in] payload: data pointer + * @param[in] payload_len: length of payload + * @return none + */ +bool ppt_push_tx_fifo(uint8_t entry, uint8_t *payload, uint16_t payload_len); + +/** + * @brief update the payload field to the tx fifo + * + * The hardware won't copy the data, so the data need be valid until it is acknoledged. + * The other fields will be filled to the transmitted frame automatically by the hardware. + * @param[in] entry: channel index + * @param[in] payload: data pointer + * @param[in] payload_len: length of payload + * @return none + */ +bool ppt_update_tx_fifo(uint8_t entry, uint8_t *payload, uint16_t payload_len); + +/** + * @brief flush the tx fifo + * @param[in] entry: channel index + * @return read pointer + */ +uint8_t ppt_flush_tx_fifo(uint8_t entry); + +/** + * @brief get the tx fifo read pointer + * @param[in] entry: channel index + * @return read pointer + */ +uint8_t ppt_get_tx_fifo_rdptr(uint8_t entry); + +/** + * @brief set the tx fifo read pointer + * @param[in] entry: channel index + * @param[in] ptr: where to start tx next time + * @return none + */ +void ppt_set_tx_fifo_rdptr(uint8_t entry, uint8_t ptr); + +/** + * @brief get the tx fifo write pointer + * @param[in] entry: channel index + * @return write pointer + */ +uint8_t ppt_get_tx_fifo_wrptr(uint8_t entry); + +/** + * @brief reset the tx fifo sw & hw state + * + * The read and write pointer will be set to zero, and the fifo will be empty. + * @param[in] entry: channel index + * @return none + */ +void ppt_reset_tx_fifo(uint8_t entry); + +/** + * @brief pop the received pdu from the rx fifo + * + * The rx fifo contains the PDU data including the header fields and payload field. + * @param[in] entry: channel index + * @param[out] pdu: data pointer + * @param[in] pdu_len: length of pdu, attained from the rx stack information + * @return none + */ +bool ppt_pop_rx_fifo(uint8_t entry, uint8_t *pdu, uint16_t pdu_len); + +/** + * @brief flush the rx fifo + * + * Since the receiving state may be disrupted, some residual data needs be flushed from the rx fifo. + * @return none + */ +void ppt_flush_rx_fifo(void); + +/** + * @brief flush the pending tx & rx interrupt + * + * The tx & rx interrupt isn't cared about, so flush them. + * @return none + */ +void ppt_flush_trx_int(void); + +/** + * @brief get rssi dbm value + * @param[in] rssi_raw: rssi register value + * @return the value + */ +int8_t ppt_get_rssi(uint16_t rssi_raw); + +/** + * @brief get channel setting from radio frequncy + * @param[in] frequency: radio frequency in MHz + * @param[out] bank: frequency band index + * @param[out] channel: frequency offset + * @return get result + * @retval true: the input radio frequency is supported by the module + * @retval false: the input raido frequency isn't supported by the module + */ +bool ppt_get_bank_channel(uint16_t frequency, uint8_t *bank, uint8_t *channel); + +/** + * @brief get ble logic channel + * @param[in] bank: frequency band index + * @param[in] channel: frequency offset + * @return logic channel index + * @retval >= 0: the input channel is supported by ble + * @retval < 0: the input channel isn't supported by ble + */ +int8_t ppt_get_ble_logic_chan(uint8_t bank, uint8_t channel); + +/** + * @brief get ble logic channel + * @param[in] frequency: frequency in MHz + * @return logic channel index + * @retval >= 0: the input channel is supported by ble + * @retval < 0: the input channel isn't supported by ble + */ +int8_t ppt_get_ble_logic_chan_via_freq(uint16_t frequency); + +/** + * @brief get whitening init value from ble logic channel + * @param[in] le_logic_chan: ble logic channel + * @return whitening init value + */ +uint32_t ppt_get_ble_white_init(uint8_t le_logic_chan); + +/** + * @brief format diagnose output with raw data + * @param[in] data: raw data + * @param[in] data_len: raw data length + * @param[in] fmt: format string + * @return none + */ +void ppt_dump_log(uint8_t *data, uint32_t data_len, char *fmt, ...); + +/** + * @brief dump all non-zero register + * @return none + */ +void ppt_dump_nonzero_reg(void); + +/** + * @brief calculate the crc of the designated data + * @param[in] param: the crc paramters + * @param[in] data: raw data + * @param[in] data_len: the data length in bytes for byte mode and in bits for bit mode + * @return the CRC result + */ +uint32_t ppt_crc_eng_cal(ppt_crc_eng_param_t *param, uint8_t *data, uint32_t data_len); + +/** + * @brief initialize the dlps setting + * + * This api shall be called early before enter dlps, for example in the pwr_mgr_init. + * @return none + */ +void ppt_dlps_init(void); + +/** + * @brief deinitialize the dlps setting when disable 2.4g module + * @return none + */ +void ppt_dlps_deinit(void); + +/** + * @brief register function that check wheather the 2.4g module can enter sleep mode + * @param[in] check_cb: dlps enter check callback function pointer + * @return none + */ +void ppt_dlps_reg_mac_check_cb(bool (*check_cb)(uint32_t *wakeup_time_diff)); + +/** + * @brief register function that store some setting when mac enter dlps + * @param[in] enter_cb: dlps enter store function pointer + * @return none + */ +void ppt_dlps_reg_mac_enter_cb(void (*enter_cb)(void)); + +/** + * @brief register function that restore some setting when mac exit dlps + * @param[in] exit_cb: dlps exit restore function pointer + * @return none + */ +void ppt_dlps_reg_mac_exit_cb(void (*exit_cb)(void)); + +/** + * @brief register function that store some setting when enter dlps + * @param[in] enter_cb: dlps enter store function pointer + * @return none + */ +void ppt_dlps_reg_pf_enter_cb(void (*enter_cb)(void)); + +/** + * @brief register function that restore some setting when exit dlps + * @param[in] exit_cb: dlps exit restore function pointer + * @return none + */ +void ppt_dlps_reg_pf_exit_cb(void (*exit_cb)(void)); + +/** @} End of PPT_Driver_Exported_Functions */ + +///@cond +#define TIME_DEBUG 0 + +#if TIME_DEBUG +#include "rtl_rcc.h" +#include "rtl_pinmux.h" +#include "rtl_gpio.h" + +#define TEST_PIN P2_2, P2_3, P2_4, P2_5 +#define TIME_DEBUG_CHAN_INVALID -1 +#define TIME_DEBUG_CHAN_THREAD 0 +#define TIME_DEBUG_CHAN_ISR 1 +#define TIME_DEBUG_CHAN_ISR_TX 1 +#define TIME_DEBUG_CHAN_ISR_RX 1 +#define TIME_DEBUG_CHAN_ISR_GPIO 2 +#define TIME_DEBUG_CHAN_TIMING 3 +#define TIME_DEBUG_CHAN_DLPS -1 + +extern uint8_t test_pin[]; +extern uint32_t gpio_test_pin[]; +extern bool level_test_pin[]; +#define TIME_DEBUG_CHAN_NUMBER COUNT_ARG(TEST_PIN) +void ppt_init_time_debug(void); + +#define time_debug_level_high(ch) do \ + { \ + if(ch >= 0 && ch < TIME_DEBUG_CHAN_NUMBER) \ + {\ + GPIOA->GPIO_DR |= gpio_test_pin[ch]; \ + level_test_pin[ch] = TRUE; \ + }\ + }while(0) +#define time_debug_level_low(ch) do \ + { \ + if(ch >= 0 && ch < TIME_DEBUG_CHAN_NUMBER) \ + {\ + GPIOA->GPIO_DR &= ~gpio_test_pin[ch]; \ + level_test_pin[ch] = FALSE; \ + }\ + }while(0) +#define time_debug_edge_up(ch) do \ + { \ + if(ch >= 0 && ch < TIME_DEBUG_CHAN_NUMBER) \ + {\ + GPIOA->GPIO_DR &= ~gpio_test_pin[ch]; \ + GPIOA->GPIO_DR |= gpio_test_pin[ch]; \ + level_test_pin[ch] = TRUE; \ + }\ + }while(0) +#define time_debug_edge_down(ch) do \ + { \ + if(ch >= 0 && ch < TIME_DEBUG_CHAN_NUMBER) \ + {\ + GPIOA->GPIO_DR |= gpio_test_pin[ch]; \ + GPIOA->GPIO_DR &= ~gpio_test_pin[ch]; \ + level_test_pin[ch] = FALSE; \ + }\ + }while(0) +#else +#define time_debug_level_high(...) +#define time_debug_level_low(...) +#define time_debug_edge_up(...) +#define time_debug_edge_down(...) +#endif +void ppt_dbg_port_open(uint16_t bt_port, uint16_t ppt_port, uint16_t ppt_port2); +void ppt_dbg_port_set_pin(uint8_t pin_group, uint32_t dbg_bitmap); +void ppt_dbg_port(uint16_t bt_port, uint16_t ppt_port, uint16_t ppt_port2, uint32_t dbg_bitmap); +///@endcond + +/** @} End of PPT_Driver */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bee/drivers/ppt/sync/src/driver/common/ppt_hw_reg.h b/bee/drivers/ppt/sync/src/driver/common/ppt_hw_reg.h new file mode 100644 index 00000000..4fafe0c7 --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/common/ppt_hw_reg.h @@ -0,0 +1,3896 @@ +/* based on formal_reg_230606.xlsx */ + +#define PRO_INSTRUCTION 0x0 +#define PRO_BASE0_LOWER 0x4 +#define PRO_BASE0_UPPER 0x6 +#define PRO_BASE1_LOWER 0x8 +#define PRO_BASE1_UPPER 0xa +#define PRO_PREFIX0_1 0xc +#define PRO_PREFIX2_3 0xe +#define PRO_PREFIX4_5 0x10 +#define PRO_PREFIX6_7 0x12 +#define PRO_TX_ADDRESS_LOWER 0x14 +#define PRO_TX_ADDRESS_MIDDLE 0x16 +#define PRO_TX_ADDRESS_UPPER 0x18 +#define PRO_TX_CONTROL 0x20 +#define PRO_PERIODIC_INTERVAL 0x22 +#define PRO_GPIO_TX_DELAY 0x24 +#define PRO_RX_CONTROL 0x26 +#define PRO_RX_MATCH_CONTROL 0x28 +#define PRO_TRX_CONTROL 0x2a +#define PRO_FIELD_LENGTH2 0x2c +#define PRO_FIELD_LENGTH 0x30 +#define PRO_PACKET_CONTROL 0x32 +#define PRO_GAIN 0x34 +#define PRO_TX_PKT_NUM 0x36 +#define PRO_TX_PKT_NUM2 0x38 +#define PRO_HS_LENGTH 0x3a +#define PRO_CHANNEL_BANK 0x3c +#define PRO_GPIO_CONTROL 0x3e +#define PRO_MISR 0x40 +#define PRO_IMR 0x42 +#define PRO_RSSI 0x4e +#define PRO_LENGTH_INCLUDE_ADDON 0x50 +#define PRO_LENGTH 0x52 +#define PRO_RX_STACK 0x54 +#define PRO_RX_HP 0x56 +#define PRO_RX_CRC_LOWER 0x58 +#define PRO_RX_CRC_UPPER 0x5a +#define PRO_ACCHIT_CLK_LOWER 0x5c +#define PRO_ACCHIT_CLK_UPPER 0x5e +#define PRO_RX_HS_LOWER 0x60 +#define PRO_RX_HS_UPPER 0x62 +#define PRO_TX_STACK 0x64 +#define PRO_TX_HS_LOWER 0x66 +#define PRO_TX_HS_UPPER 0x68 +#define PRO_TX_LENGTH 0x6a +#define PRO_TX_CLK_LOWER 0x6c +#define PRO_TX_CLK_UPPER 0x6e +#define PRO_HP_0 0x80 +#define PRO_HS_0_LOWER 0x82 +#define PRO_HS_0_UPPER 0x84 +#define PRO_LENGTH_0 0x86 +#define PRO_CRC_INIT0_LOWER 0x88 +#define PRO_CRC_INIT0_UPPER 0x8a +#define PRO_CRC_POLYNOMIAL_REG0_LOWER 0x8c +#define PRO_CRC_POLYNOMIAL_REG0_UPPER 0x8e +#define PRO_PACKET_RELATED_CONTROL0 0x90 +#define PRO_ADD_ON_LENGTH0 0x92 +#define PRO_WHITE_INIT0_LOWER 0x94 +#define PRO_WHITE_INIT0_UPPER 0x96 +#define PRO_WHITE_POLYNOMIAL_REG0_LOWER 0x98 +#define PRO_WHITE_POLYNOMIAL_REG0_UPPER 0x9a +#define PRO_HP_1 0xa0 +#define PRO_HS_1_LOWER 0xa2 +#define PRO_HS_1_UPPER 0xa4 +#define PRO_LENGTH_1 0xa6 +#define PRO_CRC_INIT1_LOWER 0xa8 +#define PRO_CRC_INIT1_UPPER 0xaa +#define PRO_CRC_POLYNOMIAL_REG1_LOWER 0xac +#define PRO_CRC_POLYNOMIAL_REG1_UPPER 0xae +#define PRO_PACKET_RELATED_CONTROL1 0xb0 +#define PRO_ADD_ON_LENGTH1 0xb2 +#define PRO_WHITE_INIT1_LOWER 0xb4 +#define PRO_WHITE_INIT1_UPPER 0xb6 +#define PRO_WHITE_POLYNOMIAL_REG1_LOWER 0xb8 +#define PRO_WHITE_POLYNOMIAL_REG1_UPPER 0xba +#define PRO_HP_2 0xc0 +#define PRO_HS_2_LOWER 0xc2 +#define PRO_HS_2_UPPER 0xc4 +#define PRO_LENGTH_2 0xc6 +#define PRO_CRC_INIT2_LOWER 0xc8 +#define PRO_CRC_INIT2_UPPER 0xca +#define PRO_CRC_POLYNOMIAL_REG2_LOWER 0xcc +#define PRO_CRC_POLYNOMIAL_REG2_UPPER 0xce +#define PRO_PACKET_RELATED_CONTROL2 0xd0 +#define PRO_ADD_ON_LENGTH2 0xd2 +#define PRO_WHITE_INIT2_LOWER 0xd4 +#define PRO_WHITE_INIT2_UPPER 0xd6 +#define PRO_WHITE_POLYNOMIAL_REG2_LOWER 0xd8 +#define PRO_WHITE_POLYNOMIAL_REG2_UPPER 0xda +#define PRO_RESET_CTRL_REG 0xdc +#define PRO_GPIO_CTRL_REG 0xde +#define PRO_TX_ADDRESS2_LOWER 0xe0 +#define PRO_TX_ADDRESS2_MIDDLE 0xe2 +#define PRO_TX_ADDRESS2_UPPER 0xe4 +#define PRO_ORDER_CONTROL 0xe6 +#define PRO_GPIO_CTRL1_REG 0xe8 +#define PRO_GPIO_CTRL2_REG 0xea +#define PRO_GPIO_NATIVE_CNT 0xec +#define PRO_MAX_LENGTH 0xee +#define PRO_RX_TURN_AROUND_DELAY 0x100 +#define PRO_2M_TRX_TURN_AROUND_DELAY 0x102 +#define PRO_1M_2M_TRX_TURN_AROUND_DELAY 0x104 +#define PRO_TX_TURN_AROUND_DELAY 0x106 +#define PRO_RX_TIMEOUT_DELAY 0x108 +#define PRO_TRX_ON_DELAY 0x10a +#define PRO_2M_TRX_ON_DELAY 0x10c +#define PRO_TRX_DEALY_MISC 0x10e +#define PRO_PSD_RANGE0 0x110 +#define PRO_PSD_RANGE1 0x112 +#define PRO_PSD_TIMEOUT 0x114 +#define PRO_INT_CTRL 0x118 +#define PRO_GPIO_TX_DELAY1 0x11a +#define PRO_INT_CTRL_TX 0x11c +#define PRO_GPIO_TX_DELAY2 0x11e +#define PRO_CBK_CTRL 0x120 +#define PRO_TRX_AFE 0x122 +#define PRO_DEBUG_SEL 0x124 +#define PRO_DEBUG_SEL2 0x126 +#define PRO_DSM_READ 0x128 +#define PRO_DSM_READ1 0x12a +#define PRO_CRC_ENGINE_INIT 0x12c +#define PRO_CRC_ENGINE_INIT1 0x12e +#define PRO_CRC_ENGINE_POLY 0x130 +#define PRO_CRC_ENGINE_POLY1 0x132 +#define PRO_CRC_ENGINE_IN 0x134 +#define PRO_CRC_ENGINE_IN1 0x136 +#define PRO_CRC_ENGINE_IN2 0x138 +#define PRO_CRC_ENGINE_OUT 0x13a +#define PRO_CRC_ENGINE_OUT1 0x13c +#define PRO_CRC_ENGINE_EN 0x13e +#define PRO_RX_CRC_INIT0_LOWER 0x140 +#define PRO_RX_CRC_INIT0_UPPER 0x142 +#define PRO_RX_CRC_INIT1_LOWER 0x144 +#define PRO_RX_CRC_INIT1_UPPER 0x146 +#define PRO_RX_CRC_INIT2_LOWER 0x148 +#define PRO_RX_CRC_INIT2_UPPER 0x14a +#define PRO_HP_3 0x14c +#define PRO_HP_4 0x14e +#define PRO_HP_5 0x150 +#define PRO_HS_3_LOWER 0x152 +#define PRO_HS_3_UPPER 0x154 +#define PRO_HS_4_LOWER 0x156 +#define PRO_HS_4_UPPER 0x158 +#define PRO_HS_5_LOWER 0x15a +#define PRO_HS_5_UPPER 0x15c +#define PRO_HS_6_LOWER 0x15e +#define PRO_HS_6_UPPER 0x160 +#define PRO_HS_7_LOWER 0x162 +#define PRO_HS_7_UPPER 0x164 +#define PRO_LENGTH_3 0x166 +#define PRO_LENGTH_4 0x168 +#define PRO_LENGTH_5 0x16a +#define PRO_LENGTH_6 0x16c +#define PRO_LENGTH_7 0x16e +#define PRO_ADD_ON_LENGTH3 0x170 +#define PRO_ADD_ON_LENGTH4 0x172 +#define PRO_ADD_ON_LENGTH5 0x174 +#define PRO_CRC_INIT3_LOWER 0x176 +#define PRO_CRC_INIT3_UPPER 0x178 +#define PRO_CRC_INIT4_LOWER 0x17a +#define PRO_CRC_INIT4_UPPER 0x17c +#define PRO_CRC_INIT5_LOWER 0x17e +#define PRO_CRC_INIT5_UPPER 0x180 +#define PRO_CRC_INIT6_LOWER 0x182 +#define PRO_CRC_INIT6_UPPER 0x184 +#define PRO_CRC_INIT7_LOWER 0x186 +#define PRO_CRC_INIT7_UPPER 0x188 +#define PRO_RX_CRC_INIT3_LOWER 0x18a +#define PRO_RX_CRC_INIT3_UPPER 0x18c +#define PRO_RX_CRC_INIT4_LOWER 0x18e +#define PRO_RX_CRC_INIT4_UPPER 0x190 +#define PRO_RX_CRC_INIT5_LOWER 0x192 +#define PRO_RX_CRC_INIT5_UPPER 0x194 +#define PRO_RX_CRC_INIT6_LOWER 0x196 +#define PRO_RX_CRC_INIT6_UPPER 0x198 +#define PRO_RX_CRC_INIT7_LOWER 0x19a +#define PRO_RX_CRC_INIT7_UPPER 0x19c +#define PRO_WHITE_INIT3_LOWER 0x19e +#define PRO_WHITE_INIT3_UPPER 0x1a0 +#define PRO_WHITE_INIT4_LOWER 0x1a2 +#define PRO_WHITE_INIT4_UPPER 0x1a4 +#define PRO_WHITE_INIT5_LOWER 0x1a6 +#define PRO_WHITE_INIT5_UPPER 0x1a8 +#define PRO_WHITE_INIT6_LOWER 0x1aa +#define PRO_WHITE_INIT6_UPPER 0x1ac +#define PRO_WHITE_INIT7_LOWER 0x1ae +#define PRO_WHITE_INIT7_UPPER 0x1b0 +#define PRO_WHITE_POLYNOMIAL_REG3_LOWER 0x1b2 +#define PRO_WHITE_POLYNOMIAL_REG3_UPPER 0x1b4 +#define PRO_WHITE_POLYNOMIAL_REG4_LOWER 0x1b6 +#define PRO_WHITE_POLYNOMIAL_REG4_UPPER 0x1b8 +#define PRO_WHITE_POLYNOMIAL_REG5_LOWER 0x1ba +#define PRO_WHITE_POLYNOMIAL_REG5_UPPER 0x1bc +#define PRO_WHITE_POLYNOMIAL_REG6_LOWER 0x1be +#define PRO_WHITE_POLYNOMIAL_REG6_UPPER 0x1c0 +#define PRO_WHITE_POLYNOMIAL_REG7_LOWER 0x1c2 +#define PRO_WHITE_POLYNOMIAL_REG7_UPPER 0x1c4 +#define PRO_CRC_POLYNOMIAL_REG3_LOWER 0x1c6 +#define PRO_CRC_POLYNOMIAL_REG3_UPPER 0x1c8 +#define PRO_CRC_POLYNOMIAL_REG4_LOWER 0x1ca +#define PRO_CRC_POLYNOMIAL_REG4_UPPER 0x1cc +#define PRO_CRC_POLYNOMIAL_REG5_LOWER 0x1ce +#define PRO_CRC_POLYNOMIAL_REG5_UPPER 0x1d0 +#define PRO_CRC_POLYNOMIAL_REG6_LOWER 0x1d2 +#define PRO_CRC_POLYNOMIAL_REG6_UPPER 0x1d4 +#define PRO_CRC_POLYNOMIAL_REG7_LOWER 0x1d6 +#define PRO_CRC_POLYNOMIAL_REG7_UPPER 0x1d8 +#define PRO_RX_PKT_NUM0 0x1da +#define PRO_RX_PKT_NUM1 0x1dc +#define PRO_RX_PKT_NUM2 0x1de +#define PRO_TX_PKT_NUM3 0x1e0 +#define PRO_TX_PKT_NUM4 0x1e2 +#define PRO_TX_PKT_NUM5 0x1e4 +#define PRO_WHITE_FIELD_LENGTH_0 0x1e6 +#define PRO_WHITE_FIELD_LENGTH_1 0x1e8 +#define PRO_WHITE_FIELD_LENGTH_2 0x1ea +#define PRO_WHITE_FIELD_LENGTH_3 0x1ec +#define PRO_WHITE_FIELD_LENGTH_4 0x1ee +#define PRO_FIELD_LENGTH3 0x1f0 +#define PRO_FIELD_LENGTH4 0x1f2 +#define PRO_FIELD_LENGTH5 0x1f4 +#define PRO_FIELD_LENGTH6 0x1f6 +#define PRO_FIELD_LENGTH7 0x1f8 +#define PRO_TX_ADDRESS1_LOWER 0x1fa +#define PRO_TX_ADDRESS1_MIDDLE 0x1fc +#define PRO_TX_ADDRESS1_UPPER 0x1fe +#define PRO_TX_ADDRESS3_LOWER 0x200 +#define PRO_TX_ADDRESS3_MIDDLE 0x202 +#define PRO_TX_ADDRESS3_UPPER 0x204 +#define PRO_TX_ADDRESS4_LOWER 0x206 +#define PRO_TX_ADDRESS4_MIDDLE 0x208 +#define PRO_TX_ADDRESS4_UPPER 0x20a +#define PRO_TX_ADDRESS5_LOWER 0x20c +#define PRO_TX_ADDRESS5_MIDDLE 0x20e +#define PRO_TX_ADDRESS5_UPPER 0x210 +#define PRO_TX_ADDRESS6_LOWER 0x212 +#define PRO_TX_ADDRESS6_MIDDLE 0x214 +#define PRO_TX_ADDRESS6_UPPER 0x216 +#define PRO_TX_ADDRESS7_LOWER 0x218 +#define PRO_TX_ADDRESS7_MIDDLE 0x21a +#define PRO_TX_ADDRESS7_UPPER 0x21c +#define PRO_RD_PTR_0 0x21e +#define PRO_RD_PTR_1 0x220 +#define PRO_REG_TOTAL_SIZE (PRO_RD_PTR_1 + 2) + +/* 0x0 + 7:0 RW instruction 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_INSTRUCTION_TYPE +{ + uint16_t d16; + struct + { + uint16_t instruction: 8; + uint16_t rsvd: 8; + }; +} PRO_INSTRUCTION_TYPE; + +/* 0x4 + 15:0 RW base0[15:0] 0x0 + */ +typedef union _PRO_BASE0_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t base0_15_0; + }; +} PRO_BASE0_LOWER_TYPE; + +/* 0x6 + 15:0 RW base0[31:16] 0x0 + */ +typedef union _PRO_BASE0_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t base0_31_16; + }; +} PRO_BASE0_UPPER_TYPE; + +/* 0x8 + 15:0 RW base1[15:0] 0x0 + */ +typedef union _PRO_BASE1_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t base1_15_0; + }; +} PRO_BASE1_LOWER_TYPE; + +/* 0xa + 15:0 RW base1[31:16] 0x0 + */ +typedef union _PRO_BASE1_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t base1_31_16; + }; +} PRO_BASE1_UPPER_TYPE; + +/* 0xc + 7:0 RW prefix0[7:0] 0x0 + 15:8 RW prefix1[7:0] 0x0 + */ +typedef union _PRO_PREFIX0_1_TYPE +{ + uint16_t d16; + struct + { + uint16_t prefix0_7_0: 8; + uint16_t prefix1_7_0: 8; + }; +} PRO_PREFIX0_1_TYPE; + +/* 0xe + 7:0 RW prefix2[7:0] 0x0 + 15:8 RW prefix3[7:0] 0x0 + */ +typedef union _PRO_PREFIX2_3_TYPE +{ + uint16_t d16; + struct + { + uint16_t prefix2_7_0: 8; + uint16_t prefix3_7_0: 8; + }; +} PRO_PREFIX2_3_TYPE; + +/* 0x10 + 7:0 RW prefix4[7:0] 0x0 + 15:8 RW prefix5[7:0] 0x0 + */ +typedef union _PRO_PREFIX4_5_TYPE +{ + uint16_t d16; + struct + { + uint16_t prefix4_7_0: 8; + uint16_t prefix5_7_0: 8; + }; +} PRO_PREFIX4_5_TYPE; + +/* 0x12 + 7:0 RW prefix6[7:0] 0x0 + 8 RW length_order_first_rx 0x0 + 9 RW length_split_first_rx 0x0 + 10 RW hp_order_first_rx 0x0 + 11 RW hp_split_first_rx 0x0 + 12 RW hs_order_first_rx 0x0 + 13 RW hs_split_first_rx 0x0 + 15:14 RO rsvd 0x0 + */ +typedef union _PRO_PREFIX6_7_TYPE +{ + uint16_t d16; + struct + { + uint16_t prefix6_7_0: 8; + uint16_t length_order_first_rx: 1; + uint16_t length_split_first_rx: 1; + uint16_t hp_order_first_rx: 1; + uint16_t hp_split_first_rx: 1; + uint16_t hs_order_first_rx: 1; + uint16_t hs_split_first_rx: 1; + uint16_t rsvd: 2; + }; +} PRO_PREFIX6_7_TYPE; + +/* 0x14 + 15:0 RW tx_address[15:0] 0x0 + */ +typedef union _PRO_TX_ADDRESS_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address_15_0; + }; +} PRO_TX_ADDRESS_LOWER_TYPE; + +/* 0x16 + 15:0 RW tx_address[31:16] 0x0 + */ +typedef union _PRO_TX_ADDRESS_MIDDLE_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address_31_16; + }; +} PRO_TX_ADDRESS_MIDDLE_TYPE; + +/* 0x18 + 7:0 RW tx_address[39:32] 0x0 + 15:8 RW prefix7[7:0] 0x0 + */ +typedef union _PRO_TX_ADDRESS_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address_39_32: 8; + uint16_t prefix7_7_0: 8; + }; +} PRO_TX_ADDRESS_UPPER_TYPE; + +/* 0x20 + 0 RW periodic_enable 0x0 + 1 RW no_tx0 0x0 + 2 RW auto_no_tx_option0 0x0 + 3 RW lock0 0x0 + 4 RW fix_tx_address_option 0x0 + 5 RW empty_flag1 0x0 + 6 RW clear_hw_no_tx1 0x0 + 7 RW clear_hw_rd_ptr0 0x0 + 8 RW clear_hw_rd_ptr1 0x0 + 9 RW clear_hw_no_tx0 0x0 + 10 RO read_clear_hw_rd_ptr0 0x0 + 11 RO read_clear_hw_rd_ptr1 0x0 + 12 RO read_clear_hw_no_tx0 0x0 + 13 RO read_clear_hw_no_tx1 0x0 + 14 RW empty_flag0 0x0 + 15 RW lock1 0x0 + */ +typedef union _PRO_TX_CONTROL_TYPE +{ + uint16_t d16; + struct + { + uint16_t periodic_enable: 1; + uint16_t no_tx0: 1; + uint16_t auto_no_tx_option0: 1; + uint16_t lock0: 1; + uint16_t fix_tx_address_option: 1; + uint16_t empty_flag1: 1; + uint16_t clear_hw_no_tx1: 1; + uint16_t clear_hw_rd_ptr0: 1; + uint16_t clear_hw_rd_ptr1: 1; + uint16_t clear_hw_no_tx0: 1; + uint16_t read_clear_hw_rd_ptr0: 1; + uint16_t read_clear_hw_rd_ptr1: 1; + uint16_t read_clear_hw_no_tx0: 1; + uint16_t read_clear_hw_no_tx1: 1; + uint16_t empty_flag0: 1; + uint16_t lock1: 1; + }; +} PRO_TX_CONTROL_TYPE; + +/* 0x22 + 15:0 RW periodic_interval[15:0] 0x0 + */ +typedef union _PRO_PERIODIC_INTERVAL_TYPE +{ + uint16_t d16; + struct + { + uint16_t periodic_interval_15_0; + }; +} PRO_PERIODIC_INTERVAL_TYPE; + +/* 0x24 + 15:0 RW gpio_tx_delay[15:0] 0x0 + */ +typedef union _PRO_GPIO_TX_DELAY_TYPE +{ + uint16_t d16; + struct + { + uint16_t gpio_tx_delay_15_0; + }; +} PRO_GPIO_TX_DELAY_TYPE; + +/* 0x26 + 6:0 RO rsvd 0x0 + 7 RW lna_en 0x0 + 8 RW max_length_kept 0x0 + 9 RW rx_phy 0x0 + 10 RW continuous_rx 0x1 + 11 RW rx2rx_option 0x0 + 12 RW fix_entry0_option 0x0 + 13 RW fix_entry1_option 0x0 + 14 RW error_address_kept 0x0 + 15 RW acchit_time_enable 0x0 + */ +typedef union _PRO_RX_CONTROL_TYPE +{ + uint16_t d16; + struct + { + uint16_t rsvd: 7; + uint16_t lna_en: 1; + uint16_t max_length_kept: 1; + uint16_t rx_phy: 1; + uint16_t continuous_rx: 1; + uint16_t rx2rx_option: 1; + uint16_t fix_entry0_option: 1; + uint16_t fix_entry1_option: 1; + uint16_t error_address_kept: 1; + uint16_t acchit_time_enable: 1; + }; +} PRO_RX_CONTROL_TYPE; + +/* 0x28 + 7:0 RW rx_match_enable_bit[7:0] 0x0 + 8 RW compare_prefix_option 0x0 + 9 RW tx_sdcnt_enable 0x0 + 10 RW rx_sdcnt_enable 0x0 + 11 RW use_maxlength_compare 0x0 + 12 RW opt_crc_err_no_ack 0x0 + 15:13 RO rsvd 0x0 + */ +typedef union _PRO_RX_MATCH_CONTROL_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_match_enable_bit_7_0: 8; + uint16_t compare_prefix_option: 1; + uint16_t tx_sdcnt_enable: 1; + uint16_t rx_sdcnt_enable: 1; + uint16_t use_maxlength_compare: 1; + uint16_t opt_crc_err_no_ack: 1; + uint16_t rsvd: 3; + }; +} PRO_RX_MATCH_CONTROL_TYPE; + +/* 0x2a + 0 RW ack_enable 0x0 + 4:1 RW periodic_interval[19:16] 0x0 + 5 RW auto_no_tx_option1 0x0 + 6 RW empty_flag2 0x0 + 7 RW auto_no_rdptr_option1 0x0 + 15:8 RW tifs_reg[7:0] 0x0 + */ +typedef union _PRO_TRX_CONTROL_TYPE +{ + uint16_t d16; + struct + { + uint16_t ack_enable: 1; + uint16_t periodic_interval_19_16: 4; + uint16_t auto_no_tx_option1: 1; + uint16_t empty_flag2: 1; + uint16_t auto_no_rdptr_option1: 1; + uint16_t tifs_reg_7_0: 8; + }; +} PRO_TRX_CONTROL_TYPE; + +/* 0x2c + 2:0 RW preamble_field_length1[2:0] 0x0 + 7:3 RW ramp_time_minus 0x18 + 15:8 RW early_interrupt_time 0xa + */ +typedef union _PRO_FIELD_LENGTH2_TYPE +{ + uint16_t d16; + struct + { + uint16_t preamble_field_length1_2_0: 3; + uint16_t ramp_time_minus: 5; + uint16_t early_interrupt_time: 8; + }; +} PRO_FIELD_LENGTH2_TYPE; + +/* 0x30 + 2:0 RW preamble_field_length0[2:0] 0x0 + 5:3 RW address_field_length[2:0] 0x0 + 9:6 RW hp_field_length[3:0] 0x0 + 13:10 RW length_field_length[3:0] 0x0 + 14 RW auto_no_rdptr_option0 0x0 + 15 RW dma_length_mode 0x1 + */ +typedef union _PRO_FIELD_LENGTH_TYPE +{ + uint16_t d16; + struct + { + uint16_t preamble_field_length0_2_0: 3; + uint16_t address_field_length_2_0: 3; + uint16_t hp_field_length_3_0: 4; + uint16_t length_field_length_3_0: 4; + uint16_t auto_no_rdptr_option0: 1; + uint16_t dma_length_mode: 1; + }; +} PRO_FIELD_LENGTH_TYPE; + +/* 0x32 + 0 RW crc_kept 0x0 + 1 RW crc_include_addr_enable 0x0 + 4:2 RW crc_field_length[2:0] 0x0 + 6:5 WO periodic_interval_opt[1:0] 0x0 + 7 RW force_null_packet 0x0 + 8 RW inval_crc 0x0 + 9 RW empty_kept 0x0 + 10 RW white_dis 0x0 + 11 RW hp_split 0x0 + 12 RW length_split 0x0 + 13 RW hs_split 0x0 + 14 RW white_include_addr_enable 0x0 + 15 RW rif_lbt_en_pro 0x0 + */ +typedef union _PRO_PACKET_CONTROL_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_kept: 1; + uint16_t crc_include_addr_enable: 1; + uint16_t crc_field_length_2_0: 3; + uint16_t periodic_interval_opt_1_0: 2; + uint16_t force_null_packet: 1; + uint16_t inval_crc: 1; + uint16_t empty_kept: 1; + uint16_t white_dis: 1; + uint16_t hp_split: 1; + uint16_t length_split: 1; + uint16_t hs_split: 1; + uint16_t white_include_addr_enable: 1; + uint16_t rif_lbt_en_pro: 1; + }; +} PRO_PACKET_CONTROL_TYPE; + +/* 0x34 + 7:0 RW rif_tx_gain 0x0 + 8 RW en_afc_opt0 0x1 + 9 RW en_afc_opt1 0x1 + 10 RW en_afc_opt2 0x0 + 11 RW en_afc_opt3 0x0 + 15:12 RO rsvd 0x0 + */ +typedef union _PRO_GAIN_TYPE +{ + uint16_t d16; + struct + { + uint16_t rif_tx_gain: 8; + uint16_t en_afc_opt0: 1; + uint16_t en_afc_opt1: 1; + uint16_t en_afc_opt2: 1; + uint16_t en_afc_opt3: 1; + uint16_t rsvd: 4; + }; +} PRO_GAIN_TYPE; + +/* 0x36 + 7:0 RO tx_pkt_num0 0x0 + 15:8 RO tx_pkt_num1 0x0 + */ +typedef union _PRO_TX_PKT_NUM_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_pkt_num0: 8; + uint16_t tx_pkt_num1: 8; + }; +} PRO_TX_PKT_NUM_TYPE; + +/* 0x38 + 7:0 RO tx_pkt_num2 0x0 + 8 RW modem_compare2_addr 0x0 + 15:9 RO rsvd 0x0 + */ +typedef union _PRO_TX_PKT_NUM2_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_pkt_num2: 8; + uint16_t modem_compare2_addr: 1; + uint16_t rsvd: 7; + }; +} PRO_TX_PKT_NUM2_TYPE; + +/* 0x3a + 4:0 RW hs_field_length[4:0] 0x0 + 15:5 RO rsvd 0x0 + */ +typedef union _PRO_HS_LENGTH_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_field_length_4_0: 5; + uint16_t rsvd: 11; + }; +} PRO_HS_LENGTH_TYPE; + +/* 0x3c + 6:0 RW pro_channel 0x0 + 12:7 RO rsvd2 0x0 + 15:13 RW bank 0x0 + */ +typedef union _PRO_CHANNEL_BANK_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_channel: 7; + uint16_t rsvd2: 6; + uint16_t bank: 3; + }; +} PRO_CHANNEL_BANK_TYPE; + +/* 0x3e + 0 RW fix_entry2_option 0x0 + 1 RW lock2 0x0 + 2 RO rsvd1 0x0 + 3 RW clear_hw_no_tx2 0x0 + 4 RW auto_no_tx_option2 0x0 + 5 RW no_tx2 0x0 + 8:6 RW preamble_field_length2 0x0 + 9 RW clear_hw_rd_ptr2 0x0 + 10 RW auto_no_rdptr_option2 0x0 + 11 RW reset_tx_pktnum2 0x0 + 12 RO read_clear_hw_no_tx2 0x0 + 13 RO read_clear_hw_rd_ptr2 0x0 + 15:14 RO rsvd 0x0 + */ +typedef union _PRO_GPIO_CONTROL_TYPE +{ + uint16_t d16; + struct + { + uint16_t fix_entry2_option: 1; + uint16_t lock2: 1; + uint16_t rsvd1: 1; + uint16_t clear_hw_no_tx2: 1; + uint16_t auto_no_tx_option2: 1; + uint16_t no_tx2: 1; + uint16_t preamble_field_length2: 3; + uint16_t clear_hw_rd_ptr2: 1; + uint16_t auto_no_rdptr_option2: 1; + uint16_t reset_tx_pktnum2: 1; + uint16_t read_clear_hw_no_tx2: 1; + uint16_t read_clear_hw_rd_ptr2: 1; + uint16_t rsvd: 2; + }; +} PRO_GPIO_CONTROL_TYPE; + +/* 0x40 + 0 RO tx_int 0x0 + 1 RO rx_int 0x0 + 2 RO tx_early_int 0x0 + 3 RO gpio_int 0x0 + 4 RO kill_ptx_int 0x0 + 5 RO kill_prx_int 0x0 + 6 RO reset_trig_int 0x0 + 15:7 RO rsvd 0x0 + */ +typedef union _PRO_MISR_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_int: 1; + uint16_t rx_int: 1; + uint16_t tx_early_int: 1; + uint16_t gpio_int: 1; + uint16_t kill_ptx_int: 1; + uint16_t kill_prx_int: 1; + uint16_t reset_trig_int: 1; + uint16_t rsvd: 9; + }; +} PRO_MISR_TYPE; + +/* 0x42 + 0 RW tx_int 0x0 + 1 RW rx_int 0x0 + 2 RW tx_early_int 0x0 + 3 RW gpio_int 0x0 + 4 RW kill_ptx_int 0x0 + 5 RW kill_prx_int 0x0 + 6 RW reset_trig_int 0x0 + 15:7 RW imr[15:7](rsvd) 0x0 + */ +typedef union _PRO_IMR_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_int: 1; + uint16_t rx_int: 1; + uint16_t tx_early_int: 1; + uint16_t gpio_int: 1; + uint16_t kill_ptx_int: 1; + uint16_t kill_prx_int: 1; + uint16_t reset_trig_int: 1; + uint16_t imr_15_7rsvd: 9; + }; +} PRO_IMR_TYPE; + +/* 0x4e + 15:0 RO rssi 0x0 + */ +typedef union _PRO_RSSI_TYPE +{ + uint16_t d16; + struct + { + uint16_t rssi; + }; +} PRO_RSSI_TYPE; + +/* 0x50 + 15:0 RO length_include_addon 0x0 + */ +typedef union _PRO_LENGTH_INCLUDE_ADDON_TYPE +{ + uint16_t d16; + struct + { + uint16_t length_include_addon; + }; +} PRO_LENGTH_INCLUDE_ADDON_TYPE; + +/* 0x52 + 14:0 RO rx_length 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_LENGTH_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_length: 15; + uint16_t rsvd: 1; + }; +} PRO_LENGTH_TYPE; + +/* 0x54 + 0 RO is_max_length 0x0 + 1 RO is_crc_error 0x0 + 2 RO entry[0] 0x0 + 3 RO rx_hit 0x0 + 4 RO gpio_flag 0x0 + 5 RO rx_time_out 0x0 + 6 RO rx_abort_rd 0x0 + 8:7 RO entry[2:1] 0x0 + 15:9 RO rsvd 0x0 + */ +typedef union _PRO_RX_STACK_TYPE +{ + uint16_t d16; + struct + { + uint16_t is_max_length: 1; + uint16_t is_crc_error: 1; + uint16_t entry_0: 1; + uint16_t rx_hit: 1; + uint16_t gpio_flag: 1; + uint16_t rx_time_out: 1; + uint16_t rx_abort_rd: 1; + uint16_t entry_2_1: 2; + uint16_t rsvd: 7; + }; +} PRO_RX_STACK_TYPE; + +/* 0x56 + 7:0 RO hp 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_RX_HP_TYPE +{ + uint16_t d16; + struct + { + uint16_t hp: 8; + uint16_t rsvd: 8; + }; +} PRO_RX_HP_TYPE; + +/* 0x58 + 15:0 RO rx_crc[15:0] 0x0 + */ +typedef union _PRO_RX_CRC_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_15_0; + }; +} PRO_RX_CRC_LOWER_TYPE; + +/* 0x5a + 15:0 RO rx_crc[31:16] 0x0 + */ +typedef union _PRO_RX_CRC_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_31_16; + }; +} PRO_RX_CRC_UPPER_TYPE; + +/* 0x5c + 15:0 RO acchit_clk[15:0] 0x0 + */ +typedef union _PRO_ACCHIT_CLK_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t acchit_clk_15_0; + }; +} PRO_ACCHIT_CLK_LOWER_TYPE; + +/* 0x5e + 15:0 RO acchit_clk[31:16] 0x0 + */ +typedef union _PRO_ACCHIT_CLK_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t acchit_clk_31_16; + }; +} PRO_ACCHIT_CLK_UPPER_TYPE; + +/* 0x60 + 15:0 RO hs[15:0] 0x0 + */ +typedef union _PRO_RX_HS_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_15_0; + }; +} PRO_RX_HS_LOWER_TYPE; + +/* 0x62 + 14:0 RO hs[30:16] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_RX_HS_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_30_16: 15; + uint16_t rsvd: 1; + }; +} PRO_RX_HS_UPPER_TYPE; + +/* 0x64 + 0 RO is_no_tx 0x0 + 1 RO is_empty 0x0 + 5:2 RO tx_ptr 0x0 + 7:6 RO tx_entry[1:0] 0x0 + 15:8 RO tx_hp 0x0 + */ +typedef union _PRO_TX_STACK_TYPE +{ + uint16_t d16; + struct + { + uint16_t is_no_tx: 1; + uint16_t is_empty: 1; + uint16_t tx_ptr: 4; + uint16_t tx_entry_1_0: 2; + uint16_t tx_hp: 8; + }; +} PRO_TX_STACK_TYPE; + +/* 0x66 + 15:0 RO tx_hs[15:0] 0x0 + */ +typedef union _PRO_TX_HS_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_hs_15_0; + }; +} PRO_TX_HS_LOWER_TYPE; + +/* 0x68 + 14:0 RO tx_hs[30:16] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_TX_HS_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_hs_30_16: 15; + uint16_t rsvd: 1; + }; +} PRO_TX_HS_UPPER_TYPE; + +/* 0x6a + 10:0 RO tx_length 0x0 + 11 RO tx_entry[2] 0x0 + 15:12 RO rsvd 0x0 + */ +typedef union _PRO_TX_LENGTH_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_length: 11; + uint16_t tx_entry_2: 1; + uint16_t rsvd: 4; + }; +} PRO_TX_LENGTH_TYPE; + +/* 0x6c + 15:0 RO tx_clk[15:0] 0x0 + */ +typedef union _PRO_TX_CLK_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_clk_15_0; + }; +} PRO_TX_CLK_LOWER_TYPE; + +/* 0x6e + 15:0 RO tx_clk[31:16] 0x0 + */ +typedef union _PRO_TX_CLK_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_clk_31_16; + }; +} PRO_TX_CLK_UPPER_TYPE; + +/* 0x80 + 7:0 RW hp_0[7:0] 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_HP_0_TYPE +{ + uint16_t d16; + struct + { + uint16_t hp_0_7_0: 8; + uint16_t rsvd: 8; + }; +} PRO_HP_0_TYPE; + +/* 0x82 + 15:0 RW hs_0[15:0] 0x0 + */ +typedef union _PRO_HS_0_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_0_15_0; + }; +} PRO_HS_0_LOWER_TYPE; + +/* 0x84 + 14:0 RW hs_0[30:16] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_HS_0_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_0_30_16: 15; + uint16_t rsvd: 1; + }; +} PRO_HS_0_UPPER_TYPE; + +/* 0x86 + 14:0 RW length_0[14:0] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_LENGTH_0_TYPE +{ + uint16_t d16; + struct + { + uint16_t length_0_14_0: 15; + uint16_t rsvd: 1; + }; +} PRO_LENGTH_0_TYPE; + +/* 0x88 + 15:0 RW crc_init0[15:0] 0x0 + */ +typedef union _PRO_CRC_INIT0_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init0_15_0; + }; +} PRO_CRC_INIT0_LOWER_TYPE; + +/* 0x8a + 15:0 RW crc_init0[31:16] 0x0 + */ +typedef union _PRO_CRC_INIT0_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init0_31_16; + }; +} PRO_CRC_INIT0_UPPER_TYPE; + +/* 0x8c + 15:0 RW crc_polynomial_reg0[15:0] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG0_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg0_15_0; + }; +} PRO_CRC_POLYNOMIAL_REG0_LOWER_TYPE; + +/* 0x8e + 15:0 RW crc_polynomial_reg0[31:16] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG0_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg0_31_16; + }; +} PRO_CRC_POLYNOMIAL_REG0_UPPER_TYPE; + +/* 0x90 + 0 RW tx_phy0 0x0 + 1 RW auto_add_rptr_option0 0x0 + 4:2 RW fw_rd_ptr0 0x0 + 5 RW fw_ack0 0x0 + 11:6 RW white_field_length0 0x0 + 15:12 RO rsvd 0x0 + */ +typedef union _PRO_PACKET_RELATED_CONTROL0_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_phy0: 1; + uint16_t auto_add_rptr_option0: 1; + uint16_t fw_rd_ptr0: 3; + uint16_t fw_ack0: 1; + uint16_t white_field_length0: 6; + uint16_t rsvd: 4; + }; +} PRO_PACKET_RELATED_CONTROL0_TYPE; + +/* 0x92 + 7:0 RW add_on_length0[7:0] 0x0 + 15:8 RO rx_pkt_num0 0x0 + */ +typedef union _PRO_ADD_ON_LENGTH0_TYPE +{ + uint16_t d16; + struct + { + uint16_t add_on_length0_7_0: 8; + uint16_t rx_pkt_num0: 8; + }; +} PRO_ADD_ON_LENGTH0_TYPE; + +/* 0x94 + 15:0 RW white_init0[15:0] 0x0 + */ +typedef union _PRO_WHITE_INIT0_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init0_15_0; + }; +} PRO_WHITE_INIT0_LOWER_TYPE; + +/* 0x96 + 15:0 RW white_init0[31:16] 0x0 + */ +typedef union _PRO_WHITE_INIT0_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init0_31_16; + }; +} PRO_WHITE_INIT0_UPPER_TYPE; + +/* 0x98 + 15:0 RW white_polynomial_reg0[15:0] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG0_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg0_15_0; + }; +} PRO_WHITE_POLYNOMIAL_REG0_LOWER_TYPE; + +/* 0x9a + 15:0 RW white_polynomial_reg0[31:16] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG0_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg0_31_16; + }; +} PRO_WHITE_POLYNOMIAL_REG0_UPPER_TYPE; + +/* 0xa0 + 7:0 RW hp_1[7:0] 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_HP_1_TYPE +{ + uint16_t d16; + struct + { + uint16_t hp_1_7_0: 8; + uint16_t rsvd: 8; + }; +} PRO_HP_1_TYPE; + +/* 0xa2 + 15:0 RW hs_1[15:0] 0x0 + */ +typedef union _PRO_HS_1_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_1_15_0; + }; +} PRO_HS_1_LOWER_TYPE; + +/* 0xa4 + 14:0 RW hs_1[30:16] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_HS_1_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_1_30_16: 15; + uint16_t rsvd: 1; + }; +} PRO_HS_1_UPPER_TYPE; + +/* 0xa6 + 14:0 RW length_1[14:0] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_LENGTH_1_TYPE +{ + uint16_t d16; + struct + { + uint16_t length_1_14_0: 15; + uint16_t rsvd: 1; + }; +} PRO_LENGTH_1_TYPE; + +/* 0xa8 + 15:0 RW crc_init1[15:0] 0x0 + */ +typedef union _PRO_CRC_INIT1_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init1_15_0; + }; +} PRO_CRC_INIT1_LOWER_TYPE; + +/* 0xaa + 15:0 RW crc_init1[31:16] 0x0 + */ +typedef union _PRO_CRC_INIT1_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init1_31_16; + }; +} PRO_CRC_INIT1_UPPER_TYPE; + +/* 0xac + 15:0 RW crc_polynomial_reg1[15:0] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG1_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg1_15_0; + }; +} PRO_CRC_POLYNOMIAL_REG1_LOWER_TYPE; + +/* 0xae + 15:0 RW crc_polynomial_reg1[31:16] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG1_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg1_31_16; + }; +} PRO_CRC_POLYNOMIAL_REG1_UPPER_TYPE; + +/* 0xb0 + 0 RW tx_phy1 0x0 + 1 RW auto_add_rptr_option1 0x0 + 4:2 RW fw_rd_ptr1 0x0 + 5 RW fw_ack1 0x0 + 11:6 RW white_field_length1 0x0 + 15:12 RO rsvd 0x0 + */ +typedef union _PRO_PACKET_RELATED_CONTROL1_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_phy1: 1; + uint16_t auto_add_rptr_option1: 1; + uint16_t fw_rd_ptr1: 3; + uint16_t fw_ack1: 1; + uint16_t white_field_length1: 6; + uint16_t rsvd: 4; + }; +} PRO_PACKET_RELATED_CONTROL1_TYPE; + +/* 0xb2 + 7:0 RW add_on_length1[7:0] 0x0 + 15:8 RO rx_pkt_num1 0x0 + */ +typedef union _PRO_ADD_ON_LENGTH1_TYPE +{ + uint16_t d16; + struct + { + uint16_t add_on_length1_7_0: 8; + uint16_t rx_pkt_num1: 8; + }; +} PRO_ADD_ON_LENGTH1_TYPE; + +/* 0xb4 + 15:0 RW white_init1[15:0] 0x0 + */ +typedef union _PRO_WHITE_INIT1_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init1_15_0; + }; +} PRO_WHITE_INIT1_LOWER_TYPE; + +/* 0xb6 + 15:0 RW white_init1[31:16] 0x0 + */ +typedef union _PRO_WHITE_INIT1_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init1_31_16; + }; +} PRO_WHITE_INIT1_UPPER_TYPE; + +/* 0xb8 + 15:0 RW white_polynomial_reg1[15:0] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG1_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg1_15_0; + }; +} PRO_WHITE_POLYNOMIAL_REG1_LOWER_TYPE; + +/* 0xba + 15:0 RW white_polynomial_reg1[31:16] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG1_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg1_31_16; + }; +} PRO_WHITE_POLYNOMIAL_REG1_UPPER_TYPE; + +/* 0xc0 + 7:0 RW hp_2[7:0] 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_HP_2_TYPE +{ + uint16_t d16; + struct + { + uint16_t hp_2_7_0: 8; + uint16_t rsvd: 8; + }; +} PRO_HP_2_TYPE; + +/* 0xc2 + 15:0 RW hs_2[15:0] 0x0 + */ +typedef union _PRO_HS_2_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_2_15_0; + }; +} PRO_HS_2_LOWER_TYPE; + +/* 0xc4 + 14:0 RW hs_2[30:16] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_HS_2_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_2_30_16: 15; + uint16_t rsvd: 1; + }; +} PRO_HS_2_UPPER_TYPE; + +/* 0xc6 + 14:0 RW length_2[14:0] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_LENGTH_2_TYPE +{ + uint16_t d16; + struct + { + uint16_t length_2_14_0: 15; + uint16_t rsvd: 1; + }; +} PRO_LENGTH_2_TYPE; + +/* 0xc8 + 15:0 RW crc_init2[15:0] 0x0 + */ +typedef union _PRO_CRC_INIT2_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init2_15_0; + }; +} PRO_CRC_INIT2_LOWER_TYPE; + +/* 0xca + 15:0 RW crc_init2[31:16] 0x0 + */ +typedef union _PRO_CRC_INIT2_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init2_31_16; + }; +} PRO_CRC_INIT2_UPPER_TYPE; + +/* 0xcc + 15:0 RW crc_polynomial_reg2[15:0] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG2_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg2_15_0; + }; +} PRO_CRC_POLYNOMIAL_REG2_LOWER_TYPE; + +/* 0xce + 15:0 RW crc_polynomial_reg2[31:16] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG2_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg2_31_16; + }; +} PRO_CRC_POLYNOMIAL_REG2_UPPER_TYPE; + +/* 0xd0 + 0 RW tx_phy2 0x0 + 1 RW auto_add_rptr_option2 0x0 + 4:2 RW fw_rd_ptr2 0x0 + 5 RW fw_ack2 0x0 + 11:6 RW white_field_length2 0x0 + 15:12 RO rsvd 0x0 + */ +typedef union _PRO_PACKET_RELATED_CONTROL2_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_phy2: 1; + uint16_t auto_add_rptr_option2: 1; + uint16_t fw_rd_ptr2: 3; + uint16_t fw_ack2: 1; + uint16_t white_field_length2: 6; + uint16_t rsvd: 4; + }; +} PRO_PACKET_RELATED_CONTROL2_TYPE; + +/* 0xd2 + 7:0 RW add_on_length2[7:0] 0x0 + 15:8 RW pro_rx_timeout_delay_ms 0x0 + */ +typedef union _PRO_ADD_ON_LENGTH2_TYPE +{ + uint16_t d16; + struct + { + uint16_t add_on_length2_7_0: 8; + uint16_t pro_rx_timeout_delay_ms: 8; + }; +} PRO_ADD_ON_LENGTH2_TYPE; + +/* 0xd4 + 15:0 RW white_init2[15:0] 0x0 + */ +typedef union _PRO_WHITE_INIT2_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init2_15_0; + }; +} PRO_WHITE_INIT2_LOWER_TYPE; + +/* 0xd6 + 15:0 RW white_init2[31:16] 0x0 + */ +typedef union _PRO_WHITE_INIT2_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init2_31_16; + }; +} PRO_WHITE_INIT2_UPPER_TYPE; + +/* 0xd8 + 15:0 RW white_polynomial_reg2[15:0] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG2_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg2_15_0; + }; +} PRO_WHITE_POLYNOMIAL_REG2_LOWER_TYPE; + +/* 0xda + 15:0 RW white_polynomial_reg2[31:16] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG2_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg2_31_16; + }; +} PRO_WHITE_POLYNOMIAL_REG2_UPPER_TYPE; + +/* 0xdc + 4:0 RW pro_gpio_native_counter_reg 0x18 + 5 RW gpio_tx_enable 0x0 + 6 RW gpio_tx_enable1 0x0 + 7 RW gpio_tx_enable2 0x0 + 8 RW fw_rst_trigger 0x0 + 9 RW fw_rst_enable 0x0 + 10 RW hw_rst_enable 0x0 + 11 RW gpio_in_enable 0x0 + 12 RW reset_trig_int_enable 0x1 + 15:13 RW dma_early_delay 0x0 + */ +typedef union _PRO_RESET_CTRL_REG_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_gpio_native_counter_reg: 5; + uint16_t gpio_tx_enable: 1; + uint16_t gpio_tx_enable1: 1; + uint16_t gpio_tx_enable2: 1; + uint16_t fw_rst_trigger: 1; + uint16_t fw_rst_enable: 1; + uint16_t hw_rst_enable: 1; + uint16_t gpio_in_enable: 1; + uint16_t reset_trig_int_enable: 1; + uint16_t dma_early_delay: 3; + }; +} PRO_RESET_CTRL_REG_TYPE; + +/* 0xde + 15:0 RW gpio_cnt_reg 0x1 + */ +typedef union _PRO_GPIO_CTRL_REG_TYPE +{ + uint16_t d16; + struct + { + uint16_t gpio_cnt_reg; + }; +} PRO_GPIO_CTRL_REG_TYPE; + +/* 0xe0 + 15:0 RW tx_address2[15:0] 0x0 + */ +typedef union _PRO_TX_ADDRESS2_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address2_15_0; + }; +} PRO_TX_ADDRESS2_LOWER_TYPE; + +/* 0xe2 + 15:0 RW tx_address2[31:16] 0x0 + */ +typedef union _PRO_TX_ADDRESS2_MIDDLE_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address2_31_16; + }; +} PRO_TX_ADDRESS2_MIDDLE_TYPE; + +/* 0xe4 + 7:0 RW tx_address2[39:32] 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_TX_ADDRESS2_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address2_39_32: 8; + uint16_t rsvd: 8; + }; +} PRO_TX_ADDRESS2_UPPER_TYPE; + +/* 0xe6 + 0 RW preamble_order 0x0 + 1 RW txfifo_out_order 0x0 + 2 RW txclk_order 0x0 + 3 RW rxfifo_in_order 0x0 + 4 RW tx_address_order 0x0 + 5 RW length_order 0x0 + 6 RW hp_order 0x0 + 7 RW hs_order 0x0 + 8 RW length_order_first 0x0 + 9 RW length_split_first 0x0 + 10 RW hp_order_first 0x0 + 11 RW hp_split_first 0x0 + 12 RW hs_order_first 0x0 + 13 RW hs_split_first 0x0 + 15:14 RO rsvd 0x0 + */ +typedef union _PRO_ORDER_CONTROL_TYPE +{ + uint16_t d16; + struct + { + uint16_t preamble_order: 1; + uint16_t txfifo_out_order: 1; + uint16_t txclk_order: 1; + uint16_t rxfifo_in_order: 1; + uint16_t tx_address_order: 1; + uint16_t length_order: 1; + uint16_t hp_order: 1; + uint16_t hs_order: 1; + uint16_t length_order_first: 1; + uint16_t length_split_first: 1; + uint16_t hp_order_first: 1; + uint16_t hp_split_first: 1; + uint16_t hs_order_first: 1; + uint16_t hs_split_first: 1; + uint16_t rsvd: 2; + }; +} PRO_ORDER_CONTROL_TYPE; + +/* 0xe8 + 15:0 RW gpio_cnt1_reg 0x1 + */ +typedef union _PRO_GPIO_CTRL1_REG_TYPE +{ + uint16_t d16; + struct + { + uint16_t gpio_cnt1_reg; + }; +} PRO_GPIO_CTRL1_REG_TYPE; + +/* 0xea + 15:0 RW gpio_cnt2_reg 0x1 + */ +typedef union _PRO_GPIO_CTRL2_REG_TYPE +{ + uint16_t d16; + struct + { + uint16_t gpio_cnt2_reg; + }; +} PRO_GPIO_CTRL2_REG_TYPE; + +/* 0xec + 4:0 RW pro_gpio_native_counter1_reg 0x18 + 5 RW gpio_rst_enable1 0x0 + 7:6 RO rsvd1 0x0 + 12:8 RW pro_gpio_native_counter2_reg 0x18 + 13 RW gpio_rst_enable2 0x0 + 15:14 RO rsvd2 0x0 + */ +typedef union _PRO_GPIO_NATIVE_CNT_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_gpio_native_counter1_reg: 5; + uint16_t gpio_rst_enable1: 1; + uint16_t rsvd1: 2; + uint16_t pro_gpio_native_counter2_reg: 5; + uint16_t gpio_rst_enable2: 1; + uint16_t rsvd2: 2; + }; +} PRO_GPIO_NATIVE_CNT_TYPE; + +/* 0xee + 9:0 RW max_length 0xff + 10 RW txclk_split 0x0 + 11 RW txfifo_out_split 0x0 + 12 RW rxfifo_in_split 0x0 + 15:13 RO rsvd 0x0 + */ +typedef union _PRO_MAX_LENGTH_TYPE +{ + uint16_t d16; + struct + { + uint16_t max_length: 10; + uint16_t txclk_split: 1; + uint16_t txfifo_out_split: 1; + uint16_t rxfifo_in_split: 1; + uint16_t rsvd: 3; + }; +} PRO_MAX_LENGTH_TYPE; + +/* 0x100 + 7:0 RW pro_rx2tx_delay_base 0x83 + 15:8 RW pro_rx2rx_delay 0x32 + */ +typedef union _PRO_RX_TURN_AROUND_DELAY_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_rx2tx_delay_base: 8; + uint16_t pro_rx2rx_delay: 8; + }; +} PRO_RX_TURN_AROUND_DELAY_TYPE; + +/* 0x102 + 3:0 RW pro_rx2tx_delay_1m 0x3 + 7:4 RW pro_tx2rx_delay_1m 0x0 + 11:8 RW pro_rx2tx_delay_2m 0x8 + 15:12 RW pro_tx2rx_delay_2m 0x0 + */ +typedef union _PRO_2M_TRX_TURN_AROUND_DELAY_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_rx2tx_delay_1m: 4; + uint16_t pro_tx2rx_delay_1m: 4; + uint16_t pro_rx2tx_delay_2m: 4; + uint16_t pro_tx2rx_delay_2m: 4; + }; +} PRO_2M_TRX_TURN_AROUND_DELAY_TYPE; + +/* 0x104 + 3:0 RW pro_rx2tx_delay_1m_2m 0x3 + 7:4 RW pro_tx2rx_delay_1m_2m 0x0 + 11:8 RW pro_rx2tx_delay_2m_1m 0x7 + 15:12 RW pro_tx2rx_delay_2m_1m 0x0 + */ +typedef union _PRO_1M_2M_TRX_TURN_AROUND_DELAY_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_rx2tx_delay_1m_2m: 4; + uint16_t pro_tx2rx_delay_1m_2m: 4; + uint16_t pro_rx2tx_delay_2m_1m: 4; + uint16_t pro_tx2rx_delay_2m_1m: 4; + }; +} PRO_1M_2M_TRX_TURN_AROUND_DELAY_TYPE; + +/* 0x106 + 7:0 RW pro_tx2rx_delay_base 0x83 + 13:8 RW pro_txon_early_1m 0x10 + 15:14 RO rsvd 0x0 + */ +typedef union _PRO_TX_TURN_AROUND_DELAY_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_tx2rx_delay_base: 8; + uint16_t pro_txon_early_1m: 6; + uint16_t rsvd: 2; + }; +} PRO_TX_TURN_AROUND_DELAY_TYPE; + +/* 0x108 + 7:0 RW pro_rx_timeout_delay_us 0x64 + 11:8 RW no_tx_early[3:0] 0x2 + 15:12 RW pro_txon_early_2m 0x0 + */ +typedef union _PRO_RX_TIMEOUT_DELAY_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_rx_timeout_delay_us: 8; + uint16_t no_tx_early_3_0: 4; + uint16_t pro_txon_early_2m: 4; + }; +} PRO_RX_TIMEOUT_DELAY_TYPE; + +/* 0x10a + 7:0 RW pro_txon_delay 0xdc + 15:8 RW pro_rxon_delay 0xdc + */ +typedef union _PRO_TRX_ON_DELAY_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_txon_delay: 8; + uint16_t pro_rxon_delay: 8; + }; +} PRO_TRX_ON_DELAY_TYPE; + +/* 0x10c + 7:0 RW pro_txon_delay_2m 0xdc + 15:8 RW pro_rxon_delay_2m 0xdc + */ +typedef union _PRO_2M_TRX_ON_DELAY_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_txon_delay_2m: 8; + uint16_t pro_rxon_delay_2m: 8; + }; +} PRO_2M_TRX_ON_DELAY_TYPE; + +/* 0x10e + 3:0 RW txon_ext 0x4 + 4 RW en_early_txon_tgl 0x1 + 7:5 RW no_tx_early[6:4] 0x0 + 15:8 RW get_ack_before_send 0x1 + */ +typedef union _PRO_TRX_DEALY_MISC_TYPE +{ + uint16_t d16; + struct + { + uint16_t txon_ext: 4; + uint16_t en_early_txon_tgl: 1; + uint16_t no_tx_early_6_4: 3; + uint16_t get_ack_before_send: 8; + }; +} PRO_TRX_DEALY_MISC_TYPE; + +/* 0x110 + 6:0 RW pro_psd_start 0x0 + 7 RO rsvd0 0x0 + 14:8 RW pro_psd_end 0x0 + 15 RO rsvd1 0x0 + */ +typedef union _PRO_PSD_RANGE0_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_psd_start: 7; + uint16_t rsvd0: 1; + uint16_t pro_psd_end: 7; + uint16_t rsvd1: 1; + }; +} PRO_PSD_RANGE0_TYPE; + +/* 0x112 + 6:0 RW pro_psd_step 0x0 + 7 RW pro_psd_mode 0x0 + 8 RW psd_rst_enable 0x0 + 13:9 RW psd_rst_cnt_reg 0x2 + 15:14 RO rsvd 0x0 + */ +typedef union _PRO_PSD_RANGE1_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_psd_step: 7; + uint16_t pro_psd_mode: 1; + uint16_t psd_rst_enable: 1; + uint16_t psd_rst_cnt_reg: 5; + uint16_t rsvd: 2; + }; +} PRO_PSD_RANGE1_TYPE; + +/* 0x114 + 9:0 RW pro_psd_timeout 0x114 + 15:10 RO rsvd 0x0 + */ +typedef union _PRO_PSD_TIMEOUT_TYPE +{ + uint16_t d16; + struct + { + uint16_t pro_psd_timeout: 10; + uint16_t rsvd: 6; + }; +} PRO_PSD_TIMEOUT_TYPE; + +/* 0x118 + 14:0 RO rsvd 0x0 + 15 RW rx_int_stack_ovf 0x0 + */ +typedef union _PRO_INT_CTRL_TYPE +{ + uint16_t d16; + struct + { + uint16_t rsvd: 15; + uint16_t rx_int_stack_ovf: 1; + }; +} PRO_INT_CTRL_TYPE; + +/* 0x11a + 15:0 RW gpio_tx_delay1[15:0] 0x0 + */ +typedef union _PRO_GPIO_TX_DELAY1_TYPE +{ + uint16_t d16; + struct + { + uint16_t gpio_tx_delay1_15_0; + }; +} PRO_GPIO_TX_DELAY1_TYPE; + +/* 0x11c + 14:0 RO rsvd 0x0 + 15 RW tx_int_stack_ovf 0x0 + */ +typedef union _PRO_INT_CTRL_TX_TYPE +{ + uint16_t d16; + struct + { + uint16_t rsvd: 15; + uint16_t tx_int_stack_ovf: 1; + }; +} PRO_INT_CTRL_TX_TYPE; + +/* 0x11e + 15:0 RW gpio_tx_delay2[15:0] 0x0 + */ +typedef union _PRO_GPIO_TX_DELAY2_TYPE +{ + uint16_t d16; + struct + { + uint16_t gpio_tx_delay2_15_0; + }; +} PRO_GPIO_TX_DELAY2_TYPE; + +/* 0x120 + 0 RW opt_pro_ce_end 0x0 + 1 RW reset_tx_pktnum0 0x0 + 2 RW reset_tx_pktnum1 0x0 + 3 RW reset_rx_pktnum0 0x0 + 4 RW reset_rx_pktnum1 0x0 + 5 RW tx_time_enable 0x0 + 6 RW no_tx1 0x0 + 7 RW cbk_pcd_cg 0x1 + 8 RW cbk_radio_cg 0x1 + 9 RW cbk_txrx_time_cg 0x1 + 10 RW sd_cnt_enable 0x0 + 11 RW no_tx_trig_stack_enable 0x0 + 15:12 RW dma_cnt_reg 0x7 + */ +typedef union _PRO_CBK_CTRL_TYPE +{ + uint16_t d16; + struct + { + uint16_t opt_pro_ce_end: 1; + uint16_t reset_tx_pktnum0: 1; + uint16_t reset_tx_pktnum1: 1; + uint16_t reset_rx_pktnum0: 1; + uint16_t reset_rx_pktnum1: 1; + uint16_t tx_time_enable: 1; + uint16_t no_tx1: 1; + uint16_t cbk_pcd_cg: 1; + uint16_t cbk_radio_cg: 1; + uint16_t cbk_txrx_time_cg: 1; + uint16_t sd_cnt_enable: 1; + uint16_t no_tx_trig_stack_enable: 1; + uint16_t dma_cnt_reg: 4; + }; +} PRO_CBK_CTRL_TYPE; + +/* 0x122 + 7:0 RW txon_early_afe 0x6 + 15:8 RW rxon_early_afe 0x6 + */ +typedef union _PRO_TRX_AFE_TYPE +{ + uint16_t d16; + struct + { + uint16_t txon_early_afe: 8; + uint16_t rxon_early_afe: 8; + }; +} PRO_TRX_AFE_TYPE; + +/* 0x124 + 3:0 RW ptx_dbg_sel 0x0 + 5:4 RW control_dbg_sel 0x0 + 8:6 RW trx_dbg_sel 0x0 + 11:9 RW radio_dbg_sel 0x0 + 15:12 RW dma_dbg_sel[3:0] 0x0 + */ +typedef union _PRO_DEBUG_SEL_TYPE +{ + uint16_t d16; + struct + { + uint16_t ptx_dbg_sel: 4; + uint16_t control_dbg_sel: 2; + uint16_t trx_dbg_sel: 3; + uint16_t radio_dbg_sel: 3; + uint16_t dma_dbg_sel_3_0: 4; + }; +} PRO_DEBUG_SEL_TYPE; + +/* 0x126 + 3:0 RW pcd_dbg_sel 0x0 + 5:4 RW stack_dbg_sel 0x0 + 8:6 RW top_dbg_sel 0x0 + 9 RW dma_dbg_sel[4] 0x0 + 15:10 RO rsvd 0x0 + */ +typedef union _PRO_DEBUG_SEL2_TYPE +{ + uint16_t d16; + struct + { + uint16_t pcd_dbg_sel: 4; + uint16_t stack_dbg_sel: 2; + uint16_t top_dbg_sel: 3; + uint16_t dma_dbg_sel_4: 1; + uint16_t rsvd: 6; + }; +} PRO_DEBUG_SEL2_TYPE; + +/* 0x128 + 5:0 RO radio_cst 0x0 + 6 RO radio_idle 0x1 + 10:7 RO timing_cst 0x0 + 11 RO timing_idle 0x1 + 12 RO prx_fsm_idle 0x1 + 14:13 RO prx_cst 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_DSM_READ_TYPE +{ + uint16_t d16; + struct + { + uint16_t radio_cst: 6; + uint16_t radio_idle: 1; + uint16_t timing_cst: 4; + uint16_t timing_idle: 1; + uint16_t prx_fsm_idle: 1; + uint16_t prx_cst: 2; + uint16_t rsvd: 1; + }; +} PRO_DSM_READ_TYPE; + +/* 0x12a + 0 RO ptx_fsm_idle 0x1 + 2:1 RO ptx_cst 0x0 + 6:3 RO p2s_cst 0x0 + 7 RO pcd_p2s_idle 0x1 + 12:8 RO pcd_ctrl_cst 0x0 + 13 RO pcd_idle 0x1 + 15:14 RO rsvd 0x0 + */ +typedef union _PRO_DSM_READ1_TYPE +{ + uint16_t d16; + struct + { + uint16_t ptx_fsm_idle: 1; + uint16_t ptx_cst: 2; + uint16_t p2s_cst: 4; + uint16_t pcd_p2s_idle: 1; + uint16_t pcd_ctrl_cst: 5; + uint16_t pcd_idle: 1; + uint16_t rsvd: 2; + }; +} PRO_DSM_READ1_TYPE; + +/* 0x12c + 15:0 RW crc_engine_init[15:0] 0x0 + */ +typedef union _PRO_CRC_ENGINE_INIT_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_engine_init_15_0; + }; +} PRO_CRC_ENGINE_INIT_TYPE; + +/* 0x12e + 15:0 RW crc_engine_init[31:16] 0x0 + */ +typedef union _PRO_CRC_ENGINE_INIT1_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_engine_init_31_16; + }; +} PRO_CRC_ENGINE_INIT1_TYPE; + +/* 0x130 + 15:0 RW crc_engine_polynomial[15:0] 0x0 + */ +typedef union _PRO_CRC_ENGINE_POLY_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_engine_polynomial_15_0; + }; +} PRO_CRC_ENGINE_POLY_TYPE; + +/* 0x132 + 15:0 RW crc_engine_polynomial[31:16] 0x0 + */ +typedef union _PRO_CRC_ENGINE_POLY1_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_engine_polynomial_31_16; + }; +} PRO_CRC_ENGINE_POLY1_TYPE; + +/* 0x134 + 15:0 RW crc_engine_input_data[15:0] 0x0 + */ +typedef union _PRO_CRC_ENGINE_IN_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_engine_input_data_15_0; + }; +} PRO_CRC_ENGINE_IN_TYPE; + +/* 0x136 + 15:0 RW crc_engine_input_data[31:16] 0x0 + */ +typedef union _PRO_CRC_ENGINE_IN1_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_engine_input_data_31_16; + }; +} PRO_CRC_ENGINE_IN1_TYPE; + +/* 0x138 + 7:0 RW crc_engine_input_data[39:32] 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_CRC_ENGINE_IN2_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_engine_input_data_39_32: 8; + uint16_t rsvd: 8; + }; +} PRO_CRC_ENGINE_IN2_TYPE; + +/* 0x13a + 15:0 RO crc_engine_output[15:0] 0x0 + */ +typedef union _PRO_CRC_ENGINE_OUT_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_engine_output_15_0; + }; +} PRO_CRC_ENGINE_OUT_TYPE; + +/* 0x13c + 15:0 RO crc_engine_output[31:16] 0x0 + */ +typedef union _PRO_CRC_ENGINE_OUT1_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_engine_output_31_16; + }; +} PRO_CRC_ENGINE_OUT1_TYPE; + +/* 0x13e + 0 RW crc_engine_en 0x0 + 1 RW crc_engine_rst 0x0 + 4:2 RW crc_engine_field_length 0x0 + 10:5 RW crc_engine_data_length 0x0 + 11 RW crc_engine_cg 0x1 + 12 RW crc_engine_input_endian 0x0 + 13 RW crc_engine_output_endian 0x0 + 14 RW crc_engine_data_length_in_bit 0x1 + 15 RO rsvd 0x0 + */ +typedef union _PRO_CRC_ENGINE_EN_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_engine_en: 1; + uint16_t crc_engine_rst: 1; + uint16_t crc_engine_field_length: 3; + uint16_t crc_engine_data_length: 6; + uint16_t crc_engine_cg: 1; + uint16_t crc_engine_input_endian: 1; + uint16_t crc_engine_output_endian: 1; + uint16_t crc_engine_data_length_in_bit: 1; + uint16_t rsvd: 1; + }; +} PRO_CRC_ENGINE_EN_TYPE; + +/* 0x140 + 15:0 RW rx_crc_init0[15:0] 0x0 + */ +typedef union _PRO_RX_CRC_INIT0_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init0_15_0; + }; +} PRO_RX_CRC_INIT0_LOWER_TYPE; + +/* 0x142 + 15:0 RW rx_crc_init0[31:16] 0x0 + */ +typedef union _PRO_RX_CRC_INIT0_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init0_31_16; + }; +} PRO_RX_CRC_INIT0_UPPER_TYPE; + +/* 0x144 + 15:0 RW rx_crc_init1[15:0] 0x0 + */ +typedef union _PRO_RX_CRC_INIT1_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init1_15_0; + }; +} PRO_RX_CRC_INIT1_LOWER_TYPE; + +/* 0x146 + 15:0 RW rx_crc_init1[31:16] 0x0 + */ +typedef union _PRO_RX_CRC_INIT1_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init1_31_16; + }; +} PRO_RX_CRC_INIT1_UPPER_TYPE; + +/* 0x148 + 15:0 RW rx_crc_init2[15:0] 0x0 + */ +typedef union _PRO_RX_CRC_INIT2_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init2_15_0; + }; +} PRO_RX_CRC_INIT2_LOWER_TYPE; + +/* 0x14a + 15:0 RW rx_crc_init2[31:16] 0x0 + */ +typedef union _PRO_RX_CRC_INIT2_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init2_31_16; + }; +} PRO_RX_CRC_INIT2_UPPER_TYPE; + +/* 0x14c + 7:0 RW hp_3[7:0] 0x0 + 15:8 RW hp_4[7:0] 0x0 + */ +typedef union _PRO_HP_3_TYPE +{ + uint16_t d16; + struct + { + uint16_t hp_3_7_0: 8; + uint16_t hp_4_7_0: 8; + }; +} PRO_HP_3_TYPE; + +/* 0x14e + 7:0 RW hp_5[7:0] 0x0 + 15:8 RW hp_6[7:0] 0x0 + */ +typedef union _PRO_HP_4_TYPE +{ + uint16_t d16; + struct + { + uint16_t hp_5_7_0: 8; + uint16_t hp_6_7_0: 8; + }; +} PRO_HP_4_TYPE; + +/* 0x150 + 7:0 RW hp_7[7:0] 0x0 + 8 RW fix_entry_option 0x0 + 11:9 RW fix_ptx_entry[2:0] 0x0 + 12 RW fix_entry_rx_option 0x0 + 15:13 RW fix_prx_tx_entry[2:0] 0x0 + */ +typedef union _PRO_HP_5_TYPE +{ + uint16_t d16; + struct + { + uint16_t hp_7_7_0: 8; + uint16_t fix_entry_option: 1; + uint16_t fix_ptx_entry_2_0: 3; + uint16_t fix_entry_rx_option: 1; + uint16_t fix_prx_tx_entry_2_0: 3; + }; +} PRO_HP_5_TYPE; + +/* 0x152 + 15:0 RW hs_3[15:0] 0x0 + */ +typedef union _PRO_HS_3_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_3_15_0; + }; +} PRO_HS_3_LOWER_TYPE; + +/* 0x154 + 14:0 RW hs_3[30:16] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_HS_3_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_3_30_16: 15; + uint16_t rsvd: 1; + }; +} PRO_HS_3_UPPER_TYPE; + +/* 0x156 + 15:0 RW hs_4[15:0] 0x0 + */ +typedef union _PRO_HS_4_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_4_15_0; + }; +} PRO_HS_4_LOWER_TYPE; + +/* 0x158 + 14:0 RW hs_4[30:16] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_HS_4_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_4_30_16: 15; + uint16_t rsvd: 1; + }; +} PRO_HS_4_UPPER_TYPE; + +/* 0x15a + 15:0 RW hs_5[15:0] 0x0 + */ +typedef union _PRO_HS_5_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_5_15_0; + }; +} PRO_HS_5_LOWER_TYPE; + +/* 0x15c + 14:0 RW hs_5[30:16] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_HS_5_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_5_30_16: 15; + uint16_t rsvd: 1; + }; +} PRO_HS_5_UPPER_TYPE; + +/* 0x15e + 15:0 RW hs_6[15:0] 0x0 + */ +typedef union _PRO_HS_6_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_6_15_0; + }; +} PRO_HS_6_LOWER_TYPE; + +/* 0x160 + 14:0 RW hs_6[30:16] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_HS_6_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_6_30_16: 15; + uint16_t rsvd: 1; + }; +} PRO_HS_6_UPPER_TYPE; + +/* 0x162 + 15:0 RW hs_7[15:0] 0x0 + */ +typedef union _PRO_HS_7_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_7_15_0; + }; +} PRO_HS_7_LOWER_TYPE; + +/* 0x164 + 14:0 RW hs_7[30:16] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_HS_7_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t hs_7_30_16: 15; + uint16_t rsvd: 1; + }; +} PRO_HS_7_UPPER_TYPE; + +/* 0x166 + 14:0 RW length_3[14:0] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_LENGTH_3_TYPE +{ + uint16_t d16; + struct + { + uint16_t length_3_14_0: 15; + uint16_t rsvd: 1; + }; +} PRO_LENGTH_3_TYPE; + +/* 0x168 + 14:0 RW length_4[14:0] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_LENGTH_4_TYPE +{ + uint16_t d16; + struct + { + uint16_t length_4_14_0: 15; + uint16_t rsvd: 1; + }; +} PRO_LENGTH_4_TYPE; + +/* 0x16a + 14:0 RW length_5[14:0] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_LENGTH_5_TYPE +{ + uint16_t d16; + struct + { + uint16_t length_5_14_0: 15; + uint16_t rsvd: 1; + }; +} PRO_LENGTH_5_TYPE; + +/* 0x16c + 14:0 RW length_6[14:0] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_LENGTH_6_TYPE +{ + uint16_t d16; + struct + { + uint16_t length_6_14_0: 15; + uint16_t rsvd: 1; + }; +} PRO_LENGTH_6_TYPE; + +/* 0x16e + 14:0 RW length_7[14:0] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_LENGTH_7_TYPE +{ + uint16_t d16; + struct + { + uint16_t length_7_14_0: 15; + uint16_t rsvd: 1; + }; +} PRO_LENGTH_7_TYPE; + +/* 0x170 + 7:0 RW add_on_length3[7:0] 0x0 + 15:8 RW add_on_length4[7:0] 0x0 + */ +typedef union _PRO_ADD_ON_LENGTH3_TYPE +{ + uint16_t d16; + struct + { + uint16_t add_on_length3_7_0: 8; + uint16_t add_on_length4_7_0: 8; + }; +} PRO_ADD_ON_LENGTH3_TYPE; + +/* 0x172 + 7:0 RW add_on_length5[7:0] 0x0 + 15:8 RW add_on_length6[7:0] 0x0 + */ +typedef union _PRO_ADD_ON_LENGTH4_TYPE +{ + uint16_t d16; + struct + { + uint16_t add_on_length5_7_0: 8; + uint16_t add_on_length6_7_0: 8; + }; +} PRO_ADD_ON_LENGTH4_TYPE; + +/* 0x174 + 7:0 RW add_on_length7[7:0] 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_ADD_ON_LENGTH5_TYPE +{ + uint16_t d16; + struct + { + uint16_t add_on_length7_7_0: 8; + uint16_t rsvd: 8; + }; +} PRO_ADD_ON_LENGTH5_TYPE; + +/* 0x176 + 15:0 RW crc_init3[15:0] 0x0 + */ +typedef union _PRO_CRC_INIT3_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init3_15_0; + }; +} PRO_CRC_INIT3_LOWER_TYPE; + +/* 0x178 + 15:0 RW crc_init3[31:16] 0x0 + */ +typedef union _PRO_CRC_INIT3_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init3_31_16; + }; +} PRO_CRC_INIT3_UPPER_TYPE; + +/* 0x17a + 15:0 RW crc_init4[15:0] 0x0 + */ +typedef union _PRO_CRC_INIT4_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init4_15_0; + }; +} PRO_CRC_INIT4_LOWER_TYPE; + +/* 0x17c + 15:0 RW crc_init4[31:16] 0x0 + */ +typedef union _PRO_CRC_INIT4_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init4_31_16; + }; +} PRO_CRC_INIT4_UPPER_TYPE; + +/* 0x17e + 15:0 RW crc_init5[15:0] 0x0 + */ +typedef union _PRO_CRC_INIT5_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init5_15_0; + }; +} PRO_CRC_INIT5_LOWER_TYPE; + +/* 0x180 + 15:0 RW crc_init5[31:16] 0x0 + */ +typedef union _PRO_CRC_INIT5_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init5_31_16; + }; +} PRO_CRC_INIT5_UPPER_TYPE; + +/* 0x182 + 15:0 RW crc_init6[15:0] 0x0 + */ +typedef union _PRO_CRC_INIT6_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init6_15_0; + }; +} PRO_CRC_INIT6_LOWER_TYPE; + +/* 0x184 + 15:0 RW crc_init6[31:16] 0x0 + */ +typedef union _PRO_CRC_INIT6_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init6_31_16; + }; +} PRO_CRC_INIT6_UPPER_TYPE; + +/* 0x186 + 15:0 RW crc_init7[15:0] 0x0 + */ +typedef union _PRO_CRC_INIT7_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init7_15_0; + }; +} PRO_CRC_INIT7_LOWER_TYPE; + +/* 0x188 + 15:0 RW crc_init7[31:16] 0x0 + */ +typedef union _PRO_CRC_INIT7_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_init7_31_16; + }; +} PRO_CRC_INIT7_UPPER_TYPE; + +/* 0x18a + 15:0 RW rx_crc_init3[15:0] 0x0 + */ +typedef union _PRO_RX_CRC_INIT3_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init3_15_0; + }; +} PRO_RX_CRC_INIT3_LOWER_TYPE; + +/* 0x18c + 15:0 RW rx_crc_init3[31:16] 0x0 + */ +typedef union _PRO_RX_CRC_INIT3_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init3_31_16; + }; +} PRO_RX_CRC_INIT3_UPPER_TYPE; + +/* 0x18e + 15:0 RW rx_crc_init4[15:0] 0x0 + */ +typedef union _PRO_RX_CRC_INIT4_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init4_15_0; + }; +} PRO_RX_CRC_INIT4_LOWER_TYPE; + +/* 0x190 + 15:0 RW rx_crc_init4[31:16] 0x0 + */ +typedef union _PRO_RX_CRC_INIT4_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init4_31_16; + }; +} PRO_RX_CRC_INIT4_UPPER_TYPE; + +/* 0x192 + 15:0 RW rx_crc_init5[15:0] 0x0 + */ +typedef union _PRO_RX_CRC_INIT5_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init5_15_0; + }; +} PRO_RX_CRC_INIT5_LOWER_TYPE; + +/* 0x194 + 15:0 RW rx_crc_init5[31:16] 0x0 + */ +typedef union _PRO_RX_CRC_INIT5_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init5_31_16; + }; +} PRO_RX_CRC_INIT5_UPPER_TYPE; + +/* 0x196 + 15:0 RW rx_crc_init6[15:0] 0x0 + */ +typedef union _PRO_RX_CRC_INIT6_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init6_15_0; + }; +} PRO_RX_CRC_INIT6_LOWER_TYPE; + +/* 0x198 + 15:0 RW rx_crc_init6[31:16] 0x0 + */ +typedef union _PRO_RX_CRC_INIT6_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init6_31_16; + }; +} PRO_RX_CRC_INIT6_UPPER_TYPE; + +/* 0x19a + 15:0 RW rx_crc_init7[15:0] 0x0 + */ +typedef union _PRO_RX_CRC_INIT7_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init7_15_0; + }; +} PRO_RX_CRC_INIT7_LOWER_TYPE; + +/* 0x19c + 15:0 RW rx_crc_init7[31:16] 0x0 + */ +typedef union _PRO_RX_CRC_INIT7_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_crc_init7_31_16; + }; +} PRO_RX_CRC_INIT7_UPPER_TYPE; + +/* 0x19e + 15:0 RW white_init3[15:0] 0x0 + */ +typedef union _PRO_WHITE_INIT3_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init3_15_0; + }; +} PRO_WHITE_INIT3_LOWER_TYPE; + +/* 0x1a0 + 15:0 RW white_init3[31:16] 0x0 + */ +typedef union _PRO_WHITE_INIT3_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init3_31_16; + }; +} PRO_WHITE_INIT3_UPPER_TYPE; + +/* 0x1a2 + 15:0 RW white_init4[15:0] 0x0 + */ +typedef union _PRO_WHITE_INIT4_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init4_15_0; + }; +} PRO_WHITE_INIT4_LOWER_TYPE; + +/* 0x1a4 + 15:0 RW white_init4[31:16] 0x0 + */ +typedef union _PRO_WHITE_INIT4_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init4_31_16; + }; +} PRO_WHITE_INIT4_UPPER_TYPE; + +/* 0x1a6 + 15:0 RW white_init5[15:0] 0x0 + */ +typedef union _PRO_WHITE_INIT5_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init5_15_0; + }; +} PRO_WHITE_INIT5_LOWER_TYPE; + +/* 0x1a8 + 15:0 RW white_init5[31:16] 0x0 + */ +typedef union _PRO_WHITE_INIT5_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init5_31_16; + }; +} PRO_WHITE_INIT5_UPPER_TYPE; + +/* 0x1aa + 15:0 RW white_init6[15:0] 0x0 + */ +typedef union _PRO_WHITE_INIT6_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init6_15_0; + }; +} PRO_WHITE_INIT6_LOWER_TYPE; + +/* 0x1ac + 15:0 RW white_init6[31:16] 0x0 + */ +typedef union _PRO_WHITE_INIT6_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init6_31_16; + }; +} PRO_WHITE_INIT6_UPPER_TYPE; + +/* 0x1ae + 15:0 RW white_init7[15:0] 0x0 + */ +typedef union _PRO_WHITE_INIT7_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init7_15_0; + }; +} PRO_WHITE_INIT7_LOWER_TYPE; + +/* 0x1b0 + 15:0 RW white_init7[31:16] 0x0 + */ +typedef union _PRO_WHITE_INIT7_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_init7_31_16; + }; +} PRO_WHITE_INIT7_UPPER_TYPE; + +/* 0x1b2 + 15:0 RW white_polynomial_reg3[15:0] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG3_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg3_15_0; + }; +} PRO_WHITE_POLYNOMIAL_REG3_LOWER_TYPE; + +/* 0x1b4 + 15:0 RW white_polynomial_reg3[31:16] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG3_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg3_31_16; + }; +} PRO_WHITE_POLYNOMIAL_REG3_UPPER_TYPE; + +/* 0x1b6 + 15:0 RW white_polynomial_reg4[15:0] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG4_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg4_15_0; + }; +} PRO_WHITE_POLYNOMIAL_REG4_LOWER_TYPE; + +/* 0x1b8 + 15:0 RW white_polynomial_reg4[31:16] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG4_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg4_31_16; + }; +} PRO_WHITE_POLYNOMIAL_REG4_UPPER_TYPE; + +/* 0x1ba + 15:0 RW white_polynomial_reg5[15:0] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG5_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg5_15_0; + }; +} PRO_WHITE_POLYNOMIAL_REG5_LOWER_TYPE; + +/* 0x1bc + 15:0 RW white_polynomial_reg5[31:16] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG5_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg5_31_16; + }; +} PRO_WHITE_POLYNOMIAL_REG5_UPPER_TYPE; + +/* 0x1be + 15:0 RW white_polynomial_reg6[15:0] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG6_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg6_15_0; + }; +} PRO_WHITE_POLYNOMIAL_REG6_LOWER_TYPE; + +/* 0x1c0 + 15:0 RW white_polynomial_reg6[31:16] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG6_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg6_31_16; + }; +} PRO_WHITE_POLYNOMIAL_REG6_UPPER_TYPE; + +/* 0x1c2 + 15:0 RW white_polynomial_reg7[15:0] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG7_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg7_15_0; + }; +} PRO_WHITE_POLYNOMIAL_REG7_LOWER_TYPE; + +/* 0x1c4 + 15:0 RW white_polynomial_reg7[31:16] 0x0 + */ +typedef union _PRO_WHITE_POLYNOMIAL_REG7_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_polynomial_reg7_31_16; + }; +} PRO_WHITE_POLYNOMIAL_REG7_UPPER_TYPE; + +/* 0x1c6 + 15:0 RW crc_polynomial_reg3[15:0] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG3_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg3_15_0; + }; +} PRO_CRC_POLYNOMIAL_REG3_LOWER_TYPE; + +/* 0x1c8 + 15:0 RW crc_polynomial_reg3[31:16] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG3_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg3_31_16; + }; +} PRO_CRC_POLYNOMIAL_REG3_UPPER_TYPE; + +/* 0x1ca + 15:0 RW crc_polynomial_reg4[15:0] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG4_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg4_15_0; + }; +} PRO_CRC_POLYNOMIAL_REG4_LOWER_TYPE; + +/* 0x1cc + 15:0 RW crc_polynomial_reg4[31:16] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG4_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg4_31_16; + }; +} PRO_CRC_POLYNOMIAL_REG4_UPPER_TYPE; + +/* 0x1ce + 15:0 RW crc_polynomial_reg5[15:0] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG5_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg5_15_0; + }; +} PRO_CRC_POLYNOMIAL_REG5_LOWER_TYPE; + +/* 0x1d0 + 15:0 RW crc_polynomial_reg5[31:16] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG5_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg5_31_16; + }; +} PRO_CRC_POLYNOMIAL_REG5_UPPER_TYPE; + +/* 0x1d2 + 15:0 RW crc_polynomial_reg6[15:0] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG6_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg6_15_0; + }; +} PRO_CRC_POLYNOMIAL_REG6_LOWER_TYPE; + +/* 0x1d4 + 15:0 RW crc_polynomial_reg6[31:16] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG6_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg6_31_16; + }; +} PRO_CRC_POLYNOMIAL_REG6_UPPER_TYPE; + +/* 0x1d6 + 15:0 RW crc_polynomial_reg7[15:0] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG7_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg7_15_0; + }; +} PRO_CRC_POLYNOMIAL_REG7_LOWER_TYPE; + +/* 0x1d8 + 15:0 RW crc_polynomial_reg7[31:16] 0x0 + */ +typedef union _PRO_CRC_POLYNOMIAL_REG7_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t crc_polynomial_reg7_31_16; + }; +} PRO_CRC_POLYNOMIAL_REG7_UPPER_TYPE; + +/* 0x1da + 7:0 RO rx_pkt_num2 0x0 + 15:8 RO rx_pkt_num3 0x0 + */ +typedef union _PRO_RX_PKT_NUM0_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_pkt_num2: 8; + uint16_t rx_pkt_num3: 8; + }; +} PRO_RX_PKT_NUM0_TYPE; + +/* 0x1dc + 7:0 RO rx_pkt_num4 0x0 + 15:8 RO rx_pkt_num5 0x0 + */ +typedef union _PRO_RX_PKT_NUM1_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_pkt_num4: 8; + uint16_t rx_pkt_num5: 8; + }; +} PRO_RX_PKT_NUM1_TYPE; + +/* 0x1de + 7:0 RO rx_pkt_num6 0x0 + 15:8 RO rx_pkt_num7 0x0 + */ +typedef union _PRO_RX_PKT_NUM2_TYPE +{ + uint16_t d16; + struct + { + uint16_t rx_pkt_num6: 8; + uint16_t rx_pkt_num7: 8; + }; +} PRO_RX_PKT_NUM2_TYPE; + +/* 0x1e0 + 7:0 RO tx_pkt_num3 0x0 + 15:8 RO tx_pkt_num4 0x0 + */ +typedef union _PRO_TX_PKT_NUM3_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_pkt_num3: 8; + uint16_t tx_pkt_num4: 8; + }; +} PRO_TX_PKT_NUM3_TYPE; + +/* 0x1e2 + 7:0 RO tx_pkt_num5 0x0 + 15:8 RO tx_pkt_num6 0x0 + */ +typedef union _PRO_TX_PKT_NUM4_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_pkt_num5: 8; + uint16_t tx_pkt_num6: 8; + }; +} PRO_TX_PKT_NUM4_TYPE; + +/* 0x1e4 + 7:0 RO tx_pkt_num7 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_TX_PKT_NUM5_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_pkt_num7: 8; + uint16_t rsvd: 8; + }; +} PRO_TX_PKT_NUM5_TYPE; + +/* 0x1e6 + 5:0 RW white_field_length3 0x0 + 6 RW reset_rx_pktnum2 0x0 + 7 RW lock3 0x0 + 8 RW no_tx3 0x0 + 9 RW auto_no_tx_option3 0x0 + 10 RW clear_hw_no_tx3 0x0 + 11 RO read_clear_hw_no_tx3 0x0 + 12 RW empty_flag3 0x0 + 13 RW fw_ack3 0x0 + 14 RW tx_phy3 0x0 + 15 RW reset_rx_pktnum3 0x0 + */ +typedef union _PRO_WHITE_FIELD_LENGTH_0_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_field_length3: 6; + uint16_t reset_rx_pktnum2: 1; + uint16_t lock3: 1; + uint16_t no_tx3: 1; + uint16_t auto_no_tx_option3: 1; + uint16_t clear_hw_no_tx3: 1; + uint16_t read_clear_hw_no_tx3: 1; + uint16_t empty_flag3: 1; + uint16_t fw_ack3: 1; + uint16_t tx_phy3: 1; + uint16_t reset_rx_pktnum3: 1; + }; +} PRO_WHITE_FIELD_LENGTH_0_TYPE; + +/* 0x1e8 + 5:0 RW white_field_length4 0x0 + 6 RO rsvd 0x0 + 7 RW lock4 0x0 + 8 RW no_tx4 0x0 + 9 RW auto_no_tx_option4 0x0 + 10 RW clear_hw_no_tx4 0x0 + 11 RO read_clear_hw_no_tx4 0x0 + 12 RW empty_flag4 0x0 + 13 RW fw_ack4 0x0 + 14 RW tx_phy4 0x0 + 15 RW reset_rx_pktnum4 0x0 + */ +typedef union _PRO_WHITE_FIELD_LENGTH_1_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_field_length4: 6; + uint16_t rsvd: 1; + uint16_t lock4: 1; + uint16_t no_tx4: 1; + uint16_t auto_no_tx_option4: 1; + uint16_t clear_hw_no_tx4: 1; + uint16_t read_clear_hw_no_tx4: 1; + uint16_t empty_flag4: 1; + uint16_t fw_ack4: 1; + uint16_t tx_phy4: 1; + uint16_t reset_rx_pktnum4: 1; + }; +} PRO_WHITE_FIELD_LENGTH_1_TYPE; + +/* 0x1ea + 5:0 RW white_field_length5 0x0 + 6 RO rsvd 0x0 + 7 RW lock5 0x0 + 8 RW no_tx5 0x0 + 9 RW auto_no_tx_option5 0x0 + 10 RW clear_hw_no_tx5 0x0 + 11 RO read_clear_hw_no_tx5 0x0 + 12 RW empty_flag5 0x0 + 13 RW fw_ack5 0x0 + 14 RW tx_phy5 0x0 + 15 RW reset_rx_pktnum5 0x0 + */ +typedef union _PRO_WHITE_FIELD_LENGTH_2_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_field_length5: 6; + uint16_t rsvd: 1; + uint16_t lock5: 1; + uint16_t no_tx5: 1; + uint16_t auto_no_tx_option5: 1; + uint16_t clear_hw_no_tx5: 1; + uint16_t read_clear_hw_no_tx5: 1; + uint16_t empty_flag5: 1; + uint16_t fw_ack5: 1; + uint16_t tx_phy5: 1; + uint16_t reset_rx_pktnum5: 1; + }; +} PRO_WHITE_FIELD_LENGTH_2_TYPE; + +/* 0x1ec + 5:0 RW white_field_length6 0x0 + 6 RO rsvd 0x0 + 7 RW lock6 0x0 + 8 RW no_tx6 0x0 + 9 RW auto_no_tx_option6 0x0 + 10 RW clear_hw_no_tx6 0x0 + 11 RO read_clear_hw_no_tx6 0x0 + 12 RW empty_flag6 0x0 + 13 RW fw_ack6 0x0 + 14 RW tx_phy6 0x0 + 15 RW reset_rx_pktnum6 0x0 + */ +typedef union _PRO_WHITE_FIELD_LENGTH_3_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_field_length6: 6; + uint16_t rsvd: 1; + uint16_t lock6: 1; + uint16_t no_tx6: 1; + uint16_t auto_no_tx_option6: 1; + uint16_t clear_hw_no_tx6: 1; + uint16_t read_clear_hw_no_tx6: 1; + uint16_t empty_flag6: 1; + uint16_t fw_ack6: 1; + uint16_t tx_phy6: 1; + uint16_t reset_rx_pktnum6: 1; + }; +} PRO_WHITE_FIELD_LENGTH_3_TYPE; + +/* 0x1ee + 5:0 RW white_field_length7 0x0 + 6 RO rsvd 0x0 + 7 RW lock7 0x0 + 8 RW no_tx7 0x0 + 9 RW auto_no_tx_option7 0x0 + 10 RW clear_hw_no_tx7 0x0 + 11 RO read_clear_hw_no_tx7 0x0 + 12 RW empty_flag7 0x0 + 13 RW fw_ack7 0x0 + 14 RW tx_phy7 0x0 + 15 RW reset_rx_pktnum7 0x0 + */ +typedef union _PRO_WHITE_FIELD_LENGTH_4_TYPE +{ + uint16_t d16; + struct + { + uint16_t white_field_length7: 6; + uint16_t rsvd: 1; + uint16_t lock7: 1; + uint16_t no_tx7: 1; + uint16_t auto_no_tx_option7: 1; + uint16_t clear_hw_no_tx7: 1; + uint16_t read_clear_hw_no_tx7: 1; + uint16_t empty_flag7: 1; + uint16_t fw_ack7: 1; + uint16_t tx_phy7: 1; + uint16_t reset_rx_pktnum7: 1; + }; +} PRO_WHITE_FIELD_LENGTH_4_TYPE; + +/* 0x1f0 + 2:0 RW preamble_field_length3[2:0] 0x0 + 3 RW clear_hw_rd_ptr3 0x0 + 4 RO read_clear_hw_rd_ptr3 0x0 + 5 RW auto_add_rptr_option3 0x0 + 8:6 RW fw_rd_ptr3 0x0 + 9 RW auto_no_rdptr_option3 0x0 + 10 RW reset_tx_pktnum3 0x0 + 15:11 RO rsvd 0x0 + */ +typedef union _PRO_FIELD_LENGTH3_TYPE +{ + uint16_t d16; + struct + { + uint16_t preamble_field_length3_2_0: 3; + uint16_t clear_hw_rd_ptr3: 1; + uint16_t read_clear_hw_rd_ptr3: 1; + uint16_t auto_add_rptr_option3: 1; + uint16_t fw_rd_ptr3: 3; + uint16_t auto_no_rdptr_option3: 1; + uint16_t reset_tx_pktnum3: 1; + uint16_t rsvd: 5; + }; +} PRO_FIELD_LENGTH3_TYPE; + +/* 0x1f2 + 2:0 RW preamble_field_length4[2:0] 0x0 + 3 RW clear_hw_rd_ptr4 0x0 + 4 RO read_clear_hw_rd_ptr4 0x0 + 5 RW auto_add_rptr_option4 0x0 + 8:6 RW fw_rd_ptr4 0x0 + 9 RW auto_no_rdptr_option4 0x0 + 10 RW reset_tx_pktnum4 0x0 + 15:11 RO rsvd 0x0 + */ +typedef union _PRO_FIELD_LENGTH4_TYPE +{ + uint16_t d16; + struct + { + uint16_t preamble_field_length4_2_0: 3; + uint16_t clear_hw_rd_ptr4: 1; + uint16_t read_clear_hw_rd_ptr4: 1; + uint16_t auto_add_rptr_option4: 1; + uint16_t fw_rd_ptr4: 3; + uint16_t auto_no_rdptr_option4: 1; + uint16_t reset_tx_pktnum4: 1; + uint16_t rsvd: 5; + }; +} PRO_FIELD_LENGTH4_TYPE; + +/* 0x1f4 + 2:0 RW preamble_field_length5[2:0] 0x0 + 3 RW clear_hw_rd_ptr5 0x0 + 4 RO read_clear_hw_rd_ptr5 0x0 + 5 RW auto_add_rptr_option5 0x0 + 8:6 RW fw_rd_ptr5 0x0 + 9 RW auto_no_rdptr_option5 0x0 + 10 RW reset_tx_pktnum5 0x0 + 15:11 RO rsvd 0x0 + */ +typedef union _PRO_FIELD_LENGTH5_TYPE +{ + uint16_t d16; + struct + { + uint16_t preamble_field_length5_2_0: 3; + uint16_t clear_hw_rd_ptr5: 1; + uint16_t read_clear_hw_rd_ptr5: 1; + uint16_t auto_add_rptr_option5: 1; + uint16_t fw_rd_ptr5: 3; + uint16_t auto_no_rdptr_option5: 1; + uint16_t reset_tx_pktnum5: 1; + uint16_t rsvd: 5; + }; +} PRO_FIELD_LENGTH5_TYPE; + +/* 0x1f6 + 2:0 RW preamble_field_length6[2:0] 0x0 + 3 RW clear_hw_rd_ptr6 0x0 + 4 RO read_clear_hw_rd_ptr6 0x0 + 5 RW auto_add_rptr_option6 0x0 + 8:6 RW fw_rd_ptr6 0x0 + 9 RW auto_no_rdptr_option6 0x0 + 10 RW reset_tx_pktnum6 0x0 + 15:11 RO rsvd 0x0 + */ +typedef union _PRO_FIELD_LENGTH6_TYPE +{ + uint16_t d16; + struct + { + uint16_t preamble_field_length6_2_0: 3; + uint16_t clear_hw_rd_ptr6: 1; + uint16_t read_clear_hw_rd_ptr6: 1; + uint16_t auto_add_rptr_option6: 1; + uint16_t fw_rd_ptr6: 3; + uint16_t auto_no_rdptr_option6: 1; + uint16_t reset_tx_pktnum6: 1; + uint16_t rsvd: 5; + }; +} PRO_FIELD_LENGTH6_TYPE; + +/* 0x1f8 + 2:0 RW preamble_field_length7[2:0] 0x0 + 3 RW clear_hw_rd_ptr7 0x0 + 4 RO read_clear_hw_rd_ptr7 0x0 + 5 RW auto_add_rptr_option7 0x0 + 8:6 RW fw_rd_ptr7 0x0 + 9 RW auto_no_rdptr_option7 0x0 + 10 RW reset_tx_pktnum7 0x0 + 15:11 RO rsvd 0x0 + */ +typedef union _PRO_FIELD_LENGTH7_TYPE +{ + uint16_t d16; + struct + { + uint16_t preamble_field_length7_2_0: 3; + uint16_t clear_hw_rd_ptr7: 1; + uint16_t read_clear_hw_rd_ptr7: 1; + uint16_t auto_add_rptr_option7: 1; + uint16_t fw_rd_ptr7: 3; + uint16_t auto_no_rdptr_option7: 1; + uint16_t reset_tx_pktnum7: 1; + uint16_t rsvd: 5; + }; +} PRO_FIELD_LENGTH7_TYPE; + +/* 0x1fa + 15:0 RW tx_address1[15:0] 0x0 + */ +typedef union _PRO_TX_ADDRESS1_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address1_15_0; + }; +} PRO_TX_ADDRESS1_LOWER_TYPE; + +/* 0x1fc + 15:0 RW tx_address1[31:16] 0x0 + */ +typedef union _PRO_TX_ADDRESS1_MIDDLE_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address1_31_16; + }; +} PRO_TX_ADDRESS1_MIDDLE_TYPE; + +/* 0x1fe + 7:0 RW tx_address1[39:32] 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_TX_ADDRESS1_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address1_39_32: 8; + uint16_t rsvd: 8; + }; +} PRO_TX_ADDRESS1_UPPER_TYPE; + +/* 0x200 + 15:0 RW tx_address3[15:0] 0x0 + */ +typedef union _PRO_TX_ADDRESS3_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address3_15_0; + }; +} PRO_TX_ADDRESS3_LOWER_TYPE; + +/* 0x202 + 15:0 RW tx_address3[31:16] 0x0 + */ +typedef union _PRO_TX_ADDRESS3_MIDDLE_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address3_31_16; + }; +} PRO_TX_ADDRESS3_MIDDLE_TYPE; + +/* 0x204 + 7:0 RW tx_address3[39:32] 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_TX_ADDRESS3_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address3_39_32: 8; + uint16_t rsvd: 8; + }; +} PRO_TX_ADDRESS3_UPPER_TYPE; + +/* 0x206 + 15:0 RW tx_address4[15:0] 0x0 + */ +typedef union _PRO_TX_ADDRESS4_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address4_15_0; + }; +} PRO_TX_ADDRESS4_LOWER_TYPE; + +/* 0x208 + 15:0 RW tx_address4[31:16] 0x0 + */ +typedef union _PRO_TX_ADDRESS4_MIDDLE_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address4_31_16; + }; +} PRO_TX_ADDRESS4_MIDDLE_TYPE; + +/* 0x20a + 7:0 RW tx_address4[39:32] 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_TX_ADDRESS4_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address4_39_32: 8; + uint16_t rsvd: 8; + }; +} PRO_TX_ADDRESS4_UPPER_TYPE; + +/* 0x20c + 15:0 RW tx_address5[15:0] 0x0 + */ +typedef union _PRO_TX_ADDRESS5_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address5_15_0; + }; +} PRO_TX_ADDRESS5_LOWER_TYPE; + +/* 0x20e + 15:0 RW tx_address5[31:16] 0x0 + */ +typedef union _PRO_TX_ADDRESS5_MIDDLE_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address5_31_16; + }; +} PRO_TX_ADDRESS5_MIDDLE_TYPE; + +/* 0x210 + 7:0 RW tx_address5[39:32] 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_TX_ADDRESS5_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address5_39_32: 8; + uint16_t rsvd: 8; + }; +} PRO_TX_ADDRESS5_UPPER_TYPE; + +/* 0x212 + 15:0 RW tx_address6[15:0] 0x0 + */ +typedef union _PRO_TX_ADDRESS6_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address6_15_0; + }; +} PRO_TX_ADDRESS6_LOWER_TYPE; + +/* 0x214 + 15:0 RW tx_address6[31:16] 0x0 + */ +typedef union _PRO_TX_ADDRESS6_MIDDLE_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address6_31_16; + }; +} PRO_TX_ADDRESS6_MIDDLE_TYPE; + +/* 0x216 + 7:0 RW tx_address6[39:32] 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_TX_ADDRESS6_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address6_39_32: 8; + uint16_t rsvd: 8; + }; +} PRO_TX_ADDRESS6_UPPER_TYPE; + +/* 0x218 + 15:0 RW tx_address7[15:0] 0x0 + */ +typedef union _PRO_TX_ADDRESS7_LOWER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address7_15_0; + }; +} PRO_TX_ADDRESS7_LOWER_TYPE; + +/* 0x21a + 15:0 RW tx_address7[31:16] 0x0 + */ +typedef union _PRO_TX_ADDRESS7_MIDDLE_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address7_31_16; + }; +} PRO_TX_ADDRESS7_MIDDLE_TYPE; + +/* 0x21c + 7:0 RW tx_address7[39:32] 0x0 + 15:8 RO rsvd 0x0 + */ +typedef union _PRO_TX_ADDRESS7_UPPER_TYPE +{ + uint16_t d16; + struct + { + uint16_t tx_address7_39_32: 8; + uint16_t rsvd: 8; + }; +} PRO_TX_ADDRESS7_UPPER_TYPE; + +/* 0x21e + 2:0 RO rd_ptr0[2:0] 0x0 + 5:3 RO rd_ptr1[2:0] 0x0 + 8:6 RO rd_ptr2[2:0] 0x0 + 11:9 RO rd_ptr3[2:0] 0x0 + 14:12 RO rd_ptr4[2:0] 0x0 + 15 RO rsvd 0x0 + */ +typedef union _PRO_RD_PTR_0_TYPE +{ + uint16_t d16; + struct + { + uint16_t rd_ptr0_2_0: 3; + uint16_t rd_ptr1_2_0: 3; + uint16_t rd_ptr2_2_0: 3; + uint16_t rd_ptr3_2_0: 3; + uint16_t rd_ptr4_2_0: 3; + uint16_t rsvd: 1; + }; +} PRO_RD_PTR_0_TYPE; + +/* 0x220 + 2:0 RO rd_ptr5[2:0] 0x0 + 5:3 RO rd_ptr6[2:0] 0x0 + 8:6 RO rd_ptr7[2:0] 0x0 + 15:9 RO rsvd 0x0 + */ +typedef union _PRO_RD_PTR_1_TYPE +{ + uint16_t d16; + struct + { + uint16_t rd_ptr5_2_0: 3; + uint16_t rd_ptr6_2_0: 3; + uint16_t rd_ptr7_2_0: 3; + uint16_t rsvd: 7; + }; +} PRO_RD_PTR_1_TYPE; + diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/debug_port.h b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/debug_port.h new file mode 100644 index 00000000..1393eb52 --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/debug_port.h @@ -0,0 +1,81 @@ +/** +********************************************************************************************************* +* Copyright(c) 2021, Realtek Semiconductor Corporation. All rights reserved. +********************************************************************************************************* +* @file debug_port.h +* @brief +* @details +* @author +* @date 2021-4-6 +* @version v1.0 +* ********************************************************************************************************* +*/ + + +#ifndef _DEBUG_PORT_H_ +#define _DEBUG_PORT_H_ + +#ifdef __cplusplus +extern "C" { +#endif +#include "rtl876x.h" + +typedef enum _t_debug_mode +{ + DIGI_DBG_SYSON, + DIGI_DBG_BT, + DIGI_DBG_PROCESSOR_SYS, + DIGI_DBG_APB_ADC, + DIGI_DBG_RTC, + + ANA_DBG_AUX_ADC = 0x10, + ANA_DBG_RF_TRXIQ_AFE_TRMK_ADC, + ANA_DBG_RF_TRANCERIVER +} T_DEBUG_MODE; + + +typedef enum _t_debug_pin_group +{ + DBG_PIN_GROUP_A, //mapping to debug_pin_table_a + DBG_PIN_GROUP_B, //mapping to debug_pin_table_b + DBG_PIN_GROUP_C, //mapping to debug_pin_table_c + DBG_PIN_GROUP_ALL_PAD = 0xff, +} T_PIN_GROUP; + +/** + * @brief Open the debug port mode on specific pins + * @param red_table_mask: the pin bit map used in the table debug_pin_table_red. + * @param black_table_mask: the pin bit map used in the table debug_pin_table_black. + * @param debug_mode: debug mode select from T_DEBUG_MODE + * @retval None + */ +extern void (*debug_port_open)(T_DEBUG_MODE debug_mode); + +/** + * @brief Close the debug mode + * @retval None + */ +void debug_port_close(void); + +/** + * @brief set debug mode on specific pins + * @param pin_group: the pin group selected. + * This parameter can be any combination of the following values: + * @arg DBG_PIN_GROUP_A : the digital debug pin bit 0 ~ 31 could be selected as below table. + * ADC_0, ADC_1, ADC_2, ADC_3, P1_0, P1_1, P2_0, P2_1, //digi_debug_0 ~ 7 + * P2_2, P2_3, P2_4, P2_5, P3_2, P3_3, MIC2_P, MIC2_N, //digi_debug_8 ~ 15 + * MICBIAS, LOUT_P, LOUT_N, P10_0 //digi_debug_16 ~ 19 + * @arg DBG_PIN_GROUP_B : the digital debug pin bit 0 ~ 31 could be selected as below table. + * MIC1_P, MIC1_N, //digi_debug_12 ~ 13 + * @retval None + */ +extern void (*debug_port_set_pin_bit_map)(T_PIN_GROUP pin_group, uint32_t dbg_bitmap); + +void debug_port_bt_mac(bool is_set); + +#ifdef __cplusplus +} +#endif + +#endif /* _DEBUG_PORT_H_ */ + diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/platform_rtc.h b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/platform_rtc.h new file mode 100644 index 00000000..498d54aa --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/platform_rtc.h @@ -0,0 +1,83 @@ +/** +********************************************************************************************************* +* Copyright(c) 2022, Realtek Semiconductor Corporation. All rights reserved. +********************************************************************************************************** +* @file platform_rtc.h +* @brief Platfrom RTC implementation head file. +* @details +* @author js.lin +* @date 2022-8-31 +* @version v0.1 +********************************************************************************************************* +*/ + +/*============================================================================* + * Define to prevent recursive inclusion + *============================================================================*/ +#ifndef __PLATFORM_RTC_H +#define __PLATFORM_RTC_H + +/*============================================================================* + * Header Files + *============================================================================*/ +#include +#include +#include "rtl876x.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PF_RTC_BIT_NUM BIT64(32) + +/*============================================================================* + * Types + *============================================================================*/ +/** @defgroup PLATFORM_RTC_Exported_Types Platform RTC Exported Types + * @{ + */ + +typedef enum PFRTCComparator +{ + PF_RTC_COMP0 = 0x01, + PF_RTC_COMP1 = 0x02, + PF_RTC_COMP2 = 0x04, + PF_RTC_COMP3 = 0x08, +} PFRTCComparator; + +/** @} */ /* End of group PLATFORM_RTC_Exported_Types */ + +/*============================================================================* + * Functions + *============================================================================*/ +/** @defgroup PLATFORM_RTC_Exported_Functions Platform RTC Exported Functions + * @{ + */ + +extern void (*platform_rtc_aon_init)(void); +extern void (*platform_rtc_non_aon_init)(void); +extern void (*platform_rtc_reset)(void); +extern void (*platform_rtc_run_cmd)(bool); +extern uint32_t (*platform_rtc_get_counter)(void); +extern void (*platform_rtc_set_comp_intr_config)(PFRTCComparator, bool, bool); +extern void (*platform_rtc_set_comp_wk_config)(PFRTCComparator, bool); +extern void (*platform_rtc_set_intr_group_config)(bool); +extern void (*platform_rtc_set_wk_group_config)(bool); +extern bool (*platform_rtc_get_intr_group_config)(void); +extern bool (*platform_rtc_get_wk_group_config)(void); +extern void (*platform_rtc_set_comp)(PFRTCComparator, uint32_t); +extern uint32_t (*platform_rtc_get_comp)(PFRTCComparator); +extern PFRTCComparator(*platform_rtc_get_comp_intr_status)(void); +extern PFRTCComparator(*platform_rtc_get_comp_wk_status)(void); +extern void (*platform_rtc_set_comp_manual_intr)(PFRTCComparator); +extern PFRTCComparator(*platform_rtc_get_comp_manual_intr)(void); +extern void (*platform_rtc_clear_comp_intr_status)(PFRTCComparator); +extern void (*platform_rtc_clear_comp_wk_status)(PFRTCComparator); + +/** @} */ /* End of group PLATFORM_RTC_Exported_Functions */ + +#ifdef __cplusplus +} +#endif + +#endif /* __PLATFORM_RTC_H */ diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_interface.h b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_interface.h new file mode 100644 index 00000000..f647d041 --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_interface.h @@ -0,0 +1,187 @@ +/** +********************************************************************************************************* +* Copyright(c) 2021, Realtek Semiconductor Corporation. All rights reserved. +********************************************************************************************************* +* @file power_manager_interface.h +* @brief Power Manager Interface implementation head file. +* @details +* @author Kellan Ho, js.lin +* @date 2022-08-29 +* @version v1.0 +********************************************************************************************************* +*/ + +/*============================================================================* + * Define to prevent recursive inclusion + *============================================================================*/ +#ifndef __POWER_MANAGER_INTERFACE_H +#define __POWER_MANAGER_INTERFACE_H + + +/*============================================================================* + * Header Files +*============================================================================*/ +#include "stdint.h" +#include "stdbool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* for naming compatibility */ +#define PM_SLAVE_BTMAC PM_SLAVE_0 +#define PM_SLAVE_ZIGBEE PM_SLAVE_0 +#define PM_SLAVE_PROPRIETARY PM_SLAVE_0 + +#define PM_INVALID_WAKEUP_TIME_DIFF 0xffffffff + +#define PM_UNIT_MAX_NUM 4 // equal to num of pf rtc comparators +#define PM_GET_UNIT_INDEX(unit_id) (((unit_id >> 2) == PM_SYSTEM_LEVEL_PF) ? 3 : \ + ((unit_id >> 2) == PM_SYSTEM_LEVEL_1) ? 2 : (unit_id & 0x1)) + +typedef enum PMSlave +{ + PM_SLAVE_0 = 0, + PM_SLAVE_DEF_MAX, +} PMSlave; + +typedef enum PMSystemLevel +{ + PM_SYSTEM_LEVEL_0 = 0, + PM_SYSTEM_LEVEL_1 = 1, + PM_SYSTEM_LEVEL_PF = 2, + PM_SYSTEM_LEVEL_DEF_MAX, +} PMSystemLevel; + +typedef enum PMUnitStatus +{ + PM_UNIT_UNKNOWN = 0, + PM_UNIT_PEND = 1, + PM_UNIT_INACTIVE = 2, + PM_UNIT_ACTIVE = 3, +} PMUnitStatus; + +typedef enum PMCheckResult +{ + PM_CHECK_PEND = 0, + PM_CHECK_FAIL = 1, + PM_CHECK_PASS = 2, +} PMCheckResult; + +typedef enum PMAciton +{ + PM_ACTION_IDLE_CHECK_REQUEST, + PM_ACTION_IDLE_CHECK_CONTINUE, + PM_ACTION_IDLE_CHECK_RESPONSE, + PM_ACTION_CHECK_REQUEST, + PM_ACTION_CHECK_RESPONSE, + PM_ACTION_STORE_ENTER_REQUEST, + PM_ACTION_STORE_ENTER_RESPONSE, + PM_ACTION_EXIT_RESTORE_REQUEST, + PM_ACTION_EXIT_RESTORE_RESPONSE, + PM_ACTION_DEF_MAX, +} PMAciton; + +typedef enum +{ + PM_UNIT_PROPRIETARY = (PM_SYSTEM_LEVEL_0 << 2 | 0), + PM_UNIT_ZIGBEE = (PM_SYSTEM_LEVEL_0 << 2 | 1), + PM_UNIT_BTMAC = (PM_SYSTEM_LEVEL_1 << 2 | 0), + PM_UNIT_PLATFORM = (PM_SYSTEM_LEVEL_PF << 2 | 0), + PM_UNIT_HEADER_ID = 0x80, +} PMUnitID; + +typedef union PMM2SActivateMessage +{ + uint32_t d32[4]; + struct + { + uint32_t action: 4; + uint32_t unit_id: 4; + uint32_t dummy_0: 23; + uint32_t info_valid: 1; + + uint32_t dummy_1; + + uint32_t dummy_2; + + uint32_t dummy_3; + }; +} PMM2SActivateMessage; + +typedef union PMS2MActivateMessage +{ + uint32_t d32[1]; + struct + { + uint32_t action: 4; + uint32_t unit_id: 4; + uint32_t dummy_0: 23; + uint32_t info_valid: 1; + }; +} PMS2MActivateMessage; + +typedef union PMM2SInactivateMessage +{ + uint32_t d32[3]; + struct + { + uint32_t action: 4; + uint32_t unit_id: 4; + uint32_t enable_irq: 1; + uint32_t enter_wfi: 1; + uint32_t dummy_0: 21; + uint32_t info_valid: 1; + + uint32_t check_clk; + + uint32_t pre_sys_lvl_wakeup_time_diff; + }; +} PMM2SInactivateMessage; + +typedef union PMS2MInactivateMessage +{ + uint32_t d32[3]; + struct + { + uint32_t action: 4; + uint32_t unit_id: 4; + uint32_t check_result: 2; + uint32_t dummy_0: 21; + uint32_t info_valid: 1; + + uint32_t wakeup_time_diff; + + uint32_t dummy_2; + }; +} PMS2MInactivateMessage; + +extern volatile PMM2SActivateMessage g_pm_m2s_act_msg; +extern volatile PMS2MActivateMessage g_pm_s2m_act_msg; +extern volatile PMM2SInactivateMessage g_pm_m2s_inact_msg; +extern volatile PMS2MInactivateMessage g_pm_s2m_inact_msg; + +extern void (*power_manager_master_initiate_wakeup)(PMSlave slave, PMSystemLevel sys_lvl, + PMUnitID unit_id); +extern void (*power_manager_master_suspend_unit)(PMSlave slave, PMSystemLevel sys_lvl, + PMUnitID unit_id); +extern void (*power_manager_master_resume_unit)(PMSlave slave, PMSystemLevel sys_lvl, + PMUnitID unit_id); +extern void (*power_manager_master_set_timing_guarantee_sys_lvl)(PMSlave slave, + PMSystemLevel timing_guarantee_sys_lvl); +extern void (*power_manager_master_register_unit)(PMSlave slave, PMSystemLevel sys_lvl, + PMUnitID unit_id); +extern void (*power_manager_master_reset_slave)(PMSlave slave); + +void power_manager_interface_init_unit_status(void); +PMUnitStatus power_manager_interface_get_unit_status(PMSlave slave, PMUnitID unit_id); +void power_manager_interface_set_unit_status(PMSlave slave, PMUnitID unit_id, PMUnitStatus status); +bool power_manager_interface_check_unit_active(PMSlave slave, PMUnitID unit_id); +bool power_manager_interface_check_unit_active_from_isr(PMSlave slave, PMUnitID unit_id); + +#ifdef __cplusplus +} +#endif + +#endif /* __POWER_MANAGER_INTERFACE_H */ + diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_slave.h b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_slave.h new file mode 100644 index 00000000..5fcfd4d8 --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_slave.h @@ -0,0 +1,208 @@ +/** +***************************************************************************************** +* Copyright(c) 2022, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file power_manager_slave.h + * @brief Power Manager Slave implementation head file. + * @author George Chang, js.lin + * @date 2022-08-29 + * @version v1.0 + * ************************************************************************************* + */ + +/*============================================================================* + * Define to prevent recursive inclusion + *============================================================================*/ +#ifndef __POWER_MANAGER_SLAVE_H +#define __POWER_MANAGER_SLAVE_H + + +/*============================================================================* + * Header Files +*============================================================================*/ +#include +#include +#include "power_manager_interface.h" +#include "platform_rtc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IRQ_TAG_FILL_WORD ( 0x5a5a5a5aU ) + +#define POWER_MANAGER_GET_TIMESTAMP(stamp) \ + do { \ + stamp = platform_rtc_get_counter(); \ + } while(0) + +#define POWER_MANAGER_GET_TIMEDIFF(start, end, diff) \ + do { \ + diff = clk_cnt_diff(start, end, PF_RTC_BIT_NUM); \ + } while(0) + +#define POWER_MANAGER_UPDATE_EXTREMUM(diff, diff_max, diff_min) \ + do { \ + diff_max = diff > diff_max ? diff : diff_max; \ + diff_min = diff < diff_min ? diff : diff_min; \ + } while(0) + +#define PM_SYSTEM_LEVEL_UNIT_NUM 2 + +/*============================================================================* + * Types +*============================================================================*/ +/** @defgroup POWER_MANAGER_Exported_Types Power Manager Exported Types + * @{ + */ +typedef PMCheckResult(*PMUnitCheckFunc)(uint32_t, uint32_t, uint32_t *); +typedef void (*PMUnitStoreFunc)(void); +typedef void (*PMUnitRestoreFunc)(void); +typedef void (*PMUnitEnterFunc)(void); +typedef void (*PMUnitExitFunc)(void); + +typedef PMCheckResult(*PMCheckWakeupTimeFunc)(uint32_t *); +typedef void (*PMFuncToReturn)(void); + +typedef enum +{ + PROFILING_PM_SLAVE_CHECK = 0, + PROFILING_PM_SLAVE_STORE = 1, + PROFILING_PM_SLAVE_ENTER = 2, + PROFILING_PM_SLAVE_EXIT = 3, + PROFILING_PM_SLAVE_RESTORE = 4, + PROFILING_PM_SLAVE_STAGE_MAX = 5 +} ProfilingPMSlaveStage; + +typedef struct +{ + uint32_t stage_start; + uint32_t stage_end; + uint32_t stage_time[PROFILING_PM_SLAVE_STAGE_MAX]; + uint32_t stage_time_max[PROFILING_PM_SLAVE_STAGE_MAX]; + uint32_t stage_time_min[PROFILING_PM_SLAVE_STAGE_MAX]; +} ProfilingPMSlaveData; + +typedef struct +{ + PMUnitCheckFunc check_func; + PMUnitStoreFunc store_func; + PMUnitRestoreFunc restore_func; + PMUnitEnterFunc enter_func; + PMUnitExitFunc exit_func; + + PMUnitID unit_id; + + ProfilingPMSlaveData *profiling_data; +} PowerManagerSlaveUnit; + +typedef struct +{ + PMUnitID unit_id; + uint32_t pon_delay_time; + uint32_t pon_stage_time; + uint32_t pof_delay_time; + uint32_t pof_stage_time; + uint32_t minimum_sleep_guard_time; +} PowerManagerSlaveRequestResource; + +typedef struct +{ + uint32_t start_time_diff; + uint32_t end_time_diff; +} PMTimePeriod; + +typedef struct +{ + uint32_t cur_clk; + PMTimePeriod occupied; +} PMOccupyTimeInfo; + +typedef struct +{ + PowerManagerSlaveUnit *unit_array[PM_SYSTEM_LEVEL_DEF_MAX][PM_SYSTEM_LEVEL_UNIT_NUM]; + + PMOccupyTimeInfo slave[PM_SYSTEM_LEVEL_DEF_MAX][PM_SYSTEM_LEVEL_UNIT_NUM]; + + uint32_t suspended; + + PMFuncToReturn func_to_return; +} PowerManagerSlaveSystem; + +typedef union +{ + uint8_t value; + struct + { + uint8_t profiling_mode: 1; + uint8_t irq_control_by_master: 1; + uint8_t m2s_inact_msg_dbg: 1; + uint8_t s2m_inact_msg_dbg: 1; + uint8_t m2s_act_msg_dbg: 1; + uint8_t s2m_act_msg_dbg: 1; + uint8_t rsvd: 2; + }; +} PowerManagerSlaveFeatureConfig; + +/*============================================================================* + * Variables +*============================================================================*/ +/** @defgroup POWER_MANAGER_Exported_Variables Power Manager Exported Variables + * @{ + */ +extern PowerManagerSlaveFeatureConfig power_manager_slave_feature_cfg; +extern PowerManagerSlaveSystem power_manager_slave_system; + +extern bool (*power_manager_slave_check)(const PMM2SInactivateMessage *); +extern void (*power_manager_slave_store_and_enter)(const PMM2SInactivateMessage *); +extern void (*power_manager_slave_exit_and_restore)(const PMM2SActivateMessage *); +extern void (*power_manager_slave_cpu_sleep)(bool); +extern bool (*power_manager_slave_check_interrupt_occur)(void); +extern void (*power_manager_slave_handle_irq)(bool, bool *); +extern void (*power_manager_slave_init)(void); + +extern uint32_t (*power_manager_slave_align_clk)(const uint32_t, uint32_t *, uint32_t *, uint32_t); +extern PMCheckResult(*power_manager_slave_align_wakeup_time)(PMCheckWakeupTimeFunc, const uint32_t, + uint32_t *); +extern void (*power_manager_slave_set_occupied_time_descending)(const uint32_t, + const PMOccupyTimeInfo *, PMTimePeriod *, uint8_t); +extern bool (*power_manager_slave_check_occupied_time)(const PMTimePeriod *, const uint8_t, + uint32_t *, uint32_t, uint32_t); +extern void (*power_manager_slave_register_resource)(PMUnitID, uint32_t, uint32_t, uint32_t); +extern void (*power_manager_slave_unregister_resource)(PMUnitID); +extern bool (*power_manager_slave_request_resource)(const uint32_t, uint32_t *, + PowerManagerSlaveRequestResource *); + +extern bool (*power_manager_slave_inact_msg_handler)(const PMM2SInactivateMessage *, bool *); +extern void (*power_manager_slave_act_msg_handler)(const PMM2SActivateMessage *); + +extern void (*power_manager_slave_inact_action_handler)(void); +extern void (*power_manager_slave_act_action_handler)(void); +/** @} */ /* End of group POWER_MANAGER_Exported_Variables */ + +/*============================================================================* + * Functions +*============================================================================*/ + +/** @defgroup POWER_MANAGER_Exported_Functions Power Manager Exported Functions + * @{ + */ +void power_manager_slave_register_function_to_return(PMFuncToReturn func); +void power_manager_slave_register_unit(PMUnitID unit_id, PowerManagerSlaveUnit *p_unit); +PowerManagerSlaveUnit *power_manager_slave_get_unit(PMUnitID unit_id); +void power_manager_slave_suspend_unit(PMUnitID unit_id); +void power_manager_slave_resume_unit(PMUnitID unit_id); +void power_manager_slave_suspend_all(void); +void power_manager_slave_resume_all(void); + +/** @} */ /* End of group POWER_MANAGER_Exported_Functions */ + +/** @} */ /* End of group POWER_MANAGER */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __POWER_MANAGER_SLAVE_H */ + diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_unit_platform.h b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_unit_platform.h new file mode 100644 index 00000000..8cc98504 --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_unit_platform.h @@ -0,0 +1,200 @@ +/** + ***************************************************************************************** + * Copyright(c) 2022, Realtek Semiconductor Corporation. All rights reserved. + ***************************************************************************************** + * @file power_manager_unit_platform.h + * @brief Platform Power Manager implementation head file. + * @author Po Yu Chen, js.lin + * @date 2022-08-29 + * @version v1.0 + * ************************************************************************************* + */ + +/*============================================================================* + * Define to prevent recursive inclusion + *============================================================================*/ +#ifndef __POWER_MANAGER_UNIT_PLATFORM_H +#define __POWER_MANAGER_UNIT_PLATFORM_H + +/*============================================================================* + * Header Files + *============================================================================*/ +#include +#include +#include "os_queue.h" +#include "power_manager_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*============================================================================* + * Types + *============================================================================*/ +/** @defgroup PLATFORM_PM_Exported_Types Platform_PM Exported Types + * @{ + */ + +typedef enum +{ + //PLATFORM_POWEROFF = 0, /**< Power off */ // Bee4 not support + PLATFORM_POWERDOWN = 1, /**< Power down */ + PLATFORM_DLPS_PFM = 2, /**< DLPS (PFM) */ + PLATFORM_DLPS_RET = 3, /**< DLPS (RET) */ + //PLATFORM_LPS_PFM = 4, /**< LPS */ // Bee4 not support + PLATFORM_ACTIVE = 5, /**< Active */ + PLATFORM_POWER_MODE_MAX = 6 +} PlatformPowerMode; + +typedef enum +{ + PLATFORM_PM_CHECK = 0, + PLATFORM_PM_STORE = 1, + PLATFORM_PM_ENTER = 2, + PLATFORM_PM_EXIT = 3, + PLATFORM_PM_RESTORE = 4, + PLATFORM_PM_PEND = 5, + PLATFORM_PM_STAGE_MAX = 6, +} PlatformPMStage; + +typedef enum +{ + PLATFORM_PM_WAKEUP_UNKNOWN = 0x00000000, + PLATFORM_PM_WAKEUP_PRE_SYSTEM_LEVEL = 0x00000001, + PLATFORM_PM_WAKEUP_PF_RTC = 0x00000100, + PLATFORM_PM_WAKEUP_RTC = 0x00000200, + PLATFORM_PM_WAKEUP_MAC = 0x00000400, + PLATFORM_PM_WAKEUP_GPIO = 0x00000800, + PLATFORM_WAKEUP_REASON_USB = 0x00001000, + PLATFORM_WAKEUP_REASON_LPCOMP = 0x00002000, + PLATFORM_WAKEUP_REASON_AON_QUAD_DEC = 0x00004000, + PLATFORM_PM_WAKEUP_DEF_MAX = 0xFFFFFFFF, +} PlatformWakeupReason; + +typedef enum +{ + PLATFORM_PM_ERROR_UNKNOWN = 0x0, + PLATFORM_PM_ERROR_POWER_MODE = 0x1, + PLATFORM_PM_ERROR_DISABLE_DLPS_TIME = 0x2, + PLATFORM_PM_ERROR_32K_CHECK_LOCK = 0x3, + PLATFORM_PM_ERROR_LOG_DMA_NOT_IDLE = 0x4, + PLATFORM_PM_ERROR_LOG_BUFFER_NOT_EMPTY = 0x5, + PLATFORM_PM_ERROR_CALLBACK_CHECK = 0x6, + PLATFORM_PM_ERROR_INTERRUPT_OCCURRED = 0x7, + PLATFORM_PM_ERROR_WAKEUP_TIME = 0x8, + PLATFORM_PM_ERROR_OCCUPIED_TIME = 0x9, +} PlatformPowerModeErrorCode; + +typedef PMCheckResult(*PlatformPMCheckCbFunc)(uint32_t *); +typedef void (*PlatformPMStoreCbFunc)(void); +typedef void (*PlatformPMRestoreCbFunc)(void); +typedef void (*PlatformPMEnterCbFunc)(void); +typedef void (*PlatformPMExitCbFunc)(void); +typedef void (*PlatformPMPendCbFunc)(void); + +typedef void (*PlatformPMScheduleBottomHalfFunc)(void (*)(void)); + +typedef struct _PlatformPMCbFuncQueueElem +{ + struct _PlatformPMCbFuncQueueElem *pNext; + void *cb_func; + int8_t priority; +} PlatformPMCbFuncQueueElem; + +typedef struct _PlatformPMSystem +{ + PlatformPowerMode power_mode; + + PlatformWakeupReason wakeup_reason; + PlatformPowerModeErrorCode error_code; + uint32_t *refuse_reason; + + uint32_t wakeup_count; + uint32_t allow_sleep_count; + uint32_t last_wakeup_clk; + uint32_t last_sleep_clk; + uint32_t total_wakeup_time; + uint32_t total_sleep_time; + + uint32_t stage_time[PLATFORM_PM_STAGE_MAX]; + uint32_t minimum_sleep_time; + uint32_t learning_guard_time; + + T_OS_QUEUE callback_func_queue[PLATFORM_PM_STAGE_MAX]; + + PlatformPMScheduleBottomHalfFunc schedule_bottom_half_callback; +} PlatformPMSystem; + +typedef union _PlatformPMFeatureConfig +{ + uint8_t value[1]; + struct + { + uint8_t platform_check_dbg: 1; + uint8_t platform_enter_dbg: 1; + uint8_t platform_exit_dbg: 1; + uint8_t platform_stage_time: 1; + uint8_t platform_statistics: 1; + uint8_t rsvd1: 3; + }; +} PlatformPMFeatureConfig; + +/** @} */ /* End of group PLATFORM_PM_Exported_Types */ + +/*============================================================================* + * Variables + *============================================================================*/ +/** @defgroup PLATFORM_PM_Exported_Variables Platform_PM Exported Variables + * @{ + */ + +extern PlatformPMFeatureConfig platform_pm_feature_cfg; +extern PlatformPMSystem platform_pm_system; + +extern void (*platform_pm_hw_enter_flow)(void); +extern void (*platform_pm_hw_exit_flow)(void); +extern void (*platform_pm_cpu_enter_flow)(void); +extern void (*platform_pm_cpu_exit_flow)(void); + +extern PMCheckResult(*platform_pm_wakeup_time)(const uint32_t, const uint32_t, uint32_t *); +extern PMCheckResult(*platform_pm_check)(const uint32_t, const uint32_t, uint32_t *); +extern void (*platform_pm_store)(void); +extern void (*platform_pm_restore)(void); +extern void (*platform_pm_enter)(void); +extern void (*platform_pm_exit)(void); +extern void (*platform_pm_pend)(void); +extern void (*platform_pm_init)(void); +extern void (*platform_pm_statistics)(void); + +extern uint32_t (*platform_pm_get_extra_wait_cnt)(void); +extern void (*platfom_pm_callback_queue_in)(T_OS_QUEUE *, PlatformPMCbFuncQueueElem *, bool); +extern void (*platform_pm_register_callback_func_with_priority)(void *, PlatformPMStage, int8_t); +extern void (*platform_pm_register_callback_func)(void *, PlatformPMStage); +/** @} */ /* End of group PLATFORM_PM_Exported_Variables */ + +/*============================================================================* + * Functions + *============================================================================*/ + +/** @defgroup PLATFORM_PM_Exported_Functions Platform_PM Exported Functions + * @{ + */ + +void platform_pm_set_power_mode(PlatformPowerMode pf_power_mode_user); +PlatformPowerMode platform_pm_get_power_mode(void); +PlatformPowerModeErrorCode platform_pm_get_error_code(void); +uint32_t *platform_pm_get_refuse_reason(void); +void platform_pm_get_statistics(uint32_t *wakeup_count, uint32_t *last_wakeup_clk, + uint32_t *last_sleep_clk); +PlatformWakeupReason platform_pm_get_wakeup_reason(void); +void platform_pm_register_schedule_bottom_half_callback_func(PlatformPMScheduleBottomHalfFunc + cb_func); + +/** @} */ /* End of group PLATFORM_PM_Exported_Functions */ + +#ifdef __cplusplus +} +#endif + +#endif /* __POWER_MANAGER_UNIT_PLATFORM_H */ + diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/ppt_pf.h b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/ppt_pf.h new file mode 100644 index 00000000..b35fb926 --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/ppt_pf.h @@ -0,0 +1,57 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_pf.h + * @brief Head file for 2.4G module platform related driver. + * @details data structs and external functions declaration. + * @author bill + * @date 2023-10-27 + * @version v0.1 + * ************************************************************************************* + */ + +/* Define to prevent recursive inclusion */ +#ifndef _PPT_PF_H_ +#define _PPT_PF_H_ + +/* Add Includes here */ +#include "debug_port.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup PPT_PF + * @{ + */ + +/** @defgroup PPT_PF_Exported_Macros Exported Macros + * @brief + * @{ + */ + +/** @} End of PPT_PF_Exported_Macros */ + +/** @defgroup PPT_PF_Exported_Types Exported Types + * @brief + * @{ + */ + +/** @} End of PPT_PF_Exported_Types */ + +/** @defgroup PPT_PF_Exported_Functions Exported Functions + * @brief + * @{ + */ + + +/** @} End of PPT_PF_Exported_Functions */ + +/** @} End of PPT_PF */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/ppt_pm.h b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/ppt_pm.h new file mode 100644 index 00000000..50fc3806 --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/ppt_pm.h @@ -0,0 +1,59 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_pm.h + * @brief Head file for 2.4G module power driver. + * @details data structs and external functions declaration. + * @author bill + * @date 2023-10-27 + * @version v0.1 + * ************************************************************************************* + */ + +/* Define to prevent recursive inclusion */ +#ifndef _PPT_PM_H_ +#define _PPT_PM_H_ + +/* Add Includes here */ +#include "power_manager_interface.h" +#include "power_manager_slave.h" +#include "power_manager_unit_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup PPT_PM + * @{ + */ + +/** @defgroup PPT_PM_Exported_Macros Exported Macros + * @brief + * @{ + */ + +/** @} End of PPT_PM_Exported_Macros */ + +/** @defgroup PPT_PM_Exported_Types Exported Types + * @brief + * @{ + */ + +/** @} End of PPT_PM_Exported_Types */ + +/** @defgroup PPT_PM_Exported_Functions Exported Functions + * @brief + * @{ + */ + + +/** @} End of PPT_PM_Exported_Functions */ + +/** @} End of PPT_PM */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bee/drivers/ppt/sync/src/driver/simple/ppt_simple.c b/bee/drivers/ppt/sync/src/driver/simple/ppt_simple.c new file mode 100644 index 00000000..fc35025e --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/simple/ppt_simple.c @@ -0,0 +1,504 @@ +/** +***************************************************************************************** +* Copyright(c) 2021, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_simple.c + * @brief Source file for simple wrapper of 2.4G module common driver. + * @details + * @author bill + * @date 2021-11-26 + * @version v0.1 + * ************************************************************************************* + */ + +#include +//#include "trace.h" +#include "ppt_simple.h" +#include "os_mem.h" + +LOG_MODULE_REGISTER(ppt_simple, PPT_DRIVER_LOG_LEVEL); + +extern uint8_t (*modem_psd_get_entry_num)(void); +extern int8_t (*get_phy_rssi0_dbm)(uint8_t); +extern uint8_t(*modem_psd_get_rf_mode)(void); +uint8_t modem_psd_get_scan_mode(void); + +ppt_ctx_t ppt_ctx_imp; +ppt_ctx_t *ppt_ctx = &ppt_ctx_imp; + +void ppt_set_ptx_mode_ext(ppt_ptx_mode_ext_t *param) +{ + ppt_ptx_mode_t *base = ¶m->base; + ppt_ctx->ack = base->ack_mode; + ppt_ctx->oneshot = !base->periodic_mode; + ppt_ctx->retransmit_times = ppt_ctx->oneshot ? 0 : param->retransmit_times; + ppt_set_ptx_mode(base); +} + +void ppt_set_prx_mode_ext(ppt_prx_mode_ext_t *param) +{ + ppt_prx_mode_t *base = ¶m->base; + ppt_ctx->ack = base->ack_mode; + ppt_ctx->oneshot = !base->continuous_mode; + ppt_set_prx_mode(base); +} + +bool ppt_set_psd_mode_ext(ppt_psd_mode_ext_t *param) +{ + ppt_psd_mode_t *base = ¶m->base; + if (base->chann_stop >= PSD_CHANN_NUM || base->chann_step >= PSD_CHANN_NUM) + { + return false; + } + if (base->chann_start > base->chann_stop) + { + return false; + } + else if (base->chann_start < base->chann_stop) + { + if (base->chann_step == 0) + { + return false; + } + uint8_t chann_num = base->chann_stop - base->chann_start + 1; + chann_num = (chann_num + base->chann_step - 1) / base->chann_step; + if (chann_num > modem_psd_get_entry_num()) + { + return false; + } + } + ppt_ctx->psd_mode = *base; + ppt_set_psd_mode(base); + return true; +} + +PPT_API_SECTION uint8_t *ppt_get_tx_buffer(uint16_t payload_len) +{ + if (payload_len > PPT_TX_BUFFER_SIZE) + { + APP_PRINT_ERROR1("ppt_get_tx_buffer: fail, payload size %d exceed limit", payload_len); + return NULL; + } + + return ppt_ctx->tx_buffer[0]; +} + +uint8_t *ppt_get_tx_buffer_by_entry(uint8_t entry, uint16_t payload_len) +{ + if (payload_len > PPT_TX_BUFFER_SIZE) + { + APP_PRINT_ERROR1("ppt_get_tx_buffer_entry: fail, payload size %d exceed limit", payload_len); + return NULL; + } + + if (entry >= PPT_TX_BUFFER_NUM) + { + APP_PRINT_ERROR1("ppt_get_tx_buffer_entry: fail, entry %d exceed limit", entry); + return NULL; + } + + return ppt_ctx->tx_buffer[entry]; +} + +PPT_API_SECTION uint8_t *ppt_get_rx_buffer(uint16_t pdu_len) +{ + if (pdu_len > PPT_RX_BUFFER_SIZE) + { + APP_PRINT_ERROR1("ppt_get_rx_buffer: fail, pdu size %d exceed limit", pdu_len); + return NULL; + } + + return ppt_ctx->rx_buffer; +} + +PPT_API_SECTION void ppt_push_tx_data(uint16_t len, uint8_t *data) +{ + uint8_t *tx_buffer = ppt_get_tx_buffer(len); + memcpy(tx_buffer, data, len); + ppt_push_tx_fifo(0, tx_buffer, len); +} + +void ppt_update_tx_data(uint16_t len, uint8_t *data) +{ + uint8_t *tx_buffer = ppt_get_tx_buffer(len); + memcpy(tx_buffer, data, len); + ppt_update_tx_fifo(0, tx_buffer, len); +} + +PPT_API_SECTION void ppt_push_tx_data_by_entry(uint8_t entry, uint16_t len, uint8_t *data) +{ + uint8_t *tx_buffer = ppt_get_tx_buffer_by_entry(entry, len); + memcpy(tx_buffer, data, len); + ppt_push_tx_fifo(entry, tx_buffer, len); +} + +PPT_API_SECTION uint8_t *ppt_pop_rx_data(uint16_t len) +{ + uint8_t *rx_buffer = ppt_get_rx_buffer(len); + ppt_pop_rx_fifo(0, rx_buffer, len); + return rx_buffer; +} + +PPT_API_SECTION uint8_t *ppt_pop_rx_data_by_entry(uint8_t entry, uint16_t len) +{ + uint8_t *rx_buffer = ppt_get_rx_buffer(len); + ppt_pop_rx_fifo(entry, rx_buffer, len); + return rx_buffer; +} + +void ppt_async_cb_template(void) +{ + /* make the async way easy to use */ +} + +void ppt_enable_ptx(void (*async_cb)(void)) +{ + if (ppt_ctx->fsm != PPT_FSM_STANDBY) + { + return; + } + + ppt_ctx->fsm = PPT_FSM_PTX; + ppt_ctx->retransmit_counter = ppt_ctx->retransmit_times; + ppt_ctx->async_cb = async_cb; + ppt_ctx->sync_flag = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); + if (ppt_ctx->async_cb == NULL) + { + while (ppt_ctx->sync_flag); + ppt_ctx->fsm = PPT_FSM_STANDBY; + } +} + +void ppt_disable_ptx(void (*async_cb)(void)) +{ + if (ppt_ctx->fsm != PPT_FSM_PTX) + { + return; + } + + ppt_ctx->async_cb = async_cb; + ppt_ctx->sync_flag = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_DISABLE); + if (ppt_ctx->async_cb == NULL) + { + while (ppt_ctx->sync_flag); + ppt_ctx->fsm = PPT_FSM_STANDBY; + } +} + +void ppt_enable_prx(void (*async_cb)(void)) +{ + if (ppt_ctx->fsm != PPT_FSM_STANDBY) + { + return; + } + + ppt_ctx->fsm = PPT_FSM_PRX; + ppt_ctx->async_cb = async_cb; + ppt_ctx->sync_flag = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_ENABLE); + if (ppt_ctx->async_cb == NULL) + { + while (ppt_ctx->sync_flag); + ppt_ctx->fsm = PPT_FSM_STANDBY; + } +} + +void ppt_disable_prx(void (*async_cb)(void)) +{ + if (ppt_ctx->fsm != PPT_FSM_PRX) + { + return; + } + + ppt_ctx->async_cb = async_cb; + ppt_ctx->sync_flag = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_DISABLE); + if (ppt_ctx->async_cb == NULL) + { + while (ppt_ctx->sync_flag); + ppt_ctx->fsm = PPT_FSM_STANDBY; + } +} + +PPT_API_SECTION void ppt_reset_hw_fsm(void) +{ + WR_PPT_REG_FIELD(PRO_RESET_CTRL_REG, fw_rst_trigger, 1); + ppt_clear_radio(); + PRO_MISR_TYPE reg_misr; + do + { + reg_misr.d16 = RD_PPT_REG(PRO_MISR); + } + while (reg_misr.reset_trig_int == 0); + WR_PPT_REG(PRO_MISR, BIT6); + /* workaround for dma error if reset when dma start */ + ppt_clear_dma_tx_done(); + /* trx interrupts may be already pended before reset */ + ppt_flush_trx_int(); + /* the hw rx fifo shall be cleared here after reset */ + ppt_flush_rx_fifo(); +} + +PPT_API_SECTION bool ppt_kill_hw_fsm(ppt_fsm_t fsm) +{ + if (fsm == PPT_FSM_PTX) + { + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_DISABLE); + ppt_clear_radio(); + PRO_MISR_TYPE reg_misr; + do + { + reg_misr.d16 = RD_PPT_REG(PRO_MISR); + } + while (reg_misr.kill_ptx_int == 0); + WR_PPT_REG(PRO_MISR, BIT4); + } + else if (fsm == PPT_FSM_PRX) + { + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_DISABLE); + ppt_clear_radio(); + PRO_MISR_TYPE reg_misr; + do + { + reg_misr.d16 = RD_PPT_REG(PRO_MISR); + } + while (reg_misr.kill_prx_int == 0); + WR_PPT_REG(PRO_MISR, BIT5); + } + else + { + return false; + } + + /* clear all trx interrupts */ + ppt_flush_trx_int(); + /* the hw rx fifo shall be cleared here after kill */ + ppt_flush_rx_fifo(); + return true; +} + +PPT_API_SECTION void ppt_psd_isr_handler_imp(void) +{ + for (uint8_t loop = 0; loop < modem_psd_get_entry_num(); loop++) + { + //DBG_DIRECT("psd: 0x%08x 0x%08x 0x%08x 0x%08x", g_modem_psd_report_array[loop].d32_array[0], + //g_modem_psd_report_array[loop].d32_array[1], g_modem_psd_report_array[loop].d32_array[2], + //g_modem_psd_report_array[loop].d32_array[3]); + if (g_modem_psd_report_array[loop].signature_bit == 0) + { + int16_t mp_gain, psd_db, base_tmp; + base_tmp = (get_phy_rssi0_dbm(modem_psd_get_rf_mode()) << 4); + uint8_t chann = ppt_ctx->psd_mode.chann_start + ppt_ctx->psd_mode.chann_step * loop; + mp_gain = g_modem_psd_report_array[loop].mp_gain_idx; + if (modem_psd_get_scan_mode() == 2) + { + psd_db = g_modem_psd_report_array[loop].psd_avg_neg; + psd_db = (psd_db >= 1024) ? ((psd_db - 2048)) : psd_db; + ppt_ctx->psd_result[chann] = (psd_db + base_tmp + (mp_gain << (1 + 4))) << 3; + if (chann <= PSD_CHANN_NUM - 2) + { + psd_db = g_modem_psd_report_array[loop].psd_avg_dc; + psd_db = (psd_db >= 1024) ? ((psd_db - 2048)) : psd_db; + ppt_ctx->psd_result[chann + 1] = (psd_db + base_tmp + (mp_gain << (1 + 4))) << 3; + } + if (chann <= PSD_CHANN_NUM - 3) + { + psd_db = g_modem_psd_report_array[loop].psd_avg_pos; + psd_db = (psd_db >= 1024) ? ((psd_db - 2048)) : psd_db; + ppt_ctx->psd_result[chann + 2] = (psd_db + base_tmp + (mp_gain << (1 + 4))) << 3; + } + //APP_PRINT_INFO4("ppt: psd chan %d, rssi %d %d %d", chann, ppt_ctx->psd_result[chann], ppt_ctx->psd_result[chann + 1], ppt_ctx->psd_result[chann + 2]); + } + else + { + psd_db = g_modem_psd_report_array[loop].psd_avg_dc; + psd_db = (psd_db >= 1024) ? ((psd_db - 2048)) : psd_db; + ppt_ctx->psd_result[chann] = (psd_db + base_tmp + (mp_gain << (1 + 4))) << 3; + //APP_PRINT_INFO2("ppt: psd chan %d, rssi %d %d %d",chann, ppt_ctx->psd_result[chann]); + } + } + else + { + break; + } + } + + RTK_WRITE_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x14), ppt_ctx->psd_tmp_flag); + + ppt_ctx->fsm = PPT_FSM_STANDBY; + APP_PRINT_INFO0("[ppt psd isr] set sync flag false here"); + ppt_ctx->sync_flag = false; + ppt_dlps_mac_idle = true; + if (ppt_ctx->async_cb) + { + ppt_ctx->async_cb(); + } +} +uint8_t test_array[4] = {0}; +uint32_t *test_ptr = test_array; +#include "trace.h" +void ppt_enable_psd(void (*async_cb)(void)) +{ + if (ppt_ctx->fsm != PPT_FSM_STANDBY) + { + return; + } + + /* save modem param */ + ppt_ctx->psd_tmp_flag = RTK_READ_MODEM_REG_PI(MODEM_PI_PAGE_0, TRANS_MODEM_REG(0x14)); + + for (uint8_t loop = 0; loop < modem_psd_get_entry_num(); loop++) + { + g_modem_psd_report_array[loop].signature_bit = 3; + }; + ppt_reg_psd_handler(ppt_psd_isr_handler_imp); + ppt_ctx->fsm = PPT_FSM_PSD; + ppt_ctx->sync_flag = true; + ppt_ctx->async_cb = async_cb; + ppt_dlps_mac_idle = false; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PSD_ENABLE); + if (ppt_ctx->async_cb == NULL) + { + while (ppt_ctx->sync_flag); + } +} + +void ppt_disable_psd(void (*async_cb)(void)) +{ + if (ppt_ctx->fsm != PPT_FSM_PSD) + { + return; + } + + ppt_ctx->async_cb = async_cb; + ppt_ctx->sync_flag = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PSD_DISABLE); + if (ppt_ctx->async_cb == NULL) + { + while (ppt_ctx->sync_flag); + ppt_ctx->fsm = PPT_FSM_STANDBY; + } +} + +void ppt_clear_psd_result(void) +{ + memset(ppt_ctx->psd_result, 0, sizeof(ppt_ctx->psd_result)); +} + +int16_t ppt_get_psd_result(uint8_t chann) +{ + if (chann >= PSD_CHANN_NUM) + { + return 0x7fff; + } + + return ppt_ctx->psd_result[chann] / 128; +} + + void ppt_init_sw(void) +{ + for (uint8_t loop = 0; loop < PPT_TX_BUFFER_NUM; loop++) + { + if (ppt_ctx->tx_buffer[loop] == NULL) + { + ppt_ctx->tx_buffer[loop] = os_mem_alloc(RAM_TYPE_BUFFER_ON, PPT_TX_BUFFER_SIZE); + } + } + + if (ppt_ctx->rx_buffer == NULL) + { + ppt_ctx->rx_buffer = os_mem_alloc(RAM_TYPE_BUFFER_ON, PPT_RX_BUFFER_SIZE); + } +} + +static void ppt_deinit_sw(void) +{ + for (uint8_t loop = 0; loop < PPT_TX_BUFFER_NUM; loop++) + { + if (ppt_ctx->tx_buffer[loop]) + { + os_mem_free(ppt_ctx->tx_buffer[loop]); + } + } + + if (ppt_ctx->rx_buffer) + { + os_mem_free(ppt_ctx->rx_buffer); + } + memset(ppt_ctx, 0, sizeof(ppt_ctx_t)); +} + +static void ppt_init_hw(void) +{ + uint16_t pro_reg; + WR_PPT_REG(PRO_IMR, 0xFFFF); + + pro_reg = RD_PPT_REG(PRO_PACKET_CONTROL); + ((PRO_PACKET_CONTROL_TYPE *)(&pro_reg))->crc_kept = 0; + WR_PPT_REG(PRO_PACKET_CONTROL, pro_reg); + + pro_reg = RD_PPT_REG(PRO_RX_CONTROL); + ((PRO_RX_CONTROL_TYPE *)(&pro_reg))->max_length_kept = 0; + WR_PPT_REG(PRO_RX_CONTROL, pro_reg); + + pro_reg = RD_PPT_REG(PRO_MAX_LENGTH); + ((PRO_MAX_LENGTH_TYPE *)(&pro_reg))->max_length = PPT_TX_BUFFER_SIZE; + WR_PPT_REG(PRO_MAX_LENGTH, pro_reg); + + for (uint8_t entry = 0; entry < PPT_ENTRY_NUM; entry++) + { + ppt_set_auto_no_tx(entry, false); + ppt_set_auto_ack(entry, false); + } + + ppt_reset_trx_pkt_num(); + + pro_reg = RD_PPT_REG(PRO_TX_TURN_AROUND_DELAY); + ((PRO_TX_TURN_AROUND_DELAY_TYPE *)(&pro_reg))->pro_txon_early_1m = 0x8; + WR_PPT_REG(PRO_TX_TURN_AROUND_DELAY, pro_reg); + + PRO_TRX_ON_DELAY_TYPE trx_on_delay = {.d16 = RD_PPT_REG(PRO_TRX_ON_DELAY)}; + trx_on_delay.pro_txon_delay = 58; + trx_on_delay.pro_rxon_delay = 58; + WR_PPT_REG(PRO_TRX_ON_DELAY, trx_on_delay.d16); + + pro_reg = RD_PPT_REG(PRO_RX_TIMEOUT_DELAY); + ((PRO_RX_TIMEOUT_DELAY_TYPE *)(&pro_reg))->pro_txon_early_2m = 0x2; + WR_PPT_REG(PRO_RX_TIMEOUT_DELAY, pro_reg); + + PRO_2M_TRX_ON_DELAY_TYPE trx_on_delay_2m = {.d16 = RD_PPT_REG(PRO_2M_TRX_ON_DELAY)}; + trx_on_delay_2m.pro_txon_delay_2m = 58; + trx_on_delay_2m.pro_rxon_delay_2m = 58; + WR_PPT_REG(PRO_2M_TRX_ON_DELAY, trx_on_delay_2m.d16); + + WR_PPT_REG(PRO_IMR, 0); +} + +void ppt_init(void) +{ + ppt_init_sw(); + ppt_notify_btmac(true); + ppt_reset_core_domain(true); +#if 0 + bt_retention_domain_reset(); + phy_init(false); +#endif +#if TIME_DEBUG + ppt_init_time_debug(); +#endif + ppt_init_hw(); + ppt_init_dma(); + ppt_init_misc(); +} + +void ppt_deinit(void) +{ + ppt_reset_core_domain(false); + ppt_deinit_misc(); + ppt_deinit_dma(); + ppt_notify_btmac(false); + + ppt_deinit_sw(); +} diff --git a/bee/drivers/ppt/sync/src/driver/simple/ppt_simple.h b/bee/drivers/ppt/sync/src/driver/simple/ppt_simple.h new file mode 100644 index 00000000..358557b8 --- /dev/null +++ b/bee/drivers/ppt/sync/src/driver/simple/ppt_simple.h @@ -0,0 +1,330 @@ +/** +***************************************************************************************** +* Copyright(c) 2021, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_simple.h + * @brief Head file for simple wrapper of 2.4G module common driver. + * @details data structs and external functions declaration. + * @author bill + * @date 2021-11-26 + * @version v0.1 + * ************************************************************************************* + */ + +/* Define to prevent recursive inclusion */ +#ifndef _PPT_SIMPLE_H_ +#define _PPT_SIMPLE_H_ + +/* Add Includes here */ +#include +#include +#include "ppt_driver.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup PPT_Simple + * @{ + */ + +/** @defgroup PPT_Simple_Exported_Macros Exported Macros + * @brief + * @{ + */ + +/* payload length */ +#ifndef PPT_TX_BUFFER_NUM +#define PPT_TX_BUFFER_NUM 2 +#endif +#define PPT_TX_BUFFER_SIZE 255 +/* header + payload length */ +#define PPT_RX_BUFFER_SIZE (PPT_TX_BUFFER_SIZE + 8) + +/** @} End of PPT_Simple_Exported_Macros */ + +/** @defgroup PPT_Simple_Exported_Types Exported Types + * @brief + * @{ + */ + +typedef struct +{ + ppt_ptx_mode_t base; + uint16_t retransmit_times; +} ppt_ptx_mode_ext_t; + +typedef struct +{ + ppt_prx_mode_t base; +} ppt_prx_mode_ext_t; + +typedef struct +{ + ppt_psd_mode_t base; +} ppt_psd_mode_ext_t; + +typedef struct +{ + volatile ppt_fsm_t fsm; + bool ack; + bool oneshot; + uint16_t retransmit_times; + volatile uint16_t retransmit_counter; + uint8_t *tx_buffer[PPT_TX_BUFFER_NUM]; + uint8_t *rx_buffer; + volatile bool sync_flag; + void (*async_cb)(void); + struct + { + ppt_psd_mode_t psd_mode; + int16_t psd_result[PSD_CHANN_NUM]; + uint16_t psd_tmp_flag; + }; +} ppt_ctx_t; + +extern ppt_ctx_t *ppt_ctx; +/** @} End of PPT_Simple_Exported_Types */ + +/** @defgroup PPT_Simple_Exported_Functions Exported Functions + * @brief + * @{ + */ + +/** + * @brief initialize the radio + * @return none + */ +void ppt_init(void); + +/** + * @brief deinitialize the radio + * @return none + */ +void ppt_deinit(void); + +/** + * @brief set the ptx modes + * @param[in] param: parameters + * @return none + */ +void ppt_set_ptx_mode_ext(ppt_ptx_mode_ext_t *param); + +/** + * @brief set the prx modes + * @param[in] param: parameters + * @return none + */ +void ppt_set_prx_mode_ext(ppt_prx_mode_ext_t *param); + +/** + * @brief set the psd modes + * @param[in] param: parameters + * @return parameters set result + */ +bool ppt_set_psd_mode_ext(ppt_psd_mode_ext_t *param); + +/** + * @brief get tx buffer used by DMA for sending data + * + * The RAM type used by DMA is limited to a specific RAM type. + * This driver manages the memory for easy to use at the application. + * @param[in] payload_len: length of payload + * @return buffer address + */ +uint8_t *ppt_get_tx_buffer(uint16_t payload_len); + +/** + * @brief get tx buffer used by DMA for sending data + * + * The RAM type used by DMA is limited to a specific RAM type. + * This driver manages the memory for easy to use at the application. + * @param[in] entry: channel index + * @param[in] payload_len: length of payload + * @return buffer address + */ +uint8_t *ppt_get_tx_buffer_by_entry(uint8_t entry, uint16_t payload_len); + +/** + * @brief get rx buffer used by DMA for receiving data + * + * The RAM type used by DMA is limited to a specific RAM type. + * This driver manages the memory for easy to use at the application. + * @param[in] pdu_len: length of PDU + * @return buffer address + */ +uint8_t *ppt_get_rx_buffer(uint16_t pdu_len); + +/** + * @brief copy and push the data to the hw fifo + * + * The data will be copied, so the RAM type of data can be any type. + * @param[in] len: length of data + * @param[in] data: data to send + * @return none + */ +void ppt_push_tx_data(uint16_t len, uint8_t *data); + +/** + * @brief copy and update the data to the hw fifo + * + * The last pushed data can be updated by this function. + * @param[in] len: length of data + * @param[in] data: data to send + * @return none + */ +void ppt_update_tx_data(uint16_t len, uint8_t *data); + +/** + * @brief copy and push the data to the hw fifo of the specific channel + * + * The data will be copied, so the RAM type of data can be any type. + * @param[in] entry: channel index + * @param[in] len: length of data + * @param[in] data: data to send + * @return none + */ +void ppt_push_tx_data_by_entry(uint8_t entry, uint16_t len, uint8_t *data); + +/** + * @brief pop and copy the data from the hw fifo + * + * The data will be copied to the buffer RAM, its pointer will be returned. + * @param[in] len: length of data + * @return received data pointer + */ +uint8_t *ppt_pop_rx_data(uint16_t len); + +/** + * @brief pop and copy the data from the hw fifo of the specific channel + * + * The data will be copied to the buffer RAM, its pointer will be returned. + * @param[in] entry: channel index + * @param[in] len: length of data + * @return received data pointer + */ +uint8_t *ppt_pop_rx_data_by_entry(uint8_t entry, uint16_t len); + +/** + * @brief async callback template + * @return none + */ +void ppt_async_cb_template(void); + +/** + * @brief enable ptx state + * + * If the callback is null, then the api returns after the procedure is done in a sync way. + * Otherwise, the caller will be notified via the callback when the procedure is done in an async way. + * @param[in] async_cb: the callback function pointer + * @return none + */ +void ppt_enable_ptx(void (*async_cb)(void)); + +/** + * @brief disable ptx state + * + * If the callback is null, then the api returns after the procedure is done in a sync way. + * Otherwise, the caller will be notified via the callback when the procedure is done in an async way. + * @param[in] async_cb: the callback function pointer + * @return none + */ +void ppt_disable_ptx(void (*async_cb)(void)); + +/** + * @brief enable prx state + * + * If the callback is null, then the api returns after the procedure is done in a sync way. + * Otherwise, the caller will be notified via the callback when the procedure is done in an async way. + * @param[in] async_cb: the callback function pointer + * @return none + */ +void ppt_enable_prx(void (*async_cb)(void)); + +/** + * @brief disable prx state + * + * If the callback is null, then the api returns after the procedure is done in a sync way. + * Otherwise, the caller will be notified via the callback when the procedure is done in an async way. + * @param[in] async_cb: the callback function pointer + * @return none + */ +void ppt_disable_prx(void (*async_cb)(void)); + +/** + * @brief reset the finite state machine of the hardware module + * + * The reset feature shall be enabled previously by fw_rst_enable flag in @ref PRO_RESET_CTRL_REG. + * This API will generate mac interrupt, so it shall be called in critical section if at lower priority. + * All the internal states will be reset except for the interface registers in ppt_hw_reg.h. + * @return none + */ +void ppt_reset_hw_fsm(void); + +/** + * @brief kill the finite state machine of the hardware module + * + * This API will generate mac interrupt, so it shall be called in critical section if at lower priority. + * @return the result + */ +bool ppt_kill_hw_fsm(ppt_fsm_t fsm); + +/** + * @brief enable psd procedure + * + * If the callback is null, then the api returns after the procedure is done in a sync way. + * Otherwise, the caller will be notified via the callback when the procedure is done in an async way. + * @param[in] async_cb: the callback function pointer + * @return none + * + * Example usage + * \code{.c} + ppt_psd_mode_ext_t param = + { + { + .chann_start = 0, + .chann_stop = 0, + .chann_step = 1, + .mode = 0, + .timeout = PSD_TIMEOUT_DEFAULT + } + }; + ppt_set_psd_mode_ext(¶m); + ppt_enable_psd(NULL); + int16_t rssi = ppt_get_psd_result(0); + * \endcode + */ +void ppt_enable_psd(void (*async_cb)(void)); + +/** + * @brief disable psd procedure + * + * If the callback is null, then the api returns after the procedure is done in a sync way. + * Otherwise, the caller will be notified via the callback when the procedure is done in an async way. + * @param[in] async_cb: the callback function pointer + * @return none + */ +void ppt_disable_psd(void (*async_cb)(void)); + +/** + * @brief get the stored psd result + * @param[in] chann: the channel index, shall be less than @ref PSD_CHANN_NUM + * @return dbm value + */ +int16_t ppt_get_psd_result(uint8_t chann); + +/** + * @brief clear the stored psd result + * @return none + */ +void ppt_clear_psd_result(void); + +/** @} End of PPT_Simple_Exported_Functions */ + +/** @} End of PPT_Simple */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bee/drivers/ppt/sync/src/sync/inc/ppt_sync.h b/bee/drivers/ppt/sync/src/sync/inc/ppt_sync.h new file mode 100644 index 00000000..a11aace0 --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/inc/ppt_sync.h @@ -0,0 +1,585 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_sync.h + * @brief Head file for sync protocol. + * @details data structs and external functions declaration. + * @author bill + * @date 2022-03-15 + * @version v0.1 + * ************************************************************************************* + */ + +/* Define to prevent recursive inclusion */ +#ifndef _PPT_SYNC_H_ +#define _PPT_SYNC_H_ + +/* Add Includes here */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup PPT_SYNC_Driver + * @{ + */ + +/** @defgroup PPT_SYNC_Driver_Exported_Macros Exported Macros + * @brief + * @{ + */ +#define SYNC_OPCODE_APP_START_VALUE 0x10 + +#define SYNC_MSG_RETRANS_NUM_INFINITE 0xff +#define SYNC_SYNC_FAST_PERIOD_DISABLE 0xffffffff + +/** @} End of PPT_SYNC_Driver_Exported_Macros */ + +/** @defgroup PPT_SYNC_Driver_Exported_Types Exported Types + * @brief + * @{ + */ + +/** error code */ +typedef enum +{ + SYNC_ERR_CODE_SUCCESS, + SYNC_ERR_CODE_UNKNOWN, + SYNC_ERR_CODE_INVALID_LENGTH, + SYNC_ERR_CODE_INVALID_PARAM, + SYNC_ERR_CODE_INVALID_STATE, + SYNC_ERR_CODE_NULL_POINTER, + SYNC_ERR_CODE_FULL_QUEUE, + SYNC_ERR_CODE_EMPTY_QUEUE, + SYNC_ERR_CODE_NO_MEM, + SYNC_ERR_CODE_NOT_FOUND +} sync_err_code_t; + +#pragma pack(1) + +/** access address */ +typedef struct +{ + uint32_t addr; +} sync_acc_t; + +/** bond information */ +typedef struct +{ + sync_acc_t acc; +} sync_bond_info_t; + +#pragma pack() + +/** role definition */ +typedef enum +{ + SYNC_ROLE_UNKNOWN, + SYNC_ROLE_MASTER, + SYNC_ROLE_SLAVE +} sync_role_t; + +/** connect state */ +typedef enum +{ + SYNC_STATE_IDLE, + SYNC_STATE_SYNC_SLOW, + SYNC_STATE_SYNC, + SYNC_STATE_SYNC_FAST +} sync_state_t; + +/** pair state */ +typedef enum +{ + SYNC_PAIR_STATE_IDLE, + SYNC_PAIR_STATE_PAIRING, + SYNC_PAIR_STATE_PAIRED, +} sync_pair_state_t; + +/** events triggered by the sync protocol */ +typedef enum +{ + SYNC_EVENT_PAIRED, + SYNC_EVENT_PAIR_TIMEOUT, + SYNC_EVENT_CONNECTED, + SYNC_EVENT_CONNECT_TIMEOUT, + SYNC_EVENT_CONNECT_LOST, +#ifdef SYNC_DEBUG + SYNC_EVENT_SYNC_LOST, + SYNC_EVENT_SYNC_RECONNECTED +#endif +} sync_event_t; + +/** Message types with difference performances */ +typedef enum +{ + /** The message will be sent only once regardless of the ack. */ + SYNC_MSG_TYPE_ONESHOT, + /** + * The message will be sent finite times even it is not acked. + * The retransmission will be stopped right after it is acked. + * Refer to @ref sync_msg_set_finite_retrans, it sets the finite retransmission times. + */ + SYNC_MSG_TYPE_FINITE_RETRANS, + /** + * The message will be sent infinite times until it is acked. + * The retransmission will be stopped right after it is acked. + */ + SYNC_MSG_TYPE_INFINITE_RETRANS, + /** + * Depending whether there are other messages pended, the message will be sent variable times if it is not acked. + * The retransmission will be stopped right after it is acked. + * Refer to @ref sync_msg_set_dynamic_retrans, it sets the lower and upper limitation of the retransmission times. + * If not acked, the message will be sent at least the lower limitation times and at most the upper limitation times. + */ + SYNC_MSG_TYPE_DYNAMIC_RETRANS, + SYNC_MSG_TYPE_NUM, + SYNC_MSG_TYPE_ALL = SYNC_MSG_TYPE_NUM +} sync_msg_type_t; + +/** Message transmit result */ +typedef enum +{ + /** The transmitted message is received by the peer device. */ + SYNC_SEND_RESULT_ACKED, + /** The transmitted message isn't received by the peer device. */ + SYNC_SEND_RESULT_NACKED, + /** Don't known whether the transmitted message is received. */ + SYNC_SEND_RESULT_UNKNOWN, + /** The transmitted message is flushed by myself. */ + SYNC_SEND_RESULT_FLUSHED +} sync_send_result_t; + +/** Message transmit general information */ +typedef struct +{ + sync_send_result_t res; + /** retransmit counter */ + uint8_t retrans_count; +} sync_send_info_t; + +/** Message receive general information */ +typedef struct +{ + int8_t rssi; +} sync_receive_info_t; + +/** Time related parameters used by the sync protocol */ +typedef enum +{ + /** Synchronization Interval, unit is us */ + SYNC_TIME_PARAM_CONNECT_INTERVAL, + /** + * Retransmit Interval, unit is us + * If it is equal to Synchronization Interval, there will be no chance to retransmit during the sync interval. + * Retransmit Interval shall be a divisor of Synchronization Interval. + * For example 1K report rate, Synchronization Interval = 1000us and Retransmit Interval = 250us, + * then there will be 3 times chance to retransmit in the sync interval. + */ + SYNC_TIME_PARAM_CONNECT_INTERVAL_HIGH, + /** Synchronization lost state life cycle, unit is us */ + SYNC_TIME_PARAM_SYNC_LOST_PERIOD, + /** Synchronization fast state life cycle, unit is us, use value @ref SYNC_SYNC_FAST_PERIOD_DISABLE can remove synchronization fast state */ + SYNC_TIME_PARAM_SYNC_FAST_PERIOD, + /** heartbeat timeout times used to control heartbeat exit sensitivity */ + SYNC_TIME_PARAM_HB_TO_TIMES, + /** channel assess times used by the slave to evaluate the channel quality */ + SYNC_TIME_PARAM_CHANNEL_ASSESS_TIMES, + /** slave hopping speed at sync low state, unit is us */ + SYNC_TIME_PARAM_SLAVE_HOPPING_SPEED +} sync_time_param_t; + +/** feature value definition */ +typedef enum +{ + SYNC_FEATURE_USE_DEFAULT, + SYNC_FEATURE_ENABLE, + SYNC_FEATURE_DISABLE +} sync_feature_t; + +/** + * @brief sync stack event notify callback + * @param[in] event: event type + * @return none + */ +typedef void (*sync_event_cb_t)(sync_event_t event); + +/** + * @brief sync stack message send result callback + * @param[in] type: msg type + * @param[in] data: the original data + * @param[in] len: the original length + * @param[in] result: the send result + * @return none + */ +typedef void (*sync_msg_send_cb_t)(sync_msg_type_t type, uint8_t *data, uint16_t len, + sync_send_info_t *info); + +/** + * @brief sync stack message receive callback + * @param[in] data: the data + * @param[in] len: the length + * @param[in] info: the receive information pointer + * @return none + */ +typedef void (*sync_msg_receive_cb_t)(uint8_t *data, uint16_t len, sync_receive_info_t *info); + +/** heartbeat callback parameters */ +typedef struct +{ + bool enter; + uint32_t interval; //!< unit of us +} sync_hb_cb_param_t; + +/** + * @brief heartbeat callback function pointer type + * @param[in] param: heartbeat callback parameters pointer + * @return none + */ +typedef void (*sync_hb_cb_t)(sync_hb_cb_param_t *param); + +/** + * @brief sync stack uses this callback to request real time environment + * + * When the stack requires real time environment at special scenarios, the app shall try their best to provide. + * For example, avoid using critical section, avoid entering DLPS, and increase cpu clock etc. + * @param[in] required: true means request real time environment + * @return none + */ +typedef void (*sync_rte_req_cb_t)(bool required); + +/** @} End of PPT_SYNC_Driver_Exported_Types */ + +/** @defgroup PPT_SYNC_Driver_Exported_Functions Exported Functions + * @brief + * @{ + */ + +/** + * @brief initialize the protocol + * + * The protocol will initiate some default settings. + * After initialization, the app can set some parameters before @ref sync_enable to the value they like. + * @param[in] role: the protocol role + * @return none + */ +void sync_init(sync_role_t role); + +/** + * @brief set the crc parameters + * + * The protocol will initiate CRC default settings. + * After initialization, the app can use this api to overwrite the default settings. + * @param[in] crc_len: crc bit length + * @param[in] crc_poly: crc poly + * @param[in] crc_init: crc init + * @return none + * Example usage + * \code{.c} + // set crc8 + sync_crc_set(8, 0x07, 0xff); + // set crc16 + sync_crc_set(16, 0x8005, 0xffff); + // set crc24 + sync_crc_set(24, 0x00065b, 0x555555); + // set crc32 + sync_crc_set(32, 0x04c11db7, 0xffffffff); + * \endcode + */ +void sync_crc_set(uint8_t crc_len, uint32_t crc_poly, uint32_t crc_init); + +/** + * @brief set timing related parameters + * @param[in] type: the parameter type + * @param[in] time: the time value, the unit is usually us depend on the type + * @return set result + */ +bool sync_time_set(sync_time_param_t type, uint32_t time); + +/** + * @brief get timing related parameters + * @param[in] type: the parameter type + * @param[out] time: the time value, the unit is usually us depend on the type + * @return get result + */ +bool sync_time_get(sync_time_param_t type, uint32_t *time); + +/** + * @brief set rf channels + * @param[in] chan_num: the number of rf channels + * @param[in] group_num: the number of groups + * @param[in] freq: the frequency array + * @return set result + */ +bool sync_channel_set(uint8_t chan_num, uint8_t group_num, uint16_t freq[]); + +/** + * @brief set rf tx power + * + * If dynamic is true, the tx power will be changed automatically at the range according to the link quality. + * If dynamic is false, the tx_power_dbm_max will be used all the time. + * @param[in] dynamic: whether to change automatically at run time + * @param[in] tx_power_dbm_max: maximum tx power + * @param[in] tx_power_dbm_min: minimum tx power + * @return set result + */ +bool sync_tx_power_set(bool dynamic, int8_t tx_power_dbm_max, int8_t tx_power_dbm_min); + +/** + * @brief turn on/off sync log + * @param[in] level: the log level + * @param[in] on: true: on, false: off + * @return set result + */ +bool sync_log_set(uint8_t level, bool on); + +/** + * @brief set rssi threshold when pairing + * + * Only the requesting device with rssi value equal or bigger than the threshold will be accepted. + * @param[in] rssi: the rssi level + * @return set result + */ +bool sync_pair_rssi_set(int8_t rssi); + +/** + * @brief set time of inter-frame space (tifs) + * + * The tifs used at different process or sync interval may be different. + * There are 2 levels used: + * Level 0 is used for sync slow process and sync interval >= 500us. + * Level 1 is used for sync interval < 500us. + * @param[in] level: level index + * @param[in] tifs: time of inter-frame space + * @return set result + */ +bool sync_tifs_set(uint8_t level, uint8_t tifs); + +/** + * @brief check whether the access address is valid + * + * The access address is used by the hardware to match the right packet with the same access address. + * The hardware has some limitations on the value of the access address. + * Usually it is randomly generated and checked until it is valid. + * @param[in] acc: the access address value + * @return 0: valid, other: invalid + */ +int sync_acc_check(sync_acc_t acc); + +/** + * @brief set the access address of the broadcast channel used by the pairing process + * + * Invoke this api will overwrite the default value. + * The user should check whether the new value is valid by call @ref sync_acc_check. + * @param[in] acc_br: the access address value + * @return none + */ +void sync_acc_set_br(sync_acc_t acc_br); + +/** + * @brief enable the protocol + * + * It shall be called after @ref sync_init to enable the protocol. + * @return none + */ +void sync_enable(void); + +/** + * @brief register event callback function + * + * The event @ref sync_event_t include the pairing and connection result. + * @param[in] cb: event callback + * @return none + */ +void sync_event_cb_reg(sync_event_cb_t cb); + +/** + * @brief get bond information from flash + * + * After pairing, the bond information wil be stored to the flash automatically. + * The bond information is used for connection. + * @return operation result + * @retval SYNC_ERR_CODE_SUCCESS: bond information get successfully + * @retval SYNC_ERR_CODE_NOT_FOUND: bond information doesn't exist + */ +sync_err_code_t sync_nvm_get_bond_info(sync_bond_info_t *bond_info); + +/** + * @brief set bond information to flash + * + * The app may want to add bond information manually by this api. + * @return operation result + * @retval SYNC_ERR_CODE_SUCCESS: bond information set successfully + * @retval SYNC_ERR_CODE_UNKNOWN: bond information storage fail + */ +sync_err_code_t sync_nvm_set_bond_info(sync_bond_info_t *bond_info); + +/** + * @brief clear bond information in flash + * + * The app can clear the bond information to restore unpaired state. + * @return operation result + * @retval SYNC_ERR_CODE_SUCCESS: bond information set successfully + * @retval SYNC_ERR_CODE_UNKNOWN: bond information clear fail + */ +sync_err_code_t sync_nvm_clear_bond_info(void); + +/** + * @brief start pairing + * + * After pairing, two new devices is connected and bonded. + * The pairing result is notified at the event callback registered by @ref sync_event_cb_reg. + * @return operation result + */ +sync_err_code_t sync_pair(void); + +/** + * @brief start connect + * + * If two bonded devices are disconnected, call this api to connect each other. + * The connection result is notified at the event callback registered by @ref sync_event_cb_reg. + * @param[in] bond_info: bond information used to create a connection + * @return operation result + */ +sync_err_code_t sync_connect(sync_bond_info_t *bond_info); + +/** + * @brief stop the connection or the pairing procedure + * + * If the device is connecting, pairing or connected, call this api to stop. + * @return none + */ +void sync_stop(void); + +/** + * @brief deinitiate the protocol to release all resources + * + * If the device is running, the api @ref sync_stop shall be called before this. + * @return none + */ +void sync_deinit(void); + +/** + * @brief initialize the sync protocol dlps setting + * @return none + */ +void sync_dlps_init(void); + +/** + * @brief set message queue size of each type + * + * Different message types have different queue size. + * @param[in] msg_quota: queue size + * @return none + */ +void sync_msg_set_quota(uint8_t msg_quota[SYNC_MSG_TYPE_NUM]); + +/** + * @brief set message queue size of the specific type + * + * Different message types have different queue size. + * @param[in] type: the message type + * @param[in] quota: the message type + * @return set result + */ +bool sync_msg_set_quota_by_type(sync_msg_type_t type, uint8_t quota); + +/** + * @brief set message retranmsit times for @ref SYNC_MSG_TYPE_FINITE_RETRANS type msg + * @param[in] msg_retrans_num: retranmsit times + * @return none + */ +void sync_msg_set_finite_retrans(uint8_t msg_retrans_num); + +/** + * @brief set message retranmsit times for @ref SYNC_MSG_TYPE_DYNAMIC_RETRANS type msg + * @param[in] msg_retrans_num_lower: retranmsit times lower limit + * @param[in] msg_retrans_num_upper: retranmsit times upper limit + * @return none + */ +void sync_msg_set_dynamic_retrans(uint8_t msg_retrans_num_lower, uint8_t msg_retrans_num_upper); + +/** + * @brief register message receive callback function + * @param[in] cb: callback function + * @return none + */ +void sync_msg_reg_receive_cb(sync_msg_receive_cb_t cb); + +/** + * @brief send message + * + * The first byte of data buffer shall contain the opcode starting from SYNC_OPCODE_APP_START_VALUE. + * @param[in] type: msg type, different types have different retransmit behavior + * @param[in] data: the data buffer + * @param[in] len: the data length + * @param[in] send_cb: the send complete callback + * @return send result + * @retval SYNC_ERR_CODE_SUCCESS: the msg is successfully pushed into the queue to send + */ +sync_err_code_t sync_msg_send(sync_msg_type_t type, uint8_t *data, uint16_t len, + sync_msg_send_cb_t send_cb); + +/** + * @brief get the free number of the message buffer with the specific type + * @param[in] type: the msg type + * @return the free messages number + */ +uint8_t sync_msg_get_available_number(sync_msg_type_t type); + +/** + * @brief flush the msg sending queue + * + * Each message type can be flushed independently. + * @param[in] type: the msg type + * @return the flushed messages number + */ +uint8_t sync_msg_flush(sync_msg_type_t type); + +/** + * @brief reset the message tx queue + * + * It is safe to be called when the device is disconnected and at the app task context. + * @return none + */ +void sync_msg_reset(void); + +/** + * @brief enter critical section + * @return interrupt mask + */ +uint32_t sync_enter_critical(void); + +/** + * @brief exit critical section + * @param[in] flag: interrupt mask + * @return none + */ +void sync_exit_critical(uint32_t flag); + +/** + * @brief register heartbeat callback + * @param[in] cb: function pointer + * @return none + */ +void sync_hb_reg_cb(sync_hb_cb_t cb); + +/** + * @brief register heartbeat callback + * @param[in] cb: function pointer + * @return none + */ +void sync_rte_req_reg_cb(sync_rte_req_cb_t cb); + +/** @} End of PPT_SYNC_Driver_Exported_Functions */ + +/** @} End of PPT_SYNC_Driver */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bee/drivers/ppt/sync/src/sync/inc/ppt_sync_master.h b/bee/drivers/ppt/sync/src/sync/inc/ppt_sync_master.h new file mode 100644 index 00000000..3ac382d7 --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/inc/ppt_sync_master.h @@ -0,0 +1,184 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_sync_master.h + * @brief Head file for sync master. + * @details data structs and external functions declaration. + * @author bill + * @date 2022-03-15 + * @version v0.1 + * ************************************************************************************* + */ + +/* Define to prevent recursive inclusion */ +#ifndef _PPT_SYNC_MASTER_H_ +#define _PPT_SYNC_MASTER_H_ + +/* Add Includes here */ +#include +#include +#include "ppt_sync.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup PPT_SYNC_MASTER_Driver + * @{ + */ + +/** @defgroup PPT_SYNC_MASTER_Driver_Exported_Macros Exported Macros + * @brief + * @{ + */ + +#define SYNC_MASTER_HB_RHY_PERIOD_MAX 0xffffffff + +/** @} End of PPT_SYNC_MASTER_Driver_Exported_Macros */ + +/** @defgroup PPT_SYNC_MASTER_Driver_Exported_Types Exported Types + * @brief + * @{ + */ + +/** tx scheme details */ +typedef struct +{ + /** the sync interval will be aligned to the app tx interval if enabled */ + sync_feature_t tx_align; + /** the stack will use high priority to tx if enabled */ + sync_feature_t early_high_prio; + /** the stack will retransmit the pkt when attain the ack information if enabled */ + sync_feature_t retrans_at_ack; + /** the stack will transmit a new pkt if available at the retransmit interval if enabled */ + sync_feature_t tx_new_at_retrans_int; +} sync_master_tx_scheme_t; + +/** tx prepare callback function pointer type */ +typedef void (*sync_tx_prepare_cb_t)(void); + +/** @} End of PPT_SYNC_MASTER_Driver_Exported_Types */ + +/** @defgroup PPT_SYNC_MASTER_Driver_Exported_Functions Exported Functions + * @brief + * @{ + */ + +/** + * @brief initialize the protocol + * + * The protocol will initiate some default settings. + * After initialization, the app can set some parameters before @ref sync_master_enable to the value they like. + * @return none + */ +void sync_master_init(void); + +/** + * @brief set heartbeat parameters + * + * The heartbeat supports multi-levels. At each level, there are two parameters, including interval and beat times. + * The radio will beat in a certain interval by the designed times. + * Except the maximum level, the heartbeat will enter next level when it beats over the limit times. + * For level 0, the interval is the connection interval, so the interval parameter is useless. + * For level Max(2), the heartbeat will beat all the time, so the beat_times parameter is useless. + * @param[in] level: the heartbeat level, value range from 0 to MAX + * @param[in] interval: the heartbeat interval in unit of microsecond + * @param[in] beat_times: the heartbeat repeat times. + * @return the result + * Example usage + * \code{.c} + // sample: disable heartbeat + sync_master_set_hb_param(0, 0, SYNC_MASTER_HB_RHY_PERIOD_MAX); + + // sample: enable heartbeat + // set heartbeat level 0 to sync interval * 40 + sync_master_set_hb_param(0, 0, 40); + // set heartbeat level 1 to 10ms * 200 + sync_master_set_hb_param(1, 10000, 200); + // set heartbeat level 2 to 100ms + sync_master_set_hb_param(2, 100000, 0); + * \endcode + */ +bool sync_master_set_hb_param(uint8_t level, uint32_t interval, uint32_t beat_times); + +/** + * @brief wakeup heartbeat right now + * @return true if wakeup by this call, else false + */ +bool sync_master_wakeup_hb(void); + +/** + * @brief set tx scheme + * + * Refer to @sync_master_tx_scheme_t for the different tx scheme. + * @param[in] tx_scheme: the tx scheme + * @return none + */ +void sync_master_set_tx_scheme(sync_master_tx_scheme_t tx_scheme); + +/** + * @brief register tx prepare callback which will be called at each interval + * @param[in] cb: the function pointer + * @return none + */ +void sync_master_reg_tx_prepare_cb(sync_tx_prepare_cb_t cb); + +/** + * @brief enable the protocol + * + * It shall be called after @ref sync_master_init to enable the protocol. + * @return none + */ +void sync_master_enable(void); + +/** + * @brief start pairing + * + * After pairing, the master will connect the slave and bonded together. + * The pairing result is notified at the event callback registered by @ref sync_event_cb_reg. + * @return operation result + */ +sync_err_code_t sync_master_pair(void); + +/** + * @brief start connect + * + * If two bonded devices are disconnected, call this api to connect each other. + * The connection result is notified at the event callback registered by @ref sync_event_cb_reg. + * @param[in] bond_info: bond information used to create a connection + * @return operation result + */ +sync_err_code_t sync_master_connect(sync_bond_info_t *bond_info); + +/** + * @brief stop the connection or the pairing procedure + * + * If the device is connecting, pairing or connected, call this api to stop. + * @return none + */ +void sync_master_stop(void); + +/** + * @brief deinitiate the protocol to release all resources + * + * If the device is running, the api @ref sync_master_stop shall be called before this. + * @return none + */ +void sync_master_deinit(void); + +/** + * @brief initialize the sync protocol dlps setting + * @return none + */ +void sync_master_dlps_init(void); + +/** @} End of PPT_SYNC_MASTER_Driver_Exported_Functions */ + +/** @} End of PPT_SYNC_MASTER_Driver */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bee/drivers/ppt/sync/src/sync/inc/ppt_sync_slave.h b/bee/drivers/ppt/sync/src/sync/inc/ppt_sync_slave.h new file mode 100644 index 00000000..339974f5 --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/inc/ppt_sync_slave.h @@ -0,0 +1,116 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_sync_slave.h + * @brief Head file for sync slave. + * @details data structs and external functions declaration. + * @author bill + * @date 2022-03-15 + * @version v0.1 + * ************************************************************************************* + */ + +/* Define to prevent recursive inclusion */ +#ifndef _PPT_SYNC_SLAVE_H_ +#define _PPT_SYNC_SLAVE_H_ + +/* Add Includes here */ +#include +#include +#include "ppt_sync.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup PPT_SYNC_SLAVE_Driver + * @{ + */ + +/** @defgroup PPT_SYNC_SLAVE_Driver_Exported_Macros Exported Macros + * @brief + * @{ + */ + + +/** @} End of PPT_SYNC_SLAVE_Driver_Exported_Macros */ + +/** @defgroup PPT_SYNC_SLAVE_Driver_Exported_Types Exported Types + * @brief + * @{ + */ + +/** @} End of PPT_SYNC_SLAVE_Driver_Exported_Types */ + +/** @defgroup PPT_SYNC_SLAVE_Driver_Exported_Functions Exported Functions + * @brief + * @{ + */ + +/** + * @brief initialize the protocol + * + * The protocol will initiate some default settings. + * After initialization, the app can set some parameters before @ref sync_slave_enable to the value they like. + * @return none + */ +void sync_slave_init(void); + +/** + * @brief enable the protocol + * + * It shall be called after @ref sync_slave_init to enable the protocol. + * @return none + */ +void sync_slave_enable(void); +/** + * @brief start pairing + * + * After pairing, the slave will connect the master and bonded together. + * The pairing result is notified at the event callback registered by @ref sync_event_cb_reg. + * @return operation result + */ +sync_err_code_t sync_slave_pair(void); + +/** + * @brief start connect + * + * If two bonded devices are disconnected, call this api to connect each other. + * The connection result is notified at the event callback registered by @ref sync_event_cb_reg. + * @param[in] bond_info: bond information used to create a connection + * @return operation result + */ +sync_err_code_t sync_slave_connect(sync_bond_info_t *bond_info); + +/** + * @brief stop the connection or the pairing procedure + * + * If the device is connecting, pairing or connected, call this api to stop. + * @return none + */ +void sync_slave_stop(void); + +/** + * @brief deinitiate the protocol to release all resources + * + * If the device is running, the api @ref sync_slave_stop shall be called before this. + * @return none + */ +void sync_slave_deinit(void); + +/** + * @brief initialize the sync protocol dlps setting + * @return none + */ +void sync_slave_dlps_init(void); + +/** @} End of PPT_SYNC_SLAVE_Driver_Exported_Functions */ + +/** @} End of PPT_SYNC_SLAVE_Driver */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bee/drivers/ppt/sync/src/sync/src/platform_list.c b/bee/drivers/ppt/sync/src/sync/src/platform_list.c new file mode 100644 index 00000000..e14709ef --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/src/platform_list.c @@ -0,0 +1,105 @@ +/** +***************************************************************************************** +* Copyright(c) 2015, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file platform_list.c + * @brief Source file for link list structures. + * @details Data types and external functions declaration. + * @author bill + * @date 2017-3-3 + * @version v1.0 + * ************************************************************************************* + */ + +/* Add Includes here */ +#include "platform_types.h" +#include "platform_list.h" +//#include "app_section.h" +#define RAM_FUNCTION + +RAM_FUNCTION void plt_list_push(plt_list_t *plist, void *plist_e) +{ + plt_list_e_t *pelement = (plt_list_e_t *)plist_e; + pelement->pnext = NULL; + if (plist->count == 0) + { + plist->pfirst = pelement; + } + else + { + plist->plast->pnext = pelement; + } + plist->plast = pelement; + plist->count++; +} + +RAM_FUNCTION void *plt_list_pop(plt_list_t *plist) +{ + plt_list_e_t *pelement = NULL; + if (plist->count > 0) + { + if (plist->count == 1) + { + plist->plast = NULL; + } + plist->count--; + pelement = plist->pfirst; + plist->pfirst = pelement->pnext; + } + return pelement; +} + +void plt_list_insert(plt_list_t *plist, void *plist_e, void *plist_e_new) +{ + plt_list_e_t *pelement = (plt_list_e_t *)plist_e; + plt_list_e_t *pelement_new = (plt_list_e_t *)plist_e_new; + + if (pelement == NULL) + { + pelement_new->pnext = plist->pfirst; + plist->pfirst = pelement_new; + if (plist->count == 0) + { + plist->plast = pelement_new; + } + } + else + { + pelement_new->pnext = pelement->pnext; + pelement->pnext = pelement_new; + if (pelement_new->pnext == NULL) + { + plist->plast = pelement_new; + } + } + plist->count++; +} + +void plt_list_delete(plt_list_t *plist, void *plist_e_previous, void *plist_e) +{ + plt_list_e_t *pelement = (plt_list_e_t *)plist_e; + if (plist_e_previous == NULL)//(plist->pfirst == pelement) + { + plist->pfirst = pelement->pnext; + if (pelement->pnext == NULL) + { + plist->plast = NULL; + } + } + else + { + /* + plt_list_e_t * pprevious = plist->pfirst; + while(pprevious->pnext != pelement) + pprevious = pprevious->pnext; + */ + plt_list_e_t *pprevious = (plt_list_e_t *)plist_e_previous; + pprevious->pnext = pelement->pnext; + if (pelement->pnext == NULL) + { + plist->plast = pprevious; + } + } + plist->count--; +} + diff --git a/bee/drivers/ppt/sync/src/sync/src/platform_list.h b/bee/drivers/ppt/sync/src/sync/src/platform_list.h new file mode 100644 index 00000000..0a9d2e6a --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/src/platform_list.h @@ -0,0 +1,90 @@ +/** +***************************************************************************************** +* Copyright(c) 2015, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file platform_list.h + * @brief Head file for list structure related. + * @details Data types and external functions declaration. + * @author bill + * @date 2017-9-28 + * @version v1.0 + * ************************************************************************************* + */ + +/* Define to prevent recursive inclusion */ +#ifndef _PLATFORM_LIST_ +#define _PLATFORM_LIST_ + +#include "platform_types.h" + +BEGIN_DECLS + +/** @addtogroup Platform_List + * @{ + */ + +/** @defgroup Platform_List_Exported_Types Exported Types + * @{ + */ + +/** @brief general element type of link list */ +typedef struct _plt_list_e_t +{ + struct _plt_list_e_t *pnext; + uint8_t data[4]; +} plt_list_e_t; + +/** @brief general list type of link list */ +typedef struct +{ + plt_list_e_t *pfirst; + plt_list_e_t *plast; + uint32_t count; +} plt_list_t; + +/** @} */ + +/** @defgroup Platform_List_Exported_Functions Exported Functions + * @brief + * @{ + */ + +/** + * @brief push in the element to the end of the list + * @param[in] plist: the list pointer + * @param[in] plist_e: the element pointer + * @return none + */ +void plt_list_push(plt_list_t *plist, void *plist_e); + +/** + * @brief pop out the first element of the list + * @param[in] plist: the list pointer + * @return the list element + */ +void *plt_list_pop(plt_list_t *plist); + +/** + * @brief insert the element to any position of the list + * @param[in] plist: the list pointer + * @param[in] plist_e: the ahead element pointer + * @param[in] plist_e_new: the new element pointer + * @return none + */ +void plt_list_insert(plt_list_t *plist, void *plist_e, void *plist_e_new); + +/** + * @brief delete the element at any position of the list + * @param[in] plist: the list pointer + * @param[in] plist_e_previous: the ahead element pointer + * @param[in] plist_e: the deleted element pointer + * @return none + */ +void plt_list_delete(plt_list_t *plist, void *plist_e_previous, void *plist_e); + +/** @} */ +/** @} */ + +END_DECLS + +#endif /* _PLATFORM_LIST_ */ diff --git a/bee/drivers/ppt/sync/src/sync/src/platform_macros.h b/bee/drivers/ppt/sync/src/sync/src/platform_macros.h new file mode 100644 index 00000000..10315259 --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/src/platform_macros.h @@ -0,0 +1,165 @@ +/** +***************************************************************************************** +* Copyright(c) 2015, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file platform_macros.h + * @brief Head file for platform macros. + * @details Compile, mathematic etc related + * @author bill + * @date 2017-3-3 + * @version v1.0 + * ************************************************************************************* + */ + +#ifndef _PLATFORM_MACROS_H_ +#define _PLATFORM_MACROS_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Platform_Macros + * @{ + */ + +/** @defgroup Platform_Macros_Exported_Macros Exported Macros + * @{ + */ + +#ifndef _PACKED_ +#define _PACKED_ __attribute__ ((packed)) +#endif + +#ifndef _SHORT_ENUM_ +#define _SHORT_ENUM_ __attribute__ ((packed)) +#endif + +#ifndef __INLINE +#define __INLINE __inline +#endif + +#undef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#undef MIN +#define MIN(a, b) ((a) > (b) ? (b) : (a)) + +#undef ABS +#define ABS(a) ((a < 0) ? -(a) : (a)) + +#undef CLAMP +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +#undef MEMBER_OFFSET +#define MEMBER_OFFSET(struct_type, member) ((uint32_t)&((struct_type *)0)->member) + +#undef CONTAINER_OF +#define CONTAINER_OF(member_ptr, struct_type, member) \ + ({ \ + (struct_type *)((char *)member_ptr - MEMBER_OFFSET(struct_type, member)); \ + }) + +#undef ARRAY_LEN +#define ARRAY_LEN(arr) (sizeof(arr) / sizeof((arr)[0])) + +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + +#define BIT(_n) (uint32_t)(1U << (_n)) + +#define BYTE0(data) ((uint8_t)data) +#define BYTE1(data) ((uint8_t)(data >> 8)) +#define BYTE2(data) ((uint8_t)(data >> 16)) +#define BYTE3(data) ((uint8_t)(data >> 24)) +#define BYTE(data, n) ((uint8_t)(data >> 8*n)) +#define BYTES2(data) BYTE0(data), BYTE1(data) +#define BYTES4(data) BYTE0(data), BYTE1(data), BYTE2(data), BYTE3(data) + +/* Get local WORD from external 2 BYTE, Big-Endian format STANDARD NETWORK BYTE ORDER */ +#define BE_EXTRN2WORD(p) ((*((p)+1)) & 0xff) + ((*(p)) << 8) + +/* Get local DWORD from external 4 BYTE, Big-Endian format STANDARD NETWORK BYTE ORDER */ +#define BE_EXTRN2DWORD(p) ((unsigned long)(*((p)+3)) & 0xff) + ((unsigned long)(*((p)+2)) << 8) \ + + ((unsigned long)(*((p)+1)) << 16) + ((unsigned long)(*((p)+0)) << 24) + +/* PUT external 2 CHARS from local SHORT, Big-Endian format STANDARD NETWORK BYTE ORDER */ +#define BE_WORD2EXTRN(p,w) \ + {*((p)+1) = (unsigned char)((w) & 0xff); \ + *(p) = /*lint -e(572,778)*/ (unsigned char)(((w)>>8) & 0xff);} + +/* PUT external 4 BYTE from local DWORD, Big-Endian format STANDARD NETWORK BYTE ORDER */ +#define BE_DWORD2EXTRN(p,w) \ + {*((p)+3) = (unsigned char)((w) & 0xff); \ + *((p)+2) = /*lint -e(572,778)*/ (unsigned char)(((w)>>8) & 0xff); \ + *((p)+1) = /*lint -e(572,778)*/ (unsigned char)(((w)>>16) & 0xff); \ + *((p)+0) = /*lint -e(572,778)*/ (unsigned char)(((w)>>24) & 0xff);} + +/* Get local WORD from external 2 BYTE, Little-Endian format */ +#define LE_EXTRN2WORD(p) (((*(p)) & 0xff) + ((*((p)+1)) << 8)) + +/* Get local DWORD from external 4 BYTE, Little-Endian format */ +#define LE_EXTRN2DWORD(p) (((unsigned long)(*((p)+0)) & 0xff) + ((unsigned long)(*((p)+1)) << 8) \ + + ((unsigned long)(*((p)+2)) << 16) + ((unsigned long)(*((p)+3)) << 24)) + +/* PUT external 2 BYTE from local WORD, Little-Endian Format */ +#define LE_WORD2EXTRN(p,w) \ + {*((unsigned char*)p) = (unsigned char)((unsigned short)(w) & 0xff); \ + *(((unsigned char*)p)+1) = /*lint -e(572,778)*/ (unsigned char)(((unsigned short)(w)>>8) & 0xff);} + +/* PUT external 4 BYTE from local DWORD, Little endian Format */ +#define LE_DWORD2EXTRN(p,w) \ + {*((unsigned char*)p) = (unsigned char)((unsigned long)(w) & 0xff); \ + *(((unsigned char*)p)+1) = (unsigned char)(((unsigned long)(w)>>8) & 0xff); \ + *(((unsigned char*)p)+2) = (unsigned char)(((unsigned long)(w)>>16) & 0xff); \ + *(((unsigned char*)p)+3) = (unsigned char)(((unsigned long)(w)>>24) & 0xff);} + +#ifdef __cplusplus +#define BEGIN_DECLS extern "C" { +#define END_DECLS } +#else +#define BEGIN_DECLS +#define END_DECLS +#endif + +#ifndef UNUSED +#define UNUSED(x) ((void)(x)) +#endif + +/** @} */ +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _PLATFORM_MACROS_H_ */ diff --git a/bee/drivers/ppt/sync/src/sync/src/platform_types.h b/bee/drivers/ppt/sync/src/sync/src/platform_types.h new file mode 100644 index 00000000..74f2a80d --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/src/platform_types.h @@ -0,0 +1,55 @@ +/** +***************************************************************************************** +* Copyright(c) 2015, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file platform_types.h + * @brief Head file for platform types. + * @details Basic types related. + * @author bill + * @date 2017-3-3 + * @version v1.0 + * ************************************************************************************* + */ + +#ifndef _PLATFORM_TYPES_H_ +#define _PLATFORM_TYPES_H_ + +#include +#include +#include +#include "platform_macros.h" + +BEGIN_DECLS + +/** @addtogroup Platform_Types + * @{ + */ + +/** @defgroup Platform_Types_Exported_Macros Exported Macros + * @brief + * @{ + */ + +/* boolean definition */ +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#ifndef NULL +#ifdef __cplusplus +#define NULL (0L) +#else +#define NULL ((void *)0) +#endif +#endif + +/** @} */ +/** @} */ + +END_DECLS + +#endif /* _PLATFORM_TYPES_H_ */ diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync.c b/bee/drivers/ppt/sync/src/sync/src/ppt_sync.c new file mode 100644 index 00000000..352ea974 --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/src/ppt_sync.c @@ -0,0 +1,1643 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_sync.c + * @brief Source file for sync protocol. + * @details + * @author bill + * @date 2022-03-15 + * @version v0.1 + * ************************************************************************************* + */ + +/* Add Includes here */ +#include +#include "ppt_simple.h" +#include "ppt_sync_int.h" +#include "ppt_sync_master.h" +#include "ppt_sync_slave.h" +//#include "trace.h" +#include "os_mem.h" +#include "os_sync.h" +#include "rtl876x.h" +#include "rtl_rcc.h" +#include "rtl_nvic.h" +#include "rtl_enh_tim.h" +#include +#include +#include "vector_table.h" + +LOG_MODULE_REGISTER(ppt_sync, PPT_SYNC_LOG_LEVEL); + +#define SYNC_VERSION "v1.17.1" +#define SYNC_FTL_OFFSET 0 +#define SYNC_HW_TIMER ENH_TIM1 +#define SYNC_HW_TIMER_IRQN Enhanced_Timer1_IRQn +#define SYNC_HW_TIMER_VECTORN Enhanced_Timer1_VECTORn //Enhanced_Timer1_Handler +#define SYNC_HW_TIMER1 ENH_TIM2 +#define SYNC_HW_TIMER1_IRQN Enhanced_Timer2_IRQn +#define SYNC_HW_TIMER1_VECTORN Enhanced_Timer2_VECTORn +#define SYNC_HW_TIMER_IP NVIC_GetPriority(BTMAC_IRQn) +#define SYNC_HW_TIMER_CLOCK 40 +#define SYNC_HW_TIMER_SHIFT_COMPENSATE 2 +#define SYNC_HW_TIMER_SHIFT_TOLERANCE 5 +#define SYNC_HW_FRTIMER ENH_TIM0 +#define SYNC_HW_FRTIMER_IRQN Enhanced_Timer0_IRQn +#define SYNC_HW_FRTIMER_VECTORN Enhanced_Timer0_VECTORn + +#define SYNC_TIME_DEBUG_TIMER1 -1 + +sync_ctx_t sync_ctx = +{ + .conn_interval = SYNC_SYNC_SPEED_DV, + .conn_interval_high = SYNC_SYNC_SPEED_HIGH_DV, + .acc_br.addr = SYNC_ACC_ADDR_BR_DV, + .tifs = {SYNC_TIFS0_DV, SYNC_TIFS1_DV}, +}; + +typedef struct +{ + sync_bond_info_t bond_info; +} sync_nvm_bond_info_t; + +typedef struct +{ + sync_nvm_bond_info_t nvm_bond_info; +} sync_nvm_t; + +#define FAIL_MSG "fail (err %d)\n" +#define SYNC_NVM_BOND_INFO_F "pptsync/bondinfo" + +struct direct_immediate_value { + size_t len; + void *dest; + uint8_t fetched; +}; + +static int sync_direct_loader_immediate_value(const char *name, size_t len, + settings_read_cb read_cb, void *cb_arg, + void *param) +{ + const char *next; + size_t name_len; + int rc; + struct direct_immediate_value *one_value = + (struct direct_immediate_value *)param; + + name_len = settings_name_next(name, &next); + + if (name_len == 0) { + if (len == one_value->len) { + rc = read_cb(cb_arg, one_value->dest, len); + if (rc >= 0) { + one_value->fetched = 1; + //printk("immediate load: OK.\n"); + return 0; + } + + printk(FAIL_MSG, rc); + return rc; + } + return -EINVAL; + } + + /* other keys aren't served by the callback + * Return success in order to skip them + * and keep storage processing. + */ + return 0; +} + +int sync_load_immediate_value(const char *name, void *dest, size_t len) +{ + int rc; + struct direct_immediate_value dov; + + dov.fetched = 0; + dov.len = len; + dov.dest = dest; + + rc = settings_load_subtree_direct(name, sync_direct_loader_immediate_value, + (void *)&dov); + if (rc == 0) { + if (!dov.fetched) { + rc = -ENOENT; + } + } + + return rc; +} + +sync_err_code_t sync_nvm_get_bond_info(sync_bond_info_t *bond_info) +{ + sync_nvm_bond_info_t nvm_bond_info; + if (0 != sync_load_immediate_value(SYNC_NVM_BOND_INFO_F, &nvm_bond_info, sizeof(sync_nvm_bond_info_t))) + { + return SYNC_ERR_CODE_NOT_FOUND; + } + + *bond_info = nvm_bond_info.bond_info; + return SYNC_ERR_CODE_SUCCESS; +} + +sync_err_code_t sync_nvm_set_bond_info(sync_bond_info_t *bond_info) +{ + sync_nvm_bond_info_t nvm_bond_info = {}; + nvm_bond_info.bond_info = *bond_info; + if (0 != settings_save_one(SYNC_NVM_BOND_INFO_F, &nvm_bond_info, sizeof(sync_nvm_bond_info_t))) + { + return SYNC_ERR_CODE_UNKNOWN; + } + return SYNC_ERR_CODE_SUCCESS; +} + +sync_err_code_t sync_nvm_clear_bond_info(void) +{ + if(0 != settings_delete(SYNC_NVM_BOND_INFO_F)) + { + return SYNC_ERR_CODE_UNKNOWN; + } + + return SYNC_ERR_CODE_SUCCESS; +} + +void sync_ppt_init(void) +{ + ppt_init(); + + { + ppt_phy_param_t param = + { + .bank = 0, + .channel = 0, + .rx_phy = PPT_PHY_TYPE_BLE_2M + }; + ppt_set_phy_param(¶m); + } + + { + ppt_phy_entry_param_t param = + { + .preamble_len = 1, + .tx_phy = PPT_PHY_TYPE_BLE_2M + }; + ppt_set_phy_entry_param(0, ¶m); + } + + { + ppt_white_entry_param_t param = + { + .len = 7, + .value = {0x91, 0x53} + }; + ppt_set_white_entry_param(0, ¶m); + } + + { + ppt_pkt_format_t param = + { + .addr_len = 4, + .hp_len = SYNC_HEADER_LEN, + .length_len = SYNC_LENGTH_LEN, + .hs_len = 0, + .header_order = PPT_FIELD_BIT_ORDER_LSB, + .payload_order = PPT_FIELD_BIT_ORDER_LSB + }; + ppt_set_pkt_format(¶m); + } + + { + ppt_crc_param_t param = + { + .len = SYNC_CRC_LEN >> 3, + .include_addr = false + }; + ppt_set_crc_param(¶m); + } + + { + ppt_crc_entry_param_t param = + { + .value = {SYNC_CRC_POLY, SYNC_CRC_INIT} + }; + ppt_set_crc_entry_param(0, ¶m); + } + + PRO_RESET_CTRL_REG_TYPE reset_ctrl = {.d16 = RD_PPT_REG(PRO_RESET_CTRL_REG)}; + reset_ctrl.fw_rst_enable = 1; + WR_PPT_REG(PRO_RESET_CTRL_REG, reset_ctrl.d16); + + /* enable length check */ + WR_PPT_REG_FIELD(PRO_RX_MATCH_CONTROL, use_maxlength_compare, 1); + WR_PPT_REG_FIELD(PRO_MAX_LENGTH, max_length, SYNC_MSG_MAX_LEN); + + /* set rf time parameters */ + sync_ppt_set_rf_time(); + +#if SYNC_SUPPORT_MT1R + { + ppt_timing_latch_t param = + { + .tx_on = true, + .acc_hit = true + }; + ppt_set_timing_latch(¶m); + } +#endif +} + +void sync_ppt_set_tifs(bool init) +{ + uint8_t sync_tifs = 0; + if (sync_ctx.tifs[0] != sync_ctx.tifs[1]) + { + sync_tifs = init ? sync_ctx.tifs[0] : (SYNC_SYNC_SPEED >= 500 ? sync_ctx.tifs[0] : + sync_ctx.tifs[1]); + } + else + { + sync_tifs = sync_ctx.tifs[0]; + } + + ppt_set_tifs(sync_tifs); +} + +void sync_ppt_set_rf_time(void) +{ + PRO_2M_TRX_ON_DELAY_TYPE trx_on_delay = {.d16 = RD_PPT_REG(PRO_2M_TRX_ON_DELAY)}; + trx_on_delay.pro_txon_delay_2m = 54; + trx_on_delay.pro_rxon_delay_2m = 40; + WR_PPT_REG(PRO_2M_TRX_ON_DELAY, trx_on_delay.d16); + PRO_RX_TIMEOUT_DELAY_TYPE rx_to = {.d16 = RD_PPT_REG(PRO_RX_TIMEOUT_DELAY)}; + rx_to.pro_txon_early_2m = 1; + rx_to.pro_rx_timeout_delay_us = 55; + WR_PPT_REG(PRO_RX_TIMEOUT_DELAY, rx_to.d16); +} + +#if SYNC_SUPPORT_MT1R +void sync_ppt_set_rf_time_mt1r(void) +{ + PRO_2M_TRX_ON_DELAY_TYPE trx_on_delay = {.d16 = RD_PPT_REG(PRO_2M_TRX_ON_DELAY)}; + trx_on_delay.pro_rxon_delay_2m = 40; + trx_on_delay.pro_txon_delay_2m = 40; + WR_PPT_REG(PRO_2M_TRX_ON_DELAY, trx_on_delay.d16); + + PRO_RX_TIMEOUT_DELAY_TYPE rx_to = {.d16 = RD_PPT_REG(PRO_RX_TIMEOUT_DELAY)}; + rx_to.pro_txon_early_2m = 0; + rx_to.pro_rx_timeout_delay_us = 90; + WR_PPT_REG(PRO_RX_TIMEOUT_DELAY, rx_to.d16); +} +#endif + +bool sync_channel_set(uint8_t chan_num, uint8_t group_num, uint16_t freq[]) +{ + if (chan_num % group_num != 0) + { + return false; + } + sync_ctx.chan_num = chan_num; + sync_ctx.chan_group_num = group_num; + if (sync_ctx.chan_cfg) + { + os_mem_free(sync_ctx.chan_cfg); + } + sync_ctx.chan_cfg = (sync_channel_cfg_t *)os_mem_alloc(RAM_TYPE_DATA_ON, + sizeof(sync_channel_cfg_t) * sync_ctx.chan_num); + if (sync_ctx.chan_cfg == NULL) + { + return false; + } + for (uint8_t loop = 0; loop < sync_ctx.chan_num; loop++) + { + sync_ctx.chan_cfg[loop].freq = freq[loop]; + ppt_get_bank_channel(freq[loop], &sync_ctx.chan_cfg[loop].bank, + &sync_ctx.chan_cfg[loop].channel); + } + sync_ctx.chan_cur = 0; + return true; +} + +void sync_channel_change(uint8_t idx) +{ + if (idx >= sync_ctx.chan_num) + { + APP_PRINT_ERROR2("sync_channel_change: fail set to %d, total %d", idx, sync_ctx.chan_num); + return; + } + uint8_t old_idx = sync_ctx.chan_cur; + sync_ctx.chan_cur = idx; + PRO_CHANNEL_BANK_TYPE ch_bank; + ch_bank.bank = sync_ctx.chan_cfg[idx].bank; + ch_bank.pro_channel = sync_ctx.chan_cfg[idx].channel; + WR_PPT_REG(PRO_CHANNEL_BANK, ch_bank.d16); + +#if SYNC_FRAME_BLE_COMPATIBLE + uint32_t white_init = ppt_get_ble_white_init(ppt_get_ble_logic_chan_via_freq( + sync_ctx.chan_cfg[idx].freq)); +#else + uint32_t white_init = ppt_get_ble_white_init(idx); +#endif + WR_PPT_REG(PRO_WHITE_INIT0_LOWER, white_init & 0xffff); + WR_PPT_REG(PRO_WHITE_INIT0_UPPER, (white_init >> 16) & 0xffff); + sync_ctx.chan_used_count = 0; + sync_ctx.chan_miss_count = 0; + +#ifdef SYNC_DEBUG + sync_ctx.chan_cfg[idx].choose_times += 1; + if (sync_ctx.chan_cfg[old_idx].fail_burst_count) + { + if (sync_ctx.chan_cfg[old_idx].fail_burst_count > sync_ctx.chan_cfg[old_idx].fail_burst_max) + { + sync_ctx.chan_cfg[old_idx].fail_burst_max = sync_ctx.chan_cfg[old_idx].fail_burst_count; + } + } + sync_ctx.chan_cfg[old_idx].fail_burst_count = 0; +#endif +} + +void sync_channel_change_next(void) +{ + sync_channel_change((sync_ctx.chan_cur + 1) % sync_ctx.chan_num); +} + +void sync_channel_change_next_in_group(void) +{ + uint8_t chan_min, chan_max, chan_num, chan_next; + chan_num = sync_ctx.chan_num / sync_ctx.chan_group_num; + chan_min = (sync_ctx.chan_cur / chan_num) * chan_num; + chan_max = chan_min + chan_num - 1; + chan_next = sync_ctx.chan_cur >= chan_max ? chan_min : sync_ctx.chan_cur + 1; + sync_channel_change(chan_next); +} + +#ifdef SYNC_DEBUG +void sync_channel_statistic(bool ok) +{ + uint8_t idx = sync_ctx.chan_cur; + if (sync_ctx.chan_cfg[idx].choose_times == 0) + { + sync_ctx.chan_cfg[idx].choose_times += 1; + } + sync_ctx.chan_cfg[idx].used_count += 1; + if (ok == false) + { + sync_ctx.chan_cfg[idx].fail_count += 1; + sync_ctx.chan_cfg[idx].fail_burst_count += 1; + } + else + { + if (sync_ctx.chan_cfg[idx].fail_burst_count) + { + if (sync_ctx.chan_cfg[idx].fail_burst_count > sync_ctx.chan_cfg[idx].fail_burst_max) + { + sync_ctx.chan_cfg[idx].fail_burst_max = sync_ctx.chan_cfg[idx].fail_burst_count; + } + } + sync_ctx.chan_cfg[idx].fail_burst_count = 0; + } +} + +void sync_channel_clear_statistic(void) +{ + for (uint8_t idx = 0; idx < sync_ctx.chan_num; idx++) + { + sync_ctx.chan_cfg[idx].choose_times = 0; + sync_ctx.chan_cfg[idx].used_count = 0; + sync_ctx.chan_cfg[idx].fail_count = 0; + sync_ctx.chan_cfg[idx].fail_burst_count = 0; + sync_ctx.chan_cfg[idx].fail_burst_max = 0; + } +} + +void sync_channel_print_statistic(void) +{ + for (uint8_t idx = 0; idx < sync_ctx.chan_num; idx++) + { + APP_PRINT_INFO5("sync: channel statistic %d, choose %d, used %d, miss %d, burst max %d", idx, + sync_ctx.chan_cfg[idx].choose_times, + sync_ctx.chan_cfg[idx].used_count, sync_ctx.chan_cfg[idx].fail_count, + sync_ctx.chan_cfg[idx].fail_burst_max); + } +} +#endif + +void sync_acc_set(sync_acc_t acc) +{ + { + ppt_tx_addr_t param; + LE_DWORD2EXTRN(param.tx_addr, acc.addr); + ppt_set_tx_addr(0, ¶m); + } + + { + ppt_rx_addr_t param = + { + .enable = true + }; + LE_DWORD2EXTRN(param.rx_addr, acc.addr); + ppt_set_rx_addr(0, ¶m); + } + +#if SYNC_FRAME_BLE_COMPATIBLE == 0 + uint8_t rx_addr[4]; + LE_DWORD2EXTRN(rx_addr, acc.addr); + ppt_crc_eng_param_t crc_param = + { + .poly = SYNC_CRC_POLY, + .init = SYNC_CRC_INIT, + .length = SYNC_CRC_LEN, + .bit_mode = 0, + .input_endian = 0, + .output_endian = 0 + }; + uint32_t init = ppt_crc_eng_cal(&crc_param, rx_addr, 4); + { + ppt_crc_entry_param_t param = + { + .value = {SYNC_CRC_POLY, SYNC_CRC_INIT} + }; + param.value.init = init; + ppt_set_crc_entry_param(0, ¶m); + } +#endif +} + +int sync_acc_check(sync_acc_t acc) +{ + uint32_t acc_addr = acc.addr; + uint8_t count = 0; + uint8_t transition = 0; + bool last_bit = false; + + uint8_t addr0 = acc_addr & 0xff; + if (addr0 == 0x55 || addr0 == 0xaa) + { + return __LINE__; + } + + uint8_t addr1 = (acc_addr >> 8) & 0xff; + if (addr1 == 0x55 || addr1 == 0xaa) + { + return __LINE__; + } + + uint8_t loop = 1; + for (; loop < 4; loop++) + { + uint8_t value = (acc_addr >> (8 * loop)) & 0xff; + if (value != (acc_addr & 0xff)) + { + break; + } + } + if (loop >= 4) + { + return __LINE__; + } + + for (loop = 0; loop < 32; loop++) + { + bool curr_bit = acc_addr & BIT(31 - loop); + if (loop != 0) + { + if (curr_bit != last_bit) + { + transition += 1; + count = 0; + } + else + { + count += 1; + if (count >= 6) + { + return __LINE__; + } + } + + if (loop == 5 && transition < 2) + { + return __LINE__; + } + } + last_bit = curr_bit; + } + + if (transition > 24) + { + return __LINE__; + } + + return 0; +} + +sync_acc_t sync_acc_gen_data(void) +{ + sync_acc_t acc; +#if SYNC_FRAME_BLE_COMPATIBLE + acc.addr = SYNC_ACC_ADDR_BR + 1 + return acc; +#else + while (1) + { + acc.addr = sync_rand(); + if (acc.addr != SYNC_ACC_ADDR_BR && 0 == sync_acc_check(acc)) + { + APP_PRINT_INFO1("sync: acc gen 0x%08x", acc.addr); + return acc; + } + } +#endif +} + +void sync_acc_set_br(sync_acc_t acc_br) +{ + sync_ctx.acc_br = acc_br; + APP_PRINT_INFO2("sync: acc set br 0x%08x (%d)", acc_br.addr, sync_acc_check(acc_br)); +} + +void sync_hw_timer_handler(void); +void sync_hw_timer_init(void) +{ + RCC_PeriphClockCmd(APBPeriph_ENHTIMER, APBPeriph_ENHTIMER_CLOCK, ENABLE); + + ENHTIM_InitTypeDef TIM_InitStruct; + ENHTIM_StructInit(&TIM_InitStruct); + TIM_InitStruct.ENHTIM_Mode = ENHTIM_MODE_PWM_MANUAL; + ENHTIM_Init(SYNC_HW_TIMER, &TIM_InitStruct); + + NVIC_InitTypeDef NVIC_InitStruct; + NVIC_InitStruct.NVIC_IRQChannel = SYNC_HW_TIMER_IRQN; + NVIC_InitStruct.NVIC_IRQChannelPriority = SYNC_HW_TIMER_IP; + NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStruct); + RamVectorTableUpdate(SYNC_HW_TIMER_VECTORN, sync_hw_timer_handler); + + ENHTIM_ClearINTPendingBit(SYNC_HW_TIMER, ENHTIM_INT_TIM); + ENHTIM_INTConfig(SYNC_HW_TIMER, ENHTIM_INT_TIM, ENABLE); +} + +static PPT_ISR_SECTION void sync_hw_timer_disable(void) +{ + uint32_t lock = sync_enter_critical(); + ENHTIM_Cmd(SYNC_HW_TIMER, DISABLE); + ENHTIM_ClearINTPendingBit(SYNC_HW_TIMER, ENHTIM_INT_TIM); + NVIC_ClearPendingIRQ(SYNC_HW_TIMER_IRQN); + sync_exit_critical(lock); + sync_ctx.hw_timer_periodic = false; + sync_hw_timer_impr_ip(false); +} + +PPT_ISR_SECTION void sync_hw_timer_start(uint32_t period_us, sync_hw_timer_cb_t cb) +{ + sync_hw_timer_disable(); + sync_ctx.hw_timer_cb = cb; +#if SYNC_SUPPORT_MT1R + sync_ctx.hw_timer_period = period_us; + sync_ctx.hw_timer_shift_state = SYNC_HW_TIMER_SHIFT_STATE_IDLE; +#endif + SYNC_HW_TIMER->ENHTIM_MAX_CNT = period_us * SYNC_HW_TIMER_CLOCK; + ENHTIM_Cmd(SYNC_HW_TIMER, ENABLE); + time_debug_level_low(TIME_DEBUG_CHAN_ISR_GPIO); + time_debug_level_high(TIME_DEBUG_CHAN_ISR_GPIO); +} + +PPT_ISR_SECTION void sync_hw_timer_restart(void) +{ + time_debug_level_low(TIME_DEBUG_CHAN_ISR_GPIO); + time_debug_level_high(TIME_DEBUG_CHAN_ISR_GPIO); + sync_hw_timer_disable(); + ENHTIM_Cmd(SYNC_HW_TIMER, ENABLE); +} + +PPT_ISR_SECTION void sync_hw_timer_stop(void) +{ + time_debug_level_low(TIME_DEBUG_CHAN_ISR_GPIO); + sync_hw_timer_disable(); + sync_ctx.hw_timer_cb = NULL; +#if SYNC_SUPPORT_MT1R + sync_ctx.hw_timer_shift_state = SYNC_HW_TIMER_SHIFT_STATE_IDLE; +#endif +} + +PPT_ISR_SECTION uint32_t sync_hw_timer_get_left(void) +{ + uint32_t curr = SYNC_HW_TIMER->ENHTIM_CUR_CNT / SYNC_HW_TIMER_CLOCK; + return curr; +} + +PPT_ISR_SECTION bool sync_hw_timer_check_pending(void) +{ + return NVIC_GetPendingIRQ(SYNC_HW_TIMER_IRQN); +} + +PPT_ISR_SECTION void sync_hw_timer_set_periodic(bool enable) +{ + sync_ctx.hw_timer_periodic = enable; +} + +PPT_ISR_SECTION void sync_hw_timer_impr_ip(bool enable) +{ + NVIC_SetPriority(SYNC_HW_TIMER_IRQN, SYNC_HW_TIMER_IP - enable); +} + +PPT_ISR_SECTION void sync_hw_timer_handler(void) +{ + time_debug_level_low(TIME_DEBUG_CHAN_ISR_GPIO); + time_debug_level_high(TIME_DEBUG_CHAN_TIMING); + sync_hw_timer_cb_t tmp_cb = sync_ctx.hw_timer_cb; + +#if SYNC_SUPPORT_MT1R + uint32_t period = (sync_ctx.hw_timer_periodic && + (sync_ctx.hw_timer_shift_state == SYNC_HW_TIMER_SHIFT_STATE_SHIFTING)) ? + sync_ctx.hw_timer_shift_period - SYNC_HW_TIMER_SHIFT_COMPENSATE : sync_ctx.hw_timer_period; + uint32_t left = sync_hw_timer_get_left(); + sync_ctx.hw_timer_pend_time = left == 0 ? 0 : period - left; +#if SYNC_TRACE + if (sync_ctx.hw_timer_pend_time > sync_ctx.hw_timer_pend_time_max) + { + sync_ctx.hw_timer_pend_time_max = sync_ctx.hw_timer_pend_time; + } +#endif +#endif + + if (sync_ctx.hw_timer_periodic == false) + { + sync_ctx.hw_timer_cb = NULL; + /* Disable */ + ENHTIM_Cmd(SYNC_HW_TIMER, DISABLE); + } + else + { +#if SYNC_SUPPORT_MT1R + if (sync_ctx.hw_timer_shift_state == SYNC_HW_TIMER_SHIFT_STATE_PENDING) + { + /* in case already pend too long, shift next time */ + if (sync_ctx.hw_timer_pend_time < SYNC_HW_TIMER_SHIFT_TOLERANCE) + { + sync_ctx.hw_timer_shift_state = SYNC_HW_TIMER_SHIFT_STATE_SHIFTING; + sync_ctx.hw_timer_shift_period = sync_ctx.hw_timer_period + sync_ctx.hw_timer_shift_time; + ENHTIM_Cmd(SYNC_HW_TIMER, DISABLE); + SYNC_HW_TIMER->ENHTIM_MAX_CNT = (sync_ctx.hw_timer_shift_period - SYNC_HW_TIMER_SHIFT_COMPENSATE) * + SYNC_HW_TIMER_CLOCK; + ENHTIM_Cmd(SYNC_HW_TIMER, ENABLE); + time_debug_level_high(TIME_DEBUG_CHAN_ISR_GPIO); + time_debug_level_low(TIME_DEBUG_CHAN_ISR_GPIO); + } + } + else if (sync_ctx.hw_timer_shift_state == SYNC_HW_TIMER_SHIFT_STATE_SHIFTING) + { + uint32_t new_period = sync_ctx.hw_timer_period; + if (sync_ctx.hw_timer_pend_time < SYNC_HW_TIMER_SHIFT_TOLERANCE) + { + sync_ctx.hw_timer_shift_state = SYNC_HW_TIMER_SHIFT_STATE_IDLE; + } + else + { + if (sync_ctx.hw_timer_pend_time + 20 < sync_ctx.hw_timer_period) + { + /* re-enter shift state */ + new_period = sync_ctx.hw_timer_period - sync_ctx.hw_timer_pend_time; + sync_ctx.hw_timer_shift_time = sync_ctx.hw_timer_pend_time; + sync_ctx.hw_timer_shift_period = new_period; + new_period -= SYNC_HW_TIMER_SHIFT_COMPENSATE; + time_debug_level_high(TIME_DEBUG_CHAN_ISR_GPIO); + time_debug_level_low(TIME_DEBUG_CHAN_ISR_GPIO); + } + else + { + /* can't handle this case */ + sync_ctx.hw_timer_shift_state = SYNC_HW_TIMER_SHIFT_STATE_IDLE; + } + } + ENHTIM_Cmd(SYNC_HW_TIMER, DISABLE); + SYNC_HW_TIMER->ENHTIM_MAX_CNT = new_period * SYNC_HW_TIMER_CLOCK; + ENHTIM_Cmd(SYNC_HW_TIMER, ENABLE); + } +#endif + time_debug_level_high(TIME_DEBUG_CHAN_ISR_GPIO); + } + /* Clear interrupt */ + ENHTIM_ClearINTPendingBit(SYNC_HW_TIMER, ENHTIM_INT_TIM); + NVIC_ClearPendingIRQ(SYNC_HW_TIMER_IRQN); + if (tmp_cb) + { + //APP_PRINT_INFO1("sync_hw_timer_handler: callback 0x%x", tmp_cb); + tmp_cb(); + } + time_debug_level_low(TIME_DEBUG_CHAN_TIMING); +} + +#if SYNC_SUPPORT_MT1R +PPT_ISR_SECTION void sync_hw_timer_shift(int32_t time) +{ + bool shift_pend = false; + bool timer_pend = false; + uint32_t left = 0; + int32_t new_period = 0; + if (sync_ctx.hw_timer_shift_state != SYNC_HW_TIMER_SHIFT_STATE_IDLE) + { + if (SYNC_ISR_DEBUG) + { + APP_PRINT_WARN1("sync_isr: timer shift state error %d", sync_ctx.hw_timer_shift_state); + } + return; + } + if (time == 0 || (time < 0 && sync_ctx.hw_timer_period <= -time)) + { + if (SYNC_ISR_DEBUG) + { + APP_PRINT_WARN2("sync_isr: timer shift time error, period %d, shift %d", sync_ctx.hw_timer_period, + time); + } + return; + } + + left = sync_hw_timer_get_left(); + timer_pend = sync_hw_timer_check_pending(); + if (timer_pend) + { + shift_pend = true; + } + else + { + new_period = left + time; + if (left < 10 || (left + 2) >= sync_ctx.hw_timer_period || new_period < 20) + { + shift_pend = true; + } + } + + if (shift_pend) + { + sync_ctx.hw_timer_shift_state = SYNC_HW_TIMER_SHIFT_STATE_PENDING; + sync_ctx.hw_timer_shift_time = time; + } + else + { + time_debug_level_low(TIME_DEBUG_CHAN_ISR_GPIO); + sync_ctx.hw_timer_shift_state = SYNC_HW_TIMER_SHIFT_STATE_SHIFTING; + sync_ctx.hw_timer_shift_period = new_period; + sync_ctx.hw_timer_shift_time = time; + ENHTIM_Cmd(SYNC_HW_TIMER, DISABLE); + ENHTIM_ClearINTPendingBit(SYNC_HW_TIMER, ENHTIM_INT_TIM); + NVIC_ClearPendingIRQ(SYNC_HW_TIMER_IRQN); + SYNC_HW_TIMER->ENHTIM_MAX_CNT = (new_period - SYNC_HW_TIMER_SHIFT_COMPENSATE) * SYNC_HW_TIMER_CLOCK; + ENHTIM_Cmd(SYNC_HW_TIMER, ENABLE); + time_debug_level_high(TIME_DEBUG_CHAN_ISR_GPIO); + time_debug_level_low(TIME_DEBUG_CHAN_ISR_GPIO); + time_debug_level_high(TIME_DEBUG_CHAN_ISR_GPIO); + } + //APP_PRINT_INFO4("sync_isr: shift %d, left %d, pending %d, after state %d", time, left, timer_pend, sync_ctx.hw_timer_shift_state); +} + +void sync_hw_timer1_handler(void); +void sync_hw_timer1_init(void) +{ + RCC_PeriphClockCmd(APBPeriph_ENHTIMER, APBPeriph_ENHTIMER_CLOCK, ENABLE); + ENHTIM_InitTypeDef TIM_InitStruct; + ENHTIM_StructInit(&TIM_InitStruct); + TIM_InitStruct.ENHTIM_Mode = ENHTIM_MODE_PWM_MANUAL; + ENHTIM_Init(SYNC_HW_TIMER1, &TIM_InitStruct); + + NVIC_InitTypeDef NVIC_InitStruct; + NVIC_InitStruct.NVIC_IRQChannel = SYNC_HW_TIMER1_IRQN; + NVIC_InitStruct.NVIC_IRQChannelPriority = SYNC_HW_TIMER_IP; + NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStruct); + + ENHTIM_ClearINTPendingBit(SYNC_HW_TIMER1, ENHTIM_INT_TIM); + ENHTIM_INTConfig(SYNC_HW_TIMER1, ENHTIM_INT_TIM, ENABLE); + RamVectorTableUpdate(SYNC_HW_TIMER1_VECTORN, sync_hw_timer1_handler); +} + +static PPT_ISR_SECTION void sync_hw_timer1_disable(void) +{ + uint32_t lock = sync_enter_critical(); + ENHTIM_Cmd(SYNC_HW_TIMER1, DISABLE); + ENHTIM_ClearINTPendingBit(SYNC_HW_TIMER1, ENHTIM_INT_TIM); + NVIC_ClearPendingIRQ(SYNC_HW_TIMER1_IRQN); + sync_exit_critical(lock); +} + +PPT_ISR_SECTION void sync_hw_timer1_start(uint32_t period_us, sync_hw_timer_cb_t cb) +{ + sync_hw_timer1_disable(); + sync_ctx.hw_timer1_cb = cb; + SYNC_HW_TIMER1->ENHTIM_MAX_CNT = period_us * SYNC_HW_TIMER_CLOCK; + ENHTIM_Cmd(SYNC_HW_TIMER1, ENABLE); + time_debug_level_low(SYNC_TIME_DEBUG_TIMER1); + time_debug_level_high(SYNC_TIME_DEBUG_TIMER1); +} + +PPT_ISR_SECTION void sync_hw_timer1_stop(void) +{ + if (sync_ctx.hw_timer1_cb) + { + time_debug_level_low(SYNC_TIME_DEBUG_TIMER1); + sync_hw_timer1_disable(); + sync_ctx.hw_timer1_cb = NULL; + } +} + +PPT_ISR_SECTION void sync_hw_timer1_handler(void) +{ + time_debug_level_low(SYNC_TIME_DEBUG_TIMER1); + sync_hw_timer_cb_t tmp_cb = sync_ctx.hw_timer1_cb; + sync_ctx.hw_timer1_cb = NULL; + ENHTIM_Cmd(SYNC_HW_TIMER1, DISABLE); + + ENHTIM_ClearINTPendingBit(SYNC_HW_TIMER1, ENHTIM_INT_TIM); + NVIC_ClearPendingIRQ(SYNC_HW_TIMER1_IRQN); + + if (tmp_cb) + { + //APP_PRINT_INFO1("sync_hw_timer1_handler: callback 0x%x", tmp_cb); + tmp_cb(); + } +} +#endif + +void sync_hw_frtimer_init(void) +{ + RCC_PeriphClockCmd(APBPeriph_ENHTIMER, APBPeriph_ENHTIMER_CLOCK, ENABLE); + ENHTIM_InitTypeDef TIM_InitStruct; + ENHTIM_StructInit(&TIM_InitStruct); + TIM_InitStruct.ENHTIM_ClockDiv = ENHTIM_CLOCK_DIVIDER_1; + TIM_InitStruct.ENHTIM_Mode = ENHTIM_MODE_FreeRun; + TIM_InitStruct.ENHTIM_LatchCountEn[1] = ENABLE; + TIM_InitStruct.ENHTIM_LatchCountEn[2] = ENABLE; + ENHTIM_Init(SYNC_HW_FRTIMER, &TIM_InitStruct); + sync_hw_frtimer_start(); +} + +void sync_hw_frtimer_start(void) +{ + ENHTIM_Cmd(SYNC_HW_FRTIMER, ENABLE); +} + +void sync_hw_frtimer_stop(void) +{ + ENHTIM_Cmd(SYNC_HW_FRTIMER, DISABLE); +} + +PPT_API_SECTION uint32_t sync_hw_frtimer_get_count(void) +{ + return ENHTIM_GetCurrentCount(SYNC_HW_FRTIMER); +} + +PPT_API_SECTION int32_t sync_hw_frtimer_time_diff(uint32_t time1, uint32_t time2) +{ + int32_t diff_us; + uint32_t abs = time1 >= time2 ? time1 - time2 : time2 - time1; + if (abs > (SYNC_HW_FRTIMER_MAX_VALUE >> 1)) + { + if (time1 >= time2) + { + diff_us = time1 - time2 - SYNC_HW_FRTIMER_MAX_VALUE - 1; + } + else + { + diff_us = SYNC_HW_FRTIMER_MAX_VALUE + 1 - time2 + time1; + } + } + else + { + diff_us = time1 - time2; + } + + return diff_us / SYNC_HW_FRTIMER_CLOCK; +} + +static void sync_sw_timer_cb(void *timer) +{ + uint32_t timer_id = 0; + os_timer_id_get(&timer, &timer_id); + if (timer_id) + { + sync_sw_timer_t *sync_timer = (sync_sw_timer_t *)timer_id; + //uint32_t lock = sync_enter_critical(); + sync_sw_timer_cb_t cb = sync_timer->cb; + if (cb) + { + cb(sync_timer); + } + //sync_exit_critical(lock); + } +} + +void sync_sw_timer_start(sync_sw_timer_t *sync_timer, uint32_t interval_ms, sync_sw_timer_cb_t cb) +{ + sync_timer->cb = cb; + if (sync_timer->timer == NULL) + { + os_timer_create(&sync_timer->timer, "sync", (uint32_t)sync_timer, interval_ms, true, + sync_sw_timer_cb); + os_timer_start(&sync_timer->timer); + } + else + { + os_timer_restart(&sync_timer->timer, interval_ms); + } +} + +void sync_sw_timer_stop(sync_sw_timer_t *sync_timer) +{ + if (sync_timer->timer) + { + os_timer_delete(&sync_timer->timer); + sync_timer->timer = NULL; + } + sync_timer->cb = NULL; +} + +PPT_API_SECTION uint64_t sync_native_time_read(void) +{ + uint32_t clk; + uint16_t cnt; + do + { + clk = BB_read_native_clock(); + cnt = BB_read_native_counter(); + } + while (clk != BB_read_native_clock()); + uint64_t time = (((uint64_t)(clk & 0xfffffffe) * 625) >> 1) + 624 - cnt; + return time; +} + +void sync_event_cb_reg(sync_event_cb_t cb) +{ + sync_ctx.event_cb = cb; +} + +void sync_msg_reg_receive_cb(sync_msg_receive_cb_t cb) +{ + sync_ctx.msg_receive_cb = cb; +} + +void sync_msg_set_quota(uint8_t msg_quota[SYNC_MSG_TYPE_NUM]) +{ + memcpy(sync_ctx.msg_quota, msg_quota, SYNC_MSG_TYPE_NUM); +} + +bool sync_msg_set_quota_by_type(sync_msg_type_t type, uint8_t quota) +{ + if (type >= SYNC_MSG_TYPE_NUM) + { + return false; + } + + sync_ctx.msg_quota[type] = quota; + return true; +} + +void sync_msg_set_finite_retrans(uint8_t msg_retrans_num) +{ + sync_ctx.msg_finite_retrans_num = msg_retrans_num; +} + +void sync_msg_set_dynamic_retrans(uint8_t msg_retrans_num_lower, uint8_t msg_retrans_num_upper) +{ + if (msg_retrans_num_lower <= msg_retrans_num_upper) + { + sync_ctx.msg_dynamic_retrans_num_lower = msg_retrans_num_lower; + sync_ctx.msg_dynamic_retrans_num_upper = msg_retrans_num_upper; + } +} + +void sync_msg_init(void) +{ + sync_ctx.msg_quota_total = 0; + for (uint8_t loop = 0; loop < SYNC_MSG_TYPE_NUM; loop++) + { + sync_ctx.msg_quota_total += sync_ctx.msg_quota[loop]; + } + + if (sync_ctx.msg_quota_total) + { + sync_ctx.msg_quota_total += 1; + if (sync_ctx.pmsg_buff == NULL) + { + sync_msg_t *pmsg = (sync_msg_t *)os_mem_alloc(RAM_TYPE_BUFFER_ON, + sync_ctx.msg_quota_total * sizeof(sync_msg_t)); + for (uint8_t loop = 0; loop < sync_ctx.msg_quota_total; loop++) + { + plt_list_push(&sync_ctx.msg_free, &pmsg[loop]); + } + sync_ctx.pmsg_buff = pmsg; + } + } +} + +void sync_msg_reset(void) +{ +#if 1 + /* print message queue state for debug */ + APP_PRINT_INFO4("sync: msg reset, total %d free %d sending %d, buff addr 0x%x", + sync_ctx.msg_quota_total, + sync_ctx.msg_free.count, sync_ctx.msg_send.count, sync_ctx.pmsg_buff); + for (uint8_t loop = 0; loop < SYNC_MSG_TYPE_NUM; loop++) + { + APP_PRINT_INFO3("sync: msg reset, type %d quota %d count %d", loop, sync_ctx.msg_quota[loop], + sync_ctx.msg_count[loop]); + } + + { + uint8_t msg_count = 0; + sync_msg_t *pmsg = (sync_msg_t *)sync_ctx.msg_send.pfirst; + while (pmsg) + { + msg_count += 1; + APP_PRINT_INFO3("sync: msg reset, send queue count %d ptr 0x%x next 0x%x", msg_count, pmsg, + pmsg->pnext); + pmsg = (sync_msg_t *)pmsg->pnext; + } + } + + { + uint8_t msg_count = 0; + sync_msg_t *pmsg = (sync_msg_t *)sync_ctx.msg_free.pfirst; + while (pmsg) + { + msg_count += 1; + APP_PRINT_INFO3("sync: msg reset, free queue count %d ptr 0x%x next 0x%x", msg_count, pmsg, + pmsg->pnext); + pmsg = (sync_msg_t *)pmsg->pnext; + } + } +#endif + + for (uint8_t loop = 0; loop < SYNC_MSG_TYPE_NUM; loop++) + { + sync_ctx.msg_count[loop] = 0; + } + sync_ctx.msg_send.pfirst = NULL; + sync_ctx.msg_send.plast = NULL; + sync_ctx.msg_send.count = 0; + sync_ctx.msg_free.pfirst = NULL; + sync_ctx.msg_free.plast = NULL; + sync_ctx.msg_free.count = 0; + sync_msg_t *pmsg = sync_ctx.pmsg_buff; + for (uint8_t loop = 0; loop < sync_ctx.msg_quota_total; loop++) + { + plt_list_push(&sync_ctx.msg_free, &pmsg[loop]); + } +} + +PPT_ISR_SECTION void sync_msg_handle_tx_done(void *para1, uint32_t para2) +{ + sync_msg_t *pmsg = (sync_msg_t *)para1; + uint32_t lock = sync_enter_critical(); + sync_ctx.msg_count[pmsg->type] -= 1; + sync_exit_critical(lock); + if (pmsg->send_cb) + { + pmsg->send_cb(pmsg->type, pmsg->data, pmsg->len, &pmsg->send_info); + } + lock = sync_enter_critical(); + plt_list_push(&sync_ctx.msg_free, pmsg); + sync_exit_critical(lock); +} + +PPT_API_SECTION sync_err_code_t sync_msg_send(sync_msg_type_t type, uint8_t *data, uint16_t len, + sync_msg_send_cb_t send_cb) +{ + if (type >= SYNC_MSG_TYPE_NUM || data == NULL) + { + return SYNC_ERR_CODE_INVALID_PARAM; + } + + if (len == 0 || len > SYNC_MSG_MAX_LEN) + { + return SYNC_ERR_CODE_INVALID_LENGTH; + } + + if (sync_ctx.msg_count[type] >= sync_ctx.msg_quota[type]) + { + return SYNC_ERR_CODE_FULL_QUEUE; + } + + uint32_t lock = sync_enter_critical(); + sync_msg_t *pmsg = (sync_msg_t *)plt_list_pop(&sync_ctx.msg_free); + sync_exit_critical(lock); + if (pmsg == NULL) + { + return SYNC_ERR_CODE_UNKNOWN; + } + + pmsg->type = type; + memcpy(pmsg->data, data, len); + pmsg->len = len; + pmsg->send_cb = send_cb; + + lock = sync_enter_critical(); + plt_list_push(&sync_ctx.msg_send, (void *)pmsg); + sync_ctx.msg_count[type] += 1; + uint32_t list_size = sync_ctx.msg_send.count; + if (list_size == 1) + { +#if SYNC_ROLE_MASTER_SUPPORT + if (sync_ctx.role == SYNC_ROLE_MASTER) + { + sync_master_handle_tx_trigger(); + } +#endif +#if SYNC_ROLE_SLAVE_SUPPORT + if (sync_ctx.role == SYNC_ROLE_SLAVE) + { + sync_slave_handle_tx_trigger(); + } +#endif + } + sync_exit_critical(lock); + + return SYNC_ERR_CODE_SUCCESS; +} + +uint8_t sync_msg_get_available_number(sync_msg_type_t type) +{ + if (type >= SYNC_MSG_TYPE_NUM && type != SYNC_MSG_TYPE_ALL) + { + return 0; + } + uint8_t count = 0; + if (type == SYNC_MSG_TYPE_ALL) + { + for (uint8_t loop = 0; loop < SYNC_MSG_TYPE_NUM; loop++) + { + count += sync_ctx.msg_quota[loop] - sync_ctx.msg_count[loop]; + } + } + else + { + count = sync_ctx.msg_quota[type] - sync_ctx.msg_count[type]; + } + return count; +} + +uint8_t sync_msg_flush(sync_msg_type_t type) +{ + uint8_t count = 0; + if (type >= SYNC_MSG_TYPE_NUM && type != SYNC_MSG_TYPE_ALL) + { + return 0; + } + uint32_t lock = sync_enter_critical(); + sync_msg_t *pmsg_prev = NULL; + sync_msg_t *pmsg = (sync_msg_t *)sync_ctx.msg_send.pfirst; + while (pmsg) + { + if (type == SYNC_MSG_TYPE_ALL || type == pmsg->type) + { + plt_list_delete(&sync_ctx.msg_send, pmsg_prev, pmsg); + sync_msg_t *pmsg_next = (sync_msg_t *)pmsg->pnext; + if (pmsg->send_cb) + { + pmsg->send_info.res = SYNC_SEND_RESULT_FLUSHED; + sync_pendcall(sync_msg_handle_tx_done, pmsg, 0); + } + else + { + sync_ctx.msg_count[pmsg->type] -= 1; + plt_list_push(&sync_ctx.msg_free, pmsg); + } + pmsg = pmsg_next; + count += 1; + } + else + { + pmsg_prev = pmsg; + pmsg = (sync_msg_t *)pmsg->pnext; + } + } +#if SYNC_ROLE_MASTER_SUPPORT + if (sync_ctx.role == SYNC_ROLE_MASTER) + { + sync_master_msg_flush(type); + } +#endif + sync_exit_critical(lock); + return count; +} + +PPT_API_SECTION uint32_t sync_rand(void) +{ + return ((uint32_t)rand() & 0xffffff) + ((uint32_t)rand() << 24); +} + +PPT_API_SECTION uint32_t sync_enter_critical(void) +{ + uint32_t flag = __get_PRIMASK(); + __disable_irq(); + return flag; +} + +PPT_API_SECTION void sync_exit_critical(uint32_t flag) +{ + if (flag == 0) + { + __enable_irq(); + } +} + +void sync_init(sync_role_t role) +{ + APP_PRINT_INFO1("sync: version "SYNC_VERSION", role %d", role); + + uint32_t random_seed_value = platform_random(0xffffffff); + srand(random_seed_value); + + uint16_t freq[] = SYNC_CHANNEL_FREQUENCY; + if (SYNC_CHANNEL_NUM_DV != sizeof(freq) / sizeof(freq[0])) + { + APP_PRINT_ERROR2("sync: channel wrong %d %d", SYNC_CHANNEL_NUM_DV, sizeof(freq) / sizeof(freq[0])); + return; + } + sync_channel_set(SYNC_CHANNEL_NUM_DV, SYNC_CHANNEL_GROUP_NUM_DV, freq); + + sync_ctx.crc_len = SYNC_CRC_LEN_DV; + sync_ctx.crc_poly = SYNC_CRC_POLY_DV; + sync_ctx.crc_init = SYNC_CRC_INIT_DV; + + sync_ctx.conn_interval = SYNC_SYNC_SPEED_DV; + sync_ctx.slave_hopping_speed = SYNC_SLAVE_HOPPING_SPEED_DV; + + sync_ctx.tx_power_adaptive = false; + sync_ctx.tx_power_dbm_max = 8; + sync_ctx.tx_power_dbm_min = 8; + + sync_ctx.msg_quota[SYNC_MSG_TYPE_ONESHOT] = 2; + sync_ctx.msg_quota[SYNC_MSG_TYPE_FINITE_RETRANS] = 2; + sync_ctx.msg_quota[SYNC_MSG_TYPE_DYNAMIC_RETRANS] = 2; + sync_ctx.msg_quota[SYNC_MSG_TYPE_INFINITE_RETRANS] = 4; + sync_ctx.msg_finite_retrans_num = 2; + sync_ctx.msg_dynamic_retrans_num_lower = 0; + sync_ctx.msg_dynamic_retrans_num_upper = SYNC_MSG_RETRANS_NUM_INFINITE; + + sync_ctx.log_isr = 0; + sync_ctx.pair_rssi_th = -128; + sync_ctx.hb_to_times = SYNC_HB_TO_TIMES_DV; + sync_ctx.cca_times = SYNC_CCA_TIMES_DV; + + sync_ctx.role = role; + +#if SYNC_ROLE_MASTER_SUPPORT + if (sync_ctx.role == SYNC_ROLE_MASTER) + { + sync_master_init(); + } +#endif +#if SYNC_ROLE_SLAVE_SUPPORT + if (sync_ctx.role == SYNC_ROLE_SLAVE) + { + sync_slave_init(); + } +#endif +} + +void sync_enable(void) +{ + sync_msg_init(); + +#if SYNC_ROLE_MASTER_SUPPORT + if (sync_ctx.role == SYNC_ROLE_MASTER) + { + sync_master_enable(); + } +#endif +#if SYNC_ROLE_SLAVE_SUPPORT + if (sync_ctx.role == SYNC_ROLE_SLAVE) + { + sync_slave_enable(); + } +#endif +} + +sync_err_code_t sync_pair(void) +{ +#ifdef SYNC_ROLE_MASTER_SUPPORT + if (sync_ctx.role == SYNC_ROLE_MASTER) + { + return sync_master_pair(); + } +#endif +#ifdef SYNC_ROLE_SLAVE_SUPPORT + if (sync_ctx.role == SYNC_ROLE_SLAVE) + { + return sync_slave_pair(); + } +#endif + return SYNC_ERR_CODE_UNKNOWN; +} + +sync_err_code_t sync_connect(sync_bond_info_t *bond_info) +{ +#if SYNC_ROLE_MASTER_SUPPORT + if (sync_ctx.role == SYNC_ROLE_MASTER) + { + return sync_master_connect(bond_info); + } +#endif +#if SYNC_ROLE_SLAVE_SUPPORT + if (sync_ctx.role == SYNC_ROLE_SLAVE) + { + return sync_slave_connect(bond_info); + } +#endif + return SYNC_ERR_CODE_UNKNOWN; +} + +void sync_stop(void) +{ +#if SYNC_ROLE_MASTER_SUPPORT + if (sync_ctx.role == SYNC_ROLE_MASTER) + { + sync_master_stop(); + } +#endif +#if SYNC_ROLE_SLAVE_SUPPORT + if (sync_ctx.role == SYNC_ROLE_SLAVE) + { + sync_slave_stop(); + } +#endif +} + +void sync_deinit(void) +{ + if (sync_ctx.chan_cfg) + { + os_mem_free(sync_ctx.chan_cfg); + sync_ctx.chan_cfg = NULL; + } + +#if SYNC_ROLE_MASTER_SUPPORT + if (sync_ctx.role == SYNC_ROLE_MASTER) + { + sync_master_deinit(); + } +#endif +#if SYNC_ROLE_SLAVE_SUPPORT + if (sync_ctx.role == SYNC_ROLE_SLAVE) + { + sync_slave_deinit(); + } +#endif +} + +void sync_dlps_init(void) +{ +#if SYNC_ROLE_MASTER_SUPPORT + if (sync_ctx.role == SYNC_ROLE_MASTER) + { + return sync_master_dlps_init(); + } +#endif +#if SYNC_ROLE_SLAVE_SUPPORT + if (sync_ctx.role == SYNC_ROLE_SLAVE) + { + return sync_slave_dlps_init(); + } +#endif +} + +void sync_crc_set(uint8_t crc_len, uint32_t crc_poly, uint32_t crc_init) +{ + sync_ctx.crc_len = crc_len; + sync_ctx.crc_poly = crc_poly; + sync_ctx.crc_init = crc_init; + WR_PPT_REG_FIELD(PRO_PACKET_CONTROL, crc_field_length_2_0, sync_ctx.crc_len >> 3); +} + +bool sync_time_set(sync_time_param_t type, uint32_t time) +{ + bool ret = true; + switch (type) + { + case SYNC_TIME_PARAM_CONNECT_INTERVAL: +#if SYNC_SUPPORT_MT1R + sync_ctx.mt1r.flag = time == 125; + sync_ctx.conn_interval = sync_ctx.mt1r.flag ? 250 : time; +#else + sync_ctx.conn_interval = time; +#endif + break; + case SYNC_TIME_PARAM_CONNECT_INTERVAL_HIGH: + sync_ctx.conn_interval_high = time; + break; + case SYNC_TIME_PARAM_SYNC_LOST_PERIOD: + sync_ctx.sync_lost_period = time; + break; + case SYNC_TIME_PARAM_SYNC_FAST_PERIOD: + sync_ctx.sync_fast_period = time; + break; + case SYNC_TIME_PARAM_HB_TO_TIMES: + sync_ctx.hb_to_times = time; + break; + case SYNC_TIME_PARAM_CHANNEL_ASSESS_TIMES: + sync_ctx.cca_times = time; + break; + case SYNC_TIME_PARAM_SLAVE_HOPPING_SPEED: + sync_ctx.slave_hopping_speed = time; + break; + default: + ret = false; + break; + } + return ret; +} + +bool sync_time_get(sync_time_param_t type, uint32_t *time) +{ + bool ret = true; + switch (type) + { + case SYNC_TIME_PARAM_CONNECT_INTERVAL: +#if SYNC_SUPPORT_MT1R + *time = sync_ctx.mt1r.flag ? 125 : sync_ctx.conn_interval; +#else + *time = sync_ctx.conn_interval; +#endif + break; + case SYNC_TIME_PARAM_CONNECT_INTERVAL_HIGH: + *time = sync_ctx.conn_interval_high; + break; + default: + ret = false; + break; + } + return ret; +} + +bool sync_log_set(uint8_t level, bool on) +{ + sync_ctx.log_isr = on; + return true; +} + +#if SYNC_SUPPORT_POWER_CONTROL +static const int8_t sync_power_level[] = {-40, -20, -10, 0, 4, 8, 10, 14, 18, 20}; +static uint8_t sync_power_ctrl_get_idx(int8_t power_dbm) +{ + uint8_t table_size = sizeof(sync_power_level) / sizeof(sync_power_level[0]); + if (sync_power_level[table_size - 1] < power_dbm) + { + return table_size - 1; + } + for (int loop = table_size - 1; loop >= 0; loop--) + { + if (sync_power_level[loop] >= power_dbm && (loop == 0 || sync_power_level[loop - 1] < power_dbm)) + { + return loop; + } + } + return table_size - 1; +} + +static void sync_power_ctrl_adjust(sync_sw_timer_t *sync_timer) +{ + if (sync_ctx.tx_power_levels <= 1) + { + return; + } + + uint8_t adjust = 0; + + uint32_t count_fail = sync_ctx.power_ctrl_count_fail; + uint32_t count = sync_ctx.power_ctrl_count; + uint16_t old_per = sync_ctx.power_ctrl_per; + if (count && count_fail < count) + { + uint16_t per = count_fail * 1000 / count; + sync_ctx.power_ctrl_per = (old_per * 8 + per * 2) / 10; + if (sync_ctx.power_ctrl_per > 100) + { + adjust = 2; + } + else if (sync_ctx.power_ctrl_per < 10) + { + adjust = 1; + } + } + //APP_PRINT_INFO5("sync: power ctrl count %d, count_fail %d, per %d -> %d, adjust %d", count, count_fail, old_per, sync_ctx.power_ctrl_per, adjust); + sync_ctx.power_ctrl_count = 0; + sync_ctx.power_ctrl_count_fail = 0; + + if (adjust == 1) + { + if (sync_ctx.tx_power_idx_cur > sync_ctx.tx_power_idx_min) + { + sync_ctx.tx_power_idx_cur -= 1; + ppt_set_tx_power_dbm(sync_power_level[sync_ctx.tx_power_idx_cur]); + APP_PRINT_INFO3("sync: power ctrl %d decrease from %d to %d", sync_ctx.power_ctrl_per, + sync_power_level[sync_ctx.tx_power_idx_cur + 1], sync_power_level[sync_ctx.tx_power_idx_cur]); + sync_ctx.power_ctrl_per = 0; + } + + } + else if (adjust == 2) + { + if (sync_ctx.tx_power_idx_cur < sync_ctx.tx_power_idx_max) + { + sync_ctx.tx_power_idx_cur += 1; + ppt_set_tx_power_dbm(sync_power_level[sync_ctx.tx_power_idx_cur]); + APP_PRINT_INFO3("sync: power ctrl %d increase from %d to %d", sync_ctx.power_ctrl_per, + sync_power_level[sync_ctx.tx_power_idx_cur - 1], sync_power_level[sync_ctx.tx_power_idx_cur]); + sync_ctx.power_ctrl_per = 0; + } + } +} + +void sync_power_ctrl_init(void) +{ + sync_ctx.tx_power_idx_max = sync_power_ctrl_get_idx(sync_ctx.tx_power_dbm_max); + sync_ctx.tx_power_idx_min = sync_power_ctrl_get_idx(sync_ctx.tx_power_dbm_min); + sync_ctx.tx_power_levels = sync_ctx.tx_power_idx_min - sync_ctx.tx_power_idx_max + 1; + if (sync_ctx.tx_power_levels > 1) + { + sync_ctx.tx_power_idx_cur = sync_ctx.tx_power_idx_max; + ppt_set_tx_power_dbm(sync_power_level[sync_ctx.tx_power_idx_cur]); + } + else + { + ppt_set_tx_power_dbm(sync_ctx.tx_power_dbm_max); + } +} + +void sync_power_ctrl_start(void) +{ + if (sync_ctx.tx_power_levels <= 1) + { + return; + } + + sync_ctx.power_ctrl_count = 0; + sync_ctx.power_ctrl_count_fail = 0; + sync_ctx.power_ctrl_per = 0; + sync_sw_timer_start(&sync_ctx.power_ctrl_timer, SYNC_SUPPORT_POWER_CONTROL_PERIOD, + sync_power_ctrl_adjust); +} + +void sync_power_ctrl_stop(void) +{ + if (sync_ctx.tx_power_levels <= 1) + { + return; + } + + sync_sw_timer_stop(&sync_ctx.power_ctrl_timer); +} +#endif + +bool sync_tx_power_set(bool dynamic, int8_t tx_power_dbm_max, int8_t tx_power_dbm_min) +{ + if (tx_power_dbm_max < tx_power_dbm_min) + { + return false; + } + + sync_ctx.tx_power_adaptive = dynamic; + sync_ctx.tx_power_dbm_max = tx_power_dbm_max; + sync_ctx.tx_power_dbm_min = tx_power_dbm_min; + return true; +} + +bool sync_pair_rssi_set(int8_t rssi) +{ + sync_ctx.pair_rssi_th = rssi; + return true; +} + +bool sync_tifs_set(uint8_t level, uint8_t tifs) +{ + if (level >= sizeof(sync_ctx.tifs) / sizeof(sync_ctx.tifs[0])) + { + return false; + } + sync_ctx.tifs[level] = tifs; + return true; +} + +void sync_check_feature(void) +{ +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_power_ctrl_init(); + } + else +#endif + { + ppt_set_tx_power_dbm(sync_ctx.tx_power_dbm_max); + } +} + +void sync_hb_reg_cb(sync_hb_cb_t cb) +{ + sync_ctx.hb_cb = cb; +} + +void sync_rte_req_reg_cb(sync_rte_req_cb_t cb) +{ + sync_ctx.rte_req_cb = cb; +} diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync_int.h b/bee/drivers/ppt/sync/src/sync/src/ppt_sync_int.h new file mode 100644 index 00000000..c7fd232f --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/src/ppt_sync_int.h @@ -0,0 +1,462 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** +* @file ppt_sync_int.h +* @brief Head file for sync protocol. +* @details data structs and external functions declaration. +* @author bill +* @date 2022-03-15 +* @version v0.1 +* ************************************************************************************* +*/ + +/* Define to prevent recursive inclusion */ +#ifndef _PPT_SYNC_INT_H_ +#define _PPT_SYNC_INT_H_ + +/* Add Includes here */ +#include +#include +#include "platform_macros.h" +#include "platform_list.h" +#include "ppt_sync.h" +#include "ppt_sync_pendcall.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup PPT_SYNC_Driver + * @{ + */ +#define PPT_SYNC_LOG_LEVEL CONFIG_PPT_DRIVER_LOG_LEVEL + +/** @defgroup PPT_SYNC_Driver_Exported_Macros Exported Macros + * @brief + * @{ + */ +#define SYNC_ROLE_MASTER_SUPPORT 1 +#define SYNC_ROLE_SLAVE_SUPPORT 0 +#define SYNC_CHANNEL_FREQUENCY \ + {2432, 2447, 2462, 2477, 2407, 2422, 2437, 2452, 2442, 2457, 2472, 2413} +#define SYNC_CHANNEL_NUM_DV 12 +#define SYNC_CHANNEL_GROUP_NUM_DV 3 +#define SYNC_HEADER_LEN 8 // bit +#define SYNC_HEADER_SEQ_LEN 7 // bit +#define SYNC_LENGTH_LEN 7 // bit +#define SYNC_MSG_MAX_LEN 127 // byte +#define SYNC_CRC_LEN_DV 8 // bit +#define SYNC_CRC_POLY_DV 0x07 +#define SYNC_CRC_INIT_DV 0xff +#define SYNC_OVERHEAD_LEN 2 // byte +#define SYNC_ACC_ADDR_BR_DV 0x8EBE89D6 +#define SYNC_TIFS0_DV 40 // us +#define SYNC_TIFS1_DV 40 // us +#define SYNC_PAIR_SPEED_DV 1000 // us +#define SYNC_PAIR_PERIOD 1000000 // us +#define SYNC_PAIR_CFM_PERIOD_DV 8 // times +#define SYNC_SYNC_SLOW_SPEED_DV 1000 // us +#define SYNC_SYNC_SLOW_PERIOD 1000000 // us +#define SYNC_SYNC_SPEED_DV 1000 // us +#define SYNC_SYNC_SPEED_HIGH_DV 250 // us +#define SYNC_SYNC_SPEED_HIGH_MIN 250 // us +#define SYNC_SYNC_LOST_PERIOD_DV 8 // times +#define SYNC_SYNC_FAST_SPEED_DV 1000 // us +#define SYNC_SYNC_FAST_PERIOD_DV 3 // times +#define SYNC_HB_TO_TIMES_DV 3 // times +#define SYNC_CCA_TIMES_DV 3 // times +#define SYNC_SLAVE_HOPPING_SPEED_DV 200000 // us + +#define SYNC_PARAM_CONFIG 1 +#if SYNC_PARAM_CONFIG +#define SYNC_CRC_LEN sync_ctx.crc_len +#define SYNC_CRC_POLY sync_ctx.crc_poly +#define SYNC_CRC_INIT sync_ctx.crc_init +#define SYNC_PAIR_SPEED sync_ctx.conn_interval +#define SYNC_SYNC_SLOW_SPEED sync_ctx.conn_interval +#define SYNC_SYNC_SPEED sync_ctx.conn_interval +#define SYNC_SYNC_SPEED_HIGH \ + (sync_ctx.conn_interval_high <= sync_ctx.conn_interval ? sync_ctx.conn_interval_high \ + : sync_ctx.conn_interval) +#define SYNC_SYNC_FAST_SPEED sync_ctx.conn_interval +#define SYNC_CHANNEL_NUM sync_ctx.chan_num +#define SYNC_CHANNEL_GROUP_NUM sync_ctx.chan_group_num +#define SYNC_ACC_ADDR_BR sync_ctx.acc_br.addr +#define SYNC_HB_TO_TIMES sync_ctx.hb_to_times +#define SYNC_CCA_TIMES sync_ctx.cca_times +#define SYNC_SLAVE_HOPPING_SPEED sync_ctx.slave_hopping_speed +#define SYNC_ISR_DEBUG sync_ctx.log_isr +#else +#define SYNC_CRC_LEN SYNC_CRC_LEN_DV +#define SYNC_CRC_POLY SYNC_CRC_POLY_DV +#define SYNC_CRC_INIT SYNC_CRC_INIT_DV +#define SYNC_PAIR_SPEED SYNC_PAIR_SPEED_DV +#define SYNC_SYNC_SLOW_SPEED SYNC_SYNC_SLOW_SPEED_DV +#define SYNC_SYNC_SPEED SYNC_SYNC_SPEED_DV +#define SYNC_SYNC_SPEED_HIGH SYNC_SYNC_SPEED_DV +#define SYNC_SYNC_FAST_SPEED SYNC_SYNC_FAST_SPEED_DV +#define SYNC_CHANNEL_NUM SYNC_CHANNEL_NUM_DV +#define SYNC_CHANNEL_GROUP_NUM SYNC_CHANNEL_GROUP_NUM_DV +#define SYNC_ACC_ADDR_BR SYNC_ACC_ADDR_BR_DV +#define SYNC_HB_TO_TIMES SYNC_HB_TO_TIMES_DV +#define SYNC_CCA_TIMES SYNC_CCA_TIMES_DV +#define SYNC_SLAVE_HOPPING_SPEED SYNC_SLAVE_HOPPING_SPEED_DV +#define SYNC_ISR_DEBUG 0 +#endif + +#define SYNC_PAIR_CFM_PERIOD (SYNC_SYNC_SPEED * SYNC_PAIR_CFM_PERIOD_DV) +#define SYNC_SYNC_LOST_PERIOD \ + (sync_ctx.sync_lost_period ? sync_ctx.sync_lost_period \ + : SYNC_SYNC_SPEED * SYNC_SYNC_LOST_PERIOD_DV) +#define SYNC_SYNC_FAST_PERIOD \ + (sync_ctx.sync_fast_period \ + ? sync_ctx.sync_fast_period \ + : (SYNC_SYNC_LOST_PERIOD + SYNC_SYNC_FAST_PERIOD_DV * SYNC_SYNC_FAST_SPEED * \ + SYNC_CHANNEL_NUM / SYNC_CHANNEL_GROUP_NUM)) + +#define SYNC_SUPPORT_POWER_CONTROL 1 +#define SYNC_SUPPORT_POWER_CONTROL_PERIOD 1000 // ms + +#define SYNC_SUPPORT_MT1R 1 +#define SYNC_SUFFIX_LEN 7 // bit +#define SYNC_CONNECT_DELAY 300 +#define SYNC_HEADER_LEN_MT1R 6 // bit + +#define SYNC_SUPPORT_SYNC_FAST_DISABLE 1 + +#define SYNC_FRAME_BLE_COMPATIBLE 0 +#if SYNC_FRAME_BLE_COMPATIBLE +#undef SYNC_CHANNEL_FREQUENCY +#define SYNC_CHANNEL_FREQUENCY \ + {2402, 2406, 2410, 2414, 2418, 2422, 2426, 2430, 2434, 2438, 2442, 2446} +#undef SYNC_HEADER_LEN +#define SYNC_HEADER_LEN 8 // bit +#undef SYNC_LENGTH_LEN +#define SYNC_LENGTH_LEN 8 // bit +#undef SYNC_CRC_LEN +#define SYNC_CRC_LEN 24 +#undef SYNC_CRC_POLY +#define SYNC_CRC_POLY 0x00065b +#undef SYNC_CRC_INIT +#define SYNC_CRC_INIT 0x555555 +#undef SYNC_ACC_ADDR_BR_DV +#define SYNC_ACC_ADDR_BR_DV 0x8E89BED6 +#endif +/** @} End of PPT_SYNC_Driver_Exported_Macros */ + +/** @defgroup PPT_SYNC_Driver_Exported_Types Exported Types + * @brief + * @{ + */ + +#pragma pack(1) + +typedef union { + uint8_t value; + struct { + uint8_t ack : 1; + uint8_t seq : SYNC_HEADER_SEQ_LEN; + }; +#if SYNC_SUPPORT_MT1R + struct { + uint8_t ack : 1; + uint8_t seq : 4; + uint8_t req_ack : 1; + } m; + struct { + uint8_t ack : 1; + uint8_t seq : 4; + uint8_t rfu : 1; + } s; +#endif +} sync_header_t; + +typedef enum { + SYNC_OPCODE_EMPTY, + SYNC_OPCODE_PAIR_REQ, + SYNC_OPCODE_PAIR_RSP, + SYNC_OPCODE_PAIR_CONF, + SYNC_OPCODE_CONN_REQ, + SYNC_OPCODE_CONN_RSP, + SYNC_OPCODE_HEARTBEAT, + SYNC_OPCODE_APP_START = SYNC_OPCODE_APP_START_VALUE, +} sync_opcode_t; + +typedef struct { + uint16_t interval; +} sync_pair_req_t; + +typedef struct { + sync_acc_t acc; +} sync_pair_rsp_t; + +typedef struct { + uint16_t interval; +} sync_conn_req_t; + +typedef struct { + uint16_t interval; +} sync_heartbeat_t; + +typedef struct { +#if SYNC_FRAME_BLE_COMPATIBLE + uint8_t addr[5]; +#endif + uint32_t seq; + // uint8_t padding[10]; +} sync_app_test_t; + +typedef struct { +#if SYNC_FRAME_BLE_COMPATIBLE + uint8_t addr[5]; +#endif + uint32_t seq; + // uint8_t padding[22]; +} sync_app_test1_t; + +typedef struct { + sync_opcode_t opcode; + union { + sync_pair_req_t pair_req; + sync_pair_rsp_t pair_rsp; + sync_conn_req_t conn_req; + sync_heartbeat_t heartbeat; + sync_app_test_t app_test; + }; +} sync_pdu_t; + +#pragma pack() + +typedef struct { + void *pnext; + sync_msg_send_cb_t send_cb; + sync_msg_type_t type; + uint16_t len; + uint8_t data[SYNC_MSG_MAX_LEN]; + sync_send_info_t send_info; +} sync_msg_t; + +typedef struct { + uint16_t freq; + uint8_t bank; + uint8_t channel; + int16_t rssi; +#ifdef SYNC_DEBUG + uint32_t choose_times; + uint32_t used_count; + uint32_t fail_count; + uint32_t fail_burst_count; + uint32_t fail_burst_max; +#endif +} sync_channel_cfg_t; + +struct _sync_sw_timer_t; +typedef void (*sync_sw_timer_cb_t)(struct _sync_sw_timer_t *p_timer); +typedef struct _sync_sw_timer_t { + void *timer; + sync_sw_timer_cb_t cb; +} sync_sw_timer_t; + +typedef void (*sync_hw_timer_cb_t)(void); + +#if SYNC_SUPPORT_MT1R +typedef enum { + SYNC_HW_TIMER_SHIFT_STATE_IDLE, + SYNC_HW_TIMER_SHIFT_STATE_PENDING, + SYNC_HW_TIMER_SHIFT_STATE_SHIFTING +} sync_hw_timer_shift_state_t; +#endif + +typedef struct { + sync_role_t role; + uint8_t crc_len; // bit + uint32_t crc_poly; + uint32_t crc_init; + uint8_t chan_num; + uint8_t chan_group_num; + uint8_t chan_cur; + sync_channel_cfg_t *chan_cfg; + uint32_t chan_used_count; + uint32_t chan_miss_count; + sync_hw_timer_cb_t hw_timer_cb; + bool hw_timer_periodic; + sync_event_cb_t event_cb; + uint8_t msg_quota[SYNC_MSG_TYPE_NUM]; + uint8_t msg_finite_retrans_num; + uint8_t msg_dynamic_retrans_num_lower; + uint8_t msg_dynamic_retrans_num_upper; + uint8_t msg_retrans_count; + sync_msg_receive_cb_t msg_receive_cb; + uint8_t msg_quota_total; + uint8_t msg_count[SYNC_MSG_TYPE_NUM]; + plt_list_t msg_free; + plt_list_t msg_send; + sync_msg_t *pmsg_buff; + uint32_t conn_interval; //!< us + uint32_t conn_interval_high; //!< us + uint32_t sync_lost_period; //!< us + uint32_t sync_fast_period; //!< us + bool log_isr; + bool tx_power_adaptive; + int8_t tx_power_dbm_max; + int8_t tx_power_dbm_min; +#if SYNC_SUPPORT_POWER_CONTROL + int8_t tx_power_idx_cur; + int8_t tx_power_idx_max; + int8_t tx_power_idx_min; + uint8_t tx_power_levels; + sync_sw_timer_t power_ctrl_timer; + uint32_t power_ctrl_count; + uint32_t power_ctrl_count_fail; + uint16_t power_ctrl_per; +#endif + int8_t pair_rssi_th; + sync_acc_t acc_br; + uint8_t tifs[2]; + sync_hb_cb_t hb_cb; + uint8_t hb_to_times; + uint8_t cca_times; + uint32_t slave_hopping_speed; + sync_rte_req_cb_t rte_req_cb; +#if SYNC_SUPPORT_MT1R + struct { + bool flag; + } mt1r; + uint32_t hw_timer_period; + int32_t hw_timer_shift_time; + uint32_t hw_timer_shift_period; + uint32_t hw_timer_pend_time; + sync_hw_timer_shift_state_t hw_timer_shift_state; + sync_hw_timer_cb_t hw_timer1_cb; +#endif +} sync_ctx_t; + +extern sync_ctx_t sync_ctx; + +/** @} End of PPT_SYNC_Driver_Exported_Types */ + +/** @defgroup PPT_SYNC_Driver_Exported_Functions Exported Functions + * @brief + * @{ + */ + +/** + * @brief format diagnose output with raw data + * @param[in] data: raw data + * @param[in] data_len: raw data length + * @param[in] fmt: format string + * @return none + */ +void sync_ppt_init(void); +void sync_ppt_set_rf_time(void); +void sync_ppt_set_tifs(bool init); +void sync_channel_init(void); +void sync_channel_change(uint8_t idx); +void sync_channel_change_next(void); +void sync_channel_change_next_in_group(void); +#ifdef SYNC_DEBUG +void sync_channel_statistic(bool ok); +void sync_channel_clear_statistic(void); +void sync_channel_print_statistic(void); +#else +#define sync_channel_statistic(...) +#define sync_channel_clear_statistic(...) +#define sync_channel_print_statistic(...) +#endif +void sync_acc_set(sync_acc_t acc); +sync_acc_t sync_acc_gen_data(void); + +void sync_hw_timer_init(void); +void sync_hw_timer_start(uint32_t period_us, sync_hw_timer_cb_t cb); +void sync_hw_timer_restart(void); +void sync_hw_timer_stop(void); +uint32_t sync_hw_timer_get_left(void); +bool sync_hw_timer_check_pending(void); +void sync_hw_timer_set_periodic(bool enable); +void sync_hw_timer_impr_ip(bool enable); + +void sync_hw_timer_shift(int32_t time); +void sync_hw_timer1_init(void); +void sync_hw_timer1_start(uint32_t period_us, sync_hw_timer_cb_t cb); +void sync_hw_timer1_stop(void); + +void sync_hw_frtimer_init(void); +void sync_hw_frtimer_start(void); +void sync_hw_frtimer_stop(void); +uint32_t sync_hw_frtimer_get_count(void); +int32_t sync_hw_frtimer_time_diff(uint32_t time1, uint32_t time2); +#define SYNC_HW_FRTIMER_MAX_VALUE 0xFFFFFFFE +#define SYNC_HW_FRTIMER_CLOCK 40 +#define sync_hw_frtimer_time_elapse(late, early) \ + (((late) >= (early) ? (late) - (early) : (SYNC_HW_FRTIMER_MAX_VALUE - (early) + (late) + 1)) / \ + SYNC_HW_FRTIMER_CLOCK) + +void sync_sw_timer_start(sync_sw_timer_t *sync_timer, uint32_t interval_ms, sync_sw_timer_cb_t cb); +void sync_sw_timer_stop(sync_sw_timer_t *sync_timer); + +uint64_t sync_native_time_read(void); + +void sync_msg_init(void); +void sync_msg_handle_tx_done(void *para1, uint32_t para2); + +void sync_check_feature(void); + +void sync_master_handle_tx_trigger(void); +void sync_master_msg_flush(sync_msg_type_t type); + +void sync_slave_handle_tx_trigger(void); + +#if SYNC_SUPPORT_POWER_CONTROL +void sync_power_ctrl_init(void); +void sync_power_ctrl_start(void); +void sync_power_ctrl_stop(void); +#endif + +#define sync_set_header(ack_tmp, seq_tmp) \ + do { \ + sync_header_t header = {0}; \ + header.ack = ack_tmp; \ + header.seq = seq_tmp; \ + PRO_HP_0_TYPE hp = {0}; \ + hp.hp_0_7_0 = header.value; \ + WR_PPT_REG(PRO_HP_0 + PRO_ENTRY_FIELD_BASE(0), hp.d16); \ + } while (0) + +#if SYNC_SUPPORT_MT1R +#define sync_master_set_header(ack_tmp, seq_tmp, req_ack_tmp) \ + do { \ + sync_header_t header = {0}; \ + header.m.ack = ack_tmp; \ + header.m.seq = seq_tmp; \ + header.m.req_ack = req_ack_tmp; \ + PRO_HP_0_TYPE hp = {0}; \ + hp.hp_0_7_0 = header.value; \ + WR_PPT_REG(PRO_HP_0 + PRO_ENTRY_FIELD_BASE(0), hp.d16); \ + } while (0) +#define sync_slave_set_header(ack_tmp, seq_tmp) \ + do { \ + sync_header_t header = {0}; \ + header.s.ack = ack_tmp; \ + header.s.seq = seq_tmp; \ + header.s.rfu = 0; \ + PRO_HP_0_TYPE hp = {0}; \ + hp.hp_0_7_0 = header.value; \ + WR_PPT_REG(PRO_HP_0 + PRO_ENTRY_FIELD_BASE(0), hp.d16); \ + } while (0) +void sync_ppt_set_rf_time_mt1r(void); +#endif + +uint32_t sync_rand(void); + +#include "os_timer.h" +#define os_timer_pendcall(func, para1, para2, wait) os_timer_pend_function_call(func, para1, para2) +/** @} End of PPT_SYNC_Driver_Exported_Functions */ + +/** @} End of PPT_SYNC_Driver */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync_master.c b/bee/drivers/ppt/sync/src/sync/src/ppt_sync_master.c new file mode 100644 index 00000000..6c134820 --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/src/ppt_sync_master.c @@ -0,0 +1,2886 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_sync_master.c + * @brief Source file for sync protocol. + * @details + * @author bill + * @date 2022-03-15 + * @version v0.1 + * ************************************************************************************* + */ + +/* Add Includes here */ +#include +#include "ppt_simple.h" +#include "ppt_sync_int.h" +#include "ppt_sync_master.h" +#include "os_sync.h" +#include "os_timer.h" +//#include "trace.h" + +LOG_MODULE_REGISTER(ppt_sync_master, PPT_SYNC_LOG_LEVEL); + +#define SYNC_MASTER_PAIR_TIMES (SYNC_PAIR_PERIOD/SYNC_PAIR_SPEED) +#define SYNC_MASTER_PAIR_CFM_TIMES (SYNC_PAIR_CFM_PERIOD/SYNC_SYNC_SPEED) +#define SYNC_MASTER_SYNC_SLOW_TIMES (SYNC_SYNC_SLOW_PERIOD/SYNC_SYNC_SLOW_SPEED) +#define SYNC_MASTER_SYNC_LOST_TIMES (SYNC_SYNC_LOST_PERIOD/SYNC_SYNC_SPEED) +#define SYNC_MASTER_SYNC_FAST_TIMES (SYNC_SYNC_FAST_PERIOD/SYNC_SYNC_FAST_SPEED) +#define SYNC_MASTER_HB_SLEEP_MIN_PERIOD 3000 // us +#define SYNC_MASTER_HB_SLEEP_MIN_PERIOD_MT1R 10000 // us +#define SYNC_MASTER_HB_SLEEP_OH 1500 // us + +#define SYNC_MASTER_TIME_DEBUG_TX -1 +#define SYNC_MASTER_TIME_DEBUG_ACK -2 +#define SYNC_MASTER_TIME_DEBUG_NACK -2 +#define SYNC_MASTER_TIME_DEBUG_CB -1 +#define SYNC_MASTER_TIME_DEBUG_SEQ_ACK -3 + +typedef enum +{ + SYNC_MASTER_PAIR_PHASE_SEND_REQ, + SYNC_MASTER_PAIR_PHASE_SEND_CFM +} sync_master_pair_phase_t; + +typedef enum +{ + SYNC_MASTER_HB_STATE_IDLE, + SYNC_MASTER_HB_STATE_EMPTY_SEND, + SYNC_MASTER_HB_STATE_HB_SEND +} sync_master_hb_state_t; + +typedef struct +{ + uint32_t speed; // us + uint32_t period; // counter +} sync_master_hb_rhy_t; + +sync_master_hb_rhy_t sync_master_hb_rhy[] = +{ + {0, 20}, //SYNC_SYNC_SPEED + {8000, 10}, + {30000, SYNC_MASTER_HB_RHY_PERIOD_MAX} +}; +#define SYNC_MASTER_HB_RHY_NUMBER (sizeof(sync_master_hb_rhy)/sizeof(sync_master_hb_rhy_t)) + +#define SYNC_MASTER_MSG_SENDING_NUM 3 + +typedef struct +{ + sync_msg_t *pmsg; + uint8_t tx_count; + bool flush; +} sync_master_msg_sending_info_t; + +typedef struct +{ + sync_master_msg_sending_info_t info[SYNC_MASTER_MSG_SENDING_NUM]; + volatile uint8_t head; + volatile uint8_t tail; +} sync_master_msg_sending_list_t; + +struct +{ + sync_state_t state; + sync_pair_state_t pair_state; + uint8_t phase; + uint16_t count; + uint16_t count1; + sync_acc_t acc_data; + bool ack; + uint8_t seq_tx; + uint8_t seq_rx; + uint32_t crc_rx; + struct + { + uint16_t interval; + sync_master_hb_state_t state; + bool slow; + uint8_t rhy_idx; + bool rhy_changing; + uint8_t rhy_changing_idx; + uint32_t rhy_counter; + uint32_t time_left; + uint32_t sleep_clk; + uint8_t unack_count; + uint8_t rhy_idx_old; +#if SYNC_SUPPORT_MT1R + uint32_t time_left1; + uint32_t speed; + uint16_t ce_cnt; + uint64_t sleep_time; + bool sleep; + bool sleep_mac; + bool sleep_cal; + bool oversleep; +#endif + } hb; + volatile bool busy; //!< tx or rx ongoing + bool rx; + uint8_t tx_ptr; + uint8_t tx_ptr_hw; + bool tx_null; + bool tx_old; + uint8_t tx_fail_count; + volatile bool handling_ack; + sync_master_msg_sending_list_t msg_sending_list; + sync_msg_t *volatile pmsg_pend_free; + bool tx_align; + volatile uint32_t curr_time_in_ce; //!< us + bool new_event; //!< use a flag to indicate new while the curr_time_in_ce is changing +#if SYNC_SUPPORT_MT1R + struct + { + bool enable; + uint16_t ce_cnt; + uint8_t count; + uint8_t tx_count; + sync_msg_t *msg_txed[7]; + uint8_t msg_ack; + bool skip; + bool req_ack; + uint8_t seq_ack; + uint32_t ce_clk; + } quick; + uint8_t tx_fifo_bitmap; +#endif +} sync_master_ctx; + +struct +{ + sync_master_tx_scheme_t tx_scheme; + bool tx_align_enable; + bool early_high_prio_enable; + bool retrans_at_ack_enable; + bool tx_new_at_retrans_int_enable; + sync_tx_prepare_cb_t tx_prepare_cb; +} sync_master_param; + +#define sync_master_last_tx_is_valid() (sync_master_ctx.tx_null == false && sync_master_ctx.tx_old == false) +#define sync_master_tx_fifo_is_empty(entry) (ppt_get_tx_fifo_rdptr(entry) == ppt_get_tx_fifo_wrptr(entry)) + +void sync_master_start_sync_timer(void); +static void sync_master_ppt_isr_handler(void); + +static void sync_master_init_tx_fifo(void) +{ + ppt_set_tx_fifo_rdptr(0, PPT_TX_FIFO_SIZE - 1); + ppt_force_fw_ack(0); + sync_master_ctx.tx_ptr = ppt_flush_tx_fifo(0); +} + +static void sync_master_close(void) +{ + ppt_reset_hw_fsm(); + sync_master_ctx.tx_ptr = ppt_force_fw_ack(0); +} + +void sync_master_msg_flush(sync_msg_type_t type) +{ + uint8_t head = sync_master_ctx.msg_sending_list.head; + uint8_t tail = sync_master_ctx.msg_sending_list.tail; + while (head != tail) + { + sync_msg_t *pmsg = sync_master_ctx.msg_sending_list.info[head].pmsg; + if (pmsg) + { + if (type == SYNC_MSG_TYPE_ALL || type == pmsg->type) + { + sync_master_ctx.msg_sending_list.info[head].flush = true; + } + } + head = (head + 1) % SYNC_MASTER_MSG_SENDING_NUM; + } +} + +static void sync_master_msg_sending_free(void) +{ +#if SYNC_SUPPORT_MT1R + if (sync_ctx.mt1r.flag) + { + for (uint8_t loop = 0; loop < 7; loop++) + { + sync_msg_t *pmsg = sync_master_ctx.quick.msg_txed[loop]; + if (pmsg == NULL) + { + continue; + } + sync_master_ctx.quick.msg_txed[loop] = NULL; + if (pmsg->send_cb) + { + pmsg->send_info.res = SYNC_SEND_RESULT_UNKNOWN; + sync_pendcall(sync_msg_handle_tx_done, pmsg, 0); + } + else + { + sync_ctx.msg_count[pmsg->type] -= 1; + plt_list_push(&sync_ctx.msg_free, pmsg); + } + } + } +#endif + uint8_t head = sync_master_ctx.msg_sending_list.head; + uint8_t tail = sync_master_ctx.msg_sending_list.tail; + while (head != tail) + { + sync_msg_t *pmsg = sync_master_ctx.msg_sending_list.info[head].pmsg; + if (pmsg) + { + if (pmsg->send_cb) + { + pmsg->send_info.res = SYNC_SEND_RESULT_UNKNOWN; + sync_pendcall(sync_msg_handle_tx_done, pmsg, 0); + } + else + { + sync_ctx.msg_count[pmsg->type] -= 1; + plt_list_push(&sync_ctx.msg_free, pmsg); + } + } + head = (head + 1) % SYNC_MASTER_MSG_SENDING_NUM; + } + sync_master_ctx.msg_sending_list.head = 0; + sync_master_ctx.msg_sending_list.tail = 0; + + /* free the pend msg at the early handler */ + if (sync_master_ctx.pmsg_pend_free) + { + sync_pendcall(sync_msg_handle_tx_done, sync_master_ctx.pmsg_pend_free, 0); + sync_master_ctx.pmsg_pend_free = NULL; + } +} + +PPT_API_SECTION bool sync_master_msg_sending_is_full(void) +{ + uint8_t head = sync_master_ctx.msg_sending_list.head; + uint8_t tail = sync_master_ctx.msg_sending_list.tail; + uint8_t tail_next = (tail + 1) % SYNC_MASTER_MSG_SENDING_NUM; + return tail_next == head; +} + +PPT_API_SECTION bool sync_master_msg_sending_is_empty(void) +{ + uint8_t head = sync_master_ctx.msg_sending_list.head; + uint8_t tail = sync_master_ctx.msg_sending_list.tail; + return head == tail; +} + +PPT_API_SECTION uint8_t sync_master_msg_sending_get_num(void) +{ + uint8_t head = sync_master_ctx.msg_sending_list.head; + uint8_t tail = sync_master_ctx.msg_sending_list.tail; + return tail >= head ? tail - head : tail + SYNC_MASTER_MSG_SENDING_NUM - head; +} + +PPT_API_SECTION uint8_t sync_master_msg_sending_get_data_num(void) +{ + uint8_t head = sync_master_ctx.msg_sending_list.head; + uint8_t tail = sync_master_ctx.msg_sending_list.tail; + uint8_t count = 0; + while (head != tail) + { + if (sync_master_ctx.msg_sending_list.info[head].pmsg) + { + count += 1; + } + head = (head + 1) % SYNC_MASTER_MSG_SENDING_NUM; + } + + return count; +} + +PPT_API_SECTION bool sync_master_msg_sending_push(sync_msg_t *pmsg) +{ + uint8_t head = sync_master_ctx.msg_sending_list.head; + uint8_t tail = sync_master_ctx.msg_sending_list.tail; + uint8_t tail_next = (tail + 1) % SYNC_MASTER_MSG_SENDING_NUM; + if (tail_next == head) + { + APP_PRINT_WARN0("sync: fail push, sending fifo is full"); + return false; + } + memset(&sync_master_ctx.msg_sending_list.info[tail], 0, sizeof(sync_master_msg_sending_info_t)); + sync_master_ctx.msg_sending_list.info[tail].pmsg = pmsg; + sync_master_ctx.msg_sending_list.tail = tail_next; + return true; +} + +PPT_API_SECTION sync_master_msg_sending_info_t *sync_master_msg_sending_peak(void) +{ + uint8_t head = sync_master_ctx.msg_sending_list.head; + uint8_t tail = sync_master_ctx.msg_sending_list.tail; + if (head == tail) + { + return NULL; + } + + return &sync_master_ctx.msg_sending_list.info[head]; +} + +PPT_API_SECTION sync_master_msg_sending_info_t *sync_master_msg_sending_pop(void) +{ + sync_master_msg_sending_info_t *ret = NULL; + uint8_t head = sync_master_ctx.msg_sending_list.head; + uint8_t tail = sync_master_ctx.msg_sending_list.tail; + if (head == tail) + { + APP_PRINT_WARN0("sync: fail pop, sending fifo is empty"); + return ret; + } + ret = &sync_master_ctx.msg_sending_list.info[head]; + sync_master_ctx.msg_sending_list.head = (head + 1) % SYNC_MASTER_MSG_SENDING_NUM; + return ret; +} + +static void sync_master_handle_pair_to(void *para1, uint32_t para2) +{ + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_PAIR_TIMEOUT); + } +} + +static void sync_master_handle_paired(void *para1, uint32_t para2) +{ + sync_bond_info_t bond_info; + bond_info.acc = sync_master_ctx.acc_data; + sync_nvm_set_bond_info(&bond_info); + + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_PAIRED); + } + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_CONNECTED); + } +} + +static void sync_master_handle_connect_to(void *para1, uint32_t para2) +{ + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_CONNECT_TIMEOUT); + } +} + +static void sync_master_handle_connected(void *para1, uint32_t para2) +{ + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_CONNECTED); + } +} + +static void sync_master_handle_disconnected(void *para1, uint32_t para2) +{ + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_CONNECT_LOST); + } +} + +#ifdef SYNC_DEBUG +static void sync_master_handle_sync_lost(void *para1, uint32_t para2) +{ + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_SYNC_LOST); + } +} + +static void sync_master_handle_sync_reconnected(void *para1, uint32_t para2) +{ + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_SYNC_RECONNECTED); + } +} +#endif + +static void sync_master_handle_exception(void) +{ + sync_master_stop(); + sync_pendcall(sync_master_handle_disconnected, 0, 0); +} + +PPT_ISR_SECTION void sync_master_handle_hb_to(void) +{ + if (sync_master_hb_rhy[sync_master_ctx.hb.rhy_idx].period != SYNC_MASTER_HB_RHY_PERIOD_MAX + && sync_master_ctx.hb.rhy_changing == false) + { + sync_master_ctx.hb.rhy_counter += 1; + if (sync_master_ctx.hb.rhy_counter >= sync_master_hb_rhy[sync_master_ctx.hb.rhy_idx].period) + { + sync_master_ctx.hb.rhy_changing_idx = sync_master_ctx.hb.rhy_idx; + sync_master_ctx.hb.rhy_idx += 1; + sync_master_ctx.hb.rhy_changing = true; + APP_PRINT_INFO1("sync: hb changing %dus", sync_master_hb_rhy[sync_master_ctx.hb.rhy_idx].speed); + } + } + + sync_set_header(true, sync_master_ctx.seq_tx); + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_HEARTBEAT; + pdu.heartbeat.interval = sync_master_hb_rhy[sync_master_ctx.hb.rhy_idx].speed / 1000; + ppt_push_tx_data(sizeof(sync_opcode_t) + sizeof(sync_heartbeat_t), (uint8_t *)&pdu); + sync_master_msg_sending_push(NULL); + sync_master_ctx.busy = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); +} + +PPT_ISR_SECTION void sync_master_hb_exit(bool real) +{ + if (sync_master_ctx.hb.slow) + { + sync_master_ctx.hb.slow = false; + APP_PRINT_INFO0("sync: hb exit"); + if (sync_ctx.hb_cb) + { + sync_hb_cb_param_t param = {}; + param.enter = false; + sync_ctx.hb_cb(¶m); + } +#if SYNC_SUPPORT_MT1R + if (sync_master_ctx.quick.enable) + { + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(true); + } + } + else +#endif + { + sync_master_ctx.curr_time_in_ce = 0; + sync_master_start_sync_timer(); + if (SYNC_SYNC_SPEED_HIGH <= SYNC_SYNC_SPEED_HIGH_MIN) + { + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(true); + } + } + } + } + sync_master_ctx.hb.rhy_idx_old = real ? 0 : sync_master_ctx.hb.rhy_idx; + sync_master_ctx.hb.state = SYNC_MASTER_HB_STATE_IDLE; + sync_master_ctx.hb.rhy_changing = false; + sync_master_ctx.hb.rhy_idx = 0; + sync_master_ctx.hb.rhy_counter = 0; + sync_master_ctx.hb.unack_count = 0; +} +#include "rtl_pinmux.h" +PPT_ISR_SECTION void sync_master_push_data(void) +{ + uint32_t lock = sync_enter_critical(); + time_debug_level_high(TIME_DEBUG_CHAN_THREAD); + time_debug_level_low(TIME_DEBUG_CHAN_THREAD); + if (sync_master_msg_sending_is_full()) + { + sync_exit_critical(lock); + return; + } + sync_msg_t *pmsg = NULL; + if (sync_ctx.msg_send.count == 0 +#if SYNC_SUPPORT_MT1R + || sync_master_ctx.quick.req_ack +#endif + ) + { + sync_master_ctx.hb.state = SYNC_MASTER_HB_STATE_EMPTY_SEND; + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_EMPTY; + ppt_push_tx_data(sizeof(sync_opcode_t), (uint8_t *)&pdu); + } + else + { + /* clear hb state & counter in case of tx empty ahead of this */ + sync_master_ctx.hb.state = SYNC_MASTER_HB_STATE_IDLE; + sync_master_ctx.hb.rhy_counter = 0; + pmsg = (sync_msg_t *)plt_list_pop(&sync_ctx.msg_send); + Pad_Config(P3_0, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, + PAD_OUT_LOW); + Pad_Config(P3_0, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, + PAD_OUT_HIGH); + ppt_push_tx_fifo(0, pmsg->data, pmsg->len); + Pad_Config(P3_0, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, + PAD_OUT_LOW); + } + sync_master_msg_sending_push(pmsg); + + /* update at last in case of tx fail */ +#if SYNC_SUPPORT_MT1R + if (sync_master_ctx.quick.enable) + { + sync_master_set_header(sync_master_ctx.quick.seq_ack, sync_master_ctx.seq_tx, + sync_master_ctx.quick.req_ack); + } + else +#endif + { + sync_set_header(sync_master_ctx.rx, sync_master_ctx.seq_tx); + } + time_debug_level_high(TIME_DEBUG_CHAN_THREAD); + time_debug_level_low(TIME_DEBUG_CHAN_THREAD); + sync_exit_critical(lock); +} + +PPT_API_SECTION static void sync_master_tx_restart(void) +{ + sync_master_push_data(); + sync_master_ctx.busy = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); + sync_master_ctx.curr_time_in_ce = 0; + sync_master_start_sync_timer(); +} + +PPT_ISR_SECTION void sync_master_handle_tx_trigger(void) +{ + if (sync_master_ctx.pair_state != SYNC_PAIR_STATE_PAIRED + || sync_master_ctx.state != SYNC_STATE_SYNC) + { + return; + } + +#if SYNC_SUPPORT_MT1R + if (sync_master_ctx.quick.enable) + { + if (sync_master_ctx.busy == false) + { + if (sync_master_ctx.hb.slow) + { + void sync_master_hb_sleep_resume(void); + sync_master_hb_sleep_resume(); + sync_master_push_data(); + sync_master_hb_exit(true); + } + } + } + else +#endif + { + if (sync_master_ctx.busy == false) + { + if (sync_master_ctx.hb.slow) + { + /* quit hb to send msg */ + sync_master_push_data(); + sync_master_ctx.busy = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); + sync_master_hb_exit(true); + APP_PRINT_INFO0("sync: hb exit by tx trigger"); + return; + } + + if (sync_master_param.tx_align_enable) + { + /* in case of retrans ongoing */ + if (sync_master_msg_sending_get_num() == 0) + { + sync_master_tx_restart(); + } + } + } + else + { + if (sync_master_param.tx_align_enable) + { + if (sync_master_msg_sending_get_data_num() == 0) + { + sync_master_ctx.tx_align = true; + } + } + } + } +} + +PPT_ISR_SECTION void sync_master_handle_ack(bool rx, sync_header_t rx_header) +{ + bool enter_critical = false; + uint32_t lock = 0; + /* update ack field first in case tx fail next time */ + sync_set_header(rx, sync_master_ctx.seq_tx); + /* rx ack handling */ + sync_master_ctx.rx = rx; + + if (sync_master_ctx.tx_null) + { + /* empty msg send since no data pushed timely */ + uint8_t seg_vld = RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(0)); + if (sync_master_ctx.tx_fail_count >= 3)//seg_vld == 0 || + { + APP_PRINT_WARN2("sync: fail, tx null ptr %d data 0x%02x, stop here", + sync_master_ctx.tx_ptr_hw, seg_vld); + sync_master_handle_exception(); + return; + } + else + { + /* the null pkt may exhaust one seq, so use new to make sure of normal pkt being handled */ + sync_master_ctx.seq_tx += 1; + sync_set_header(rx, sync_master_ctx.seq_tx); + } + } + else if (sync_master_ctx.tx_old) + { + /* this msg should have already been handled before, so no need handle again */ + uint8_t seg_vld = RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(0)); + /* next pkt may be late */ + //if(seg_vld | (~(1 << sync_master_ctx.tx_ptr_hw))) + /* tx_ptr_hw maybe wrong */ + //if(seg_vld & (1 << sync_master_ctx.tx_ptr_hw)) + if (sync_master_ctx.tx_fail_count >= 3) + { + APP_PRINT_WARN2("sync: fail, tx %d old due to valid data 0x%02x, stop here", + sync_master_ctx.tx_ptr_hw, seg_vld); + sync_master_handle_exception(); + return; + } + } + else + { + bool retrans = false; + sync_master_msg_sending_info_t *pmsg_info = sync_master_msg_sending_peak(); + sync_msg_t *pmsg = pmsg_info ? pmsg_info->pmsg : NULL; + if (pmsg_info && pmsg_info->flush) + { + + } + else if (pmsg && (rx == false || rx_header.ack == false)) + { + switch (pmsg->type) + { + case SYNC_MSG_TYPE_FINITE_RETRANS: + if (sync_ctx.msg_retrans_count < sync_ctx.msg_finite_retrans_num) + { + retrans = true; + } + break; + case SYNC_MSG_TYPE_INFINITE_RETRANS: + retrans = true; + break; + case SYNC_MSG_TYPE_DYNAMIC_RETRANS: + if (sync_ctx.msg_retrans_count < sync_ctx.msg_dynamic_retrans_num_lower || + (sync_ctx.msg_send.count == 0 && + (sync_ctx.msg_dynamic_retrans_num_upper == SYNC_MSG_RETRANS_NUM_INFINITE || + sync_ctx.msg_retrans_count < sync_ctx.msg_dynamic_retrans_num_upper))) + { + retrans = true; + } + break; + default: + break; + } + } + + if (retrans) + { + sync_ctx.msg_retrans_count += 1; + /* header already updated above */ + } + else + { + sync_master_ctx.tx_ptr = ppt_trigger_fw_ack(0); + sync_master_ctx.seq_tx += 1; + sync_master_msg_sending_pop(); + if (pmsg) + { + if (pmsg->send_cb) + { + pmsg->send_info.res = rx ? (rx_header.ack ? SYNC_SEND_RESULT_ACKED : SYNC_SEND_RESULT_NACKED) : + SYNC_SEND_RESULT_UNKNOWN; + pmsg->send_info.retrans_count = sync_ctx.msg_retrans_count; + sync_pendcall(sync_msg_handle_tx_done, pmsg, 0); + } + else + { + sync_ctx.msg_count[pmsg->type] -= 1; + plt_list_push(&sync_ctx.msg_free, pmsg); + } + } + sync_ctx.msg_retrans_count = 0; + } + } + + if (sync_master_param.retrans_at_ack_enable) + { + if (sync_master_tx_fifo_is_empty(0) == false) + { + if (enter_critical == false) + { + lock = sync_enter_critical(); + enter_critical = true; + time_debug_level_low(TIME_DEBUG_CHAN_ISR); + time_debug_level_high(TIME_DEBUG_CHAN_ISR); + } + if (sync_master_ctx.busy == false) + { + /* re-trigger right now */ + sync_master_ctx.busy = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); + /* mark as retransmit tx */ + sync_master_ctx.curr_time_in_ce += 1; + sync_master_ctx.new_event = false; + } + } + } + + if (sync_master_param.early_high_prio_enable) + { + if (sync_master_tx_fifo_is_empty(0)) + { + if (enter_critical == false) + { + lock = sync_enter_critical(); + enter_critical = true; + time_debug_level_low(TIME_DEBUG_CHAN_ISR); + time_debug_level_high(TIME_DEBUG_CHAN_ISR); + } + + if (sync_master_ctx.busy) + { + if (sync_master_ctx.curr_time_in_ce != 0) + { + if (sync_master_param.tx_new_at_retrans_int_enable && sync_ctx.msg_send.count != 0) + { + sync_master_push_data(); + } + else + { + /* cancel before rx handler */ + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_DISABLE); + sync_master_ctx.busy = false; + } + } + else + { + sync_master_push_data(); + } + } + } + } + + if (sync_master_param.tx_align_enable && sync_master_ctx.tx_align) + { + sync_master_ctx.tx_align = false; + if (sync_master_tx_fifo_is_empty(0)) + { + if (enter_critical == false) + { + lock = sync_enter_critical(); + enter_critical = true; + time_debug_level_low(TIME_DEBUG_CHAN_ISR); + time_debug_level_high(TIME_DEBUG_CHAN_ISR); + } + if (sync_master_ctx.busy == false) + { + sync_master_tx_restart(); + } + } + } + + /* update header in case tx at early */ + sync_set_header(rx, sync_master_ctx.seq_tx); + time_debug_level_low(TIME_DEBUG_CHAN_ISR); + time_debug_level_high(TIME_DEBUG_CHAN_ISR); + sync_master_ctx.handling_ack = false; + if (enter_critical) + { + sync_exit_critical(lock); + } +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_ctx.power_ctrl_count_fail += !(rx && rx_header.ack); + } +#endif +} + +PPT_ISR_SECTION void sync_master_handle_rx(bool rx, sync_header_t rx_header, sync_pdu_t *rx_pdu, + uint16_t rx_len, uint16_t rssi, uint32_t rx_crc) +{ + uint32_t lock = sync_enter_critical(); + time_debug_level_low(TIME_DEBUG_CHAN_ISR); + if (sync_master_tx_fifo_is_empty(0)) + { + /* hb handling */ + bool rx_empty = false; + bool rx_ack = false; + if (rx) + { + if (rx_len == sizeof(sync_opcode_t) && rx_pdu->opcode == SYNC_OPCODE_EMPTY) + { + rx_empty = true; + } + if (rx_header.ack) + { + rx_ack = true; + } + } + //APP_PRINT_INFO4("sync: hb rhy idx %d counter %d, state %d, rx emtpy %d", + // sync_master_ctx.hb.rhy_idx, + // sync_master_ctx.hb.rhy_counter, sync_master_ctx.hb.state, rx_empty); + if (SYNC_MASTER_HB_RHY_NUMBER <= 1 || sync_master_ctx.hb.state == SYNC_MASTER_HB_STATE_IDLE) + { + + } + else if (sync_master_ctx.hb.state == SYNC_MASTER_HB_STATE_EMPTY_SEND) + { + bool tx_hb = false; + /* tx pend message */ + if (sync_ctx.msg_send.count != 0) + { + sync_master_hb_exit(true); + } + else if (rx_empty && rx_ack) + { + if (sync_master_hb_rhy[0].period != SYNC_MASTER_HB_RHY_PERIOD_MAX) + { + if (sync_master_ctx.hb.rhy_idx_old != 0) + { + tx_hb = true; + sync_master_ctx.hb.rhy_idx = sync_master_ctx.hb.rhy_idx_old; + sync_master_ctx.hb.rhy_changing = true; + sync_master_ctx.hb.rhy_changing_idx = 0; + } + else + { + sync_master_ctx.hb.rhy_counter += 1; + if (sync_master_ctx.hb.rhy_counter >= sync_master_hb_rhy[0].period) + { + tx_hb = true; + sync_master_ctx.hb.rhy_idx = 1; + sync_master_ctx.hb.rhy_changing = true; + sync_master_ctx.hb.rhy_changing_idx = 0; + } + } + } + } + else + { + /* ignore if be disturbed */ + if (sync_master_ctx.rx) + { + sync_master_ctx.hb.rhy_counter = 0; + } + } + if (sync_master_ctx.hb.rhy_changing) + { + uint32_t speed = sync_master_ctx.hb.rhy_changing_idx == 0 ? SYNC_SYNC_SPEED : + sync_master_hb_rhy[sync_master_ctx.hb.rhy_changing_idx].speed; + sync_hw_timer_start(speed, sync_master_handle_hb_to); + if (sync_master_param.early_high_prio_enable) + { + /* cancel if already triggered */ + if (sync_master_ctx.busy) + { + ppt_kill_hw_fsm(PPT_FSM_PTX); + sync_master_ctx.busy = false; + } + } + sync_master_ctx.hb.state = SYNC_MASTER_HB_STATE_HB_SEND; + sync_master_ctx.hb.slow = true; + APP_PRINT_INFO1("sync: hb changing %dus", sync_master_hb_rhy[sync_master_ctx.hb.rhy_idx].speed); + } + } + else if (sync_master_ctx.hb.state == SYNC_MASTER_HB_STATE_HB_SEND) + { + bool hb_enter = false; + bool hb_continue = false; + /* tx pend message */ + if (sync_ctx.msg_send.count != 0) + { + sync_master_hb_exit(true); + } + /* rx empty ack message */ + else if (rx_ack && rx_empty) + { + sync_master_ctx.hb.unack_count = 0; + hb_enter = true; + } + else + { + /* rx non-empty message */ + if (sync_master_ctx.rx && rx_empty == false) + { + sync_master_hb_exit(true); + } + /* no rx or rx non-ack */ + else if (rx_ack == false) + { + /* skip wakeup only when already in the hb state */ + if (sync_master_ctx.hb.slow) + { + sync_master_ctx.hb.unack_count += 1; + if (sync_master_ctx.hb.unack_count >= SYNC_HB_TO_TIMES) + { + sync_master_hb_exit(false); + } + else + { + hb_continue = true; + sync_master_ctx.hb.rhy_counter -= 1; + } + } + } + } + + if (hb_enter || hb_continue) + { + if (hb_enter && sync_master_ctx.hb.rhy_changing) + { + sync_master_ctx.hb.rhy_changing = false; + sync_master_ctx.hb.rhy_counter = 0; + APP_PRINT_INFO1("sync: hb enter %dus", + sync_master_hb_rhy[sync_master_ctx.hb.rhy_idx].speed); + if (sync_ctx.hb_cb) + { + sync_hb_cb_param_t param = {}; + param.enter = true; + param.interval = sync_master_hb_rhy[sync_master_ctx.hb.rhy_idx].speed; + sync_ctx.hb_cb(¶m); + } + } + uint32_t speed = 0; + if (sync_master_ctx.hb.rhy_changing) + { + speed = sync_master_ctx.hb.rhy_changing_idx == 0 ? SYNC_SYNC_SPEED : + sync_master_hb_rhy[sync_master_ctx.hb.rhy_changing_idx].speed; + } + else + { + speed = sync_master_hb_rhy[sync_master_ctx.hb.rhy_idx].speed; + } + sync_hw_timer_start(speed, sync_master_handle_hb_to); + sync_master_ctx.hb.slow = true; + if (speed > SYNC_SYNC_SPEED) + { + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(false); + } + } + } + } + } + time_debug_level_high(TIME_DEBUG_CHAN_ISR); + sync_exit_critical(lock); + + if (rx) + { + if (rx_header.seq != sync_master_ctx.seq_rx)// || rx_crc != sync_master_ctx.crc_rx + { + sync_master_ctx.seq_rx = rx_header.seq; + sync_master_ctx.crc_rx = rx_crc; + if (rx_len > 0) + { + if (rx_pdu->opcode >= SYNC_OPCODE_APP_START) + { + if (sync_ctx.msg_receive_cb) + { + sync_receive_info_t info = {}; + info.rssi = ppt_get_rssi(rssi); + sync_ctx.msg_receive_cb((uint8_t *)rx_pdu, rx_len, &info); + } + } + else + { + bool handle = false; + switch (rx_pdu->opcode) + { + case SYNC_OPCODE_EMPTY: + if (rx_len == 1) + { + handle = true; + } + break; + default: + break; + } + if (handle == false) + { + APP_PRINT_WARN3("sync: rx unknown opcode %d, len %d, data %b", rx_pdu->opcode, rx_len, + TRACE_BINARY(rx_len, rx_pdu)); + } + } + } + } + } +} + +PPT_ISR_SECTION void sync_master_handle_sync_fast_early(void) +{ + sync_hw_timer_start(SYNC_SYNC_FAST_SPEED, sync_master_handle_sync_fast_early); + if (sync_master_ctx.busy) + { + return; + } + sync_master_ctx.busy = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); +} + +PPT_ISR_SECTION void sync_master_handle_sync_slow_early(void) +{ + uint32_t speed = sync_master_ctx.pair_state == SYNC_PAIR_STATE_PAIRED ? SYNC_SYNC_SLOW_SPEED : + sync_master_ctx.phase == SYNC_MASTER_PAIR_PHASE_SEND_CFM ? SYNC_SYNC_SPEED : SYNC_PAIR_SPEED; + sync_hw_timer_start(speed, sync_master_handle_sync_slow_early); + if (sync_master_ctx.busy) + { + return; + } + sync_master_ctx.busy = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); +} + +PPT_ISR_SECTION void sync_master_handle_sync_early(void) +{ + sync_master_ctx.curr_time_in_ce += SYNC_SYNC_SPEED_HIGH; + if (sync_master_ctx.busy) + { + APP_PRINT_WARN1("sync: busy at %d", sync_master_ctx.curr_time_in_ce); + return; + } + + if (sync_master_ctx.curr_time_in_ce >= SYNC_SYNC_SPEED) + { + sync_master_ctx.curr_time_in_ce = 0; + time_debug_level_low(TIME_DEBUG_CHAN_TIMING); + time_debug_level_high(TIME_DEBUG_CHAN_TIMING); + } + + /* tx if necessary */ + sync_master_msg_sending_info_t *pmsg_info = sync_master_msg_sending_peak(); + bool tx_new = sync_master_ctx.curr_time_in_ce == 0 + || (sync_master_param.tx_new_at_retrans_int_enable && sync_ctx.msg_send.count != 0); + if (tx_new || (false == sync_master_tx_fifo_is_empty(0) && pmsg_info && pmsg_info->pmsg && + pmsg_info->tx_count)) + { + sync_master_ctx.busy = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); + sync_master_ctx.new_event = sync_master_ctx.curr_time_in_ce == 0; + time_debug_level_low(TIME_DEBUG_CHAN_TIMING); + + if (sync_master_ctx.curr_time_in_ce == 0) + { + if (sync_master_param.early_high_prio_enable == false) + { + if (sync_master_param.tx_prepare_cb != NULL) + { + time_debug_level_high(TIME_DEBUG_CHAN_TIMING); + /* in case of calling tx trigger */ + sync_master_param.tx_prepare_cb(); + time_debug_level_low(TIME_DEBUG_CHAN_TIMING); + } + } + } + + if (tx_new) + { + if (sync_master_ctx.handling_ack == false) + { + /* quit dynamic msg if necessary */ + if (pmsg_info) + { + time_debug_level_high(TIME_DEBUG_CHAN_TIMING); + time_debug_level_low(TIME_DEBUG_CHAN_TIMING); + sync_msg_t *pmsg = pmsg_info->pmsg; + if (pmsg && pmsg->type == SYNC_MSG_TYPE_DYNAMIC_RETRANS + && sync_ctx.msg_retrans_count > sync_ctx.msg_dynamic_retrans_num_lower + && sync_ctx.msg_send.count != 0) + { + sync_master_ctx.tx_ptr = ppt_trigger_fw_ack(0); + sync_master_ctx.seq_tx += 1; + pmsg->send_info.retrans_count = sync_ctx.msg_retrans_count; + sync_master_msg_sending_pop(); + sync_master_push_data(); + if (pmsg->send_cb) + { + pmsg->send_info.res = SYNC_SEND_RESULT_UNKNOWN; + /* the early handler can't call os api when at high priority */ + if (sync_master_param.early_high_prio_enable) + { + if (sync_master_ctx.pmsg_pend_free) + { + APP_PRINT_ERROR0("sync: pmsg_pend_free error"); + while (1); + } + sync_master_ctx.pmsg_pend_free = pmsg; + } + else + { + sync_pendcall(sync_msg_handle_tx_done, pmsg, 0); + } + } + else + { + sync_ctx.msg_count[pmsg->type] -= 1; + plt_list_push(&sync_ctx.msg_free, pmsg); + } + } + } + else + { + if (sync_master_msg_sending_get_num() <= 1) + { + sync_master_push_data(); + } + } + } + } + + time_debug_level_high(TIME_DEBUG_CHAN_TIMING); + //APP_PRINT_TRACE1("sync: early curr_time_in_ce %d", sync_master_ctx.curr_time_in_ce); + } +} + +PPT_ISR_SECTION void sync_master_handle_sync_fast_to(void) +{ + sync_master_ctx.count += 1; + if ( +#if SYNC_SUPPORT_SYNC_FAST_DISABLE + SYNC_SYNC_FAST_PERIOD == SYNC_SYNC_FAST_PERIOD_DISABLE || +#endif + sync_master_ctx.count >= SYNC_MASTER_SYNC_FAST_TIMES) + { + sync_hw_timer_stop(); + sync_master_close(); + sync_master_msg_sending_free(); +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_power_ctrl_stop(); + } +#endif +#if SYNC_SUPPORT_MT1R + ppt_reg_handler(sync_master_ppt_isr_handler); +#endif + sync_master_ctx.pair_state = SYNC_PAIR_STATE_IDLE; + sync_master_ctx.state = SYNC_STATE_IDLE; + APP_PRINT_INFO0("sync: disconnected!"); + sync_channel_print_statistic(); + sync_pendcall(sync_master_handle_disconnected, 0, 0); + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(false); + } + } + else + { + sync_channel_change_next_in_group(); + if (sync_master_msg_sending_is_empty()) + { + sync_master_push_data(); + } + } +} + +PPT_ISR_SECTION void sync_master_handle_sync_lost_to(void) +{ + sync_header_t rx_header = {}; + sync_master_handle_rx(false, rx_header, NULL, 0, 0, 0); + + sync_ctx.chan_miss_count += 1; + if (sync_master_ctx.curr_time_in_ce == 0 || sync_master_ctx.new_event) + { + sync_master_ctx.count += 1; + } + if (sync_master_ctx.count >= SYNC_MASTER_SYNC_LOST_TIMES) + { + sync_hw_timer_stop(); + if (sync_master_param.early_high_prio_enable) + { + if (sync_master_ctx.busy) + { + ppt_kill_hw_fsm(PPT_FSM_PTX); + sync_master_ctx.busy = false; + } + } + APP_PRINT_INFO4("sync: lost at %d[%d], miss %d/%d!", sync_ctx.chan_cur, + sync_ctx.chan_cfg[sync_ctx.chan_cur].freq, + sync_ctx.chan_miss_count, sync_ctx.chan_used_count); +#ifdef SYNC_DEBUG + sync_pendcall(sync_master_handle_sync_lost, 0, 0); +#endif +#if SYNC_SUPPORT_SYNC_FAST_DISABLE + if (SYNC_SYNC_FAST_PERIOD == SYNC_SYNC_FAST_PERIOD_DISABLE) + { + sync_master_handle_sync_fast_to(); + } + else +#endif + { + sync_master_ctx.state = SYNC_STATE_SYNC_FAST; + sync_master_ctx.count = 0; + sync_channel_change_next_in_group(); + if (sync_master_msg_sending_is_empty()) + { + sync_master_push_data(); + } + sync_master_ctx.busy = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); + sync_hw_timer_start(SYNC_SYNC_FAST_SPEED, sync_master_handle_sync_fast_early); + } + } +} + +PPT_ISR_SECTION void sync_master_handle_sync_slow_to(void) +{ + sync_master_ctx.count += 1; + if (sync_master_ctx.count < SYNC_MASTER_SYNC_SLOW_TIMES) + { + sync_channel_change_next(); + } + else + { + sync_hw_timer_stop(); + sync_master_close(); + sync_master_ctx.pair_state = SYNC_PAIR_STATE_IDLE; + sync_master_ctx.state = SYNC_STATE_IDLE; + APP_PRINT_INFO0("sync: connect failed!"); + sync_pendcall(sync_master_handle_connect_to, 0, 0); + } +} + +PPT_ISR_SECTION void sync_master_handle_pair_req_to(void) +{ + sync_master_ctx.count += 1; + if (sync_master_ctx.count < SYNC_MASTER_PAIR_TIMES) + { + sync_channel_change_next(); + } + else + { + sync_hw_timer_stop(); + sync_master_close(); + sync_master_ctx.pair_state = SYNC_PAIR_STATE_IDLE; + sync_master_ctx.state = SYNC_STATE_IDLE; + APP_PRINT_INFO0("sync: pair failed!"); + sync_pendcall(sync_master_handle_pair_to, 0, 0); + } +} + +PPT_ISR_SECTION void sync_master_handle_pair_cfm_to(void) +{ + sync_master_ctx.count1 += 1; + if (sync_master_ctx.count1 >= SYNC_MASTER_PAIR_CFM_TIMES) + { + sync_master_ctx.phase = SYNC_MASTER_PAIR_PHASE_SEND_REQ; + APP_PRINT_ERROR0("sync: back to pair!"); + + sync_master_ctx.tx_ptr = ppt_trigger_fw_ack(0); + sync_ppt_set_tifs(true); + sync_hw_timer_start(SYNC_PAIR_SPEED, sync_master_handle_sync_slow_early); + sync_acc_t acc = {.addr = SYNC_ACC_ADDR_BR}; + sync_acc_set(acc); + + uint16_t interval = SYNC_SYNC_SPEED; +#if SYNC_SUPPORT_MT1R + if (sync_ctx.mt1r.flag) + { + interval = 125; + sync_ctx.conn_interval = 250; + } +#endif + sync_set_header(0, 0); + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_PAIR_REQ; + pdu.pair_req.interval = interval; + ppt_push_tx_data(sizeof(sync_opcode_t) + sizeof(sync_pair_req_t), (uint8_t *)&pdu); + + sync_master_handle_pair_req_to(); + } +} + +PPT_API_SECTION void sync_master_start_sync_timer(void) +{ + sync_hw_timer_start(SYNC_SYNC_SPEED_HIGH, sync_master_handle_sync_early); + sync_hw_timer_set_periodic(true); + if (sync_master_param.early_high_prio_enable) + { + sync_hw_timer_impr_ip(true); + } +} + +#if SYNC_SUPPORT_MT1R +PPT_ISR_SECTION void sync_master_pop_data(void) +{ + sync_master_ctx.tx_ptr = ppt_trigger_fw_ack(0); + sync_master_ctx.seq_tx += 1; + sync_master_msg_sending_info_t *pmsg_info = sync_master_msg_sending_pop(); + sync_msg_t *pmsg = pmsg_info ? pmsg_info->pmsg : NULL; + if (pmsg) + { + if (sync_master_ctx.quick.msg_txed[sync_master_ctx.quick.tx_count] != NULL) + { + APP_PRINT_ERROR2("sync: fail to free msg, slot %d, ptr 0x%0x", sync_master_ctx.quick.tx_count, + sync_master_ctx.quick.msg_txed[sync_master_ctx.quick.tx_count]); + sync_pendcall(sync_msg_handle_tx_done, + sync_master_ctx.quick.msg_txed[sync_master_ctx.quick.tx_count], 0); + } + sync_master_ctx.quick.msg_txed[sync_master_ctx.quick.tx_count] = pmsg; + } +} + +PPT_ISR_SECTION void sync_master_handle_rx_quick(bool rx, sync_header_t rx_header, + sync_pdu_t *rx_pdu, + uint16_t rx_len, uint16_t rssi, uint32_t rx_crc) +{ + if (rx && rx_header.s.seq == sync_master_ctx.seq_rx) + { + if (SYNC_ISR_DEBUG) + { + APP_PRINT_WARN0("sync_isr: ptx rx old pkt"); + } + rx = false; + } + + sync_master_ctx.quick.req_ack = rx == false; + sync_master_ctx.quick.seq_ack += rx; + sync_master_ctx.rx = rx; +#if TIME_DEBUG + if (sync_master_ctx.quick.seq_ack & 0x1) + { + time_debug_level_high(SYNC_MASTER_TIME_DEBUG_SEQ_ACK); + } + else + { + time_debug_level_low(SYNC_MASTER_TIME_DEBUG_SEQ_ACK); + } +#endif + sync_master_set_header(sync_master_ctx.quick.seq_ack, sync_master_ctx.seq_tx, + sync_master_ctx.quick.req_ack); + WR_PPT_REG_FIELD(PRO_TRX_CONTROL, ack_enable, sync_master_ctx.quick.req_ack); + if (sync_master_ctx.quick.req_ack) + { + time_debug_level_high(TIME_DEBUG_CHAN_THREAD); + time_debug_level_low(TIME_DEBUG_CHAN_THREAD); + } + + sync_master_pop_data(); + + /* hb handling */ + if (sync_master_ctx.quick.tx_count == 6) + { + bool rx_empty = false; + bool rx_ack = false; + if (rx) + { + if (rx_len == sizeof(sync_opcode_t) && rx_pdu->opcode == SYNC_OPCODE_EMPTY) + { + rx_empty = true; + } + if (rx_header.ack) + { + rx_ack = true; + } + } + //APP_PRINT_INFO4("sync: hb rhy idx %d counter %d, state %d, rx emtpy %d", + // sync_master_ctx.hb.rhy_idx, + // sync_master_ctx.hb.rhy_counter, sync_master_ctx.hb.state, rx_empty); + if (SYNC_MASTER_HB_RHY_NUMBER <= 1 || sync_master_ctx.hb.state == SYNC_MASTER_HB_STATE_IDLE) + { + + } + else if (sync_master_ctx.hb.state == SYNC_MASTER_HB_STATE_EMPTY_SEND) + { + bool tx_hb = false; + /* tx pend message */ + if (sync_ctx.msg_send.count != 0) + { + sync_master_ctx.hb.rhy_counter = 0; + } + else if (rx_empty && rx_ack) + { + if (sync_master_hb_rhy[0].period != SYNC_MASTER_HB_RHY_PERIOD_MAX) + { + if (sync_master_ctx.hb.rhy_idx_old != 0) + { + tx_hb = true; + sync_master_ctx.hb.rhy_idx = sync_master_ctx.hb.rhy_idx_old; + sync_master_ctx.hb.rhy_changing = true; + sync_master_ctx.hb.rhy_changing_idx = 0; + } + else + { + sync_master_ctx.hb.rhy_counter += 1; + if (sync_master_ctx.hb.rhy_counter >= sync_master_hb_rhy[0].period) + { + tx_hb = true; + sync_master_ctx.hb.rhy_idx = 1; + sync_master_ctx.hb.rhy_changing = true; + sync_master_ctx.hb.rhy_changing_idx = 0; + } + } + } + } + else + { + /* ignore if be disturbed */ + if (sync_master_ctx.rx) + { + sync_master_ctx.hb.rhy_counter = 0; + } + } + if (sync_master_ctx.hb.rhy_changing) + { + sync_master_ctx.hb.speed = sync_master_ctx.hb.rhy_changing_idx == 0 ? SYNC_SYNC_SPEED : + sync_master_hb_rhy[sync_master_ctx.hb.rhy_changing_idx].speed; + sync_master_ctx.hb.ce_cnt = sync_master_ctx.quick.ce_cnt + sync_master_ctx.hb.speed / + SYNC_SYNC_SPEED; + if (sync_master_param.early_high_prio_enable) + { + /* cancel if already triggered */ + if (sync_master_ctx.busy) + { + ppt_kill_hw_fsm(PPT_FSM_PTX); + sync_master_ctx.busy = false; + } + } + sync_master_ctx.hb.state = SYNC_MASTER_HB_STATE_HB_SEND; + sync_master_ctx.hb.slow = true; + } + } + else if (sync_master_ctx.hb.state == SYNC_MASTER_HB_STATE_HB_SEND) + { + bool hb_enter = false; + bool hb_continue = false; + /* tx pend message */ + if (sync_ctx.msg_send.count != 0) + { + sync_master_ctx.hb.slow = false; + sync_master_hb_exit(true); + } + /* rx empty ack message */ + else if (rx_ack && rx_empty) + { + sync_master_ctx.hb.unack_count = 0; + hb_enter = true; + } + else + { + /* rx non-empty message */ + if (sync_master_ctx.rx && rx_empty == false) + { + sync_master_ctx.hb.slow = false; + sync_master_hb_exit(true); + } + /* no rx or rx non-ack */ + else if (rx_ack == false) + { + /* skip wakeup only when already in the hb state */ + if (sync_master_ctx.hb.slow) + { + sync_master_ctx.hb.unack_count += 1; + if (sync_master_ctx.hb.unack_count >= SYNC_HB_TO_TIMES) + { + sync_master_ctx.hb.slow = false; + sync_master_ctx.count = 0; + sync_master_hb_exit(false); + } + else + { + hb_continue = true; + sync_master_ctx.hb.rhy_counter -= 1; + } + } + } + } + + if (hb_enter || hb_continue) + { + if (hb_enter && sync_master_ctx.hb.rhy_changing) + { + sync_master_ctx.hb.rhy_changing = false; + sync_master_ctx.hb.rhy_counter = 0; + APP_PRINT_INFO1("sync: hb enter %dus", + sync_master_hb_rhy[sync_master_ctx.hb.rhy_idx].speed); + if (sync_ctx.hb_cb) + { + sync_hb_cb_param_t param = {}; + param.enter = true; + param.interval = sync_master_hb_rhy[sync_master_ctx.hb.rhy_idx].speed; + sync_ctx.hb_cb(¶m); + } + } + if (sync_master_ctx.hb.rhy_changing) + { + sync_master_ctx.hb.speed = sync_master_ctx.hb.rhy_changing_idx == 0 ? SYNC_SYNC_SPEED : + sync_master_hb_rhy[sync_master_ctx.hb.rhy_changing_idx].speed; + } + else + { + sync_master_ctx.hb.speed = sync_master_hb_rhy[sync_master_ctx.hb.rhy_idx].speed; + } + sync_master_ctx.hb.ce_cnt = sync_master_ctx.quick.ce_cnt + sync_master_ctx.hb.speed / + SYNC_SYNC_SPEED; + sync_master_ctx.hb.slow = true; + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(false); + } + } + } + } + + if (sync_master_ctx.hb.slow == false) + { + sync_master_push_data(); + } + + if (sync_master_ctx.quick.req_ack == false) + { + for (uint8_t loop = 0; loop < 7; loop++) + { + bool ack = rx && (sync_master_ctx.quick.msg_ack & BIT(loop)); +#if TIME_DEBUG + if (ack) + { + time_debug_level_high(SYNC_MASTER_TIME_DEBUG_ACK); + time_debug_level_low(SYNC_MASTER_TIME_DEBUG_ACK); + } + else + { + time_debug_level_high(SYNC_MASTER_TIME_DEBUG_NACK); + time_debug_level_low(SYNC_MASTER_TIME_DEBUG_NACK); + time_debug_level_high(SYNC_MASTER_TIME_DEBUG_NACK); + time_debug_level_low(SYNC_MASTER_TIME_DEBUG_NACK); + } +#endif + sync_msg_t *pmsg = sync_master_ctx.quick.msg_txed[loop]; + if (pmsg) + { + sync_master_ctx.quick.msg_txed[loop] = NULL; + pmsg->send_info.res = ack ? SYNC_SEND_RESULT_ACKED : SYNC_SEND_RESULT_NACKED; + sync_pendcall(sync_msg_handle_tx_done, pmsg, 0); + } + } + } + + if (rx) + { + if (rx_header.s.seq != sync_master_ctx.seq_rx)// || rx_crc != sync_master_ctx.crc_rx + { + sync_master_ctx.seq_rx = rx_header.s.seq; + sync_master_ctx.crc_rx = rx_crc; + if (rx_len > 0) + { + if (rx_pdu->opcode >= SYNC_OPCODE_APP_START) + { + if (sync_ctx.msg_receive_cb) + { + sync_receive_info_t info = {}; + info.rssi = ppt_get_rssi(rssi); + sync_ctx.msg_receive_cb((uint8_t *)rx_pdu, rx_len, &info); + } + } + else + { + bool handle = false; + switch (rx_pdu->opcode) + { + case SYNC_OPCODE_EMPTY: + if (rx_len == 1) + { + handle = true; + } + break; + default: + break; + } + if (handle == false) + { + APP_PRINT_WARN3("sync: rx unknown opcode %d, len %d, data %b", rx_pdu->opcode, rx_len, + TRACE_BINARY(rx_len, rx_pdu)); + } + } + } + } + } +} + +PPT_ISR_SECTION void sync_master_handle_sync_fast_to_quick(void) +{ + sync_header_t rx_header = {}; + sync_master_handle_rx_quick(false, rx_header, NULL, 0, 0, 0); + + if (sync_master_ctx.quick.tx_count < 6) + { + return; + } + + sync_master_ctx.count += 1; + if ( +#if SYNC_SUPPORT_SYNC_FAST_DISABLE + SYNC_SYNC_FAST_PERIOD == SYNC_SYNC_FAST_PERIOD_DISABLE || +#endif + sync_master_ctx.count >= SYNC_MASTER_SYNC_FAST_TIMES) + { + sync_hw_timer_stop(); + sync_master_close(); + sync_master_msg_sending_free(); +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_power_ctrl_stop(); + } +#endif + ppt_reg_handler(sync_master_ppt_isr_handler); + sync_master_ctx.pair_state = SYNC_PAIR_STATE_IDLE; + sync_master_ctx.state = SYNC_STATE_IDLE; + APP_PRINT_INFO0("sync: disconnected!"); + sync_channel_print_statistic(); + sync_pendcall(sync_master_handle_disconnected, 0, 0); + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(false); + } + } + else + { + sync_channel_change_next_in_group(); + if (sync_master_msg_sending_is_empty()) + { + sync_master_push_data(); + } + } +} + +PPT_ISR_SECTION void sync_master_handle_sync_lost_to_quick(void) +{ + sync_header_t rx_header = {}; + sync_master_handle_rx_quick(false, rx_header, NULL, 0, 0, 0); + + if (sync_master_ctx.quick.tx_count < 6) + { + return; + } + + sync_master_ctx.count += 1; + if (sync_master_ctx.count >= SYNC_MASTER_SYNC_LOST_TIMES) + { + APP_PRINT_INFO0("sync: lost!"); +#ifdef SYNC_DEBUG + sync_pendcall(sync_master_handle_sync_lost, 0, 0); +#endif +#if SYNC_SUPPORT_SYNC_FAST_DISABLE + if (SYNC_SYNC_FAST_PERIOD == SYNC_SYNC_FAST_PERIOD_DISABLE) + { + sync_master_handle_sync_fast_to_quick(); + } + else +#endif + { + sync_master_ctx.state = SYNC_STATE_SYNC_FAST; + sync_master_ctx.count = 0; + sync_channel_change_next_in_group(); + } + } +} + +PPT_ISR_SECTION void sync_master_handle_sync_early_quick(void) +{ + sync_master_ctx.quick.count = (sync_master_ctx.quick.count + 1) & 0x7; + if (sync_master_ctx.quick.skip) + { + sync_master_ctx.quick.skip = false; + /* no need protect since it shall be idle currently */ + if (sync_master_ctx.quick.tx_count == 6) + { + sync_master_ctx.quick.tx_count = 0; + } + else + { + if (sync_master_ctx.quick.req_ack && (sync_master_ctx.quick.count & 0x1)) + { + /* in case skip even slot */ + sync_master_ctx.quick.tx_count += 2; + } + else + { + sync_master_ctx.quick.tx_count += 1; + } + } + } + if (sync_master_ctx.quick.count == 0) + { + time_debug_level_low(TIME_DEBUG_CHAN_TIMING); + sync_master_ctx.quick.ce_cnt += 1; + WR_PPT_REG_FIELD(PRO_TRX_CONTROL, ack_enable, 0); + WR_PPT_REG_FIELD(PRO_HS_LENGTH, hs_field_length_4_0, 0); + sync_master_ctx.quick.ce_clk = sync_hw_frtimer_get_count(); + } + else if (sync_master_ctx.quick.count == 7) + { + return; + } + else + { + if (sync_master_ctx.quick.count == 6) + { + WR_PPT_REG_FIELD(PRO_TRX_CONTROL, ack_enable, 1); + } + } + + if (sync_master_ctx.quick.req_ack) + { + if (sync_master_ctx.quick.count & 0x1) + { + return; + } + WR_PPT_REG_FIELD(PRO_TRX_CONTROL, ack_enable, 1); + } + + if (sync_master_ctx.hb.slow) + { + sync_master_ctx.quick.tx_count = sync_master_ctx.quick.count; + if (sync_master_ctx.hb.ce_cnt == sync_master_ctx.quick.ce_cnt || sync_master_ctx.hb.oversleep) + { + if (sync_master_ctx.quick.count == 6) + { + sync_master_handle_hb_to(); + sync_master_ctx.hb.ce_cnt += sync_master_ctx.hb.speed / SYNC_SYNC_SPEED; + sync_master_ctx.hb.oversleep = false; + } + else if (sync_master_ctx.quick.count == 5) + { + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(true); + } + } + } + } + else if (sync_ctx.hw_timer_pend_time <= 12 || sync_master_ctx.quick.count == 6) + { + sync_master_ctx.busy = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); + } + else + { + sync_master_ctx.quick.skip = true; + if (SYNC_ISR_DEBUG) + { + APP_PRINT_WARN2("sync_isr: skip count %d, timer pend %dus", sync_master_ctx.quick.count, + sync_ctx.hw_timer_pend_time); + } + } + if (sync_master_ctx.quick.count == 0) + { + time_debug_level_high(TIME_DEBUG_CHAN_TIMING); + } +} + +PPT_ISR_SECTION static void sync_master_ppt_isr_handler_quick(void) +{ + time_debug_level_high(TIME_DEBUG_CHAN_ISR); + PRO_MISR_TYPE reg_misr; + reg_misr.d16 = RD_PPT_REG(PRO_MISR); + //APP_PRINT_INFO1("sync_isr: ptx isr 0x%04x", reg_misr.d16); + + if (reg_misr.tx_int) + { + sync_master_ctx.busy = false; + /* pop tx stack */ + PRO_TX_STACK_TYPE tx_stack = {.d16 = RD_PPT_REG(PRO_TX_STACK)}; + //PRO_TX_HS_LOWER_TYPE hs_lower = {.d16 = RD_PPT_REG(PRO_TX_HS_LOWER)}; + //PRO_TX_HS_UPPER_TYPE hs_upper = {.d16 = RD_PPT_REG(PRO_TX_HS_UPPER)}; + int32_t clk_diff = 0; + if (sync_master_ctx.hb.slow == false) + { + PRO_TX_CLK_LOWER_TYPE tx_clk_lower = {.d16 = RD_PPT_REG(PRO_TX_CLK_LOWER)}; + PRO_TX_CLK_UPPER_TYPE tx_clk_upper = {.d16 = RD_PPT_REG(PRO_TX_CLK_UPPER)}; + uint32_t tx_clk = (tx_clk_upper.tx_clk_31_16 << 16) + tx_clk_lower.tx_clk_15_0; + clk_diff = sync_hw_frtimer_time_diff(tx_clk, sync_master_ctx.quick.ce_clk); + if (clk_diff >= 0) + { + uint8_t tx_count = clk_diff / 125; + if (sync_master_ctx.quick.tx_count != tx_count) + { + APP_PRINT_WARN2("sync_isr: ptx tx count fail, add %d cal %d", sync_master_ctx.quick.tx_count, + tx_count); + sync_master_ctx.quick.tx_count = tx_count; + } + } + } + PRO_TX_LENGTH_TYPE length = {.d16 = RD_PPT_REG(PRO_TX_LENGTH)}; // Read 0x6a to pop stack + sync_master_ctx.tx_fifo_bitmap = RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID(( + uint8_t)tx_stack.tx_entry_1_0)); + if (SYNC_ISR_DEBUG) + { + APP_PRINT_INFO8("sync_isr: ptx tx stack 0x%04x, bitmap 0x%02x, rd ptr hw %d sw %d, len %d, ce %d, clk %d, tx %d", + tx_stack.d16, sync_master_ctx.tx_fifo_bitmap, + tx_stack.tx_ptr, sync_master_ctx.tx_ptr, length.tx_length, sync_master_ctx.quick.ce_cnt, clk_diff, + sync_master_ctx.quick.tx_count); + } + sync_master_ctx.tx_ptr_hw = tx_stack.tx_ptr; + sync_master_ctx.tx_null = length.tx_length == 0; + sync_master_ctx.tx_old = (sync_master_ctx.tx_null == false) && + (tx_stack.tx_ptr != sync_master_ctx.tx_ptr); + + /* workaround when only tx empty pkt */ + if (sync_master_ctx.tx_null + && 0 == RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID((uint8_t)tx_stack.tx_entry_1_0)) + && tx_stack.tx_ptr != sync_master_ctx.tx_ptr + ) + { + APP_PRINT_WARN0("sync: fail, fifo error"); + sync_master_init_tx_fifo(); + } + + if (sync_master_ctx.tx_null || sync_master_ctx.tx_old) + { + APP_PRINT_WARN2("sync_isr: tx fail, null %d, old %d", sync_master_ctx.tx_null, + sync_master_ctx.tx_old); + sync_master_ctx.tx_fail_count += 1; + } + else + { + sync_master_ctx.tx_fail_count = 0; + } + + if (sync_master_ctx.quick.tx_count <= 5) + { + if (sync_master_ctx.quick.req_ack) + { + WR_PPT_REG_FIELD(PRO_HS_LENGTH, hs_field_length_4_0, SYNC_SUFFIX_LEN); + sync_master_ctx.busy = true; + } + else + { + /* don't switch pkt when tx fail */ + if (false == sync_master_ctx.tx_null && false == sync_master_ctx.tx_old && + sync_master_ctx.tx_fifo_bitmap) + { + sync_master_pop_data(); + sync_master_push_data(); + } + /* tx_count is used for determine ack timing, so update at last */ + sync_master_ctx.quick.tx_count += 1; + } + } + else + { + WR_PPT_REG_FIELD(PRO_HS_LENGTH, hs_field_length_4_0, SYNC_SUFFIX_LEN); + sync_master_ctx.busy = true; + } + } + + if (reg_misr.rx_int) + { + sync_master_ctx.busy = false; + ppt_clear_radio(); + WR_PPT_REG_FIELD(PRO_HS_LENGTH, hs_field_length_4_0, 0); + /* pop rx stack */ + //PRO_LENGTH_INCLUDE_ADDON_TYPE len_inc_addon = {.d16 = RD_PPT_REG(PRO_LENGTH_INCLUDE_ADDON)}; + PRO_RX_STACK_TYPE rx_stack = {.d16 = RD_PPT_REG(PRO_RX_STACK)}; + PRO_RX_HP_TYPE hp = {.d16 = RD_PPT_REG(PRO_RX_HP)}; + if (rx_stack.is_max_length) + { + APP_PRINT_WARN0("sync_isr: ptx rx max length"); + rx_stack.rx_time_out = 1; + } + bool rx = !(rx_stack.rx_time_out || rx_stack.rx_hit == false || rx_stack.is_crc_error); + //PRO_ACCHIT_CLK_LOWER_TYPE acchit_clk_lower = {.d16 = RD_PPT_REG(PRO_ACCHIT_CLK_LOWER)}; + //PRO_ACCHIT_CLK_UPPER_TYPE acchit_clk_upper = {.d16 = RD_PPT_REG(PRO_ACCHIT_CLK_UPPER)}; + PRO_LENGTH_TYPE length = {.d16 = RD_PPT_REG(PRO_LENGTH)}; + PRO_RSSI_TYPE rssi = {.d16 = RD_PPT_REG(PRO_RSSI)}; + PRO_RX_CRC_LOWER_TYPE rx_crc_lower = {.d16 = RD_PPT_REG(PRO_RX_CRC_LOWER)}; + PRO_RX_CRC_UPPER_TYPE rx_crc_upper = {.d16 = RD_PPT_REG(PRO_RX_CRC_UPPER)}; + uint32_t rx_crc = (rx_crc_upper.d16 << 16) + rx_crc_lower.d16; + rx_crc >>= 32 - SYNC_CRC_LEN; + PRO_ACCHIT_CLK_LOWER_TYPE acchit_clk_lower = {.d16 = RD_PPT_REG(PRO_ACCHIT_CLK_LOWER)}; + PRO_ACCHIT_CLK_UPPER_TYPE acchit_clk_upper = {.d16 = RD_PPT_REG(PRO_ACCHIT_CLK_UPPER)}; + uint32_t acchit_clk = (acchit_clk_upper.acchit_clk_31_16 << 16) + acchit_clk_lower.acchit_clk_15_0; + PRO_RX_HS_LOWER_TYPE rx_hs_lower = {.d16 = RD_PPT_REG(PRO_RX_HS_LOWER)}; + PRO_RX_HS_UPPER_TYPE rx_hs_upper = {.d16 = RD_PPT_REG(PRO_RX_HS_UPPER)}; //read 0x62 to pop rx stack + uint32_t hs = (rx_hs_upper.d16 << 16) + rx_hs_lower.d16; + sync_master_ctx.quick.msg_ack = hs; + //PRO_INT_CTRL_TYPE int_ctrl = {.d16 = RD_PPT_REG(PRO_INT_CTRL)}; + bool req_ack = sync_master_ctx.quick.req_ack; + if (SYNC_ISR_DEBUG) + { + int32_t clk_diff = 0; + clk_diff = sync_hw_frtimer_time_diff(acchit_clk, sync_master_ctx.quick.ce_clk); + APP_PRINT_INFO6("sync_isr: ptx rx stack 0x%04x, ce %d, clk %d, hp 0x%02x, hs 0x%x, len %d", + rx_stack.d16, + sync_master_ctx.quick.ce_cnt, clk_diff, hp.hp, hs, length.d16); + } + + if (rx) + { + uint16_t rx_len = length.d16 + SYNC_OVERHEAD_LEN + 2; + uint8_t *rx_buffer = ppt_get_rx_buffer(rx_len); + ppt_pop_rx_fifo(0, rx_buffer, rx_len); + sync_pdu_t *pdu = (sync_pdu_t *)(rx_buffer + SYNC_OVERHEAD_LEN + 2); + sync_header_t rx_header; + rx_header.value = hp.hp; + + //APP_PRINT_INFO2("sync_isr: ptx rx len %d, %b", rx_len, TRACE_BINARY(rx_len, rx_buffer)); + + if (sync_master_ctx.count != 0) + { + sync_master_ctx.count = 0; + } + sync_master_handle_rx_quick(true, rx_header, pdu, length.d16, + rssi.rssi, rx_crc); + if (sync_master_ctx.state == SYNC_STATE_SYNC_FAST) + { + sync_master_ctx.state = SYNC_STATE_SYNC; + sync_master_ctx.count = 0; + APP_PRINT_INFO2("sync: reconnected on %d[%d]!", sync_ctx.chan_cur, + sync_ctx.chan_cfg[sync_ctx.chan_cur].freq); +#ifdef SYNC_DEBUG + sync_pendcall(sync_master_handle_sync_reconnected, 0, 0); +#endif + } + } + else + { + if (sync_master_ctx.state == SYNC_STATE_SYNC) + { + sync_master_handle_sync_lost_to_quick(); + } + else if (sync_master_ctx.state == SYNC_STATE_SYNC_FAST) + { + sync_master_handle_sync_fast_to_quick(); + } + } + /* tx_count is used for determine ack timing, so update at last */ + if (req_ack) + { + sync_master_ctx.quick.tx_count += 2; + if (sync_master_ctx.quick.tx_count > 6) + { + sync_master_ctx.quick.tx_count = 0; + } + } + else + { + sync_master_ctx.quick.tx_count = 0; + } + } + + time_debug_level_low(TIME_DEBUG_CHAN_ISR); +} + +PPT_ISR_SECTION void sync_master_handle_sync_early_first_quick(void) +{ + sync_hw_timer_start(125, sync_master_handle_sync_early_quick); + sync_hw_timer_set_periodic(true); + sync_hw_timer_impr_ip(true); + ppt_reg_handler(sync_master_ppt_isr_handler_quick); + sync_master_ctx.quick.enable = true; + sync_master_ctx.quick.count = 7; + sync_master_ctx.quick.tx_count = 0; + sync_master_ctx.quick.ce_cnt -= 1; + WR_PPT_REG_FIELD(PRO_FIELD_LENGTH, hp_field_length_3_0, SYNC_HEADER_LEN_MT1R); + sync_master_set_header(sync_master_ctx.quick.seq_ack, sync_master_ctx.seq_tx, + sync_master_ctx.quick.req_ack); + sync_master_handle_sync_early_quick(); + sync_master_push_data(); +} + +PPT_ISR_SECTION void sync_master_handle_sync_early_hb_quick(void) +{ + time_debug_level_high(TIME_DEBUG_CHAN_DLPS); + sync_hw_timer_start(125, sync_master_handle_sync_early_quick); + sync_hw_timer_set_periodic(true); + sync_hw_timer_impr_ip(true); + time_debug_level_low(TIME_DEBUG_CHAN_DLPS); + sync_master_ctx.hb.sleep_cal = false; + uint8_t count = (sync_master_ctx.quick.count + 1) & 0x7; + if (count != 0) + { + sync_master_ctx.quick.ce_clk = sync_hw_frtimer_get_count() - count * 125 * SYNC_HW_FRTIMER_CLOCK; + } + sync_master_handle_sync_early_quick(); + //APP_PRINT_INFO4("sync: dlps mac exit, sleep %d hw %d, ce %d, count %d!", + // sync_master_ctx.hb.time_left, sync_master_ctx.hb.time_left1, sync_master_ctx.quick.ce_cnt, + // sync_master_ctx.quick.count); +} + +static void sync_master_dlps_mac_exit(void); +PPT_ISR_SECTION void sync_master_hb_sleep_resume(void) +{ + if (sync_master_ctx.hb.sleep) + { + if (sync_master_ctx.hb.sleep_mac) + { + RD_PPT_REG(PRO_MISR); + } + else + { + sync_master_dlps_mac_exit(); + } + } +} +#endif + +PPT_ISR_SECTION static void sync_master_ppt_isr_handler(void) +{ + time_debug_level_high(TIME_DEBUG_CHAN_ISR); + PRO_MISR_TYPE reg_misr; + reg_misr.d16 = RD_PPT_REG(PRO_MISR); + //APP_PRINT_INFO1("sync_isr: ptx isr 0x%04x\r\n", reg_misr.d16); + + /* free the pend msg at the early handler */ + if (sync_master_ctx.pmsg_pend_free) + { + sync_pendcall(sync_msg_handle_tx_done, sync_master_ctx.pmsg_pend_free, 0); + sync_master_ctx.pmsg_pend_free = NULL; + } + + if (reg_misr.tx_int) + { + Pad_Config(P2_3, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, + PAD_OUT_HIGH); + Pad_Config(P2_3, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, + PAD_OUT_LOW); + /* pop tx stack */ + PRO_TX_STACK_TYPE tx_stack = {.d16 = RD_PPT_REG(PRO_TX_STACK)}; + //PRO_TX_HS_LOWER_TYPE hs_lower = {.d16 = RD_PPT_REG(PRO_TX_HS_LOWER)}; + //PRO_TX_HS_UPPER_TYPE hs_upper = {.d16 = RD_PPT_REG(PRO_TX_HS_UPPER)}; +#if SYNC_SUPPORT_MT1R + if (sync_ctx.mt1r.flag) + { + if ((sync_master_ctx.pair_state == SYNC_PAIR_STATE_PAIRING && + sync_master_ctx.phase == SYNC_MASTER_PAIR_PHASE_SEND_REQ) + || (sync_master_ctx.pair_state == SYNC_PAIR_STATE_PAIRED && + sync_master_ctx.state == SYNC_STATE_SYNC_SLOW)) + { + PRO_TX_CLK_LOWER_TYPE tx_clk_lower = {.d16 = RD_PPT_REG(PRO_TX_CLK_LOWER)}; + PRO_TX_CLK_UPPER_TYPE tx_clk_upper = {.d16 = RD_PPT_REG(PRO_TX_CLK_UPPER)}; + sync_master_ctx.quick.ce_clk = (tx_clk_upper.tx_clk_31_16 << 16) + tx_clk_lower.tx_clk_15_0; + } + } +#endif + PRO_TX_LENGTH_TYPE length = {.d16 = RD_PPT_REG(PRO_TX_LENGTH)}; // Read 0x6a to pop stack + if (SYNC_ISR_DEBUG) + { + APP_PRINT_INFO6("sync_isr: ptx tx stack 0x%04x, seq 0x%02x, bitmap 0x%02x, rd ptr hw %d sw %d, len %d", + tx_stack.d16, sync_master_ctx.seq_tx, + RD_U32_BZDMA_REG(BZDMA_REG_BLEONLY_ENTRY_SEGMENT_VALID((uint8_t)tx_stack.tx_entry_1_0)), + tx_stack.tx_ptr, sync_master_ctx.tx_ptr, length.tx_length); + } + sync_master_ctx.tx_ptr_hw = tx_stack.tx_ptr; + sync_master_ctx.tx_null = length.tx_length == 0; + sync_master_ctx.tx_old = tx_stack.tx_ptr != sync_master_ctx.tx_ptr; + sync_header_t header; + header.value = tx_stack.tx_hp; + if (header.seq != (sync_master_ctx.seq_tx & ((1 << SYNC_HEADER_SEQ_LEN) - 1))) + { + sync_master_ctx.tx_old = true; + } + if (sync_master_ctx.tx_null || sync_master_ctx.tx_old) + { + APP_PRINT_WARN2("sync_isr: tx fail, null %d, old %d", sync_master_ctx.tx_null, + sync_master_ctx.tx_old); + sync_master_ctx.tx_fail_count += 1; + } + else + { + sync_master_ctx.tx_fail_count = 0; + } + +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_ctx.power_ctrl_count += 1; + } +#endif + + if (sync_master_ctx.state == SYNC_STATE_SYNC && + sync_master_ctx.pair_state == SYNC_PAIR_STATE_PAIRED + ) + { + sync_master_msg_sending_info_t *pmsg_info = sync_master_msg_sending_peak(); + if (pmsg_info) + { + pmsg_info->tx_count += 1; + if (SYNC_ISR_DEBUG) + { + sync_msg_type_t type = SYNC_MSG_TYPE_ALL; + uint8_t opcode = 0; + if (pmsg_info->pmsg) + { + type = pmsg_info->pmsg->type; + opcode = pmsg_info->pmsg->data[0]; + } + else + { + uint8_t *tx_buffer = ppt_get_tx_buffer(1); + opcode = tx_buffer[0]; + } + + APP_PRINT_INFO2("sync_isr: tx msg type %d, op 0x%02x", type, opcode); + } + } + + if (sync_master_ctx.hb.slow == false) + { + if (sync_master_param.early_high_prio_enable) + { + if (sync_master_ctx.curr_time_in_ce == 0) + { + if (sync_master_param.tx_prepare_cb != NULL) + { + sync_master_param.tx_prepare_cb(); + } + } + } + } + } + } + + if (reg_misr.rx_int) + { + sync_master_ctx.handling_ack = true; + sync_master_ctx.busy = false; + + /* pop rx stack */ + //PRO_LENGTH_INCLUDE_ADDON_TYPE len_inc_addon = {.d16 = RD_PPT_REG(PRO_LENGTH_INCLUDE_ADDON)}; + PRO_RX_STACK_TYPE rx_stack = {.d16 = RD_PPT_REG(PRO_RX_STACK)}; + PRO_RX_HP_TYPE hp = {.d16 = RD_PPT_REG(PRO_RX_HP)}; + if (rx_stack.is_max_length) + { + APP_PRINT_WARN0("sync_isr: ptx rx max length"); + rx_stack.rx_time_out = 1; + } + bool rx = !(rx_stack.rx_time_out || rx_stack.rx_hit == false || rx_stack.is_crc_error); + if (sync_master_ctx.pair_state == SYNC_PAIR_STATE_PAIRED && + sync_master_ctx.state != SYNC_STATE_SYNC_SLOW) + { + sync_header_t rx_header; + rx_header.value = hp.hp; + sync_master_handle_ack(rx, rx_header); + } + + //PRO_ACCHIT_CLK_LOWER_TYPE acchit_clk_lower = {.d16 = RD_PPT_REG(PRO_ACCHIT_CLK_LOWER)}; + //PRO_ACCHIT_CLK_UPPER_TYPE acchit_clk_upper = {.d16 = RD_PPT_REG(PRO_ACCHIT_CLK_UPPER)}; + PRO_RX_CRC_LOWER_TYPE rx_crc_lower = {.d16 = RD_PPT_REG(PRO_RX_CRC_LOWER)}; + PRO_RX_CRC_UPPER_TYPE rx_crc_upper = {.d16 = RD_PPT_REG(PRO_RX_CRC_UPPER)}; + uint32_t rx_crc = (rx_crc_upper.d16 << 16) + rx_crc_lower.d16; + rx_crc >>= 32 - SYNC_CRC_LEN; + PRO_RSSI_TYPE rssi = {.d16 = RD_PPT_REG(PRO_RSSI)}; + PRO_LENGTH_TYPE length = {.d16 = RD_PPT_REG(PRO_LENGTH)}; + //PRO_RX_HS_LOWER_TYPE rx_hs_lower = {.d16 = RD_PPT_REG(PRO_RX_HS_LOWER)}; + PRO_RX_HS_UPPER_TYPE rx_hs_upper = {.d16 = RD_PPT_REG(PRO_RX_HS_UPPER)}; //read 0x62 to pop rx stack + //PRO_INT_CTRL_TYPE int_ctrl = {.d16 = RD_PPT_REG(PRO_INT_CTRL)}; + + if (SYNC_ISR_DEBUG) + { + APP_PRINT_INFO4("sync_isr: ptx rx stack 0x%04x, rssi %d, hp 0x%02x, len %d", rx_stack.d16, + ppt_get_rssi(rssi.rssi), hp.hp, length.d16); + } + + sync_channel_statistic(rx); + sync_ctx.chan_used_count += 1; + if (false == rx) + { + if (sync_master_ctx.pair_state == SYNC_PAIR_STATE_PAIRED) + { + if (sync_master_ctx.state == SYNC_STATE_SYNC) + { + sync_master_handle_sync_lost_to(); + } + else if (sync_master_ctx.state == SYNC_STATE_SYNC_SLOW) + { + sync_master_handle_sync_slow_to(); + } + else if (sync_master_ctx.state == SYNC_STATE_SYNC_FAST) + { + sync_master_handle_sync_fast_to(); + } + } + else if (sync_master_ctx.pair_state == SYNC_PAIR_STATE_PAIRING) + { + if (sync_master_ctx.phase == SYNC_MASTER_PAIR_PHASE_SEND_REQ) + { + sync_master_handle_pair_req_to(); + } + else if (sync_master_ctx.phase == SYNC_MASTER_PAIR_PHASE_SEND_CFM) + { + sync_master_handle_pair_cfm_to(); + } + } + } + else + { + uint16_t rx_len = length.d16 + SYNC_OVERHEAD_LEN; + uint8_t *rx_buffer = ppt_get_rx_buffer(rx_len); + ppt_pop_rx_fifo(0, rx_buffer, rx_len); + sync_pdu_t *pdu = (sync_pdu_t *)(rx_buffer + SYNC_OVERHEAD_LEN); + sync_header_t rx_header; + rx_header.value = hp.hp; + + //APP_PRINT_INFO2("sync_isr: ptx rx len %d, %b", rx_len, TRACE_BINARY(rx_len, rx_buffer)); + if (sync_master_ctx.pair_state == SYNC_PAIR_STATE_PAIRED) + { + bool handle_rx = true; + if (sync_master_ctx.state == SYNC_STATE_SYNC) + { + if (sync_master_ctx.count != 0) + { + sync_master_ctx.count = 0; + } + } + else if (sync_master_ctx.state == SYNC_STATE_SYNC_SLOW) + { + handle_rx = false; + if (length.d16 == sizeof(sync_opcode_t) && pdu->opcode == SYNC_OPCODE_CONN_RSP) + { + sync_master_ctx.state = SYNC_STATE_SYNC; + sync_master_ctx.count = 0; + sync_master_ctx.tx_ptr = ppt_trigger_fw_ack(0); + sync_ppt_set_tifs(false); + APP_PRINT_INFO2("sync: connected on %d[%d]!", sync_ctx.chan_cur, + sync_ctx.chan_cfg[sync_ctx.chan_cur].freq); + sync_master_ctx.rx = 1; + sync_master_ctx.seq_tx = 0; + sync_master_ctx.handling_ack = false; +#if SYNC_SUPPORT_MT1R + if (sync_ctx.mt1r.flag) + { + uint32_t clk = sync_hw_frtimer_get_count(); + uint32_t delay = sync_hw_frtimer_time_elapse(clk, sync_master_ctx.quick.ce_clk); + delay = SYNC_CONNECT_DELAY > delay ? SYNC_CONNECT_DELAY - delay : 30; + sync_hw_timer_start(delay, sync_master_handle_sync_early_first_quick); + sync_ppt_set_rf_time_mt1r(); + sync_ctx.conn_interval = 1000; + APP_PRINT_ERROR0("sync: quick enter"); + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(true); + } + } + else +#endif + { + sync_master_ctx.curr_time_in_ce = SYNC_SYNC_SPEED; + sync_master_start_sync_timer(); + if (SYNC_SYNC_SPEED_HIGH <= SYNC_SYNC_SPEED_HIGH_MIN) + { + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(true); + } + } + } + sync_pendcall(sync_master_handle_connected, 0, 0); + sync_channel_clear_statistic(); +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_power_ctrl_start(); + } +#endif + } + else + { + sync_master_handle_sync_slow_to(); + } + } + else if (sync_master_ctx.state == SYNC_STATE_SYNC_FAST) + { + sync_master_ctx.curr_time_in_ce = SYNC_SYNC_SPEED; + sync_master_start_sync_timer(); + sync_master_ctx.state = SYNC_STATE_SYNC; + sync_master_ctx.count = 0; + APP_PRINT_INFO2("sync: reconnected on %d[%d]!", sync_ctx.chan_cur, + sync_ctx.chan_cfg[sync_ctx.chan_cur].freq); +#ifdef SYNC_DEBUG + sync_pendcall(sync_master_handle_sync_reconnected, 0, 0); +#endif + } + if (handle_rx) + { + sync_master_handle_rx(true, rx_header, (sync_pdu_t *)(rx_buffer + SYNC_OVERHEAD_LEN), length.d16, + rssi.rssi, rx_crc); + } + } + else if (sync_master_ctx.pair_state == SYNC_PAIR_STATE_PAIRING) + { + if (sync_master_ctx.phase == SYNC_MASTER_PAIR_PHASE_SEND_REQ) + { + bool retry = true; + do + { + int8_t rssi_dbm = ppt_get_rssi(rssi.rssi); + if (rssi_dbm < sync_ctx.pair_rssi_th) + { + APP_PRINT_ERROR2("sync: pair rssi check fail, %d < %d", rssi_dbm, sync_ctx.pair_rssi_th); + break; + } + if (length.d16 == sizeof(sync_opcode_t) + sizeof(sync_pair_rsp_t)) + { + sync_pdu_t *pdu = (sync_pdu_t *)(rx_buffer + SYNC_OVERHEAD_LEN); + if (pdu->opcode == SYNC_OPCODE_PAIR_RSP) + { + retry = false; + sync_master_ctx.phase = SYNC_MASTER_PAIR_PHASE_SEND_CFM; + sync_master_ctx.count1 = 0; + sync_ppt_set_tifs(false); + sync_hw_timer_start(SYNC_SYNC_SPEED, sync_master_handle_sync_slow_early); + + sync_master_ctx.acc_data = pdu->pair_rsp.acc; + sync_acc_set(sync_master_ctx.acc_data); + + sync_master_ctx.tx_ptr = ppt_trigger_fw_ack(0); + sync_master_ctx.rx = 1; + sync_master_ctx.seq_tx = 0; + sync_set_header(1, 0); + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_PAIR_CONF; + ppt_push_tx_data(sizeof(sync_opcode_t), (uint8_t *)&pdu); + + APP_PRINT_INFO3("sync: pairing 0x%08x on %d[%d]!", sync_master_ctx.acc_data.addr, sync_ctx.chan_cur, + sync_ctx.chan_cfg[sync_ctx.chan_cur].freq); + } + } + } + while (0); + if (retry) + { + sync_master_handle_pair_req_to(); + } + } + else if (sync_master_ctx.phase == SYNC_MASTER_PAIR_PHASE_SEND_CFM) + { + if (rx_header.ack) + { + sync_master_ctx.pair_state = SYNC_PAIR_STATE_PAIRED; + sync_master_ctx.state = SYNC_STATE_SYNC; + sync_master_ctx.count = 0; + sync_master_ctx.tx_ptr = ppt_trigger_fw_ack(0); + + sync_master_ctx.rx = 1; + sync_master_ctx.seq_tx = 0; + sync_master_ctx.handling_ack = false; + APP_PRINT_INFO3("sync: paired 0x%08x on %d[%d]!", sync_master_ctx.acc_data.addr, sync_ctx.chan_cur, + sync_ctx.chan_cfg[sync_ctx.chan_cur].freq); +#if SYNC_SUPPORT_MT1R + if (sync_ctx.mt1r.flag) + { + uint32_t clk = sync_hw_frtimer_get_count(); + uint32_t total_time = SYNC_PAIR_CFM_PERIOD + SYNC_CONNECT_DELAY; + uint32_t delay = sync_hw_frtimer_time_elapse(clk, sync_master_ctx.quick.ce_clk); + delay = total_time > delay ? total_time - delay : 30; + sync_hw_timer_start(delay, sync_master_handle_sync_early_first_quick); + sync_ppt_set_rf_time_mt1r(); + sync_ctx.conn_interval = 1000; + APP_PRINT_ERROR0("sync: quick enter"); + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(true); + } + } + else +#endif + { + sync_master_ctx.curr_time_in_ce = SYNC_SYNC_SPEED; + sync_master_start_sync_timer(); + if (SYNC_SYNC_SPEED_HIGH <= SYNC_SYNC_SPEED_HIGH_MIN) + { + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(true); + } + } + } + sync_pendcall(sync_master_handle_paired, 0, 0); + sync_channel_clear_statistic(); +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_power_ctrl_start(); + } +#endif + sync_header_t rx_header; + rx_header.value = hp.hp; + sync_master_handle_rx(true, rx_header, (sync_pdu_t *)(rx_buffer + SYNC_OVERHEAD_LEN), length.d16, + rssi.rssi, rx_crc); + } + else + { + sync_master_handle_pair_cfm_to(); + } + } + } + } + } + + if (reg_misr.kill_ptx_int) + { + ppt_clear_radio(); + WR_PPT_REG(PRO_MISR, BIT4); + ppt_ctx->fsm = PPT_FSM_STANDBY; + ppt_ctx->sync_flag = false; + } + time_debug_level_low(TIME_DEBUG_CHAN_ISR); +} + +void sync_master_init(void) +{ + sync_ppt_init(); + ppt_reg_handler(sync_master_ppt_isr_handler); + sync_hw_timer_init(); +#ifdef SYNC_DEBUG + sync_hw_frtimer_init(); +#endif + sync_ctx.role = SYNC_ROLE_MASTER; + memset(&sync_master_ctx, 0, sizeof(sync_master_ctx)); +} + +bool sync_master_set_hb_param(uint8_t level, uint32_t interval, uint32_t beat_times) +{ + if (level >= SYNC_MASTER_HB_RHY_NUMBER) + { + return false; + } + if (level != 0) + { + sync_master_hb_rhy[level].speed = interval; + } + if (level != SYNC_MASTER_HB_RHY_NUMBER - 1) + { + sync_master_hb_rhy[level].period = beat_times; + } + return true; +} + +bool sync_master_wakeup_hb(void) +{ + uint32_t lock = sync_enter_critical(); + if (sync_master_ctx.state != SYNC_STATE_SYNC || + sync_master_ctx.pair_state != SYNC_PAIR_STATE_PAIRED) + { + sync_exit_critical(lock); + return false; + } + + if (sync_master_ctx.hb.slow == false) + { + sync_master_hb_exit(true); + sync_exit_critical(lock); + return false; + } + + if (sync_master_ctx.busy == false) + { + /* reuse this function to activate */ + sync_master_handle_tx_trigger(); + } + else + { + sync_master_hb_exit(true); + } + sync_exit_critical(lock); + return true; +} + +void sync_master_reg_tx_prepare_cb(void (*cb)(void)) +{ + sync_master_param.tx_prepare_cb = cb; +} + +void sync_master_set_tx_scheme(sync_master_tx_scheme_t tx_scheme) +{ + sync_master_param.tx_scheme = tx_scheme; +} + +void sync_master_check_feature(void) +{ + sync_check_feature(); + +#if SYNC_SUPPORT_MT1R + if (sync_ctx.mt1r.flag == false) +#endif + { + sync_master_param.tx_align_enable = sync_master_param.tx_scheme.tx_align == SYNC_FEATURE_USE_DEFAULT + ? SYNC_SYNC_SPEED_HIGH > 1000 : sync_master_param.tx_scheme.tx_align == SYNC_FEATURE_ENABLE; + sync_master_param.early_high_prio_enable = sync_master_param.tx_scheme.early_high_prio == + SYNC_FEATURE_USE_DEFAULT ? SYNC_SYNC_SPEED_HIGH == SYNC_SYNC_SPEED_HIGH_MIN : + sync_master_param.tx_scheme.early_high_prio == SYNC_FEATURE_ENABLE; + sync_master_param.retrans_at_ack_enable = sync_master_param.tx_scheme.retrans_at_ack == + SYNC_FEATURE_USE_DEFAULT ? (SYNC_SYNC_SPEED_HIGH == SYNC_SYNC_SPEED) && + (SYNC_SYNC_SPEED_HIGH > SYNC_SYNC_SPEED_HIGH_MIN) : sync_master_param.tx_scheme.retrans_at_ack == + SYNC_FEATURE_ENABLE; + sync_master_param.tx_new_at_retrans_int_enable = sync_master_param.tx_scheme.tx_new_at_retrans_int + == SYNC_FEATURE_USE_DEFAULT ? false : sync_master_param.tx_scheme.tx_new_at_retrans_int == + SYNC_FEATURE_ENABLE; + } + uint32_t speed = SYNC_SYNC_SPEED; + sync_time_get(SYNC_TIME_PARAM_CONNECT_INTERVAL, &speed); + APP_PRINT_INFO7("sync: speed %dus high %dus, scheme %d-%d-%d-%d, tx cb 0x%x", speed, + SYNC_SYNC_SPEED_HIGH, + sync_master_param.tx_align_enable, sync_master_param.early_high_prio_enable, + sync_master_param.retrans_at_ack_enable, sync_master_param.tx_new_at_retrans_int_enable, + sync_master_param.tx_prepare_cb); +} + +void sync_master_enable(void) +{ + +} + +sync_err_code_t sync_master_pair(void) +{ + if (sync_master_ctx.state != SYNC_STATE_IDLE || sync_master_ctx.pair_state != SYNC_PAIR_STATE_IDLE) + { + APP_PRINT_INFO2("sync: fail to start pair, state %d, pair state %d!", sync_master_ctx.state, + sync_master_ctx.pair_state); + return SYNC_ERR_CODE_INVALID_STATE; + } + + memset(&sync_master_ctx, 0, sizeof(sync_master_ctx)); + sync_master_ctx.state = SYNC_STATE_SYNC_SLOW; + sync_master_ctx.pair_state = SYNC_PAIR_STATE_PAIRING; + sync_master_ctx.phase = SYNC_MASTER_PAIR_PHASE_SEND_REQ; + sync_master_ctx.seq_rx = 0xff; + sync_master_init_tx_fifo(); + sync_ppt_set_tifs(true); + sync_master_check_feature(); + + APP_PRINT_INFO0("sync: start pair!"); + sync_channel_change(0); + sync_acc_t acc = {.addr = SYNC_ACC_ADDR_BR}; + sync_acc_set(acc); + + uint16_t interval = SYNC_SYNC_SPEED; +#if SYNC_SUPPORT_MT1R + WR_PPT_REG_FIELD(PRO_FIELD_LENGTH, hp_field_length_3_0, SYNC_HEADER_LEN); + WR_PPT_REG_FIELD(PRO_HS_LENGTH, hs_field_length_4_0, 0); + sync_ppt_set_rf_time(); + if (sync_ctx.mt1r.flag) + { + interval = 125; + sync_ctx.conn_interval = 250; + sync_hw_frtimer_init(); + } +#endif + + sync_set_header(0, 0); + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_PAIR_REQ; + pdu.pair_req.interval = interval; + ppt_push_tx_data(sizeof(sync_opcode_t) + sizeof(sync_pair_req_t), (uint8_t *)&pdu); + + { + ppt_ptx_mode_ext_t param = + { + { + .ack_mode = true, + .periodic_mode = false, + .periodic_interval = SYNC_PAIR_SPEED / 125 - 1 + }, + .retransmit_times = 0xffff + }; + ppt_set_ptx_mode_ext(¶m); + } + sync_master_ctx.busy = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); + sync_hw_timer_start(SYNC_PAIR_SPEED, sync_master_handle_sync_slow_early); + return SYNC_ERR_CODE_SUCCESS; +} + +sync_err_code_t sync_master_connect(sync_bond_info_t *bond_info) +{ + if (bond_info == NULL) + { + return SYNC_ERR_CODE_NULL_POINTER; + } + if (sync_master_ctx.state != SYNC_STATE_IDLE) + { + APP_PRINT_INFO1("sync: fail to start connect, state %d!", sync_master_ctx.state); + return SYNC_ERR_CODE_INVALID_STATE; + } + + memset(&sync_master_ctx, 0, sizeof(sync_master_ctx)); + sync_master_ctx.state = SYNC_STATE_SYNC_SLOW; + sync_master_ctx.pair_state = SYNC_PAIR_STATE_PAIRED; + sync_master_ctx.acc_data = bond_info->acc; + sync_master_ctx.seq_rx = 0xff; + sync_master_init_tx_fifo(); + sync_ppt_set_tifs(true); + sync_master_check_feature(); + + APP_PRINT_INFO1("sync: start connect 0x%08x!", bond_info->acc.addr); + sync_channel_change(0); + sync_acc_set(sync_master_ctx.acc_data); + + uint16_t interval = SYNC_SYNC_SPEED; +#if SYNC_SUPPORT_MT1R + WR_PPT_REG_FIELD(PRO_FIELD_LENGTH, hp_field_length_3_0, SYNC_HEADER_LEN); + WR_PPT_REG_FIELD(PRO_HS_LENGTH, hs_field_length_4_0, 0); + sync_ppt_set_rf_time(); + if (sync_ctx.mt1r.flag) + { + interval = 125; + sync_ctx.conn_interval = 250; + sync_hw_frtimer_init(); + } +#endif + + sync_set_header(0, 0); + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_CONN_REQ; + pdu.conn_req.interval = interval; + ppt_push_tx_data(sizeof(sync_opcode_t) + sizeof(sync_conn_req_t), (uint8_t *)&pdu); + + { + ppt_ptx_mode_ext_t param = + { + { + .ack_mode = true, + .periodic_mode = false, + .periodic_interval = SYNC_SYNC_SLOW_SPEED / 125 - 1 + }, + .retransmit_times = 0xffff + }; + ppt_set_ptx_mode_ext(¶m); + } + sync_master_ctx.busy = true; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); + sync_hw_timer_start(SYNC_SYNC_SLOW_SPEED, sync_master_handle_sync_slow_early); + return SYNC_ERR_CODE_SUCCESS; +} + +void sync_master_stop(void) +{ + if (sync_master_ctx.state == SYNC_STATE_IDLE) + { + return; + } + + uint32_t lock = sync_enter_critical(); + sync_master_ctx.state = SYNC_STATE_IDLE; + sync_hw_timer_stop(); + sync_master_close(); + sync_master_msg_sending_free(); +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_power_ctrl_stop(); + } +#endif +#if SYNC_SUPPORT_MT1R + ppt_reg_handler(sync_master_ppt_isr_handler); +#endif + memset(&sync_master_ctx, 0, sizeof(sync_master_ctx)); + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(false); + } + sync_exit_critical(lock); + APP_PRINT_INFO0("sync: stopped!"); +} + +void sync_master_deinit(void) +{ + +} + +PPT_API_SECTION static bool sync_master_dlps_check(uint32_t *wakeup_time_diff) +{ + //APP_PRINT_ERROR4("sync: dlps check, state %d, pair state %d, hb slow %d busy %d", + // sync_master_ctx.state, sync_master_ctx.pair_state, + // sync_master_ctx.hb.slow, sync_master_ctx.busy); + + if (sync_master_ctx.state == SYNC_STATE_IDLE && + sync_master_ctx.pair_state == SYNC_PAIR_STATE_IDLE) + { + return true; + } + + if (sync_master_ctx.state == SYNC_STATE_SYNC && + sync_master_ctx.pair_state == SYNC_PAIR_STATE_PAIRED && + sync_master_ctx.hb.slow && sync_master_ctx.busy == false) + { +#if SYNC_SUPPORT_MT1R + if (sync_master_ctx.quick.enable) + { + if (sync_master_ctx.hb.sleep) + { + return true; + } + if (sync_master_ctx.hb.sleep_cal) + { + return false; + } + time_debug_level_high(TIME_DEBUG_CHAN_DLPS); + sync_master_ctx.hb.time_left1 = sync_hw_timer_get_left(); + sync_master_ctx.hb.sleep_time = sync_native_time_read(); + time_debug_level_low(TIME_DEBUG_CHAN_DLPS); + if (sync_hw_timer_check_pending()) + { + return false; + } + sync_master_ctx.hb.time_left = sync_master_ctx.hb.ce_cnt >= sync_master_ctx.quick.ce_cnt ? + sync_master_ctx.hb.ce_cnt - sync_master_ctx.quick.ce_cnt : 0x10000 - sync_master_ctx.quick.ce_cnt + + sync_master_ctx.hb.ce_cnt; + sync_master_ctx.hb.time_left *= 1000; + sync_master_ctx.hb.time_left += 625 + sync_master_ctx.hb.time_left1 - sync_master_ctx.quick.count * + 125; + //APP_PRINT_ERROR4("sync: dlps check, time left %dus, hw left %d, ce %d, count %d", + // sync_master_ctx.hb.time_left, sync_master_ctx.hb.time_left1, sync_master_ctx.quick.ce_cnt, + // sync_master_ctx.quick.count); + if (sync_master_ctx.hb.time_left <= SYNC_MASTER_HB_SLEEP_MIN_PERIOD_MT1R) + { + return false; + } + *wakeup_time_diff = us_to_rtc_clk(sync_master_ctx.hb.time_left - SYNC_MASTER_HB_SLEEP_OH, ROUND); + sync_hw_timer_stop(); + sync_master_ctx.hb.sleep = true; + return true; + } + else +#endif + { + sync_master_ctx.hb.time_left = sync_hw_timer_get_left(); + //APP_PRINT_ERROR1("sync: dlps check, time left %dus", sync_master_ctx.hb.time_left); + if (sync_master_ctx.hb.time_left <= SYNC_MASTER_HB_SLEEP_MIN_PERIOD) + { + return false; + } + sync_master_ctx.hb.sleep_clk = platform_rtc_get_counter(); + *wakeup_time_diff = us_to_rtc_clk(sync_master_ctx.hb.time_left, ROUND); + time_debug_level_high(TIME_DEBUG_CHAN_DLPS); + time_debug_level_low(TIME_DEBUG_CHAN_DLPS); + return true; + } + } + + return false; +} + +PPT_API_SECTION static void sync_master_dlps_mac_enter(void) +{ +#if SYNC_SUPPORT_MT1R + sync_master_ctx.hb.sleep_mac = true; +#endif +} + +PPT_API_SECTION static void sync_master_dlps_mac_exit(void) +{ +#if SYNC_SUPPORT_MT1R + if (sync_master_ctx.state == SYNC_STATE_SYNC && + sync_master_ctx.pair_state == SYNC_PAIR_STATE_PAIRED && + sync_master_ctx.hb.slow && sync_master_ctx.busy == false) + { + if (sync_master_ctx.quick.enable) + { + time_debug_level_high(TIME_DEBUG_CHAN_DLPS); + uint64_t wakeup_time = sync_native_time_read(); + time_debug_level_low(TIME_DEBUG_CHAN_DLPS); + uint32_t sleep_us = wakeup_time - sync_master_ctx.hb.sleep_time; + bool rte_req = sync_master_ctx.hb.time_left <= sleep_us + SYNC_MASTER_HB_SLEEP_MIN_PERIOD_MT1R; + uint32_t sleep_counter = 0; + sync_master_ctx.hb.oversleep = sync_master_ctx.hb.time_left <= sleep_us; + sync_master_ctx.hb.time_left = sleep_us; + sleep_us += 12; + if (sleep_us >= sync_master_ctx.hb.time_left1) + { + sleep_us -= sync_master_ctx.hb.time_left1; + sleep_counter += 1; + } + sleep_counter += sleep_us / 125 + sync_master_ctx.quick.count; + sleep_us = sleep_us % 125; + sync_master_ctx.hb.time_left1 = sleep_us; + sync_master_ctx.quick.ce_cnt += sleep_counter >> 3; + sync_master_ctx.quick.count = sleep_counter & 0x7; + sync_master_ctx.hb.sleep = false; + sync_master_ctx.hb.sleep_mac = false; + sync_master_ctx.hb.sleep_cal = true; + time_debug_level_high(TIME_DEBUG_CHAN_DLPS); + if (sleep_us == 0) + { + sync_master_ctx.quick.count -= 1; + sync_master_handle_sync_early_hb_quick(); + } + else + { + if (sleep_us >= 110) + { + sync_master_ctx.quick.count += 1; + if (sync_master_ctx.quick.count == 8) + { + sync_master_ctx.quick.count = 0; + sync_master_ctx.quick.ce_cnt += 1; + } + sync_hw_timer_start(250 - sleep_us, sync_master_handle_sync_early_hb_quick); + } + else + { + sync_hw_timer_start(125 - sleep_us, sync_master_handle_sync_early_hb_quick); + } + } + time_debug_level_low(TIME_DEBUG_CHAN_DLPS); + if (rte_req) + { + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(true); + } + } + } + } +#endif +} + +PPT_API_SECTION static void sync_master_dlps_enter(void) +{ + +} + +PPT_API_SECTION static void sync_master_dlps_exit(void) +{ + // Pad_Config(P1_0, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, + // PAD_OUT_LOW); + // Pad_Config(P1_0, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, + // PAD_OUT_HIGH); + //APP_PRINT_ERROR4("sync: dlps exit, state %d, pair state %d, hb slow %d busy %d", + // sync_master_ctx.state, sync_master_ctx.pair_state, + // sync_master_ctx.hb.slow, sync_master_ctx.busy); + uint32_t lock = sync_enter_critical(); + if (sync_master_ctx.state == SYNC_STATE_SYNC && + sync_master_ctx.pair_state == SYNC_PAIR_STATE_PAIRED && + sync_master_ctx.hb.slow && sync_master_ctx.busy == false) + { +#if SYNC_SUPPORT_MT1R + if (sync_master_ctx.quick.enable == false) +#endif + { + time_debug_level_high(TIME_DEBUG_CHAN_DLPS); + time_debug_level_low(TIME_DEBUG_CHAN_DLPS); + uint32_t last_sleep_clk = sync_master_ctx.hb.sleep_clk; + uint32_t last_wakeup_clk = platform_rtc_get_counter(); + uint32_t sleep_us = rtc_clk_to_us(clk_cnt_diff(last_sleep_clk, last_wakeup_clk, BIT64(32)), ROUND); + //APP_PRINT_INFO2("sync: dlps exit, sleep %dus, time left %dus", sleep_us, + // sync_master_ctx.hb.time_left); + if (sleep_us >= sync_master_ctx.hb.time_left) + { + sync_hw_timer_stop(); + sync_master_handle_hb_to(); + } + else + { + sync_hw_timer_start(sync_master_ctx.hb.time_left - sleep_us, sync_master_handle_hb_to); + } + } + } + sync_exit_critical(lock); + //Pad_Config(P1_0, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, + // PAD_OUT_LOW); +} + +void sync_master_dlps_init(void) +{ + ppt_dlps_init(); + ppt_dlps_reg_mac_check_cb(sync_master_dlps_check); + ppt_dlps_reg_mac_enter_cb(sync_master_dlps_mac_enter); + ppt_dlps_reg_mac_exit_cb(sync_master_dlps_mac_exit); + ppt_dlps_reg_pf_enter_cb(sync_master_dlps_enter); + ppt_dlps_reg_pf_exit_cb(sync_master_dlps_exit); +} diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync_pendcall.c b/bee/drivers/ppt/sync/src/sync/src/ppt_sync_pendcall.c new file mode 100644 index 00000000..2410e74a --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/src/ppt_sync_pendcall.c @@ -0,0 +1,41 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_sync_pendcall.c + * @brief Source file for sync protocol. + * @details + * @author bill + * @date 2022-03-15 + * @version v0.1 + * ************************************************************************************* + */ + +/* Add Includes here */ +#include "ppt_sync_pendcall.h" +#include "zephyr/kernel.h" +#include "os_mem.h" + +typedef struct { + struct k_work work; + sync_pendcall_t func; + void *para1; + uint32_t para2; +} sync_work_t; + +static void sync_work_cb(struct k_work *item) +{ + sync_work_t *psync_work = CONTAINER_OF(item, sync_work_t, work); + psync_work->func(psync_work->para1, psync_work->para2); + os_mem_free(psync_work); +} + +void sync_pendcall(sync_pendcall_t func, void *para1, uint32_t para2) +{ + sync_work_t *psync_work = (sync_work_t *)os_mem_alloc(RAM_TYPE_DATA_ON, sizeof(sync_work_t)); + k_work_init(&psync_work->work, sync_work_cb); + psync_work->func = func; + psync_work->para1 = para1; + psync_work->para2 = para2; + k_work_submit(&psync_work->work); +} diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync_pendcall.h b/bee/drivers/ppt/sync/src/sync/src/ppt_sync_pendcall.h new file mode 100644 index 00000000..26bf715b --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/src/ppt_sync_pendcall.h @@ -0,0 +1,67 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_sync_pendcall.h + * @brief Head file for sync protocol. + * @details data structs and external functions declaration. + * @author bill + * @date 2022-03-15 + * @version v0.1 + * ************************************************************************************* + */ + +/* Define to prevent recursive inclusion */ +#ifndef _PPT_SYNC_PENDCALL_H_ +#define _PPT_SYNC_PENDCALL_H_ + +/* Add Includes here */ +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup PPT_SYNC_Driver + * @{ + */ + +/** @defgroup PPT_SYNC_Driver_Exported_Macros Exported Macros + * @brief + * @{ + */ + +/** @} End of PPT_SYNC_Driver_Exported_Macros */ + +/** @defgroup PPT_SYNC_Driver_Exported_Types Exported Types + * @brief + * @{ + */ + +typedef void (*sync_pendcall_t)(void *para1, uint32_t para2); + +/** @} End of PPT_SYNC_Driver_Exported_Types */ + +/** @defgroup PPT_SYNC_Driver_Exported_Functions Exported Functions + * @brief + * @{ + */ + +/** + * @brief pend the function to execute + * @param[in] func: pend call + * @param[in] para1: pointer parameter + * @param[in] para2: normal parameter + * @return none + */ +void sync_pendcall(sync_pendcall_t func, void *para1, uint32_t para2); + +/** @} End of PPT_SYNC_Driver_Exported_Functions */ + +/** @} End of PPT_SYNC_Driver */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync_slave.c b/bee/drivers/ppt/sync/src/sync/src/ppt_sync_slave.c new file mode 100644 index 00000000..ddf74700 --- /dev/null +++ b/bee/drivers/ppt/sync/src/sync/src/ppt_sync_slave.c @@ -0,0 +1,1937 @@ +/** +***************************************************************************************** +* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file ppt_sync_slave.c + * @brief Source file for sync protocol. + * @details + * @author bill + * @date 2022-03-15 + * @version v0.1 + * ************************************************************************************* + */ + +/* Add Includes here */ +#include +#include "ppt_simple.h" +#include "ppt_sync_int.h" +#include "ppt_sync_slave.h" +//#include "trace.h" +#include "os_sync.h" +#include "os_timer.h" +#include "utils.h" + +LOG_MODULE_REGISTER(ppt_sync_slave, PPT_SYNC_LOG_LEVEL); + +#define SYNC_SLAVE_CCA_ENABLE 1 +#if SYNC_SLAVE_CCA_ENABLE == 0 +//#define SYNC_SLAVE_FIXED_CHANNEL 3 +#endif + +#define SYNC_SLAVE_TX_MSG_NUM 3 + +#define SYNC_RX_MSG_QUEUE_SIZE 16 +#define SYNC_RX_MSG_QUEUE_DATA_LEN 16 + +#define SYNC_SLAVE_TEST_RX_STOP 0 + +#define SYNC_SLAVE_TIME_DEBUG_RX -1 +#define SYNC_SLAVE_TIME_DEBUG_REQ_ACK -1 +#define SYNC_SLAVE_TIME_DEBUG_ALL_ACK -3 +#define SYNC_SLAVE_TIME_DEBUG_SEQ_ACK -2 +#define SYNC_SLAVE_TIME_DEBUG_REFRESH_ACK -4 +#define SYNC_SLAVE_TIME_DEBUG_UPDATE_ACK -5 + +typedef enum +{ + SYNC_SLAVE_PAIR_PHASE_WAIT_REQ, + SYNC_SLAVE_PAIR_PHASE_SEND_RSP, + SYNC_SLAVE_PAIR_PHASE_WAIT_CFM, + SYNC_SLAVE_PAIR_PHASE_SEND_ACK +} sync_slave_pair_phase_t; + +typedef enum +{ + SYNC_SLAVE_HB_STATE_IDLE, + SYNC_SLAVE_HB_STATE_HB_RECEIVED +} sync_slave_hb_state_t; + +struct +{ + sync_state_t state; + sync_pair_state_t pair_state; + uint8_t phase; + uint16_t count; + sync_acc_t acc_data; + bool ack; + uint8_t seq_tx; + uint8_t seq_rx; + uint32_t crc_rx; + sync_msg_t *pmsg_sending[SYNC_SLAVE_TX_MSG_NUM]; + uint8_t pmsg_ptr[SYNC_SLAVE_TX_MSG_NUM]; + uint8_t pmsg_sending_idx; + bool pmsg_switching; + bool pmsg_last_empty; + struct + { + uint16_t interval; + sync_slave_hb_state_t state; + bool slow; + bool exit_pend; + bool interval_changed; + } hb; + volatile bool busy; //!< tx or rx ongoing + bool tx_trigger_pending; +#if SYNC_SUPPORT_MT1R + struct + { + bool enable; + uint16_t ce_cnt; + volatile uint8_t count; + int8_t slot_idx; + uint8_t ack_field[3]; + uint8_t req_ack_field[3]; + uint8_t ack_rsp_idx; + uint8_t ack_curr_idx; + uint8_t seq_ack_rx; + bool req_ack; + uint32_t rx_clk; + } quick; + uint32_t ce_clk; + bool rx_ok; +#endif +} sync_slave_ctx; + +#if SYNC_SUPPORT_MT1R +typedef struct +{ + int8_t rssi; + uint8_t len; + uint8_t data[SYNC_RX_MSG_QUEUE_DATA_LEN]; +} sync_rx_msg_t; + +typedef struct +{ + sync_rx_msg_t buffer[SYNC_RX_MSG_QUEUE_SIZE]; + volatile uint8_t head; + volatile uint8_t tail; +} sync_rx_msg_queue_t; + +sync_rx_msg_queue_t sync_rx_msg_queue; +static inline uint8_t sync_slave_get_ack_field(void); +static void sync_slave_handle_rx_task(void *para1, uint32_t para2); +#endif + +static void sync_slave_ppt_isr_handler(void); + +int8_t sync_slave_cca_get(uint8_t start_idx, uint8_t num) +{ + APP_PRINT_INFO2("sync_slave_cca_get: start_idx=%d, num=%d",start_idx,num); + uint8_t idx = 0; + uint8_t count = 0; + uint8_t bank = 0; + int16_t rssi_min = 0; + uint8_t rssi_min_idx = 0; + PRO_CHANNEL_BANK_TYPE ch_bank = {.d16 = RD_PPT_REG(PRO_CHANNEL_BANK)}; + bank = ch_bank.bank; + for (count = 0; count < num; count++) + { + idx = start_idx + count; + sync_ctx.chan_cfg[idx].rssi = -128; + } + for (uint8_t loop = 0; loop < SYNC_CCA_TIMES; loop++) + { + for (count = 0; count < num; count++) + { + idx = start_idx + count; + ppt_clear_psd_result(); + if (ch_bank.bank != sync_ctx.chan_cfg[idx].bank) + { + ch_bank.bank = sync_ctx.chan_cfg[idx].bank; + WR_PPT_REG(PRO_CHANNEL_BANK, ch_bank.d16); + } + + ppt_psd_mode_ext_t param = + { + { + .chann_start = sync_ctx.chan_cfg[idx].channel, + .chann_stop = sync_ctx.chan_cfg[idx].channel, + .chann_step = 1, + .mode = 0, + .timeout = 0x3ff + } + }; + ppt_set_psd_mode_ext(¶m); + ppt_enable_psd(NULL); + int16_t rssi = ppt_get_psd_result(sync_ctx.chan_cfg[idx].channel); + //APP_PRINT_TRACE3("sync: cca loop %d %d %d", loop, idx, rssi); + if (rssi > sync_ctx.chan_cfg[idx].rssi) + { + sync_ctx.chan_cfg[idx].rssi = rssi; + } + } + } + + if (ch_bank.bank != bank) + { + ch_bank.bank = bank; + WR_PPT_REG(PRO_CHANNEL_BANK, ch_bank.d16); + } + + for (count = 0; count < num; count++) + { + idx = start_idx + count; + if (sync_ctx.chan_cfg[idx].rssi < rssi_min) + { + rssi_min = sync_ctx.chan_cfg[idx].rssi; + rssi_min_idx = idx; + } + } + + if (rssi_min >= -85) + { + APP_PRINT_WARN3("sync: cca ret %d %d %d", rssi_min_idx, sync_ctx.chan_cfg[rssi_min_idx].freq, + rssi_min); + } + return rssi_min_idx; +} + +static void sync_slave_close(bool quit_tx) +{ + ppt_kill_hw_fsm(PPT_FSM_PRX); + if (quit_tx) + { + /* not all cases need it */ + ppt_force_fw_ack(0); + } +} + +static void sync_slave_init_tx_fifo(void) +{ + ppt_set_tx_fifo_rdptr(0, PPT_TX_FIFO_SIZE - 1); + ppt_force_fw_ack(0); + ppt_flush_tx_fifo(0); +} + +static void sync_slave_free_sending_msg(void) +{ + for (uint8_t loop = 0; loop < SYNC_SLAVE_TX_MSG_NUM; loop++) + { + sync_msg_t *pmsg = sync_slave_ctx.pmsg_sending[loop]; + if (pmsg) + { + if (pmsg->send_cb) + { + pmsg->send_info.res = SYNC_SEND_RESULT_UNKNOWN; + sync_pendcall(sync_msg_handle_tx_done, pmsg, 0); + } + else + { + sync_ctx.msg_count[pmsg->type] -= 1; + plt_list_push(&sync_ctx.msg_free, pmsg); + } + sync_slave_ctx.pmsg_sending[loop] = NULL; + } + } +} + +void sync_slave_handle_hopping(void); +static void sync_slave_handle_hopping_task(void *para1, uint32_t para2) +{ + if (sync_slave_ctx.state != SYNC_STATE_SYNC_SLOW) + { + APP_PRINT_WARN1("sync: slow hopping, state invalid %d!", sync_slave_ctx.state); + return; + } + +#if SYNC_SLAVE_CCA_ENABLE + int8_t chan_next = sync_slave_cca_get(0, sync_ctx.chan_num); +#else + uint8_t chan_next = sync_rand() % sync_ctx.chan_num; +#endif + //APP_PRINT_TRACE1("sync: slow hopping, cca change to %d!", chan_next); + sync_channel_change(chan_next); + /* restart rx */ + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_ENABLE); + sync_hw_timer_start(SYNC_SLAVE_HOPPING_SPEED, sync_slave_handle_hopping); +} + +void sync_slave_handle_hopping(void) +{ + sync_slave_close(false); + if (sync_slave_ctx.pair_state == SYNC_PAIR_STATE_PAIRING) + { + if (sync_slave_ctx.phase != SYNC_SLAVE_PAIR_PHASE_WAIT_REQ) + { + sync_slave_ctx.phase = SYNC_SLAVE_PAIR_PHASE_WAIT_REQ; + } + } + sync_pendcall(sync_slave_handle_hopping_task, 0, 0); +} + +static void sync_slave_handle_paired(void *para1, uint32_t para2) +{ + sync_bond_info_t bond_info; + bond_info.acc = sync_slave_ctx.acc_data; + sync_nvm_set_bond_info(&bond_info); + + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_PAIRED); + } + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_CONNECTED); + } +} + +static void sync_slave_handle_connected(void *para1, uint32_t para2) +{ + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_CONNECTED); + } +} + +static void sync_slave_handle_disconnected(void *para1, uint32_t para2) +{ + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_CONNECT_LOST); + } +} + +static void sync_slave_handle_exception(void) +{ + sync_slave_stop(); + sync_pendcall(sync_slave_handle_disconnected, 0, 0); +} + +#ifdef SYNC_DEBUG +static void sync_slave_handle_sync_lost(void *para1, uint32_t para2) +{ + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_SYNC_LOST); + } +} + +static void sync_slave_handle_sync_reconnected(void *para1, uint32_t para2) +{ + if (sync_ctx.event_cb) + { + sync_ctx.event_cb(SYNC_EVENT_SYNC_RECONNECTED); + } +} +#endif + +void sync_slave_handle_sync_fast_to(void) +{ + sync_slave_close(true); + sync_slave_free_sending_msg(); +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_power_ctrl_stop(); + } +#endif +#if SYNC_SUPPORT_MT1R + sync_hw_timer1_stop(); + ppt_reg_handler(sync_slave_ppt_isr_handler); +#endif + sync_slave_ctx.pair_state = SYNC_PAIR_STATE_IDLE; + sync_slave_ctx.state = SYNC_STATE_IDLE; + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(false); + } + APP_PRINT_ERROR0("sync: disconnected!"); + sync_pendcall(sync_slave_handle_disconnected, 0, 0); +} + +static void sync_slave_handle_sync_lost_to_task(void *para1, uint32_t para2) +{ + if (sync_slave_ctx.state != SYNC_STATE_SYNC_FAST) + { + APP_PRINT_WARN1("sync: lost, state invalid %d!", sync_slave_ctx.state); + return; + } + + uint8_t chan_num, chan_start; + chan_num = sync_ctx.chan_num / sync_ctx.chan_group_num; + chan_start = (sync_ctx.chan_cur / chan_num) * chan_num; +#if SYNC_SLAVE_CCA_ENABLE + int8_t chan_next = sync_slave_cca_get(chan_start, chan_num); +#else + uint8_t chan_next = chan_start + sync_rand() % chan_num; +#endif + APP_PRINT_INFO2("sync: lost, cca change to %d[%d]!", chan_next, sync_ctx.chan_cfg[chan_next].freq); + sync_channel_change(chan_next); + /* restart rx */ + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_ENABLE); + sync_hw_timer_start(SYNC_SYNC_FAST_PERIOD, sync_slave_handle_sync_fast_to); +} + +void sync_slave_handle_sync_lost_to(void) +{ + sync_slave_ctx.state = SYNC_STATE_SYNC_FAST; +#ifdef SYNC_SLAVE_FIXED_CHANNEL + APP_PRINT_INFO0("sync: lost!"); +#ifdef SYNC_DEBUG + sync_pendcall(sync_slave_handle_sync_lost, 0, 0); +#endif +#if SYNC_SUPPORT_SYNC_FAST_DISABLE + if (SYNC_SYNC_FAST_PERIOD == SYNC_SYNC_FAST_PERIOD_DISABLE) + { + sync_slave_handle_sync_fast_to(); + } + else +#endif + { + sync_hw_timer_start(SYNC_SYNC_FAST_PERIOD, sync_slave_handle_sync_fast_to); + } +#else +#if SYNC_SUPPORT_SYNC_FAST_DISABLE + if (SYNC_SYNC_FAST_PERIOD == SYNC_SYNC_FAST_PERIOD_DISABLE) + { + APP_PRINT_INFO0("sync: lost!"); +#ifdef SYNC_DEBUG + sync_pendcall(sync_slave_handle_sync_lost, 0, 0); +#endif + sync_slave_handle_sync_fast_to(); + } + else +#endif + { + /* stop rx */ + sync_slave_close(false); + sync_pendcall(sync_slave_handle_sync_lost_to_task, 0, 0); + } +#endif +} + +void sync_slave_handle_pair_cfm_to(void) +{ + sync_slave_ctx.phase = SYNC_SLAVE_PAIR_PHASE_WAIT_REQ; + APP_PRINT_INFO0("sync: back to pair!"); + + ppt_force_fw_ack(0); + sync_acc_t acc = {.addr = SYNC_ACC_ADDR_BR}; + sync_acc_set(acc); + sync_ppt_set_tifs(true); + + sync_set_header(0, 0); + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_PAIR_RSP; + pdu.pair_rsp.acc = sync_slave_ctx.acc_data; + ppt_push_tx_data(sizeof(sync_opcode_t) + sizeof(sync_pair_rsp_t), (uint8_t *)&pdu); + +#ifndef SYNC_SLAVE_FIXED_CHANNEL + sync_slave_handle_hopping(); +#endif +} + +PPT_API_SECTION void sync_slave_hb_exit(void) +{ + sync_slave_ctx.hb.slow = false; + sync_slave_ctx.hb.state = SYNC_SLAVE_HB_STATE_IDLE; + sync_slave_ctx.hb.exit_pend = false; + sync_hw_timer_start(SYNC_SYNC_LOST_PERIOD, sync_slave_handle_sync_lost_to); + APP_PRINT_INFO0("sync: hb exit"); + if (sync_ctx.hb_cb) + { + sync_hb_cb_param_t param = {}; + param.enter = false; + sync_ctx.hb_cb(¶m); + } +} + +PPT_ISR_SECTION void sync_slave_handle_tx_trigger(void) +{ + if (sync_slave_ctx.pair_state != SYNC_PAIR_STATE_PAIRED + || sync_slave_ctx.state != SYNC_STATE_SYNC) + { + return; + } + + if (sync_slave_ctx.hb.slow) + { + if (sync_slave_ctx.pmsg_sending[sync_slave_ctx.pmsg_sending_idx] == NULL) + { + if (sync_slave_ctx.busy == false) + { + sync_msg_t *pmsg = (sync_msg_t *)plt_list_pop(&sync_ctx.msg_send); + ppt_update_tx_fifo(0, pmsg->data, pmsg->len); + sync_slave_ctx.pmsg_sending[sync_slave_ctx.pmsg_sending_idx] = pmsg; + } + else + { + /* trigger latter after tx */ + sync_slave_ctx.tx_trigger_pending = true; + } + } + } +} + +PPT_ISR_SECTION void sync_slave_handle_ack(bool rx, sync_header_t rx_header) +{ + /* mark busy when send pkt immediately */ + sync_slave_ctx.busy = true; + + /* the old pkt is acked, just skip */ + if (sync_slave_ctx.pmsg_switching) + { + return; + } + + bool retrans = false; + uint8_t pmsg_sending_prev_idx = (sync_slave_ctx.pmsg_sending_idx + SYNC_SLAVE_TX_MSG_NUM - 1) % + SYNC_SLAVE_TX_MSG_NUM; + sync_msg_t *pmsg_sending_prev = sync_slave_ctx.pmsg_sending[pmsg_sending_prev_idx]; + if (pmsg_sending_prev && (rx == false || rx_header.ack == false)) + { + switch (pmsg_sending_prev->type) + { + case SYNC_MSG_TYPE_FINITE_RETRANS: + if (sync_ctx.msg_retrans_count < sync_ctx.msg_finite_retrans_num) + { + retrans = true; + } + break; + case SYNC_MSG_TYPE_INFINITE_RETRANS: + retrans = true; + break; + case SYNC_MSG_TYPE_DYNAMIC_RETRANS: + if (sync_ctx.msg_retrans_count < sync_ctx.msg_dynamic_retrans_num_lower || + (sync_ctx.msg_send.count == 0 && + (sync_ctx.msg_dynamic_retrans_num_upper == SYNC_MSG_RETRANS_NUM_INFINITE || + sync_ctx.msg_retrans_count < sync_ctx.msg_dynamic_retrans_num_upper))) + { + retrans = true; + } + break; + default: + break; + } + } + + if (retrans) + { + sync_ctx.msg_retrans_count += 1; + sync_set_header(rx, sync_slave_ctx.seq_tx); + sync_slave_ctx.pmsg_switching = false; + } + else + { +#if SYNC_SUPPORT_MT1R + if (sync_slave_ctx.quick.enable) + { + ppt_lock(0, true); + ppt_trigger_fw_ack_lock(0, false); + sync_slave_ctx.seq_tx += 1; + sync_slave_set_header(rx, sync_slave_ctx.seq_tx); + WR_PPT_REG_FIELD(PRO_HS_LENGTH, hs_field_length_4_0, SYNC_SUFFIX_LEN); + uint8_t ack_field = sync_slave_get_ack_field(); + WR_PPT_REG(PRO_HS_0_LOWER, ack_field); +#if TIME_DEBUG + if (ack_field == 0x7f) + { + time_debug_level_high(SYNC_SLAVE_TIME_DEBUG_ALL_ACK); + time_debug_level_low(SYNC_SLAVE_TIME_DEBUG_ALL_ACK); + } +#endif + ppt_lock(0, false); + } + else +#endif + { + uint8_t rd_ptr = ppt_trigger_fw_ack(0); + sync_slave_ctx.pmsg_ptr[sync_slave_ctx.pmsg_sending_idx] = rd_ptr; + sync_slave_ctx.pmsg_switching = true; + sync_slave_ctx.pmsg_last_empty = pmsg_sending_prev == NULL; + sync_slave_ctx.seq_tx += 1; + sync_set_header(rx, sync_slave_ctx.seq_tx); + } + + if (pmsg_sending_prev) + { + sync_msg_t *pmsg = pmsg_sending_prev; + sync_slave_ctx.pmsg_sending[pmsg_sending_prev_idx] = NULL; + if (pmsg->send_cb) + { + pmsg->send_info.res = rx ? (rx_header.ack ? SYNC_SEND_RESULT_ACKED : SYNC_SEND_RESULT_NACKED) : + SYNC_SEND_RESULT_UNKNOWN; + pmsg->send_info.retrans_count = sync_ctx.msg_retrans_count; + sync_pendcall(sync_msg_handle_tx_done, pmsg, 0); + } + else + { + sync_ctx.msg_count[pmsg->type] -= 1; + plt_list_push(&sync_ctx.msg_free, pmsg); + } + } + + sync_ctx.msg_retrans_count = 0; + uint8_t pmsg_sending_next_idx = (sync_slave_ctx.pmsg_sending_idx + 1) % SYNC_SLAVE_TX_MSG_NUM; + if (sync_ctx.msg_send.count == 0) + { + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_EMPTY; + DBG_DIRECT("ppt slave send empty data"); + ppt_push_tx_data(sizeof(sync_opcode_t), (uint8_t *)&pdu); + sync_slave_ctx.pmsg_sending[pmsg_sending_next_idx] = NULL; + } + else + { + sync_msg_t *pmsg = (sync_msg_t *)plt_list_pop(&sync_ctx.msg_send); + DBG_DIRECT("ppt slave send pmsg data"); + ppt_push_tx_fifo(0, pmsg->data, pmsg->len); + sync_slave_ctx.pmsg_sending[pmsg_sending_next_idx] = pmsg; + } + } + +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_ctx.power_ctrl_count_fail += !(rx && rx_header.ack); + } +#endif +} + +PPT_ISR_SECTION void sync_slave_handle_rx(bool rx, sync_header_t rx_header, sync_pdu_t *rx_pdu, + uint16_t rx_len, int8_t rssi, uint32_t rx_crc) +{ + //APP_PRINT_ERROR5("sync: prx rx %d, header 0x%x, seq rx %d, len %d %b", rx, rx_header.value, sync_slave_ctx.seq_rx, rx_len, TRACE_BINARY(rx_len, rx_pdu)); + if (rx) + { + /* rx fail may due to interference, so can't exit hb */ + sync_slave_ctx.hb.state = SYNC_SLAVE_HB_STATE_IDLE; + bool seq_check = false; +#if SYNC_SUPPORT_MT1R + if (sync_slave_ctx.quick.enable) + { + if (sync_slave_ctx.seq_rx != rx_header.m.seq) + { + seq_check = true; + sync_slave_ctx.seq_rx = rx_header.m.seq; + } + } + else +#endif + { + if (sync_slave_ctx.seq_rx != rx_header.seq) + { + seq_check = true; + sync_slave_ctx.seq_rx = rx_header.seq; + } + } + if (seq_check) + { + sync_slave_ctx.crc_rx = rx_crc; + if (rx_len > 0) + { + if (rx_pdu->opcode >= SYNC_OPCODE_APP_START) + { +#if SYNC_SUPPORT_MT1R + if (sync_slave_ctx.quick.enable) + { + if (sync_rx_msg_queue.tail + 1 == sync_rx_msg_queue.head) + { + APP_PRINT_ERROR0("sync: rx full"); + } + else + { + if (rx_len <= SYNC_RX_MSG_QUEUE_DATA_LEN) + { + uint8_t idx = sync_rx_msg_queue.tail & (SYNC_RX_MSG_QUEUE_SIZE - 1); + sync_rx_msg_queue.buffer[idx].rssi = rssi; + sync_rx_msg_queue.buffer[idx].len = rx_len; + memcpy(sync_rx_msg_queue.buffer[idx].data, (uint8_t *)rx_pdu, rx_len); + sync_rx_msg_queue.tail += 1; + sync_pendcall(sync_slave_handle_rx_task, 0, 0); + } + else + { + APP_PRINT_ERROR1("sync: rx length exceed %d", rx_len); + } + } + } + else +#endif + { + if (sync_ctx.msg_receive_cb) + { + sync_receive_info_t info = {}; + info.rssi = rssi; + sync_ctx.msg_receive_cb((uint8_t *)rx_pdu, rx_len, &info); + } + } + } + else + { + bool handle = false; + switch (rx_pdu->opcode) + { + case SYNC_OPCODE_EMPTY: + if (rx_len == 1) + { + handle = true; + } + break; + case SYNC_OPCODE_HEARTBEAT: + if (rx_len == sizeof(sync_opcode_t) + sizeof(sync_heartbeat_t)) + { + handle = true; + if (rx_pdu->heartbeat.interval != 0) + { + if (sync_slave_ctx.hb.interval != rx_pdu->heartbeat.interval) + { + sync_slave_ctx.hb.interval_changed = true; + sync_slave_ctx.hb.interval = rx_pdu->heartbeat.interval; + } + sync_slave_ctx.hb.state = SYNC_SLAVE_HB_STATE_HB_RECEIVED; + } + else + { + APP_PRINT_ERROR0("sync: hb interval invalid"); + } + } + break; + default: + break; + } + if (handle == false) + { + APP_PRINT_WARN3("sync: rx unknown opcode %d, len %d, data %b", rx_pdu->opcode, rx_len, + TRACE_BINARY(rx_len, rx_pdu)); + } + } + } + } + } +} + +#if SYNC_SUPPORT_MT1R +static void sync_slave_handle_sync_lost_to_task_quick(void *para1, uint32_t para2) +{ + if (sync_slave_ctx.state != SYNC_STATE_SYNC_FAST) + { + APP_PRINT_WARN1("sync: lost, state invalid %d!", sync_slave_ctx.state); + return; + } + uint8_t chan_num, chan_start; + chan_num = sync_ctx.chan_num / sync_ctx.chan_group_num; + chan_start = (sync_ctx.chan_cur / chan_num) * chan_num; +#if SYNC_SLAVE_CCA_ENABLE + int8_t chan_next = sync_slave_cca_get(chan_start, chan_num); +#else + uint8_t chan_next = chan_start + sync_rand() % chan_num; +#endif + APP_PRINT_INFO2("sync: lost, cca change to %d[%d]!", chan_next, sync_ctx.chan_cfg[chan_next].freq); + sync_channel_change(chan_next); + + /* restart rx */ + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_ENABLE); + sync_slave_ctx.count = 0; +} + +void sync_slave_handle_sync_lost_to_quick(void) +{ + sync_slave_ctx.state = SYNC_STATE_SYNC_FAST; + sync_slave_ctx.count = 0; + + APP_PRINT_INFO0("sync: lost!"); +#ifdef SYNC_DEBUG + sync_pendcall(sync_slave_handle_sync_lost, 0, 0); +#endif + +#if SYNC_SUPPORT_SYNC_FAST_DISABLE + if (SYNC_SYNC_FAST_PERIOD == SYNC_SYNC_FAST_PERIOD_DISABLE) + { + sync_hw_timer_stop(); + sync_hw_timer1_stop(); + sync_slave_handle_sync_fast_to(); + return; + } +#endif + +#ifndef SYNC_SLAVE_FIXED_CHANNEL + { + /* stop rx */ + sync_slave_close(false); + sync_pendcall(sync_slave_handle_sync_lost_to_task_quick, 0, 0); + } +#endif +} + +void sync_rx_msg_queue_clear(void) +{ + sync_rx_msg_queue.head = 0; + sync_rx_msg_queue.tail = 0; +} + +static void sync_slave_handle_rx_task(void *para1, uint32_t para2) +{ + while (sync_rx_msg_queue.tail != sync_rx_msg_queue.head) + { + uint8_t idx = sync_rx_msg_queue.head & (SYNC_RX_MSG_QUEUE_SIZE - 1); + if (sync_ctx.msg_receive_cb) + { + sync_receive_info_t info = {}; + info.rssi = sync_rx_msg_queue.buffer[idx].rssi; + sync_ctx.msg_receive_cb(sync_rx_msg_queue.buffer[idx].data, sync_rx_msg_queue.buffer[idx].len, + &info); + } + sync_rx_msg_queue.head += 1; + } +} + +static void sync_slave_close_quick(void) +{ + PRO_MISR_TYPE reg_misr; + reg_misr.d16 = RD_PPT_REG(PRO_MISR); + bool clear = reg_misr.tx_int == false && reg_misr.rx_int == false; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_DISABLE); + ppt_clear_radio(); + do + { + reg_misr.d16 = RD_PPT_REG(PRO_MISR); + } + while (reg_misr.kill_prx_int == 0); + WR_PPT_REG(PRO_MISR, BIT5); + if (clear) + { + ppt_flush_trx_int(); + } +} + + +PPT_ISR_SECTION static inline void sync_slave_init_ack_field(void) +{ + sync_slave_ctx.quick.seq_ack_rx = 1; + sync_slave_ctx.quick.ack_rsp_idx = 2; + sync_slave_ctx.quick.ack_curr_idx = 2; + sync_slave_ctx.quick.ack_field[2] = 0xff; +} + +PPT_ISR_SECTION static inline uint8_t sync_slave_get_ack_field(void) +{ + return sync_slave_ctx.quick.ack_field[sync_slave_ctx.quick.ack_rsp_idx]; +} + +PPT_ISR_SECTION static inline void sync_slave_refresh_ack_field(void) +{ + uint8_t num = (3 - sync_slave_ctx.quick.ack_rsp_idx + sync_slave_ctx.quick.ack_curr_idx) % 3 + 1; + if (num > 1) + { + time_debug_level_high(SYNC_SLAVE_TIME_DEBUG_REFRESH_ACK); + time_debug_level_low(SYNC_SLAVE_TIME_DEBUG_REFRESH_ACK); + if (num > 2) + { + time_debug_level_high(SYNC_SLAVE_TIME_DEBUG_REFRESH_ACK); + time_debug_level_low(SYNC_SLAVE_TIME_DEBUG_REFRESH_ACK); + } + } + + if (num < 3) + { + sync_slave_ctx.quick.ack_curr_idx = (sync_slave_ctx.quick.ack_curr_idx + 1) % 3; + } + else + { + uint8_t ack = sync_slave_ctx.quick.ack_field[sync_slave_ctx.quick.ack_curr_idx]; + if (ack) + { + uint8_t idx = (sync_slave_ctx.quick.ack_curr_idx + 2) % 3; + sync_slave_ctx.quick.ack_field[idx] = ack; + } + } + sync_slave_ctx.quick.ack_field[sync_slave_ctx.quick.ack_curr_idx] = 0; + if (SYNC_ISR_DEBUG) + { + APP_PRINT_WARN7("sync_isr: ack refresh ce %d, seq %d, rsp %d, curr %d, 0x%x 0x%x 0x%x", + sync_slave_ctx.quick.ce_cnt, sync_slave_ctx.quick.seq_ack_rx, + sync_slave_ctx.quick.ack_rsp_idx, sync_slave_ctx.quick.ack_curr_idx, + sync_slave_ctx.quick.ack_field[0], sync_slave_ctx.quick.ack_field[1], + sync_slave_ctx.quick.ack_field[2]); + } + +} + +PPT_ISR_SECTION static inline bool sync_slave_update_ack_field(uint8_t slot_idx, uint8_t seq_ack, + bool req_ack) +{ + bool ack = true; + uint32_t lock = sync_enter_critical(); + if (req_ack) + { + if (sync_slave_ctx.quick.seq_ack_rx != seq_ack) + { + sync_slave_ctx.quick.ack_rsp_idx = (sync_slave_ctx.quick.ack_rsp_idx + 1) % 3; + } + else + { + ack = false; + uint8_t num = (3 - sync_slave_ctx.quick.ack_rsp_idx + sync_slave_ctx.quick.ack_curr_idx) % 3 + 1; + if (num == 3) + { + time_debug_level_high(SYNC_SLAVE_TIME_DEBUG_UPDATE_ACK); + time_debug_level_low(SYNC_SLAVE_TIME_DEBUG_UPDATE_ACK); + uint8_t ack = sync_slave_ctx.quick.ack_field[sync_slave_ctx.quick.ack_curr_idx]; + sync_slave_ctx.quick.ack_curr_idx = (sync_slave_ctx.quick.ack_curr_idx + 2) % 3; + sync_slave_ctx.quick.ack_field[sync_slave_ctx.quick.ack_curr_idx] = ack; + } + } + } + else + { + sync_slave_ctx.quick.ack_field[sync_slave_ctx.quick.ack_curr_idx] |= BIT(slot_idx); + sync_slave_ctx.quick.ack_rsp_idx = sync_slave_ctx.quick.ack_curr_idx; + } + sync_slave_ctx.quick.seq_ack_rx = seq_ack; + sync_exit_critical(lock); + if (sync_slave_ctx.quick.seq_ack_rx & 0x1) + { + time_debug_level_high(SYNC_SLAVE_TIME_DEBUG_SEQ_ACK); + } + else + { + time_debug_level_low(SYNC_SLAVE_TIME_DEBUG_SEQ_ACK); + } + return ack; +} + +PPT_ISR_SECTION void sync_slave_handle_sync_early_delay(void) +{ + WR_PPT_REG_FIELD(PRO_TRX_CONTROL, ack_enable, 1); +} + +PPT_ISR_SECTION void sync_slave_handle_sync_early_quick(void) +{ + sync_slave_ctx.quick.count = (sync_slave_ctx.quick.count + 1) & 0x7; + if (sync_slave_ctx.quick.count == 0) + { + time_debug_level_low(TIME_DEBUG_CHAN_TIMING); + sync_slave_ctx.quick.ce_cnt += 1; + sync_slave_refresh_ack_field(); + WR_PPT_REG_FIELD(PRO_TRX_CONTROL, ack_enable, 0); + + time_debug_level_high(TIME_DEBUG_CHAN_TIMING); + sync_slave_ctx.rx_ok = false; + sync_slave_ctx.ce_clk = sync_hw_frtimer_get_count(); + } + else if (sync_slave_ctx.quick.count == 7) + { + if (sync_slave_ctx.rx_ok == false) + { + if (sync_slave_ctx.state == SYNC_STATE_SYNC) + { + sync_slave_ctx.count += 1; + uint32_t hb_time = sync_slave_ctx.count * SYNC_SYNC_SPEED; + if (sync_slave_ctx.hb.slow) + { + uint32_t hb_interval = SYNC_HB_TO_TIMES * sync_slave_ctx.hb.interval * 1000; + if (hb_time >= hb_interval + SYNC_SYNC_LOST_PERIOD) + { + sync_slave_handle_sync_lost_to_quick(); + } + else if (hb_time >= hb_interval) + { + sync_hw_timer_shift(-5); + } + } + else + { + if (hb_time >= SYNC_SYNC_LOST_PERIOD) + { + sync_slave_handle_sync_lost_to_quick(); + } + else if (sync_slave_ctx.count >= 2) + { + sync_hw_timer_shift(-5); + } + } + } + else if (sync_slave_ctx.state == SYNC_STATE_SYNC_FAST) + { + sync_slave_ctx.count += 1; + if (sync_slave_ctx.count >= SYNC_SYNC_FAST_PERIOD / SYNC_SYNC_SPEED) + { + sync_hw_timer_stop(); + sync_hw_timer1_stop(); + sync_slave_handle_sync_fast_to(); + } + } + } + else + { + sync_slave_ctx.count = 0; + } + } + else + { + if (sync_slave_ctx.quick.count == 6) + { + /* delay to start ack in case of ack at slot 5 */ + sync_hw_timer1_start(30, sync_slave_handle_sync_early_delay); + } + } +} + +PPT_ISR_SECTION static void sync_slave_ppt_isr_handler_quick(void) +{ + time_debug_level_high(TIME_DEBUG_CHAN_ISR); + PRO_MISR_TYPE reg_misr; + reg_misr.d16 = RD_PPT_REG(PRO_MISR); + //APP_PRINT_INFO1("sync_isr: prx isr 0x%04x", reg_misr.d16); + + if (reg_misr.rx_int) + { + do + { + /* pop rx stack */ + //PRO_LENGTH_INCLUDE_ADDON_TYPE len_inc_addon = {.d16 = RD_PPT_REG(PRO_LENGTH_INCLUDE_ADDON)}; + PRO_RX_STACK_TYPE rx_stack = {.d16 = RD_PPT_REG(PRO_RX_STACK)}; + if (rx_stack.is_max_length) + { + if (SYNC_ISR_DEBUG) + { + APP_PRINT_WARN0("sync_isr: prx rx max length"); + } + PRO_RX_HS_UPPER_TYPE rx_hs_upper = {.d16 = RD_PPT_REG(PRO_RX_HS_UPPER)}; //read 0x62 to pop rx stack + break; + } + + if (rx_stack.rx_time_out || rx_stack.rx_abort_rd) + { + PRO_RX_HS_UPPER_TYPE rx_hs_upper = {.d16 = RD_PPT_REG(PRO_RX_HS_UPPER)}; //read 0x62 to pop rx stack + break; + } + + PRO_RX_HP_TYPE hp = {.d16 = RD_PPT_REG(PRO_RX_HP)}; + bool rx = !(rx_stack.rx_time_out || rx_stack.rx_hit == false || rx_stack.is_crc_error); + sync_slave_ctx.rx_ok = rx; + sync_slave_ctx.busy = sync_slave_ctx.rx_ok; + + PRO_LENGTH_TYPE length = {.d16 = RD_PPT_REG(PRO_LENGTH)}; + PRO_ACCHIT_CLK_LOWER_TYPE acchit_clk_lower = {.d16 = RD_PPT_REG(PRO_ACCHIT_CLK_LOWER)}; + PRO_ACCHIT_CLK_UPPER_TYPE acchit_clk_upper = {.d16 = RD_PPT_REG(PRO_ACCHIT_CLK_UPPER)}; + uint32_t acchit_clk = (acchit_clk_upper.acchit_clk_31_16 << 16) + acchit_clk_lower.acchit_clk_15_0; + sync_slave_ctx.quick.rx_clk = acchit_clk; + int32_t clk_diff = 0; + /* determine slot only when rx success */ + bool ack_slot_flag = false; + sync_slave_ctx.quick.slot_idx = -1; + if (rx_stack.rx_time_out == false && rx_stack.rx_hit) + { + time_debug_level_high(SYNC_SLAVE_TIME_DEBUG_RX); + uint32_t lock = sync_enter_critical(); + do + { + //if (sync_slave_ctx.quick.count == 6 || sync_slave_ctx.quick.count == 7) + { + clk_diff = sync_hw_frtimer_time_diff(acchit_clk, sync_slave_ctx.ce_clk); + //APP_PRINT_ERROR3("DIFF %d %x %x", clk_diff, sync_slave_ctx.ce_clk, acchit_clk); + /* the acchit time is at least 30us */ + if (clk_diff < 30) + { + break; + } + clk_diff -= 30; + sync_slave_ctx.quick.slot_idx = clk_diff / 125; + /* ack slot */ + if (clk_diff < 750 || clk_diff >= 875) + { + break; + } + ack_slot_flag = true; + } + } + while (0); + sync_exit_critical(lock); + time_debug_level_low(SYNC_SLAVE_TIME_DEBUG_RX); +#if TIME_DEBUG + if (length.d16 != 1) + { + time_debug_level_high(SYNC_SLAVE_TIME_DEBUG_RX); + time_debug_level_low(SYNC_SLAVE_TIME_DEBUG_RX); + } +#endif + } + +#if SYNC_SLAVE_TEST_RX_STOP + bool switch_ptx = false; +#endif + sync_header_t rx_header; + rx_header.value = hp.hp; + if (rx) + { + /* check ack status */ + if (sync_slave_ctx.quick.slot_idx >= 0) + { + bool ack = sync_slave_update_ack_field(sync_slave_ctx.quick.slot_idx, rx_header.m.ack, + rx_header.m.req_ack); + rx_header.ack = ack; + } + + if (sync_slave_ctx.quick.slot_idx < 6 && ((sync_slave_ctx.quick.slot_idx & 0x1) == 0) && + rx_header.m.req_ack) + { + time_debug_level_high(SYNC_SLAVE_TIME_DEBUG_REQ_ACK); + time_debug_level_low(SYNC_SLAVE_TIME_DEBUG_REQ_ACK); + + //time_debug_level_high(TIME_DEBUG_CHAN_THREAD); + ppt_kill_hw_fsm(PPT_FSM_PRX); + //time_debug_level_low(TIME_DEBUG_CHAN_THREAD); + ppt_execute_instruction(PPT_HW_INSTRUCTION_PTX_ENABLE); + sync_slave_handle_ack(rx, rx_header); + sync_slave_ctx.quick.req_ack = true; +#if SYNC_SLAVE_TEST_RX_STOP + switch_ptx = true; +#endif + } + } + + /* only ack slot ptx pkt is used */ + if (rx && ack_slot_flag) + { + sync_slave_handle_ack(rx, rx_header); + + int32_t slot_shift = clk_diff - 750; + slot_shift -= 48; + if (ABS(slot_shift) > 2) + { + uint32_t lock = sync_enter_critical(); + sync_hw_timer_shift(slot_shift); + sync_exit_critical(lock); + } + } + + PRO_RX_CRC_LOWER_TYPE rx_crc_lower = {.d16 = RD_PPT_REG(PRO_RX_CRC_LOWER)}; + PRO_RX_CRC_UPPER_TYPE rx_crc_upper = {.d16 = RD_PPT_REG(PRO_RX_CRC_UPPER)}; + uint32_t rx_crc = (rx_crc_upper.d16 << 16) + rx_crc_lower.d16; + rx_crc >>= 32 - SYNC_CRC_LEN; + //PRO_RX_HS_LOWER_TYPE rx_hs_lower = {.d16 = RD_PPT_REG(PRO_RX_HS_LOWER)}; + PRO_RSSI_TYPE rssi = {.d16 = RD_PPT_REG(PRO_RSSI)}; + PRO_RX_HS_UPPER_TYPE rx_hs_upper = {.d16 = RD_PPT_REG(PRO_RX_HS_UPPER)}; //read 0x62 to pop rx stack + //PRO_INT_CTRL_TYPE int_ctrl = {.d16 = RD_PPT_REG(PRO_INT_CTRL)}; + if (SYNC_ISR_DEBUG) + { + APP_PRINT_INFO6("sync_isr: prx rx stack 0x%04x, ce %d, clk %d, slot %d, hp 0x%02x, len %d", + rx_stack.d16, + sync_slave_ctx.quick.ce_cnt, clk_diff, sync_slave_ctx.quick.slot_idx, hp.hp, length.d16); + } + + /* clear rx fifo */ + if (ack_slot_flag || rx == false) + { + uint16_t rx_len = rx ? length.d16 + SYNC_OVERHEAD_LEN : 0; + if (rx_len & 0x1) + { + rx_len += 1; + } + ACL_RX_FIFO_S rx_fifo_ptr = {.d32 = RD_U32_BZDMA_REG(BZDMA_REG_ACL_RXFIFO_PTR)}; + uint16_t fifo_len = BZDMA_ACL_RXFIFO_GET_LEFT(rx_fifo_ptr); + while (fifo_len > rx_len) + { + uint16_t pop_len = MIN(fifo_len - rx_len, PPT_RX_BUFFER_SIZE); + uint8_t *rx_buffer = ppt_get_rx_buffer(pop_len); + ppt_pop_rx_fifo(0, rx_buffer, pop_len); + fifo_len -= pop_len; + } + } + + if (rx) + { + uint16_t rx_len = length.d16 + SYNC_OVERHEAD_LEN; + uint8_t *rx_buffer = ppt_get_rx_buffer(rx_len); + ppt_pop_rx_fifo(0, rx_buffer, rx_len); +#if SYNC_SLAVE_TEST_RX_STOP + uint32_t rand = sync_rand() % 100; + if (rand < 30) + { + time_debug_level_high(TIME_DEBUG_CHAN_THREAD); + ppt_kill_hw_fsm(switch_ptx ? PPT_FSM_PTX : PPT_FSM_PRX); + time_debug_level_low(TIME_DEBUG_CHAN_THREAD); + time_debug_level_high(TIME_DEBUG_CHAN_THREAD); + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_ENABLE); + time_debug_level_low(TIME_DEBUG_CHAN_THREAD); + WR_PPT_REG_FIELD(PRO_HS_LENGTH, hs_field_length_4_0, 0); + } + else +#endif + { + sync_pdu_t *pdu = (sync_pdu_t *)(rx_buffer + SYNC_OVERHEAD_LEN); + uint16_t buffer_head = rx_buffer[0] + (rx_buffer[1] << 8); + uint8_t buffer_len = ((buffer_head >> SYNC_HEADER_LEN_MT1R) & (0xffff >> (16 - SYNC_LENGTH_LEN))); + if (length.d16 != buffer_len) + { + APP_PRINT_INFO3("sync_isr: rx fifo corrupt, count %d, rx len %d, buffer len %d", + sync_slave_ctx.quick.count, length.d16, buffer_len); + break; + } + if (length.d16 == sizeof(sync_opcode_t) + sizeof(sync_conn_req_t) && + pdu->opcode == SYNC_OPCODE_CONN_REQ) + { + APP_PRINT_ERROR0("sync: state out of sync"); + sync_slave_handle_exception(); + break; + } + + sync_header_t rx_header; + rx_header.value = hp.hp; + int8_t rssi_dbm = ppt_get_rssi(rssi.rssi); + sync_slave_handle_rx(true, rx_header, pdu, length.d16, rssi_dbm, rx_crc); + } + } + else + { + if (ack_slot_flag) + { + sync_slave_close(false); + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_ENABLE); + } + } + } + while (0); + } + + if (reg_misr.tx_int) + { + sync_slave_ctx.busy = false; + sync_slave_ctx.count = 0; + WR_PPT_REG_FIELD(PRO_HS_LENGTH, hs_field_length_4_0, 0); + /* pop tx stack */ + PRO_TX_STACK_TYPE tx_stack = {.d16 = RD_PPT_REG(PRO_TX_STACK)}; + PRO_TX_HS_LOWER_TYPE hs_lower = {.d16 = RD_PPT_REG(PRO_TX_HS_LOWER)}; + PRO_TX_HS_UPPER_TYPE hs_upper = {.d16 = RD_PPT_REG(PRO_TX_HS_UPPER)}; + PRO_TX_CLK_LOWER_TYPE tx_clk_lower = {.d16 = RD_PPT_REG(PRO_TX_CLK_LOWER)}; + PRO_TX_CLK_UPPER_TYPE tx_clk_upper = {.d16 = RD_PPT_REG(PRO_TX_CLK_UPPER)}; + PRO_TX_LENGTH_TYPE length = {.d16 = RD_PPT_REG(PRO_TX_LENGTH)}; // Read 0x6a to pop stack + + if (sync_slave_ctx.quick.req_ack) + { + sync_slave_ctx.quick.req_ack = false; + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_ENABLE); + } + + uint32_t tx_clk = (tx_clk_upper.tx_clk_31_16 << 16) + tx_clk_lower.tx_clk_15_0; + uint32_t tx_delay = sync_hw_frtimer_time_elapse(tx_clk, sync_slave_ctx.quick.rx_clk); + + + uint32_t hs = (hs_upper.d16 << 16) + hs_lower.d16; + if (SYNC_ISR_DEBUG) + { + APP_PRINT_INFO8("sync_isr: prx tx stack 0x%04x(ptr %d), ce %d slot %d, delay %d hs 0x%x(0x%x), len %d", + tx_stack.d16, + tx_stack.tx_ptr, sync_slave_ctx.quick.ce_cnt, sync_slave_ctx.quick.slot_idx, + tx_delay, hs, sync_slave_get_ack_field(), length.tx_length); + } + if (hs != sync_slave_get_ack_field()) + { + if (SYNC_ISR_DEBUG) + { + APP_PRINT_WARN0("sync_isr: prx tx header fail"); + } + } + + sync_msg_t *pmsg = sync_slave_ctx.pmsg_sending[sync_slave_ctx.pmsg_sending_idx]; + //APP_PRINT_INFO3("sync: hb sending idx %d 0x%x, hb state %d", sync_slave_ctx.pmsg_sending_idx, pmsg, + // sync_slave_ctx.hb.state); + bool slow = false; + if (pmsg == NULL) + { + if (sync_slave_ctx.hb.state == SYNC_SLAVE_HB_STATE_HB_RECEIVED) + { + slow = true; + } + } + + if (slow) + { + if (sync_slave_ctx.hb.slow == false || sync_slave_ctx.hb.interval_changed) + { + sync_slave_ctx.hb.slow = true; + sync_slave_ctx.hb.interval_changed = false; + APP_PRINT_INFO1("sync: hb enter %dms", sync_slave_ctx.hb.interval); + if (sync_ctx.hb_cb) + { + sync_hb_cb_param_t param = {}; + param.enter = true; + param.interval = sync_slave_ctx.hb.interval; + sync_ctx.hb_cb(¶m); + } + } + } + else + { + if (sync_slave_ctx.hb.slow) + { + sync_slave_ctx.hb.slow = false; + sync_slave_ctx.hb.state = SYNC_SLAVE_HB_STATE_IDLE; + APP_PRINT_INFO0("sync: hb exit"); + if (sync_ctx.hb_cb) + { + sync_hb_cb_param_t param = {}; + param.enter = false; + sync_ctx.hb_cb(¶m); + } + } + } + sync_slave_ctx.pmsg_sending_idx = (sync_slave_ctx.pmsg_sending_idx + 1) % SYNC_SLAVE_TX_MSG_NUM; + + if (sync_slave_ctx.state == SYNC_STATE_SYNC && sync_slave_ctx.rx_ok) + { + //sync_hw_timer_set_periodic(false); + //sync_slave_handle_sync_early(); + } + } + + time_debug_level_low(TIME_DEBUG_CHAN_ISR); +} + +PPT_ISR_SECTION void sync_slave_handle_sync_early_first_quick(void) +{ + sync_hw_timer_start(125, sync_slave_handle_sync_early_quick); + sync_hw_timer_set_periodic(true); + sync_hw_timer_impr_ip(true); + ppt_reg_handler(sync_slave_ppt_isr_handler_quick); + sync_slave_ctx.quick.enable = true; + sync_slave_ctx.quick.count = 7; + sync_slave_ctx.quick.ce_cnt -= 1; + sync_slave_init_ack_field(); + WR_PPT_REG_FIELD(PRO_FIELD_LENGTH, hp_field_length_3_0, SYNC_HEADER_LEN_MT1R); + sync_slave_handle_sync_early_quick(); +} +#endif + +PPT_ISR_SECTION static void sync_slave_ppt_isr_handler(void) +{ + time_debug_level_high(TIME_DEBUG_CHAN_ISR); + PRO_MISR_TYPE reg_misr; + reg_misr.d16 = RD_PPT_REG(PRO_MISR); + //APP_PRINT_INFO1("sync_isr: prx isr 0x%04x", reg_misr.d16); + + if (reg_misr.rx_int) + { + do + { + /* pop rx stack */ + //PRO_LENGTH_INCLUDE_ADDON_TYPE len_inc_addon = {.d16 = RD_PPT_REG(PRO_LENGTH_INCLUDE_ADDON)}; + PRO_RX_STACK_TYPE rx_stack = {.d16 = RD_PPT_REG(PRO_RX_STACK)}; + if (rx_stack.is_max_length) + { + APP_PRINT_WARN0("sync_isr: prx rx max length"); + PRO_RX_HS_UPPER_TYPE rx_hs_upper = {.d16 = RD_PPT_REG(PRO_RX_HS_UPPER)}; //read 0x62 to pop rx stack + break; + } + + PRO_RX_HP_TYPE hp = {.d16 = RD_PPT_REG(PRO_RX_HP)}; + bool rx = !(rx_stack.rx_time_out || rx_stack.rx_hit == false || rx_stack.is_crc_error); + /* no need ack when rx fail */ + if (rx) + { + if (sync_slave_ctx.pair_state == SYNC_PAIR_STATE_PAIRED && + sync_slave_ctx.state != SYNC_STATE_SYNC_SLOW) + { + sync_header_t rx_header; + rx_header.value = hp.hp; + sync_slave_handle_ack(rx, rx_header); + + /* exit hb state if acked when pending */ + if (sync_slave_ctx.hb.slow && sync_slave_ctx.hb.exit_pend) + { + if (rx_header.ack) + { + sync_slave_hb_exit(); + } + } + } + } + +#if SYNC_SUPPORT_MT1R + PRO_ACCHIT_CLK_LOWER_TYPE acchit_clk_lower = {.d16 = RD_PPT_REG(PRO_ACCHIT_CLK_LOWER)}; + PRO_ACCHIT_CLK_UPPER_TYPE acchit_clk_upper = {.d16 = RD_PPT_REG(PRO_ACCHIT_CLK_UPPER)}; +#endif + PRO_RX_CRC_LOWER_TYPE rx_crc_lower = {.d16 = RD_PPT_REG(PRO_RX_CRC_LOWER)}; + PRO_RX_CRC_UPPER_TYPE rx_crc_upper = {.d16 = RD_PPT_REG(PRO_RX_CRC_UPPER)}; + uint32_t rx_crc = (rx_crc_upper.d16 << 16) + rx_crc_lower.d16; + rx_crc >>= 32 - SYNC_CRC_LEN; + //PRO_RX_HS_LOWER_TYPE rx_hs_lower = {.d16 = RD_PPT_REG(PRO_RX_HS_LOWER)}; + PRO_RSSI_TYPE rssi = {.d16 = RD_PPT_REG(PRO_RSSI)}; + PRO_LENGTH_TYPE length = {.d16 = RD_PPT_REG(PRO_LENGTH)}; + PRO_RX_HS_UPPER_TYPE rx_hs_upper = {.d16 = RD_PPT_REG(PRO_RX_HS_UPPER)}; //read 0x62 to pop rx stack + //PRO_INT_CTRL_TYPE int_ctrl = {.d16 = RD_PPT_REG(PRO_INT_CTRL)}; + if (SYNC_ISR_DEBUG) + { + APP_PRINT_INFO4("sync_isr: prx rx stack 0x%04x, rssi %d, hp 0x%02x, len %d", rx_stack.d16, + ppt_get_rssi(rssi.rssi), hp.hp, length.d16); + } + + if (rx) + { + uint16_t rx_len = length.d16 + SYNC_OVERHEAD_LEN; + uint8_t *rx_buffer = ppt_get_rx_buffer(rx_len); + ppt_pop_rx_fifo(0, rx_buffer, rx_len); + sync_pdu_t *pdu = (sync_pdu_t *)(rx_buffer + SYNC_OVERHEAD_LEN); + + if (sync_slave_ctx.pair_state == SYNC_PAIR_STATE_PAIRED) + { + bool handle_rx = true; + if (sync_slave_ctx.state == SYNC_STATE_SYNC) + { +#if SYNC_SUPPORT_MT1R + if (sync_slave_ctx.quick.enable == false) +#endif + { + sync_hw_timer_restart(); + } + } + else if (sync_slave_ctx.state == SYNC_STATE_SYNC_SLOW) + { + handle_rx = false; + if (length.d16 == sizeof(sync_opcode_t) + sizeof(sync_conn_req_t) && + pdu->opcode == SYNC_OPCODE_CONN_REQ) + { +#if SYNC_SUPPORT_MT1R + sync_ctx.mt1r.flag = pdu->conn_req.interval == 125; + sync_ctx.conn_interval = sync_ctx.mt1r.flag ? 250 : pdu->conn_req.interval; + if (sync_ctx.mt1r.flag) + { + sync_slave_ctx.ce_clk = (acchit_clk_upper.acchit_clk_31_16 << 16) + + acchit_clk_lower.acchit_clk_15_0; + } +#else + sync_ctx.conn_interval = pdu->conn_req.interval; +#endif + } + else + { + sync_slave_close(false); + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_ENABLE); + } + } + else if (sync_slave_ctx.state == SYNC_STATE_SYNC_FAST) + { + APP_PRINT_INFO2("sync: reconnected on %d[%d]!", sync_ctx.chan_cur, + sync_ctx.chan_cfg[sync_ctx.chan_cur].freq); + sync_slave_ctx.state = SYNC_STATE_SYNC; + sync_hw_timer_start(SYNC_SYNC_LOST_PERIOD, sync_slave_handle_sync_lost_to); +#ifdef SYNC_DEBUG + sync_pendcall(sync_slave_handle_sync_reconnected, 0, 0); +#endif + } + + if (handle_rx) + { + if (length.d16 == sizeof(sync_opcode_t) + sizeof(sync_conn_req_t) && + pdu->opcode == SYNC_OPCODE_CONN_REQ) + { + APP_PRINT_ERROR0("sync: state out of sync"); + sync_slave_handle_exception(); + break; + } + + sync_header_t rx_header; + rx_header.value = hp.hp; + int8_t rssi_dbm = ppt_get_rssi(rssi.rssi); + sync_slave_handle_rx(true, rx_header, pdu, length.d16, + rssi_dbm, rx_crc); + } + } + else if (sync_slave_ctx.pair_state == SYNC_PAIR_STATE_PAIRING) + { + bool retry = true; + if (sync_slave_ctx.phase == SYNC_SLAVE_PAIR_PHASE_WAIT_REQ) + { + if (length.d16 == sizeof(sync_opcode_t) + sizeof(sync_pair_req_t) && + pdu->opcode == SYNC_OPCODE_PAIR_REQ) + { + int8_t rssi_dbm = ppt_get_rssi(rssi.rssi); + if (rssi_dbm < sync_ctx.pair_rssi_th) + { + APP_PRINT_ERROR2("sync: pair rssi check fail, %d < %d", rssi_dbm, sync_ctx.pair_rssi_th); + } + else + { + retry = false; + sync_slave_ctx.phase = SYNC_SLAVE_PAIR_PHASE_SEND_RSP; +#if SYNC_SUPPORT_MT1R + sync_ctx.mt1r.flag = pdu->pair_req.interval == 125; + sync_ctx.conn_interval = sync_ctx.mt1r.flag ? 250 : pdu->pair_req.interval; + if (sync_ctx.mt1r.flag) + { + sync_slave_ctx.ce_clk = (acchit_clk_upper.acchit_clk_31_16 << 16) + + acchit_clk_lower.acchit_clk_15_0; + } +#else + sync_ctx.conn_interval = pdu->pair_req.interval; +#endif + } + } + } + else if (sync_slave_ctx.phase == SYNC_SLAVE_PAIR_PHASE_WAIT_CFM) + { + if (length.d16 == sizeof(sync_opcode_t) && pdu->opcode == SYNC_OPCODE_PAIR_CONF) + { + retry = false; + sync_slave_ctx.phase = SYNC_SLAVE_PAIR_PHASE_SEND_ACK; + } + } + + if (retry) + { + sync_slave_close(false); + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_ENABLE); + } + } + } + else + { + sync_slave_close(false); + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_ENABLE); + } + } + while (0); + } + + if (reg_misr.tx_int) + { + sync_slave_ctx.busy = false; + /* pop tx stack */ + PRO_TX_STACK_TYPE tx_stack = {.d16 = RD_PPT_REG(PRO_TX_STACK)}; + //PRO_TX_HS_LOWER_TYPE hs_lower = {.d16 = RD_PPT_REG(PRO_TX_HS_LOWER)}; + //PRO_TX_HS_UPPER_TYPE hs_upper = {.d16 = RD_PPT_REG(PRO_TX_HS_UPPER)}; + PRO_TX_LENGTH_TYPE length = {.d16 = RD_PPT_REG(PRO_TX_LENGTH)}; // Read 0x6a to pop stack + if (SYNC_ISR_DEBUG) + { + APP_PRINT_INFO3("sync_isr: prx tx stack 0x%04x(ptr %d), len %d", tx_stack.d16, tx_stack.tx_ptr, + length.tx_length); + } + +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_ctx.power_ctrl_count += 1; + } +#endif + + if (sync_slave_ctx.pair_state == SYNC_PAIR_STATE_PAIRING) + { + if (sync_slave_ctx.phase == SYNC_SLAVE_PAIR_PHASE_SEND_RSP) + { + APP_PRINT_INFO0("sync: pairing!"); + sync_slave_ctx.phase = SYNC_SLAVE_PAIR_PHASE_WAIT_CFM; + sync_hw_timer_start(SYNC_PAIR_CFM_PERIOD, sync_slave_handle_pair_cfm_to); + + sync_acc_set(sync_slave_ctx.acc_data); + ppt_trigger_fw_ack(0); + sync_ppt_set_tifs(false); + + sync_slave_ctx.seq_tx = 0; + /* ack is checked by master */ + sync_set_header(1, 0); + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_EMPTY; + ppt_push_tx_data(sizeof(sync_opcode_t), (uint8_t *)&pdu); + } + else if (sync_slave_ctx.phase == SYNC_SLAVE_PAIR_PHASE_SEND_ACK) + { + /* ack later in sync_slave_handle_ack */ + //ppt_trigger_fw_ack(0); + + sync_slave_ctx.seq_tx = 1; + sync_set_header(0, 0); + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_EMPTY; + ppt_push_tx_data(sizeof(sync_opcode_t), (uint8_t *)&pdu); + + sync_slave_ctx.pair_state = SYNC_PAIR_STATE_PAIRED; + sync_slave_ctx.state = SYNC_STATE_SYNC; + APP_PRINT_INFO3("sync: paired 0x%08x on %d[%d]!", sync_slave_ctx.acc_data.addr, sync_ctx.chan_cur, + sync_ctx.chan_cfg[sync_ctx.chan_cur].freq); +#if SYNC_SUPPORT_MT1R + if (sync_ctx.mt1r.flag) + { + uint32_t clk = sync_hw_frtimer_get_count(); + uint32_t total_time = SYNC_PAIR_CFM_PERIOD + SYNC_CONNECT_DELAY; + uint32_t delay = sync_hw_frtimer_time_elapse(clk, sync_slave_ctx.ce_clk); + delay += 29; + delay = total_time > delay ? total_time - delay : 30; + sync_hw_timer_start(delay, sync_slave_handle_sync_early_first_quick); + sync_hw_timer1_init(); + sync_ppt_set_rf_time_mt1r(); + sync_ctx.conn_interval = 1000; + APP_PRINT_ERROR0("sync: quick enter"); + } + else +#endif + { + sync_hw_timer_start(SYNC_SYNC_LOST_PERIOD, sync_slave_handle_sync_lost_to); + } + sync_pendcall(sync_slave_handle_paired, 0, 0); + sync_channel_print_statistic(); + sync_channel_clear_statistic(); +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_power_ctrl_start(); + } +#endif + } + } + else if (sync_slave_ctx.pair_state == SYNC_PAIR_STATE_PAIRED) + { + if (sync_slave_ctx.state == SYNC_STATE_SYNC_SLOW) + { + /* ack later in sync_slave_handle_ack */ + //ppt_trigger_fw_ack(0); + sync_ppt_set_tifs(false); + + sync_slave_ctx.seq_tx = 0; + sync_set_header(0, 0); + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_EMPTY; + ppt_push_tx_data(sizeof(sync_opcode_t), (uint8_t *)&pdu); + + APP_PRINT_INFO2("sync: connected on %d[%d]!", sync_ctx.chan_cur, + sync_ctx.chan_cfg[sync_ctx.chan_cur].freq); + sync_slave_ctx.state = SYNC_STATE_SYNC; +#if SYNC_SUPPORT_MT1R + if (sync_ctx.mt1r.flag) + { + uint32_t clk = sync_hw_frtimer_get_count(); + uint32_t delay = sync_hw_frtimer_time_elapse(clk, sync_slave_ctx.ce_clk); + delay += 29; + delay = SYNC_CONNECT_DELAY > delay ? SYNC_CONNECT_DELAY - delay : 30; + sync_hw_timer_start(delay, sync_slave_handle_sync_early_first_quick); + sync_hw_timer1_init(); + sync_ppt_set_rf_time_mt1r(); + sync_ctx.conn_interval = 1000; + APP_PRINT_ERROR0("sync: quick enter"); + } + else +#endif + { + sync_hw_timer_start(SYNC_SYNC_LOST_PERIOD, sync_slave_handle_sync_lost_to); + } + sync_pendcall(sync_slave_handle_connected, 0, 0); + sync_channel_print_statistic(); + sync_channel_clear_statistic(); +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_power_ctrl_start(); + } +#endif + } + else + { + uint8_t msg_idx = sync_slave_ctx.pmsg_sending_idx; + sync_msg_t *pmsg = sync_slave_ctx.pmsg_sending[msg_idx]; + //APP_PRINT_INFO3("sync: hb sending idx %d 0x%x, hb state %d", sync_slave_ctx.pmsg_sending_idx, pmsg, + // sync_slave_ctx.hb.state); + bool slow = false; + /* check if msg updated timely */ + if (sync_slave_ctx.pmsg_ptr[msg_idx] != tx_stack.tx_ptr + || (pmsg == NULL && sizeof(sync_opcode_t) != length.tx_length) + || (pmsg != NULL && pmsg->len != length.tx_length)) + { + APP_PRINT_WARN3("sync: pkt 0x%x not switched timely, tx ptr %d, pkt ptr %d", pmsg, tx_stack.tx_ptr, + sync_slave_ctx.pmsg_ptr[msg_idx]); + /* old pkt may also trigger enter hb */ + if (sync_slave_ctx.pmsg_last_empty && sync_slave_ctx.hb.state == SYNC_SLAVE_HB_STATE_HB_RECEIVED) + { + slow = true; + } + } + else + { + if (sync_slave_ctx.pmsg_switching) + { + sync_slave_ctx.pmsg_switching = false; + /* switch when new pkt is transmitted */ + sync_slave_ctx.pmsg_sending_idx = (sync_slave_ctx.pmsg_sending_idx + 1) % SYNC_SLAVE_TX_MSG_NUM; + } + + if (pmsg == NULL) + { + if (sync_slave_ctx.hb.state == SYNC_SLAVE_HB_STATE_HB_RECEIVED) + { + slow = true; + } + } + } + + if (slow) + { + sync_hw_timer_start(SYNC_HB_TO_TIMES * sync_slave_ctx.hb.interval * 1000 + SYNC_SYNC_LOST_PERIOD, + sync_slave_handle_sync_lost_to); + if (sync_slave_ctx.hb.slow == false || sync_slave_ctx.hb.interval_changed) + { + sync_slave_ctx.hb.slow = true; + sync_slave_ctx.hb.interval_changed = false; + APP_PRINT_INFO1("sync: hb enter %dms", sync_slave_ctx.hb.interval); + if (sync_ctx.hb_cb) + { + sync_hb_cb_param_t param = {}; + param.enter = true; + param.interval = sync_slave_ctx.hb.interval; + sync_ctx.hb_cb(¶m); + } + } + } + else + { + if (sync_slave_ctx.hb.slow) + { + if (sync_slave_ctx.hb.state != SYNC_SLAVE_HB_STATE_HB_RECEIVED) + { + sync_slave_hb_exit(); + } + else + { + /* exit only when acked if tx a message */ + sync_slave_ctx.hb.exit_pend = true; + } + } + } + + /* re-trigger at last in case the state is changed */ + if (sync_slave_ctx.tx_trigger_pending) + { + sync_slave_ctx.tx_trigger_pending = false; + sync_slave_handle_tx_trigger(); + } + } + } + } + + if (reg_misr.kill_prx_int) + { + ppt_clear_radio(); + WR_PPT_REG(PRO_MISR, BIT5); + ppt_flush_rx_fifo(); + ppt_ctx->fsm = PPT_FSM_STANDBY; + ppt_ctx->sync_flag = false; + } + + if (reg_misr.reset_trig_int) + { + ppt_clear_radio(); + WR_PPT_REG(PRO_MISR, BIT6); + ppt_flush_rx_fifo(); + ppt_ctx->fsm = PPT_FSM_STANDBY; + ppt_ctx->sync_flag = false; + } + time_debug_level_low(TIME_DEBUG_CHAN_ISR); +} + +void sync_slave_init(void) +{ + sync_ppt_init(); + ppt_reg_handler(sync_slave_ppt_isr_handler); + sync_hw_timer_init(); +#if defined(SYNC_DEBUG) || SYNC_SUPPORT_MT1R + sync_hw_frtimer_init(); +#endif + sync_ctx.role = SYNC_ROLE_SLAVE; + memset(&sync_slave_ctx, 0, sizeof(sync_slave_ctx)); +} + +void sync_slave_check_feature(void) +{ + sync_check_feature(); + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(true); + } +} + +void sync_slave_enable(void) +{ + +} + +sync_err_code_t sync_slave_pair(void) +{ + if (sync_slave_ctx.state != SYNC_STATE_IDLE || sync_slave_ctx.pair_state != SYNC_PAIR_STATE_IDLE) + { + APP_PRINT_INFO2("sync: fail to start pair, state %d, pair state %d!", sync_slave_ctx.state, + sync_slave_ctx.pair_state); + return SYNC_ERR_CODE_INVALID_STATE; + } + + memset(&sync_slave_ctx, 0, sizeof(sync_slave_ctx)); + sync_slave_ctx.state = SYNC_STATE_SYNC_SLOW; + sync_slave_ctx.pair_state = SYNC_PAIR_STATE_PAIRING; + sync_slave_ctx.phase = SYNC_SLAVE_PAIR_PHASE_WAIT_REQ; + sync_slave_ctx.seq_rx = 0xff; + sync_slave_init_tx_fifo(); + sync_ppt_set_tifs(true); + sync_slave_check_feature(); +#if SYNC_SUPPORT_MT1R + WR_PPT_REG_FIELD(PRO_FIELD_LENGTH, hp_field_length_3_0, SYNC_HEADER_LEN); + WR_PPT_REG_FIELD(PRO_HS_LENGTH, hs_field_length_4_0, 0); + sync_ppt_set_rf_time(); +#endif +#ifdef SYNC_SLAVE_FIXED_CHANNEL + uint8_t chan_idx = SYNC_SLAVE_FIXED_CHANNEL; +#else +#if SYNC_SLAVE_CCA_ENABLE + int8_t chan_idx = sync_slave_cca_get(0, sync_ctx.chan_num); +#else + uint8_t chan_idx = sync_rand() % sync_ctx.chan_num; +#endif + sync_hw_timer_start(SYNC_SLAVE_HOPPING_SPEED, sync_slave_handle_hopping); +#endif + + APP_PRINT_INFO2("sync: start pair on %d[%d]!", chan_idx, sync_ctx.chan_cfg[chan_idx].freq); + sync_channel_change(chan_idx); + + sync_slave_ctx.acc_data = sync_acc_gen_data(); + sync_acc_t acc = {.addr = SYNC_ACC_ADDR_BR}; + sync_acc_set(acc); + + sync_set_header(0, 0); + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_PAIR_RSP; + pdu.pair_rsp.acc = sync_slave_ctx.acc_data; + ppt_push_tx_data(sizeof(sync_opcode_t) + sizeof(sync_pair_rsp_t), (uint8_t *)&pdu); + + { + ppt_prx_mode_ext_t param = + { + { + .ack_mode = true, + .continuous_mode = true + } + }; + ppt_set_prx_mode_ext(¶m); + } + + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_ENABLE); + return SYNC_ERR_CODE_SUCCESS; +} + +sync_err_code_t sync_slave_connect(sync_bond_info_t *bond_info) +{ + if (bond_info == NULL) + { + return SYNC_ERR_CODE_NULL_POINTER; + } + + if (sync_slave_ctx.state != SYNC_STATE_IDLE) + { + APP_PRINT_INFO1("sync: fail to start connect, state %d!", sync_slave_ctx.state); + return SYNC_ERR_CODE_INVALID_STATE; + } + + memset(&sync_slave_ctx, 0, sizeof(sync_slave_ctx)); + sync_slave_ctx.state = SYNC_STATE_SYNC_SLOW; + sync_slave_ctx.pair_state = SYNC_PAIR_STATE_PAIRED; + sync_slave_ctx.seq_rx = 0xff; + sync_slave_init_tx_fifo(); + sync_ppt_set_tifs(true); + sync_slave_check_feature(); +#if SYNC_SUPPORT_MT1R + WR_PPT_REG_FIELD(PRO_FIELD_LENGTH, hp_field_length_3_0, SYNC_HEADER_LEN); + WR_PPT_REG_FIELD(PRO_HS_LENGTH, hs_field_length_4_0, 0); + sync_ppt_set_rf_time(); +#endif +#ifdef SYNC_SLAVE_FIXED_CHANNEL + uint8_t chan_idx = SYNC_SLAVE_FIXED_CHANNEL; +#else +#if SYNC_SLAVE_CCA_ENABLE + int8_t chan_idx = sync_slave_cca_get(0, sync_ctx.chan_num); +#else + uint8_t chan_idx = sync_rand() % sync_ctx.chan_num; +#endif + sync_hw_timer_start(SYNC_SLAVE_HOPPING_SPEED, sync_slave_handle_hopping); +#endif + + APP_PRINT_INFO3("sync: start connect 0x%08x on %d[%d]!", bond_info->acc.addr, chan_idx, + sync_ctx.chan_cfg[chan_idx].freq); + sync_channel_change(chan_idx); + + sync_slave_ctx.acc_data = bond_info->acc; + sync_acc_set(sync_slave_ctx.acc_data); + +#if SYNC_SUPPORT_MT1R + WR_PPT_REG_FIELD(PRO_FIELD_LENGTH, hp_field_length_3_0, SYNC_HEADER_LEN); + WR_PPT_REG_FIELD(PRO_HS_LENGTH, hs_field_length_4_0, 0); + sync_ppt_set_rf_time(); +#endif + + sync_set_header(0, 0); + sync_pdu_t pdu; + pdu.opcode = SYNC_OPCODE_CONN_RSP; + ppt_push_tx_data(sizeof(sync_opcode_t), (uint8_t *)&pdu); + + { + ppt_prx_mode_ext_t param = + { + { + .ack_mode = true, + .continuous_mode = true + } + }; + ppt_set_prx_mode_ext(¶m); + } + + ppt_execute_instruction(PPT_HW_INSTRUCTION_PRX_ENABLE); + return SYNC_ERR_CODE_SUCCESS; +} + +void sync_slave_stop(void) +{ + if (sync_slave_ctx.state == SYNC_STATE_IDLE) + { + return; + } + + uint32_t lock = sync_enter_critical(); + sync_hw_timer_stop(); +#if SYNC_SUPPORT_MT1R + sync_hw_timer1_stop(); + ppt_reg_handler(sync_slave_ppt_isr_handler); +#endif + sync_slave_close(true); + sync_slave_free_sending_msg(); +#if SYNC_SUPPORT_POWER_CONTROL + if (sync_ctx.tx_power_adaptive) + { + sync_power_ctrl_stop(); + } +#endif + memset(&sync_slave_ctx, 0, sizeof(sync_slave_ctx)); + if (sync_ctx.rte_req_cb) + { + sync_ctx.rte_req_cb(false); + } + sync_exit_critical(lock); + APP_PRINT_INFO0("sync: stopped!"); + return; +} + +void sync_slave_deinit(void) +{ + +} + +PPT_API_SECTION static bool sync_slave_dlps_check(uint32_t *wakeup_time_diff) +{ + if (sync_slave_ctx.state == SYNC_STATE_IDLE && + sync_slave_ctx.pair_state == SYNC_PAIR_STATE_IDLE) + { + return true; + } + + return false; +} + +void sync_slave_dlps_init(void) +{ + ppt_dlps_init(); + ppt_dlps_reg_mac_check_cb(sync_slave_dlps_check); +} From bf9ff0703dae010532140e6a51648b578119bfa9 Mon Sep 17 00:00:00 2001 From: Dariel Zhang Date: Mon, 27 Jul 2026 15:45:34 +0800 Subject: [PATCH 2/5] drivers: ppt: make role selection Kconfig-driven Replace hardcoded SYNC_ROLE_MASTER_SUPPORT/SLAVE_SUPPORT with CONFIG_REALTEK_USING_PPT_SYNC_MASTER/SLAVE so role switching requires only prj.conf changes, not source edits. --- bee/drivers/ppt/sync/src/sync/src/ppt_sync_int.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync_int.h b/bee/drivers/ppt/sync/src/sync/src/ppt_sync_int.h index c7fd232f..7fdd6561 100644 --- a/bee/drivers/ppt/sync/src/sync/src/ppt_sync_int.h +++ b/bee/drivers/ppt/sync/src/sync/src/ppt_sync_int.h @@ -36,8 +36,17 @@ extern "C" { * @brief * @{ */ +#ifdef CONFIG_REALTEK_USING_PPT_SYNC_MASTER #define SYNC_ROLE_MASTER_SUPPORT 1 +#else +#define SYNC_ROLE_MASTER_SUPPORT 0 +#endif + +#ifdef CONFIG_REALTEK_USING_PPT_SYNC_SLAVE +#define SYNC_ROLE_SLAVE_SUPPORT 1 +#else #define SYNC_ROLE_SLAVE_SUPPORT 0 +#endif #define SYNC_CHANNEL_FREQUENCY \ {2432, 2447, 2462, 2477, 2407, 2422, 2437, 2452, 2442, 2457, 2472, 2413} #define SYNC_CHANNEL_NUM_DV 12 From 589da8da7f8dfbf5f7d228934de9df3705956d5f Mon Sep 17 00:00:00 2001 From: Dariel Zhang Date: Fri, 31 Jul 2026 14:20:19 +0800 Subject: [PATCH 3/5] drivers: ppt: restructure source tree layout Move driver/ out of sync/src/ to ppt/driver/ so it sits at the same level as sync/, making it accessible to future transports without going through the sync subtree. Flatten sync/src/sync/src/ to sync/src/ and remove the duplicate sync/src/sync/inc/ copies (headers already live in sync/inc/). Update sync/CMakeLists.txt: introduce PPT_DRV_SRC pointing to ../driver and adjust all source/include paths accordingly. --- .../{sync/src => }/driver/common/DataType.h | 0 .../ppt/{sync/src => }/driver/common/bzdma.h | 0 .../{sync/src => }/driver/common/ll_common.h | 0 .../{sync/src => }/driver/common/ppt_driver.c | 0 .../{sync/src => }/driver/common/ppt_driver.h | 0 .../{sync/src => }/driver/common/ppt_hw_reg.h | 0 .../driver/common/rtl87x2g/debug_port.h | 0 .../driver/common/rtl87x2g/platform_rtc.h | 0 .../common/rtl87x2g/power_manager_interface.h | 0 .../common/rtl87x2g/power_manager_slave.h | 0 .../rtl87x2g/power_manager_unit_platform.h | 0 .../src => }/driver/common/rtl87x2g/ppt_pf.h | 0 .../src => }/driver/common/rtl87x2g/ppt_pm.h | 0 .../{sync/src => }/driver/simple/ppt_simple.c | 0 .../{sync/src => }/driver/simple/ppt_simple.h | 0 bee/drivers/ppt/sync/CMakeLists.txt | 23 +- .../sync/src/{sync/src => }/platform_list.c | 0 .../sync/src/{sync/src => }/platform_list.h | 0 .../sync/src/{sync/src => }/platform_macros.h | 0 .../sync/src/{sync/src => }/platform_types.h | 0 .../ppt/sync/src/{sync/src => }/ppt_sync.c | 0 .../sync/src/{sync/src => }/ppt_sync_int.h | 0 .../sync/src/{sync/src => }/ppt_sync_master.c | 0 .../src/{sync/src => }/ppt_sync_pendcall.c | 0 .../src/{sync/src => }/ppt_sync_pendcall.h | 0 .../sync/src/{sync/src => }/ppt_sync_slave.c | 0 bee/drivers/ppt/sync/src/sync/inc/ppt_sync.h | 585 ------------------ .../ppt/sync/src/sync/inc/ppt_sync_master.h | 184 ------ .../ppt/sync/src/sync/inc/ppt_sync_slave.h | 116 ---- 29 files changed, 11 insertions(+), 897 deletions(-) rename bee/drivers/ppt/{sync/src => }/driver/common/DataType.h (100%) rename bee/drivers/ppt/{sync/src => }/driver/common/bzdma.h (100%) rename bee/drivers/ppt/{sync/src => }/driver/common/ll_common.h (100%) rename bee/drivers/ppt/{sync/src => }/driver/common/ppt_driver.c (100%) rename bee/drivers/ppt/{sync/src => }/driver/common/ppt_driver.h (100%) rename bee/drivers/ppt/{sync/src => }/driver/common/ppt_hw_reg.h (100%) rename bee/drivers/ppt/{sync/src => }/driver/common/rtl87x2g/debug_port.h (100%) rename bee/drivers/ppt/{sync/src => }/driver/common/rtl87x2g/platform_rtc.h (100%) rename bee/drivers/ppt/{sync/src => }/driver/common/rtl87x2g/power_manager_interface.h (100%) rename bee/drivers/ppt/{sync/src => }/driver/common/rtl87x2g/power_manager_slave.h (100%) rename bee/drivers/ppt/{sync/src => }/driver/common/rtl87x2g/power_manager_unit_platform.h (100%) rename bee/drivers/ppt/{sync/src => }/driver/common/rtl87x2g/ppt_pf.h (100%) rename bee/drivers/ppt/{sync/src => }/driver/common/rtl87x2g/ppt_pm.h (100%) rename bee/drivers/ppt/{sync/src => }/driver/simple/ppt_simple.c (100%) rename bee/drivers/ppt/{sync/src => }/driver/simple/ppt_simple.h (100%) rename bee/drivers/ppt/sync/src/{sync/src => }/platform_list.c (100%) rename bee/drivers/ppt/sync/src/{sync/src => }/platform_list.h (100%) rename bee/drivers/ppt/sync/src/{sync/src => }/platform_macros.h (100%) rename bee/drivers/ppt/sync/src/{sync/src => }/platform_types.h (100%) rename bee/drivers/ppt/sync/src/{sync/src => }/ppt_sync.c (100%) rename bee/drivers/ppt/sync/src/{sync/src => }/ppt_sync_int.h (100%) rename bee/drivers/ppt/sync/src/{sync/src => }/ppt_sync_master.c (100%) rename bee/drivers/ppt/sync/src/{sync/src => }/ppt_sync_pendcall.c (100%) rename bee/drivers/ppt/sync/src/{sync/src => }/ppt_sync_pendcall.h (100%) rename bee/drivers/ppt/sync/src/{sync/src => }/ppt_sync_slave.c (100%) delete mode 100644 bee/drivers/ppt/sync/src/sync/inc/ppt_sync.h delete mode 100644 bee/drivers/ppt/sync/src/sync/inc/ppt_sync_master.h delete mode 100644 bee/drivers/ppt/sync/src/sync/inc/ppt_sync_slave.h diff --git a/bee/drivers/ppt/sync/src/driver/common/DataType.h b/bee/drivers/ppt/driver/common/DataType.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/DataType.h rename to bee/drivers/ppt/driver/common/DataType.h diff --git a/bee/drivers/ppt/sync/src/driver/common/bzdma.h b/bee/drivers/ppt/driver/common/bzdma.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/bzdma.h rename to bee/drivers/ppt/driver/common/bzdma.h diff --git a/bee/drivers/ppt/sync/src/driver/common/ll_common.h b/bee/drivers/ppt/driver/common/ll_common.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/ll_common.h rename to bee/drivers/ppt/driver/common/ll_common.h diff --git a/bee/drivers/ppt/sync/src/driver/common/ppt_driver.c b/bee/drivers/ppt/driver/common/ppt_driver.c similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/ppt_driver.c rename to bee/drivers/ppt/driver/common/ppt_driver.c diff --git a/bee/drivers/ppt/sync/src/driver/common/ppt_driver.h b/bee/drivers/ppt/driver/common/ppt_driver.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/ppt_driver.h rename to bee/drivers/ppt/driver/common/ppt_driver.h diff --git a/bee/drivers/ppt/sync/src/driver/common/ppt_hw_reg.h b/bee/drivers/ppt/driver/common/ppt_hw_reg.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/ppt_hw_reg.h rename to bee/drivers/ppt/driver/common/ppt_hw_reg.h diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/debug_port.h b/bee/drivers/ppt/driver/common/rtl87x2g/debug_port.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/rtl87x2g/debug_port.h rename to bee/drivers/ppt/driver/common/rtl87x2g/debug_port.h diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/platform_rtc.h b/bee/drivers/ppt/driver/common/rtl87x2g/platform_rtc.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/rtl87x2g/platform_rtc.h rename to bee/drivers/ppt/driver/common/rtl87x2g/platform_rtc.h diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_interface.h b/bee/drivers/ppt/driver/common/rtl87x2g/power_manager_interface.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_interface.h rename to bee/drivers/ppt/driver/common/rtl87x2g/power_manager_interface.h diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_slave.h b/bee/drivers/ppt/driver/common/rtl87x2g/power_manager_slave.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_slave.h rename to bee/drivers/ppt/driver/common/rtl87x2g/power_manager_slave.h diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_unit_platform.h b/bee/drivers/ppt/driver/common/rtl87x2g/power_manager_unit_platform.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/rtl87x2g/power_manager_unit_platform.h rename to bee/drivers/ppt/driver/common/rtl87x2g/power_manager_unit_platform.h diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/ppt_pf.h b/bee/drivers/ppt/driver/common/rtl87x2g/ppt_pf.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/rtl87x2g/ppt_pf.h rename to bee/drivers/ppt/driver/common/rtl87x2g/ppt_pf.h diff --git a/bee/drivers/ppt/sync/src/driver/common/rtl87x2g/ppt_pm.h b/bee/drivers/ppt/driver/common/rtl87x2g/ppt_pm.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/common/rtl87x2g/ppt_pm.h rename to bee/drivers/ppt/driver/common/rtl87x2g/ppt_pm.h diff --git a/bee/drivers/ppt/sync/src/driver/simple/ppt_simple.c b/bee/drivers/ppt/driver/simple/ppt_simple.c similarity index 100% rename from bee/drivers/ppt/sync/src/driver/simple/ppt_simple.c rename to bee/drivers/ppt/driver/simple/ppt_simple.c diff --git a/bee/drivers/ppt/sync/src/driver/simple/ppt_simple.h b/bee/drivers/ppt/driver/simple/ppt_simple.h similarity index 100% rename from bee/drivers/ppt/sync/src/driver/simple/ppt_simple.h rename to bee/drivers/ppt/driver/simple/ppt_simple.h diff --git a/bee/drivers/ppt/sync/CMakeLists.txt b/bee/drivers/ppt/sync/CMakeLists.txt index ba1380a0..941cef16 100644 --- a/bee/drivers/ppt/sync/CMakeLists.txt +++ b/bee/drivers/ppt/sync/CMakeLists.txt @@ -4,21 +4,20 @@ # add_subdirectory(lib) # Compile from source so CONFIG_PPT_DRIVER_LOG_LEVEL takes effect at build time. set(PPT_SYNC_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src) +set(PPT_DRV_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../driver) zephyr_library_named(ppt_sync) -zephyr_library_sources(${PPT_SYNC_SRC}/driver/common/ppt_driver.c) -zephyr_library_sources(${PPT_SYNC_SRC}/driver/simple/ppt_simple.c) -zephyr_library_sources(${PPT_SYNC_SRC}/sync/src/platform_list.c) -zephyr_library_sources(${PPT_SYNC_SRC}/sync/src/ppt_sync.c) -zephyr_library_sources(${PPT_SYNC_SRC}/sync/src/ppt_sync_pendcall.c) -zephyr_library_sources(${PPT_SYNC_SRC}/sync/src/ppt_sync_master.c) -zephyr_library_sources(${PPT_SYNC_SRC}/sync/src/ppt_sync_slave.c) +zephyr_library_sources(${PPT_DRV_SRC}/common/ppt_driver.c) +zephyr_library_sources(${PPT_DRV_SRC}/simple/ppt_simple.c) +zephyr_library_sources(${PPT_SYNC_SRC}/platform_list.c) +zephyr_library_sources(${PPT_SYNC_SRC}/ppt_sync.c) +zephyr_library_sources(${PPT_SYNC_SRC}/ppt_sync_pendcall.c) +zephyr_library_sources(${PPT_SYNC_SRC}/ppt_sync_master.c) +zephyr_library_sources(${PPT_SYNC_SRC}/ppt_sync_slave.c) zephyr_include_directories(inc) zephyr_include_directories(${PPT_SYNC_SRC}) -zephyr_include_directories(${PPT_SYNC_SRC}/driver/common) -zephyr_include_directories(${PPT_SYNC_SRC}/driver/common/rtl87x2g) -zephyr_include_directories(${PPT_SYNC_SRC}/driver/simple) -zephyr_include_directories(${PPT_SYNC_SRC}/sync/inc) -zephyr_include_directories(${PPT_SYNC_SRC}/sync/src) +zephyr_include_directories(${PPT_DRV_SRC}/common) +zephyr_include_directories(${PPT_DRV_SRC}/common/rtl87x2g) +zephyr_include_directories(${PPT_DRV_SRC}/simple) diff --git a/bee/drivers/ppt/sync/src/sync/src/platform_list.c b/bee/drivers/ppt/sync/src/platform_list.c similarity index 100% rename from bee/drivers/ppt/sync/src/sync/src/platform_list.c rename to bee/drivers/ppt/sync/src/platform_list.c diff --git a/bee/drivers/ppt/sync/src/sync/src/platform_list.h b/bee/drivers/ppt/sync/src/platform_list.h similarity index 100% rename from bee/drivers/ppt/sync/src/sync/src/platform_list.h rename to bee/drivers/ppt/sync/src/platform_list.h diff --git a/bee/drivers/ppt/sync/src/sync/src/platform_macros.h b/bee/drivers/ppt/sync/src/platform_macros.h similarity index 100% rename from bee/drivers/ppt/sync/src/sync/src/platform_macros.h rename to bee/drivers/ppt/sync/src/platform_macros.h diff --git a/bee/drivers/ppt/sync/src/sync/src/platform_types.h b/bee/drivers/ppt/sync/src/platform_types.h similarity index 100% rename from bee/drivers/ppt/sync/src/sync/src/platform_types.h rename to bee/drivers/ppt/sync/src/platform_types.h diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync.c b/bee/drivers/ppt/sync/src/ppt_sync.c similarity index 100% rename from bee/drivers/ppt/sync/src/sync/src/ppt_sync.c rename to bee/drivers/ppt/sync/src/ppt_sync.c diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync_int.h b/bee/drivers/ppt/sync/src/ppt_sync_int.h similarity index 100% rename from bee/drivers/ppt/sync/src/sync/src/ppt_sync_int.h rename to bee/drivers/ppt/sync/src/ppt_sync_int.h diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync_master.c b/bee/drivers/ppt/sync/src/ppt_sync_master.c similarity index 100% rename from bee/drivers/ppt/sync/src/sync/src/ppt_sync_master.c rename to bee/drivers/ppt/sync/src/ppt_sync_master.c diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync_pendcall.c b/bee/drivers/ppt/sync/src/ppt_sync_pendcall.c similarity index 100% rename from bee/drivers/ppt/sync/src/sync/src/ppt_sync_pendcall.c rename to bee/drivers/ppt/sync/src/ppt_sync_pendcall.c diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync_pendcall.h b/bee/drivers/ppt/sync/src/ppt_sync_pendcall.h similarity index 100% rename from bee/drivers/ppt/sync/src/sync/src/ppt_sync_pendcall.h rename to bee/drivers/ppt/sync/src/ppt_sync_pendcall.h diff --git a/bee/drivers/ppt/sync/src/sync/src/ppt_sync_slave.c b/bee/drivers/ppt/sync/src/ppt_sync_slave.c similarity index 100% rename from bee/drivers/ppt/sync/src/sync/src/ppt_sync_slave.c rename to bee/drivers/ppt/sync/src/ppt_sync_slave.c diff --git a/bee/drivers/ppt/sync/src/sync/inc/ppt_sync.h b/bee/drivers/ppt/sync/src/sync/inc/ppt_sync.h deleted file mode 100644 index a11aace0..00000000 --- a/bee/drivers/ppt/sync/src/sync/inc/ppt_sync.h +++ /dev/null @@ -1,585 +0,0 @@ -/** -***************************************************************************************** -* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. -***************************************************************************************** - * @file ppt_sync.h - * @brief Head file for sync protocol. - * @details data structs and external functions declaration. - * @author bill - * @date 2022-03-15 - * @version v0.1 - * ************************************************************************************* - */ - -/* Define to prevent recursive inclusion */ -#ifndef _PPT_SYNC_H_ -#define _PPT_SYNC_H_ - -/* Add Includes here */ -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/** @addtogroup PPT_SYNC_Driver - * @{ - */ - -/** @defgroup PPT_SYNC_Driver_Exported_Macros Exported Macros - * @brief - * @{ - */ -#define SYNC_OPCODE_APP_START_VALUE 0x10 - -#define SYNC_MSG_RETRANS_NUM_INFINITE 0xff -#define SYNC_SYNC_FAST_PERIOD_DISABLE 0xffffffff - -/** @} End of PPT_SYNC_Driver_Exported_Macros */ - -/** @defgroup PPT_SYNC_Driver_Exported_Types Exported Types - * @brief - * @{ - */ - -/** error code */ -typedef enum -{ - SYNC_ERR_CODE_SUCCESS, - SYNC_ERR_CODE_UNKNOWN, - SYNC_ERR_CODE_INVALID_LENGTH, - SYNC_ERR_CODE_INVALID_PARAM, - SYNC_ERR_CODE_INVALID_STATE, - SYNC_ERR_CODE_NULL_POINTER, - SYNC_ERR_CODE_FULL_QUEUE, - SYNC_ERR_CODE_EMPTY_QUEUE, - SYNC_ERR_CODE_NO_MEM, - SYNC_ERR_CODE_NOT_FOUND -} sync_err_code_t; - -#pragma pack(1) - -/** access address */ -typedef struct -{ - uint32_t addr; -} sync_acc_t; - -/** bond information */ -typedef struct -{ - sync_acc_t acc; -} sync_bond_info_t; - -#pragma pack() - -/** role definition */ -typedef enum -{ - SYNC_ROLE_UNKNOWN, - SYNC_ROLE_MASTER, - SYNC_ROLE_SLAVE -} sync_role_t; - -/** connect state */ -typedef enum -{ - SYNC_STATE_IDLE, - SYNC_STATE_SYNC_SLOW, - SYNC_STATE_SYNC, - SYNC_STATE_SYNC_FAST -} sync_state_t; - -/** pair state */ -typedef enum -{ - SYNC_PAIR_STATE_IDLE, - SYNC_PAIR_STATE_PAIRING, - SYNC_PAIR_STATE_PAIRED, -} sync_pair_state_t; - -/** events triggered by the sync protocol */ -typedef enum -{ - SYNC_EVENT_PAIRED, - SYNC_EVENT_PAIR_TIMEOUT, - SYNC_EVENT_CONNECTED, - SYNC_EVENT_CONNECT_TIMEOUT, - SYNC_EVENT_CONNECT_LOST, -#ifdef SYNC_DEBUG - SYNC_EVENT_SYNC_LOST, - SYNC_EVENT_SYNC_RECONNECTED -#endif -} sync_event_t; - -/** Message types with difference performances */ -typedef enum -{ - /** The message will be sent only once regardless of the ack. */ - SYNC_MSG_TYPE_ONESHOT, - /** - * The message will be sent finite times even it is not acked. - * The retransmission will be stopped right after it is acked. - * Refer to @ref sync_msg_set_finite_retrans, it sets the finite retransmission times. - */ - SYNC_MSG_TYPE_FINITE_RETRANS, - /** - * The message will be sent infinite times until it is acked. - * The retransmission will be stopped right after it is acked. - */ - SYNC_MSG_TYPE_INFINITE_RETRANS, - /** - * Depending whether there are other messages pended, the message will be sent variable times if it is not acked. - * The retransmission will be stopped right after it is acked. - * Refer to @ref sync_msg_set_dynamic_retrans, it sets the lower and upper limitation of the retransmission times. - * If not acked, the message will be sent at least the lower limitation times and at most the upper limitation times. - */ - SYNC_MSG_TYPE_DYNAMIC_RETRANS, - SYNC_MSG_TYPE_NUM, - SYNC_MSG_TYPE_ALL = SYNC_MSG_TYPE_NUM -} sync_msg_type_t; - -/** Message transmit result */ -typedef enum -{ - /** The transmitted message is received by the peer device. */ - SYNC_SEND_RESULT_ACKED, - /** The transmitted message isn't received by the peer device. */ - SYNC_SEND_RESULT_NACKED, - /** Don't known whether the transmitted message is received. */ - SYNC_SEND_RESULT_UNKNOWN, - /** The transmitted message is flushed by myself. */ - SYNC_SEND_RESULT_FLUSHED -} sync_send_result_t; - -/** Message transmit general information */ -typedef struct -{ - sync_send_result_t res; - /** retransmit counter */ - uint8_t retrans_count; -} sync_send_info_t; - -/** Message receive general information */ -typedef struct -{ - int8_t rssi; -} sync_receive_info_t; - -/** Time related parameters used by the sync protocol */ -typedef enum -{ - /** Synchronization Interval, unit is us */ - SYNC_TIME_PARAM_CONNECT_INTERVAL, - /** - * Retransmit Interval, unit is us - * If it is equal to Synchronization Interval, there will be no chance to retransmit during the sync interval. - * Retransmit Interval shall be a divisor of Synchronization Interval. - * For example 1K report rate, Synchronization Interval = 1000us and Retransmit Interval = 250us, - * then there will be 3 times chance to retransmit in the sync interval. - */ - SYNC_TIME_PARAM_CONNECT_INTERVAL_HIGH, - /** Synchronization lost state life cycle, unit is us */ - SYNC_TIME_PARAM_SYNC_LOST_PERIOD, - /** Synchronization fast state life cycle, unit is us, use value @ref SYNC_SYNC_FAST_PERIOD_DISABLE can remove synchronization fast state */ - SYNC_TIME_PARAM_SYNC_FAST_PERIOD, - /** heartbeat timeout times used to control heartbeat exit sensitivity */ - SYNC_TIME_PARAM_HB_TO_TIMES, - /** channel assess times used by the slave to evaluate the channel quality */ - SYNC_TIME_PARAM_CHANNEL_ASSESS_TIMES, - /** slave hopping speed at sync low state, unit is us */ - SYNC_TIME_PARAM_SLAVE_HOPPING_SPEED -} sync_time_param_t; - -/** feature value definition */ -typedef enum -{ - SYNC_FEATURE_USE_DEFAULT, - SYNC_FEATURE_ENABLE, - SYNC_FEATURE_DISABLE -} sync_feature_t; - -/** - * @brief sync stack event notify callback - * @param[in] event: event type - * @return none - */ -typedef void (*sync_event_cb_t)(sync_event_t event); - -/** - * @brief sync stack message send result callback - * @param[in] type: msg type - * @param[in] data: the original data - * @param[in] len: the original length - * @param[in] result: the send result - * @return none - */ -typedef void (*sync_msg_send_cb_t)(sync_msg_type_t type, uint8_t *data, uint16_t len, - sync_send_info_t *info); - -/** - * @brief sync stack message receive callback - * @param[in] data: the data - * @param[in] len: the length - * @param[in] info: the receive information pointer - * @return none - */ -typedef void (*sync_msg_receive_cb_t)(uint8_t *data, uint16_t len, sync_receive_info_t *info); - -/** heartbeat callback parameters */ -typedef struct -{ - bool enter; - uint32_t interval; //!< unit of us -} sync_hb_cb_param_t; - -/** - * @brief heartbeat callback function pointer type - * @param[in] param: heartbeat callback parameters pointer - * @return none - */ -typedef void (*sync_hb_cb_t)(sync_hb_cb_param_t *param); - -/** - * @brief sync stack uses this callback to request real time environment - * - * When the stack requires real time environment at special scenarios, the app shall try their best to provide. - * For example, avoid using critical section, avoid entering DLPS, and increase cpu clock etc. - * @param[in] required: true means request real time environment - * @return none - */ -typedef void (*sync_rte_req_cb_t)(bool required); - -/** @} End of PPT_SYNC_Driver_Exported_Types */ - -/** @defgroup PPT_SYNC_Driver_Exported_Functions Exported Functions - * @brief - * @{ - */ - -/** - * @brief initialize the protocol - * - * The protocol will initiate some default settings. - * After initialization, the app can set some parameters before @ref sync_enable to the value they like. - * @param[in] role: the protocol role - * @return none - */ -void sync_init(sync_role_t role); - -/** - * @brief set the crc parameters - * - * The protocol will initiate CRC default settings. - * After initialization, the app can use this api to overwrite the default settings. - * @param[in] crc_len: crc bit length - * @param[in] crc_poly: crc poly - * @param[in] crc_init: crc init - * @return none - * Example usage - * \code{.c} - // set crc8 - sync_crc_set(8, 0x07, 0xff); - // set crc16 - sync_crc_set(16, 0x8005, 0xffff); - // set crc24 - sync_crc_set(24, 0x00065b, 0x555555); - // set crc32 - sync_crc_set(32, 0x04c11db7, 0xffffffff); - * \endcode - */ -void sync_crc_set(uint8_t crc_len, uint32_t crc_poly, uint32_t crc_init); - -/** - * @brief set timing related parameters - * @param[in] type: the parameter type - * @param[in] time: the time value, the unit is usually us depend on the type - * @return set result - */ -bool sync_time_set(sync_time_param_t type, uint32_t time); - -/** - * @brief get timing related parameters - * @param[in] type: the parameter type - * @param[out] time: the time value, the unit is usually us depend on the type - * @return get result - */ -bool sync_time_get(sync_time_param_t type, uint32_t *time); - -/** - * @brief set rf channels - * @param[in] chan_num: the number of rf channels - * @param[in] group_num: the number of groups - * @param[in] freq: the frequency array - * @return set result - */ -bool sync_channel_set(uint8_t chan_num, uint8_t group_num, uint16_t freq[]); - -/** - * @brief set rf tx power - * - * If dynamic is true, the tx power will be changed automatically at the range according to the link quality. - * If dynamic is false, the tx_power_dbm_max will be used all the time. - * @param[in] dynamic: whether to change automatically at run time - * @param[in] tx_power_dbm_max: maximum tx power - * @param[in] tx_power_dbm_min: minimum tx power - * @return set result - */ -bool sync_tx_power_set(bool dynamic, int8_t tx_power_dbm_max, int8_t tx_power_dbm_min); - -/** - * @brief turn on/off sync log - * @param[in] level: the log level - * @param[in] on: true: on, false: off - * @return set result - */ -bool sync_log_set(uint8_t level, bool on); - -/** - * @brief set rssi threshold when pairing - * - * Only the requesting device with rssi value equal or bigger than the threshold will be accepted. - * @param[in] rssi: the rssi level - * @return set result - */ -bool sync_pair_rssi_set(int8_t rssi); - -/** - * @brief set time of inter-frame space (tifs) - * - * The tifs used at different process or sync interval may be different. - * There are 2 levels used: - * Level 0 is used for sync slow process and sync interval >= 500us. - * Level 1 is used for sync interval < 500us. - * @param[in] level: level index - * @param[in] tifs: time of inter-frame space - * @return set result - */ -bool sync_tifs_set(uint8_t level, uint8_t tifs); - -/** - * @brief check whether the access address is valid - * - * The access address is used by the hardware to match the right packet with the same access address. - * The hardware has some limitations on the value of the access address. - * Usually it is randomly generated and checked until it is valid. - * @param[in] acc: the access address value - * @return 0: valid, other: invalid - */ -int sync_acc_check(sync_acc_t acc); - -/** - * @brief set the access address of the broadcast channel used by the pairing process - * - * Invoke this api will overwrite the default value. - * The user should check whether the new value is valid by call @ref sync_acc_check. - * @param[in] acc_br: the access address value - * @return none - */ -void sync_acc_set_br(sync_acc_t acc_br); - -/** - * @brief enable the protocol - * - * It shall be called after @ref sync_init to enable the protocol. - * @return none - */ -void sync_enable(void); - -/** - * @brief register event callback function - * - * The event @ref sync_event_t include the pairing and connection result. - * @param[in] cb: event callback - * @return none - */ -void sync_event_cb_reg(sync_event_cb_t cb); - -/** - * @brief get bond information from flash - * - * After pairing, the bond information wil be stored to the flash automatically. - * The bond information is used for connection. - * @return operation result - * @retval SYNC_ERR_CODE_SUCCESS: bond information get successfully - * @retval SYNC_ERR_CODE_NOT_FOUND: bond information doesn't exist - */ -sync_err_code_t sync_nvm_get_bond_info(sync_bond_info_t *bond_info); - -/** - * @brief set bond information to flash - * - * The app may want to add bond information manually by this api. - * @return operation result - * @retval SYNC_ERR_CODE_SUCCESS: bond information set successfully - * @retval SYNC_ERR_CODE_UNKNOWN: bond information storage fail - */ -sync_err_code_t sync_nvm_set_bond_info(sync_bond_info_t *bond_info); - -/** - * @brief clear bond information in flash - * - * The app can clear the bond information to restore unpaired state. - * @return operation result - * @retval SYNC_ERR_CODE_SUCCESS: bond information set successfully - * @retval SYNC_ERR_CODE_UNKNOWN: bond information clear fail - */ -sync_err_code_t sync_nvm_clear_bond_info(void); - -/** - * @brief start pairing - * - * After pairing, two new devices is connected and bonded. - * The pairing result is notified at the event callback registered by @ref sync_event_cb_reg. - * @return operation result - */ -sync_err_code_t sync_pair(void); - -/** - * @brief start connect - * - * If two bonded devices are disconnected, call this api to connect each other. - * The connection result is notified at the event callback registered by @ref sync_event_cb_reg. - * @param[in] bond_info: bond information used to create a connection - * @return operation result - */ -sync_err_code_t sync_connect(sync_bond_info_t *bond_info); - -/** - * @brief stop the connection or the pairing procedure - * - * If the device is connecting, pairing or connected, call this api to stop. - * @return none - */ -void sync_stop(void); - -/** - * @brief deinitiate the protocol to release all resources - * - * If the device is running, the api @ref sync_stop shall be called before this. - * @return none - */ -void sync_deinit(void); - -/** - * @brief initialize the sync protocol dlps setting - * @return none - */ -void sync_dlps_init(void); - -/** - * @brief set message queue size of each type - * - * Different message types have different queue size. - * @param[in] msg_quota: queue size - * @return none - */ -void sync_msg_set_quota(uint8_t msg_quota[SYNC_MSG_TYPE_NUM]); - -/** - * @brief set message queue size of the specific type - * - * Different message types have different queue size. - * @param[in] type: the message type - * @param[in] quota: the message type - * @return set result - */ -bool sync_msg_set_quota_by_type(sync_msg_type_t type, uint8_t quota); - -/** - * @brief set message retranmsit times for @ref SYNC_MSG_TYPE_FINITE_RETRANS type msg - * @param[in] msg_retrans_num: retranmsit times - * @return none - */ -void sync_msg_set_finite_retrans(uint8_t msg_retrans_num); - -/** - * @brief set message retranmsit times for @ref SYNC_MSG_TYPE_DYNAMIC_RETRANS type msg - * @param[in] msg_retrans_num_lower: retranmsit times lower limit - * @param[in] msg_retrans_num_upper: retranmsit times upper limit - * @return none - */ -void sync_msg_set_dynamic_retrans(uint8_t msg_retrans_num_lower, uint8_t msg_retrans_num_upper); - -/** - * @brief register message receive callback function - * @param[in] cb: callback function - * @return none - */ -void sync_msg_reg_receive_cb(sync_msg_receive_cb_t cb); - -/** - * @brief send message - * - * The first byte of data buffer shall contain the opcode starting from SYNC_OPCODE_APP_START_VALUE. - * @param[in] type: msg type, different types have different retransmit behavior - * @param[in] data: the data buffer - * @param[in] len: the data length - * @param[in] send_cb: the send complete callback - * @return send result - * @retval SYNC_ERR_CODE_SUCCESS: the msg is successfully pushed into the queue to send - */ -sync_err_code_t sync_msg_send(sync_msg_type_t type, uint8_t *data, uint16_t len, - sync_msg_send_cb_t send_cb); - -/** - * @brief get the free number of the message buffer with the specific type - * @param[in] type: the msg type - * @return the free messages number - */ -uint8_t sync_msg_get_available_number(sync_msg_type_t type); - -/** - * @brief flush the msg sending queue - * - * Each message type can be flushed independently. - * @param[in] type: the msg type - * @return the flushed messages number - */ -uint8_t sync_msg_flush(sync_msg_type_t type); - -/** - * @brief reset the message tx queue - * - * It is safe to be called when the device is disconnected and at the app task context. - * @return none - */ -void sync_msg_reset(void); - -/** - * @brief enter critical section - * @return interrupt mask - */ -uint32_t sync_enter_critical(void); - -/** - * @brief exit critical section - * @param[in] flag: interrupt mask - * @return none - */ -void sync_exit_critical(uint32_t flag); - -/** - * @brief register heartbeat callback - * @param[in] cb: function pointer - * @return none - */ -void sync_hb_reg_cb(sync_hb_cb_t cb); - -/** - * @brief register heartbeat callback - * @param[in] cb: function pointer - * @return none - */ -void sync_rte_req_reg_cb(sync_rte_req_cb_t cb); - -/** @} End of PPT_SYNC_Driver_Exported_Functions */ - -/** @} End of PPT_SYNC_Driver */ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif diff --git a/bee/drivers/ppt/sync/src/sync/inc/ppt_sync_master.h b/bee/drivers/ppt/sync/src/sync/inc/ppt_sync_master.h deleted file mode 100644 index 3ac382d7..00000000 --- a/bee/drivers/ppt/sync/src/sync/inc/ppt_sync_master.h +++ /dev/null @@ -1,184 +0,0 @@ -/** -***************************************************************************************** -* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. -***************************************************************************************** - * @file ppt_sync_master.h - * @brief Head file for sync master. - * @details data structs and external functions declaration. - * @author bill - * @date 2022-03-15 - * @version v0.1 - * ************************************************************************************* - */ - -/* Define to prevent recursive inclusion */ -#ifndef _PPT_SYNC_MASTER_H_ -#define _PPT_SYNC_MASTER_H_ - -/* Add Includes here */ -#include -#include -#include "ppt_sync.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/** @addtogroup PPT_SYNC_MASTER_Driver - * @{ - */ - -/** @defgroup PPT_SYNC_MASTER_Driver_Exported_Macros Exported Macros - * @brief - * @{ - */ - -#define SYNC_MASTER_HB_RHY_PERIOD_MAX 0xffffffff - -/** @} End of PPT_SYNC_MASTER_Driver_Exported_Macros */ - -/** @defgroup PPT_SYNC_MASTER_Driver_Exported_Types Exported Types - * @brief - * @{ - */ - -/** tx scheme details */ -typedef struct -{ - /** the sync interval will be aligned to the app tx interval if enabled */ - sync_feature_t tx_align; - /** the stack will use high priority to tx if enabled */ - sync_feature_t early_high_prio; - /** the stack will retransmit the pkt when attain the ack information if enabled */ - sync_feature_t retrans_at_ack; - /** the stack will transmit a new pkt if available at the retransmit interval if enabled */ - sync_feature_t tx_new_at_retrans_int; -} sync_master_tx_scheme_t; - -/** tx prepare callback function pointer type */ -typedef void (*sync_tx_prepare_cb_t)(void); - -/** @} End of PPT_SYNC_MASTER_Driver_Exported_Types */ - -/** @defgroup PPT_SYNC_MASTER_Driver_Exported_Functions Exported Functions - * @brief - * @{ - */ - -/** - * @brief initialize the protocol - * - * The protocol will initiate some default settings. - * After initialization, the app can set some parameters before @ref sync_master_enable to the value they like. - * @return none - */ -void sync_master_init(void); - -/** - * @brief set heartbeat parameters - * - * The heartbeat supports multi-levels. At each level, there are two parameters, including interval and beat times. - * The radio will beat in a certain interval by the designed times. - * Except the maximum level, the heartbeat will enter next level when it beats over the limit times. - * For level 0, the interval is the connection interval, so the interval parameter is useless. - * For level Max(2), the heartbeat will beat all the time, so the beat_times parameter is useless. - * @param[in] level: the heartbeat level, value range from 0 to MAX - * @param[in] interval: the heartbeat interval in unit of microsecond - * @param[in] beat_times: the heartbeat repeat times. - * @return the result - * Example usage - * \code{.c} - // sample: disable heartbeat - sync_master_set_hb_param(0, 0, SYNC_MASTER_HB_RHY_PERIOD_MAX); - - // sample: enable heartbeat - // set heartbeat level 0 to sync interval * 40 - sync_master_set_hb_param(0, 0, 40); - // set heartbeat level 1 to 10ms * 200 - sync_master_set_hb_param(1, 10000, 200); - // set heartbeat level 2 to 100ms - sync_master_set_hb_param(2, 100000, 0); - * \endcode - */ -bool sync_master_set_hb_param(uint8_t level, uint32_t interval, uint32_t beat_times); - -/** - * @brief wakeup heartbeat right now - * @return true if wakeup by this call, else false - */ -bool sync_master_wakeup_hb(void); - -/** - * @brief set tx scheme - * - * Refer to @sync_master_tx_scheme_t for the different tx scheme. - * @param[in] tx_scheme: the tx scheme - * @return none - */ -void sync_master_set_tx_scheme(sync_master_tx_scheme_t tx_scheme); - -/** - * @brief register tx prepare callback which will be called at each interval - * @param[in] cb: the function pointer - * @return none - */ -void sync_master_reg_tx_prepare_cb(sync_tx_prepare_cb_t cb); - -/** - * @brief enable the protocol - * - * It shall be called after @ref sync_master_init to enable the protocol. - * @return none - */ -void sync_master_enable(void); - -/** - * @brief start pairing - * - * After pairing, the master will connect the slave and bonded together. - * The pairing result is notified at the event callback registered by @ref sync_event_cb_reg. - * @return operation result - */ -sync_err_code_t sync_master_pair(void); - -/** - * @brief start connect - * - * If two bonded devices are disconnected, call this api to connect each other. - * The connection result is notified at the event callback registered by @ref sync_event_cb_reg. - * @param[in] bond_info: bond information used to create a connection - * @return operation result - */ -sync_err_code_t sync_master_connect(sync_bond_info_t *bond_info); - -/** - * @brief stop the connection or the pairing procedure - * - * If the device is connecting, pairing or connected, call this api to stop. - * @return none - */ -void sync_master_stop(void); - -/** - * @brief deinitiate the protocol to release all resources - * - * If the device is running, the api @ref sync_master_stop shall be called before this. - * @return none - */ -void sync_master_deinit(void); - -/** - * @brief initialize the sync protocol dlps setting - * @return none - */ -void sync_master_dlps_init(void); - -/** @} End of PPT_SYNC_MASTER_Driver_Exported_Functions */ - -/** @} End of PPT_SYNC_MASTER_Driver */ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif diff --git a/bee/drivers/ppt/sync/src/sync/inc/ppt_sync_slave.h b/bee/drivers/ppt/sync/src/sync/inc/ppt_sync_slave.h deleted file mode 100644 index 339974f5..00000000 --- a/bee/drivers/ppt/sync/src/sync/inc/ppt_sync_slave.h +++ /dev/null @@ -1,116 +0,0 @@ -/** -***************************************************************************************** -* Copyright(c) 2020, Realtek Semiconductor Corporation. All rights reserved. -***************************************************************************************** - * @file ppt_sync_slave.h - * @brief Head file for sync slave. - * @details data structs and external functions declaration. - * @author bill - * @date 2022-03-15 - * @version v0.1 - * ************************************************************************************* - */ - -/* Define to prevent recursive inclusion */ -#ifndef _PPT_SYNC_SLAVE_H_ -#define _PPT_SYNC_SLAVE_H_ - -/* Add Includes here */ -#include -#include -#include "ppt_sync.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/** @addtogroup PPT_SYNC_SLAVE_Driver - * @{ - */ - -/** @defgroup PPT_SYNC_SLAVE_Driver_Exported_Macros Exported Macros - * @brief - * @{ - */ - - -/** @} End of PPT_SYNC_SLAVE_Driver_Exported_Macros */ - -/** @defgroup PPT_SYNC_SLAVE_Driver_Exported_Types Exported Types - * @brief - * @{ - */ - -/** @} End of PPT_SYNC_SLAVE_Driver_Exported_Types */ - -/** @defgroup PPT_SYNC_SLAVE_Driver_Exported_Functions Exported Functions - * @brief - * @{ - */ - -/** - * @brief initialize the protocol - * - * The protocol will initiate some default settings. - * After initialization, the app can set some parameters before @ref sync_slave_enable to the value they like. - * @return none - */ -void sync_slave_init(void); - -/** - * @brief enable the protocol - * - * It shall be called after @ref sync_slave_init to enable the protocol. - * @return none - */ -void sync_slave_enable(void); -/** - * @brief start pairing - * - * After pairing, the slave will connect the master and bonded together. - * The pairing result is notified at the event callback registered by @ref sync_event_cb_reg. - * @return operation result - */ -sync_err_code_t sync_slave_pair(void); - -/** - * @brief start connect - * - * If two bonded devices are disconnected, call this api to connect each other. - * The connection result is notified at the event callback registered by @ref sync_event_cb_reg. - * @param[in] bond_info: bond information used to create a connection - * @return operation result - */ -sync_err_code_t sync_slave_connect(sync_bond_info_t *bond_info); - -/** - * @brief stop the connection or the pairing procedure - * - * If the device is connecting, pairing or connected, call this api to stop. - * @return none - */ -void sync_slave_stop(void); - -/** - * @brief deinitiate the protocol to release all resources - * - * If the device is running, the api @ref sync_slave_stop shall be called before this. - * @return none - */ -void sync_slave_deinit(void); - -/** - * @brief initialize the sync protocol dlps setting - * @return none - */ -void sync_slave_dlps_init(void); - -/** @} End of PPT_SYNC_SLAVE_Driver_Exported_Functions */ - -/** @} End of PPT_SYNC_SLAVE_Driver */ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif From 54de357f18aa39a886d736bc4c3d8b31241eb0f1 Mon Sep 17 00:00:00 2001 From: Dariel Zhang Date: Fri, 31 Jul 2026 15:36:12 +0800 Subject: [PATCH 4/5] drivers: ppt: remove duplicate platform headers, fix include path Platform headers (power_manager_*.h, platform_rtc.h, debug_port.h) were duplicated under ppt/driver/common/rtl87x2g/. Remove the copies and update sync/CMakeLists.txt to add rtl8752h/boot/inc to the include search path so the canonical versions are found at build time. --- .../ppt/driver/common/rtl87x2g/debug_port.h | 81 ------- .../ppt/driver/common/rtl87x2g/platform_rtc.h | 83 ------- .../common/rtl87x2g/power_manager_interface.h | 187 ---------------- .../common/rtl87x2g/power_manager_slave.h | 208 ------------------ .../rtl87x2g/power_manager_unit_platform.h | 200 ----------------- bee/drivers/ppt/sync/CMakeLists.txt | 1 + 6 files changed, 1 insertion(+), 759 deletions(-) delete mode 100644 bee/drivers/ppt/driver/common/rtl87x2g/debug_port.h delete mode 100644 bee/drivers/ppt/driver/common/rtl87x2g/platform_rtc.h delete mode 100644 bee/drivers/ppt/driver/common/rtl87x2g/power_manager_interface.h delete mode 100644 bee/drivers/ppt/driver/common/rtl87x2g/power_manager_slave.h delete mode 100644 bee/drivers/ppt/driver/common/rtl87x2g/power_manager_unit_platform.h diff --git a/bee/drivers/ppt/driver/common/rtl87x2g/debug_port.h b/bee/drivers/ppt/driver/common/rtl87x2g/debug_port.h deleted file mode 100644 index 1393eb52..00000000 --- a/bee/drivers/ppt/driver/common/rtl87x2g/debug_port.h +++ /dev/null @@ -1,81 +0,0 @@ -/** -********************************************************************************************************* -* Copyright(c) 2021, Realtek Semiconductor Corporation. All rights reserved. -********************************************************************************************************* -* @file debug_port.h -* @brief -* @details -* @author -* @date 2021-4-6 -* @version v1.0 -* ********************************************************************************************************* -*/ - - -#ifndef _DEBUG_PORT_H_ -#define _DEBUG_PORT_H_ - -#ifdef __cplusplus -extern "C" { -#endif -#include "rtl876x.h" - -typedef enum _t_debug_mode -{ - DIGI_DBG_SYSON, - DIGI_DBG_BT, - DIGI_DBG_PROCESSOR_SYS, - DIGI_DBG_APB_ADC, - DIGI_DBG_RTC, - - ANA_DBG_AUX_ADC = 0x10, - ANA_DBG_RF_TRXIQ_AFE_TRMK_ADC, - ANA_DBG_RF_TRANCERIVER -} T_DEBUG_MODE; - - -typedef enum _t_debug_pin_group -{ - DBG_PIN_GROUP_A, //mapping to debug_pin_table_a - DBG_PIN_GROUP_B, //mapping to debug_pin_table_b - DBG_PIN_GROUP_C, //mapping to debug_pin_table_c - DBG_PIN_GROUP_ALL_PAD = 0xff, -} T_PIN_GROUP; - -/** - * @brief Open the debug port mode on specific pins - * @param red_table_mask: the pin bit map used in the table debug_pin_table_red. - * @param black_table_mask: the pin bit map used in the table debug_pin_table_black. - * @param debug_mode: debug mode select from T_DEBUG_MODE - * @retval None - */ -extern void (*debug_port_open)(T_DEBUG_MODE debug_mode); - -/** - * @brief Close the debug mode - * @retval None - */ -void debug_port_close(void); - -/** - * @brief set debug mode on specific pins - * @param pin_group: the pin group selected. - * This parameter can be any combination of the following values: - * @arg DBG_PIN_GROUP_A : the digital debug pin bit 0 ~ 31 could be selected as below table. - * ADC_0, ADC_1, ADC_2, ADC_3, P1_0, P1_1, P2_0, P2_1, //digi_debug_0 ~ 7 - * P2_2, P2_3, P2_4, P2_5, P3_2, P3_3, MIC2_P, MIC2_N, //digi_debug_8 ~ 15 - * MICBIAS, LOUT_P, LOUT_N, P10_0 //digi_debug_16 ~ 19 - * @arg DBG_PIN_GROUP_B : the digital debug pin bit 0 ~ 31 could be selected as below table. - * MIC1_P, MIC1_N, //digi_debug_12 ~ 13 - * @retval None - */ -extern void (*debug_port_set_pin_bit_map)(T_PIN_GROUP pin_group, uint32_t dbg_bitmap); - -void debug_port_bt_mac(bool is_set); - -#ifdef __cplusplus -} -#endif - -#endif /* _DEBUG_PORT_H_ */ - diff --git a/bee/drivers/ppt/driver/common/rtl87x2g/platform_rtc.h b/bee/drivers/ppt/driver/common/rtl87x2g/platform_rtc.h deleted file mode 100644 index 498d54aa..00000000 --- a/bee/drivers/ppt/driver/common/rtl87x2g/platform_rtc.h +++ /dev/null @@ -1,83 +0,0 @@ -/** -********************************************************************************************************* -* Copyright(c) 2022, Realtek Semiconductor Corporation. All rights reserved. -********************************************************************************************************** -* @file platform_rtc.h -* @brief Platfrom RTC implementation head file. -* @details -* @author js.lin -* @date 2022-8-31 -* @version v0.1 -********************************************************************************************************* -*/ - -/*============================================================================* - * Define to prevent recursive inclusion - *============================================================================*/ -#ifndef __PLATFORM_RTC_H -#define __PLATFORM_RTC_H - -/*============================================================================* - * Header Files - *============================================================================*/ -#include -#include -#include "rtl876x.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define PF_RTC_BIT_NUM BIT64(32) - -/*============================================================================* - * Types - *============================================================================*/ -/** @defgroup PLATFORM_RTC_Exported_Types Platform RTC Exported Types - * @{ - */ - -typedef enum PFRTCComparator -{ - PF_RTC_COMP0 = 0x01, - PF_RTC_COMP1 = 0x02, - PF_RTC_COMP2 = 0x04, - PF_RTC_COMP3 = 0x08, -} PFRTCComparator; - -/** @} */ /* End of group PLATFORM_RTC_Exported_Types */ - -/*============================================================================* - * Functions - *============================================================================*/ -/** @defgroup PLATFORM_RTC_Exported_Functions Platform RTC Exported Functions - * @{ - */ - -extern void (*platform_rtc_aon_init)(void); -extern void (*platform_rtc_non_aon_init)(void); -extern void (*platform_rtc_reset)(void); -extern void (*platform_rtc_run_cmd)(bool); -extern uint32_t (*platform_rtc_get_counter)(void); -extern void (*platform_rtc_set_comp_intr_config)(PFRTCComparator, bool, bool); -extern void (*platform_rtc_set_comp_wk_config)(PFRTCComparator, bool); -extern void (*platform_rtc_set_intr_group_config)(bool); -extern void (*platform_rtc_set_wk_group_config)(bool); -extern bool (*platform_rtc_get_intr_group_config)(void); -extern bool (*platform_rtc_get_wk_group_config)(void); -extern void (*platform_rtc_set_comp)(PFRTCComparator, uint32_t); -extern uint32_t (*platform_rtc_get_comp)(PFRTCComparator); -extern PFRTCComparator(*platform_rtc_get_comp_intr_status)(void); -extern PFRTCComparator(*platform_rtc_get_comp_wk_status)(void); -extern void (*platform_rtc_set_comp_manual_intr)(PFRTCComparator); -extern PFRTCComparator(*platform_rtc_get_comp_manual_intr)(void); -extern void (*platform_rtc_clear_comp_intr_status)(PFRTCComparator); -extern void (*platform_rtc_clear_comp_wk_status)(PFRTCComparator); - -/** @} */ /* End of group PLATFORM_RTC_Exported_Functions */ - -#ifdef __cplusplus -} -#endif - -#endif /* __PLATFORM_RTC_H */ diff --git a/bee/drivers/ppt/driver/common/rtl87x2g/power_manager_interface.h b/bee/drivers/ppt/driver/common/rtl87x2g/power_manager_interface.h deleted file mode 100644 index f647d041..00000000 --- a/bee/drivers/ppt/driver/common/rtl87x2g/power_manager_interface.h +++ /dev/null @@ -1,187 +0,0 @@ -/** -********************************************************************************************************* -* Copyright(c) 2021, Realtek Semiconductor Corporation. All rights reserved. -********************************************************************************************************* -* @file power_manager_interface.h -* @brief Power Manager Interface implementation head file. -* @details -* @author Kellan Ho, js.lin -* @date 2022-08-29 -* @version v1.0 -********************************************************************************************************* -*/ - -/*============================================================================* - * Define to prevent recursive inclusion - *============================================================================*/ -#ifndef __POWER_MANAGER_INTERFACE_H -#define __POWER_MANAGER_INTERFACE_H - - -/*============================================================================* - * Header Files -*============================================================================*/ -#include "stdint.h" -#include "stdbool.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* for naming compatibility */ -#define PM_SLAVE_BTMAC PM_SLAVE_0 -#define PM_SLAVE_ZIGBEE PM_SLAVE_0 -#define PM_SLAVE_PROPRIETARY PM_SLAVE_0 - -#define PM_INVALID_WAKEUP_TIME_DIFF 0xffffffff - -#define PM_UNIT_MAX_NUM 4 // equal to num of pf rtc comparators -#define PM_GET_UNIT_INDEX(unit_id) (((unit_id >> 2) == PM_SYSTEM_LEVEL_PF) ? 3 : \ - ((unit_id >> 2) == PM_SYSTEM_LEVEL_1) ? 2 : (unit_id & 0x1)) - -typedef enum PMSlave -{ - PM_SLAVE_0 = 0, - PM_SLAVE_DEF_MAX, -} PMSlave; - -typedef enum PMSystemLevel -{ - PM_SYSTEM_LEVEL_0 = 0, - PM_SYSTEM_LEVEL_1 = 1, - PM_SYSTEM_LEVEL_PF = 2, - PM_SYSTEM_LEVEL_DEF_MAX, -} PMSystemLevel; - -typedef enum PMUnitStatus -{ - PM_UNIT_UNKNOWN = 0, - PM_UNIT_PEND = 1, - PM_UNIT_INACTIVE = 2, - PM_UNIT_ACTIVE = 3, -} PMUnitStatus; - -typedef enum PMCheckResult -{ - PM_CHECK_PEND = 0, - PM_CHECK_FAIL = 1, - PM_CHECK_PASS = 2, -} PMCheckResult; - -typedef enum PMAciton -{ - PM_ACTION_IDLE_CHECK_REQUEST, - PM_ACTION_IDLE_CHECK_CONTINUE, - PM_ACTION_IDLE_CHECK_RESPONSE, - PM_ACTION_CHECK_REQUEST, - PM_ACTION_CHECK_RESPONSE, - PM_ACTION_STORE_ENTER_REQUEST, - PM_ACTION_STORE_ENTER_RESPONSE, - PM_ACTION_EXIT_RESTORE_REQUEST, - PM_ACTION_EXIT_RESTORE_RESPONSE, - PM_ACTION_DEF_MAX, -} PMAciton; - -typedef enum -{ - PM_UNIT_PROPRIETARY = (PM_SYSTEM_LEVEL_0 << 2 | 0), - PM_UNIT_ZIGBEE = (PM_SYSTEM_LEVEL_0 << 2 | 1), - PM_UNIT_BTMAC = (PM_SYSTEM_LEVEL_1 << 2 | 0), - PM_UNIT_PLATFORM = (PM_SYSTEM_LEVEL_PF << 2 | 0), - PM_UNIT_HEADER_ID = 0x80, -} PMUnitID; - -typedef union PMM2SActivateMessage -{ - uint32_t d32[4]; - struct - { - uint32_t action: 4; - uint32_t unit_id: 4; - uint32_t dummy_0: 23; - uint32_t info_valid: 1; - - uint32_t dummy_1; - - uint32_t dummy_2; - - uint32_t dummy_3; - }; -} PMM2SActivateMessage; - -typedef union PMS2MActivateMessage -{ - uint32_t d32[1]; - struct - { - uint32_t action: 4; - uint32_t unit_id: 4; - uint32_t dummy_0: 23; - uint32_t info_valid: 1; - }; -} PMS2MActivateMessage; - -typedef union PMM2SInactivateMessage -{ - uint32_t d32[3]; - struct - { - uint32_t action: 4; - uint32_t unit_id: 4; - uint32_t enable_irq: 1; - uint32_t enter_wfi: 1; - uint32_t dummy_0: 21; - uint32_t info_valid: 1; - - uint32_t check_clk; - - uint32_t pre_sys_lvl_wakeup_time_diff; - }; -} PMM2SInactivateMessage; - -typedef union PMS2MInactivateMessage -{ - uint32_t d32[3]; - struct - { - uint32_t action: 4; - uint32_t unit_id: 4; - uint32_t check_result: 2; - uint32_t dummy_0: 21; - uint32_t info_valid: 1; - - uint32_t wakeup_time_diff; - - uint32_t dummy_2; - }; -} PMS2MInactivateMessage; - -extern volatile PMM2SActivateMessage g_pm_m2s_act_msg; -extern volatile PMS2MActivateMessage g_pm_s2m_act_msg; -extern volatile PMM2SInactivateMessage g_pm_m2s_inact_msg; -extern volatile PMS2MInactivateMessage g_pm_s2m_inact_msg; - -extern void (*power_manager_master_initiate_wakeup)(PMSlave slave, PMSystemLevel sys_lvl, - PMUnitID unit_id); -extern void (*power_manager_master_suspend_unit)(PMSlave slave, PMSystemLevel sys_lvl, - PMUnitID unit_id); -extern void (*power_manager_master_resume_unit)(PMSlave slave, PMSystemLevel sys_lvl, - PMUnitID unit_id); -extern void (*power_manager_master_set_timing_guarantee_sys_lvl)(PMSlave slave, - PMSystemLevel timing_guarantee_sys_lvl); -extern void (*power_manager_master_register_unit)(PMSlave slave, PMSystemLevel sys_lvl, - PMUnitID unit_id); -extern void (*power_manager_master_reset_slave)(PMSlave slave); - -void power_manager_interface_init_unit_status(void); -PMUnitStatus power_manager_interface_get_unit_status(PMSlave slave, PMUnitID unit_id); -void power_manager_interface_set_unit_status(PMSlave slave, PMUnitID unit_id, PMUnitStatus status); -bool power_manager_interface_check_unit_active(PMSlave slave, PMUnitID unit_id); -bool power_manager_interface_check_unit_active_from_isr(PMSlave slave, PMUnitID unit_id); - -#ifdef __cplusplus -} -#endif - -#endif /* __POWER_MANAGER_INTERFACE_H */ - diff --git a/bee/drivers/ppt/driver/common/rtl87x2g/power_manager_slave.h b/bee/drivers/ppt/driver/common/rtl87x2g/power_manager_slave.h deleted file mode 100644 index 5fcfd4d8..00000000 --- a/bee/drivers/ppt/driver/common/rtl87x2g/power_manager_slave.h +++ /dev/null @@ -1,208 +0,0 @@ -/** -***************************************************************************************** -* Copyright(c) 2022, Realtek Semiconductor Corporation. All rights reserved. -***************************************************************************************** - * @file power_manager_slave.h - * @brief Power Manager Slave implementation head file. - * @author George Chang, js.lin - * @date 2022-08-29 - * @version v1.0 - * ************************************************************************************* - */ - -/*============================================================================* - * Define to prevent recursive inclusion - *============================================================================*/ -#ifndef __POWER_MANAGER_SLAVE_H -#define __POWER_MANAGER_SLAVE_H - - -/*============================================================================* - * Header Files -*============================================================================*/ -#include -#include -#include "power_manager_interface.h" -#include "platform_rtc.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define IRQ_TAG_FILL_WORD ( 0x5a5a5a5aU ) - -#define POWER_MANAGER_GET_TIMESTAMP(stamp) \ - do { \ - stamp = platform_rtc_get_counter(); \ - } while(0) - -#define POWER_MANAGER_GET_TIMEDIFF(start, end, diff) \ - do { \ - diff = clk_cnt_diff(start, end, PF_RTC_BIT_NUM); \ - } while(0) - -#define POWER_MANAGER_UPDATE_EXTREMUM(diff, diff_max, diff_min) \ - do { \ - diff_max = diff > diff_max ? diff : diff_max; \ - diff_min = diff < diff_min ? diff : diff_min; \ - } while(0) - -#define PM_SYSTEM_LEVEL_UNIT_NUM 2 - -/*============================================================================* - * Types -*============================================================================*/ -/** @defgroup POWER_MANAGER_Exported_Types Power Manager Exported Types - * @{ - */ -typedef PMCheckResult(*PMUnitCheckFunc)(uint32_t, uint32_t, uint32_t *); -typedef void (*PMUnitStoreFunc)(void); -typedef void (*PMUnitRestoreFunc)(void); -typedef void (*PMUnitEnterFunc)(void); -typedef void (*PMUnitExitFunc)(void); - -typedef PMCheckResult(*PMCheckWakeupTimeFunc)(uint32_t *); -typedef void (*PMFuncToReturn)(void); - -typedef enum -{ - PROFILING_PM_SLAVE_CHECK = 0, - PROFILING_PM_SLAVE_STORE = 1, - PROFILING_PM_SLAVE_ENTER = 2, - PROFILING_PM_SLAVE_EXIT = 3, - PROFILING_PM_SLAVE_RESTORE = 4, - PROFILING_PM_SLAVE_STAGE_MAX = 5 -} ProfilingPMSlaveStage; - -typedef struct -{ - uint32_t stage_start; - uint32_t stage_end; - uint32_t stage_time[PROFILING_PM_SLAVE_STAGE_MAX]; - uint32_t stage_time_max[PROFILING_PM_SLAVE_STAGE_MAX]; - uint32_t stage_time_min[PROFILING_PM_SLAVE_STAGE_MAX]; -} ProfilingPMSlaveData; - -typedef struct -{ - PMUnitCheckFunc check_func; - PMUnitStoreFunc store_func; - PMUnitRestoreFunc restore_func; - PMUnitEnterFunc enter_func; - PMUnitExitFunc exit_func; - - PMUnitID unit_id; - - ProfilingPMSlaveData *profiling_data; -} PowerManagerSlaveUnit; - -typedef struct -{ - PMUnitID unit_id; - uint32_t pon_delay_time; - uint32_t pon_stage_time; - uint32_t pof_delay_time; - uint32_t pof_stage_time; - uint32_t minimum_sleep_guard_time; -} PowerManagerSlaveRequestResource; - -typedef struct -{ - uint32_t start_time_diff; - uint32_t end_time_diff; -} PMTimePeriod; - -typedef struct -{ - uint32_t cur_clk; - PMTimePeriod occupied; -} PMOccupyTimeInfo; - -typedef struct -{ - PowerManagerSlaveUnit *unit_array[PM_SYSTEM_LEVEL_DEF_MAX][PM_SYSTEM_LEVEL_UNIT_NUM]; - - PMOccupyTimeInfo slave[PM_SYSTEM_LEVEL_DEF_MAX][PM_SYSTEM_LEVEL_UNIT_NUM]; - - uint32_t suspended; - - PMFuncToReturn func_to_return; -} PowerManagerSlaveSystem; - -typedef union -{ - uint8_t value; - struct - { - uint8_t profiling_mode: 1; - uint8_t irq_control_by_master: 1; - uint8_t m2s_inact_msg_dbg: 1; - uint8_t s2m_inact_msg_dbg: 1; - uint8_t m2s_act_msg_dbg: 1; - uint8_t s2m_act_msg_dbg: 1; - uint8_t rsvd: 2; - }; -} PowerManagerSlaveFeatureConfig; - -/*============================================================================* - * Variables -*============================================================================*/ -/** @defgroup POWER_MANAGER_Exported_Variables Power Manager Exported Variables - * @{ - */ -extern PowerManagerSlaveFeatureConfig power_manager_slave_feature_cfg; -extern PowerManagerSlaveSystem power_manager_slave_system; - -extern bool (*power_manager_slave_check)(const PMM2SInactivateMessage *); -extern void (*power_manager_slave_store_and_enter)(const PMM2SInactivateMessage *); -extern void (*power_manager_slave_exit_and_restore)(const PMM2SActivateMessage *); -extern void (*power_manager_slave_cpu_sleep)(bool); -extern bool (*power_manager_slave_check_interrupt_occur)(void); -extern void (*power_manager_slave_handle_irq)(bool, bool *); -extern void (*power_manager_slave_init)(void); - -extern uint32_t (*power_manager_slave_align_clk)(const uint32_t, uint32_t *, uint32_t *, uint32_t); -extern PMCheckResult(*power_manager_slave_align_wakeup_time)(PMCheckWakeupTimeFunc, const uint32_t, - uint32_t *); -extern void (*power_manager_slave_set_occupied_time_descending)(const uint32_t, - const PMOccupyTimeInfo *, PMTimePeriod *, uint8_t); -extern bool (*power_manager_slave_check_occupied_time)(const PMTimePeriod *, const uint8_t, - uint32_t *, uint32_t, uint32_t); -extern void (*power_manager_slave_register_resource)(PMUnitID, uint32_t, uint32_t, uint32_t); -extern void (*power_manager_slave_unregister_resource)(PMUnitID); -extern bool (*power_manager_slave_request_resource)(const uint32_t, uint32_t *, - PowerManagerSlaveRequestResource *); - -extern bool (*power_manager_slave_inact_msg_handler)(const PMM2SInactivateMessage *, bool *); -extern void (*power_manager_slave_act_msg_handler)(const PMM2SActivateMessage *); - -extern void (*power_manager_slave_inact_action_handler)(void); -extern void (*power_manager_slave_act_action_handler)(void); -/** @} */ /* End of group POWER_MANAGER_Exported_Variables */ - -/*============================================================================* - * Functions -*============================================================================*/ - -/** @defgroup POWER_MANAGER_Exported_Functions Power Manager Exported Functions - * @{ - */ -void power_manager_slave_register_function_to_return(PMFuncToReturn func); -void power_manager_slave_register_unit(PMUnitID unit_id, PowerManagerSlaveUnit *p_unit); -PowerManagerSlaveUnit *power_manager_slave_get_unit(PMUnitID unit_id); -void power_manager_slave_suspend_unit(PMUnitID unit_id); -void power_manager_slave_resume_unit(PMUnitID unit_id); -void power_manager_slave_suspend_all(void); -void power_manager_slave_resume_all(void); - -/** @} */ /* End of group POWER_MANAGER_Exported_Functions */ - -/** @} */ /* End of group POWER_MANAGER */ - - -#ifdef __cplusplus -} -#endif - -#endif /* __POWER_MANAGER_SLAVE_H */ - diff --git a/bee/drivers/ppt/driver/common/rtl87x2g/power_manager_unit_platform.h b/bee/drivers/ppt/driver/common/rtl87x2g/power_manager_unit_platform.h deleted file mode 100644 index 8cc98504..00000000 --- a/bee/drivers/ppt/driver/common/rtl87x2g/power_manager_unit_platform.h +++ /dev/null @@ -1,200 +0,0 @@ -/** - ***************************************************************************************** - * Copyright(c) 2022, Realtek Semiconductor Corporation. All rights reserved. - ***************************************************************************************** - * @file power_manager_unit_platform.h - * @brief Platform Power Manager implementation head file. - * @author Po Yu Chen, js.lin - * @date 2022-08-29 - * @version v1.0 - * ************************************************************************************* - */ - -/*============================================================================* - * Define to prevent recursive inclusion - *============================================================================*/ -#ifndef __POWER_MANAGER_UNIT_PLATFORM_H -#define __POWER_MANAGER_UNIT_PLATFORM_H - -/*============================================================================* - * Header Files - *============================================================================*/ -#include -#include -#include "os_queue.h" -#include "power_manager_interface.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/*============================================================================* - * Types - *============================================================================*/ -/** @defgroup PLATFORM_PM_Exported_Types Platform_PM Exported Types - * @{ - */ - -typedef enum -{ - //PLATFORM_POWEROFF = 0, /**< Power off */ // Bee4 not support - PLATFORM_POWERDOWN = 1, /**< Power down */ - PLATFORM_DLPS_PFM = 2, /**< DLPS (PFM) */ - PLATFORM_DLPS_RET = 3, /**< DLPS (RET) */ - //PLATFORM_LPS_PFM = 4, /**< LPS */ // Bee4 not support - PLATFORM_ACTIVE = 5, /**< Active */ - PLATFORM_POWER_MODE_MAX = 6 -} PlatformPowerMode; - -typedef enum -{ - PLATFORM_PM_CHECK = 0, - PLATFORM_PM_STORE = 1, - PLATFORM_PM_ENTER = 2, - PLATFORM_PM_EXIT = 3, - PLATFORM_PM_RESTORE = 4, - PLATFORM_PM_PEND = 5, - PLATFORM_PM_STAGE_MAX = 6, -} PlatformPMStage; - -typedef enum -{ - PLATFORM_PM_WAKEUP_UNKNOWN = 0x00000000, - PLATFORM_PM_WAKEUP_PRE_SYSTEM_LEVEL = 0x00000001, - PLATFORM_PM_WAKEUP_PF_RTC = 0x00000100, - PLATFORM_PM_WAKEUP_RTC = 0x00000200, - PLATFORM_PM_WAKEUP_MAC = 0x00000400, - PLATFORM_PM_WAKEUP_GPIO = 0x00000800, - PLATFORM_WAKEUP_REASON_USB = 0x00001000, - PLATFORM_WAKEUP_REASON_LPCOMP = 0x00002000, - PLATFORM_WAKEUP_REASON_AON_QUAD_DEC = 0x00004000, - PLATFORM_PM_WAKEUP_DEF_MAX = 0xFFFFFFFF, -} PlatformWakeupReason; - -typedef enum -{ - PLATFORM_PM_ERROR_UNKNOWN = 0x0, - PLATFORM_PM_ERROR_POWER_MODE = 0x1, - PLATFORM_PM_ERROR_DISABLE_DLPS_TIME = 0x2, - PLATFORM_PM_ERROR_32K_CHECK_LOCK = 0x3, - PLATFORM_PM_ERROR_LOG_DMA_NOT_IDLE = 0x4, - PLATFORM_PM_ERROR_LOG_BUFFER_NOT_EMPTY = 0x5, - PLATFORM_PM_ERROR_CALLBACK_CHECK = 0x6, - PLATFORM_PM_ERROR_INTERRUPT_OCCURRED = 0x7, - PLATFORM_PM_ERROR_WAKEUP_TIME = 0x8, - PLATFORM_PM_ERROR_OCCUPIED_TIME = 0x9, -} PlatformPowerModeErrorCode; - -typedef PMCheckResult(*PlatformPMCheckCbFunc)(uint32_t *); -typedef void (*PlatformPMStoreCbFunc)(void); -typedef void (*PlatformPMRestoreCbFunc)(void); -typedef void (*PlatformPMEnterCbFunc)(void); -typedef void (*PlatformPMExitCbFunc)(void); -typedef void (*PlatformPMPendCbFunc)(void); - -typedef void (*PlatformPMScheduleBottomHalfFunc)(void (*)(void)); - -typedef struct _PlatformPMCbFuncQueueElem -{ - struct _PlatformPMCbFuncQueueElem *pNext; - void *cb_func; - int8_t priority; -} PlatformPMCbFuncQueueElem; - -typedef struct _PlatformPMSystem -{ - PlatformPowerMode power_mode; - - PlatformWakeupReason wakeup_reason; - PlatformPowerModeErrorCode error_code; - uint32_t *refuse_reason; - - uint32_t wakeup_count; - uint32_t allow_sleep_count; - uint32_t last_wakeup_clk; - uint32_t last_sleep_clk; - uint32_t total_wakeup_time; - uint32_t total_sleep_time; - - uint32_t stage_time[PLATFORM_PM_STAGE_MAX]; - uint32_t minimum_sleep_time; - uint32_t learning_guard_time; - - T_OS_QUEUE callback_func_queue[PLATFORM_PM_STAGE_MAX]; - - PlatformPMScheduleBottomHalfFunc schedule_bottom_half_callback; -} PlatformPMSystem; - -typedef union _PlatformPMFeatureConfig -{ - uint8_t value[1]; - struct - { - uint8_t platform_check_dbg: 1; - uint8_t platform_enter_dbg: 1; - uint8_t platform_exit_dbg: 1; - uint8_t platform_stage_time: 1; - uint8_t platform_statistics: 1; - uint8_t rsvd1: 3; - }; -} PlatformPMFeatureConfig; - -/** @} */ /* End of group PLATFORM_PM_Exported_Types */ - -/*============================================================================* - * Variables - *============================================================================*/ -/** @defgroup PLATFORM_PM_Exported_Variables Platform_PM Exported Variables - * @{ - */ - -extern PlatformPMFeatureConfig platform_pm_feature_cfg; -extern PlatformPMSystem platform_pm_system; - -extern void (*platform_pm_hw_enter_flow)(void); -extern void (*platform_pm_hw_exit_flow)(void); -extern void (*platform_pm_cpu_enter_flow)(void); -extern void (*platform_pm_cpu_exit_flow)(void); - -extern PMCheckResult(*platform_pm_wakeup_time)(const uint32_t, const uint32_t, uint32_t *); -extern PMCheckResult(*platform_pm_check)(const uint32_t, const uint32_t, uint32_t *); -extern void (*platform_pm_store)(void); -extern void (*platform_pm_restore)(void); -extern void (*platform_pm_enter)(void); -extern void (*platform_pm_exit)(void); -extern void (*platform_pm_pend)(void); -extern void (*platform_pm_init)(void); -extern void (*platform_pm_statistics)(void); - -extern uint32_t (*platform_pm_get_extra_wait_cnt)(void); -extern void (*platfom_pm_callback_queue_in)(T_OS_QUEUE *, PlatformPMCbFuncQueueElem *, bool); -extern void (*platform_pm_register_callback_func_with_priority)(void *, PlatformPMStage, int8_t); -extern void (*platform_pm_register_callback_func)(void *, PlatformPMStage); -/** @} */ /* End of group PLATFORM_PM_Exported_Variables */ - -/*============================================================================* - * Functions - *============================================================================*/ - -/** @defgroup PLATFORM_PM_Exported_Functions Platform_PM Exported Functions - * @{ - */ - -void platform_pm_set_power_mode(PlatformPowerMode pf_power_mode_user); -PlatformPowerMode platform_pm_get_power_mode(void); -PlatformPowerModeErrorCode platform_pm_get_error_code(void); -uint32_t *platform_pm_get_refuse_reason(void); -void platform_pm_get_statistics(uint32_t *wakeup_count, uint32_t *last_wakeup_clk, - uint32_t *last_sleep_clk); -PlatformWakeupReason platform_pm_get_wakeup_reason(void); -void platform_pm_register_schedule_bottom_half_callback_func(PlatformPMScheduleBottomHalfFunc - cb_func); - -/** @} */ /* End of group PLATFORM_PM_Exported_Functions */ - -#ifdef __cplusplus -} -#endif - -#endif /* __POWER_MANAGER_UNIT_PLATFORM_H */ - diff --git a/bee/drivers/ppt/sync/CMakeLists.txt b/bee/drivers/ppt/sync/CMakeLists.txt index 941cef16..dbf14fc4 100644 --- a/bee/drivers/ppt/sync/CMakeLists.txt +++ b/bee/drivers/ppt/sync/CMakeLists.txt @@ -21,3 +21,4 @@ zephyr_include_directories(${PPT_SYNC_SRC}) zephyr_include_directories(${PPT_DRV_SRC}/common) zephyr_include_directories(${PPT_DRV_SRC}/common/rtl87x2g) zephyr_include_directories(${PPT_DRV_SRC}/simple) +zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../rtl8752h/boot/inc) From baa4a6858b1b07fbeb9a8ed7c2ce3fa4a4625bcf Mon Sep 17 00:00:00 2001 From: Dariel Zhang Date: Fri, 31 Jul 2026 16:20:45 +0800 Subject: [PATCH 5/5] rtl87x2g: add missing platform headers; fix ppt include path Move power_manager_slave.h, debug_port.h, and platform_rtc.h into bee/rtl87x2g/boot/inc/ so they are provided alongside the other RTL87X2G platform headers already in that directory. Remove the erroneous rtl8752h/boot/inc include path from ppt/sync/CMakeLists.txt, which caused CMSIS double-include conflicts when building for RTL87X2G targets. --- bee/drivers/ppt/sync/CMakeLists.txt | 1 - bee/rtl87x2g/boot/inc/debug_port.h | 81 ++++++++ bee/rtl87x2g/boot/inc/platform_rtc.h | 83 ++++++++ bee/rtl87x2g/boot/inc/power_manager_slave.h | 207 ++++++++++++++++++++ 4 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 bee/rtl87x2g/boot/inc/debug_port.h create mode 100644 bee/rtl87x2g/boot/inc/platform_rtc.h create mode 100644 bee/rtl87x2g/boot/inc/power_manager_slave.h diff --git a/bee/drivers/ppt/sync/CMakeLists.txt b/bee/drivers/ppt/sync/CMakeLists.txt index dbf14fc4..941cef16 100644 --- a/bee/drivers/ppt/sync/CMakeLists.txt +++ b/bee/drivers/ppt/sync/CMakeLists.txt @@ -21,4 +21,3 @@ zephyr_include_directories(${PPT_SYNC_SRC}) zephyr_include_directories(${PPT_DRV_SRC}/common) zephyr_include_directories(${PPT_DRV_SRC}/common/rtl87x2g) zephyr_include_directories(${PPT_DRV_SRC}/simple) -zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../rtl8752h/boot/inc) diff --git a/bee/rtl87x2g/boot/inc/debug_port.h b/bee/rtl87x2g/boot/inc/debug_port.h new file mode 100644 index 00000000..1393eb52 --- /dev/null +++ b/bee/rtl87x2g/boot/inc/debug_port.h @@ -0,0 +1,81 @@ +/** +********************************************************************************************************* +* Copyright(c) 2021, Realtek Semiconductor Corporation. All rights reserved. +********************************************************************************************************* +* @file debug_port.h +* @brief +* @details +* @author +* @date 2021-4-6 +* @version v1.0 +* ********************************************************************************************************* +*/ + + +#ifndef _DEBUG_PORT_H_ +#define _DEBUG_PORT_H_ + +#ifdef __cplusplus +extern "C" { +#endif +#include "rtl876x.h" + +typedef enum _t_debug_mode +{ + DIGI_DBG_SYSON, + DIGI_DBG_BT, + DIGI_DBG_PROCESSOR_SYS, + DIGI_DBG_APB_ADC, + DIGI_DBG_RTC, + + ANA_DBG_AUX_ADC = 0x10, + ANA_DBG_RF_TRXIQ_AFE_TRMK_ADC, + ANA_DBG_RF_TRANCERIVER +} T_DEBUG_MODE; + + +typedef enum _t_debug_pin_group +{ + DBG_PIN_GROUP_A, //mapping to debug_pin_table_a + DBG_PIN_GROUP_B, //mapping to debug_pin_table_b + DBG_PIN_GROUP_C, //mapping to debug_pin_table_c + DBG_PIN_GROUP_ALL_PAD = 0xff, +} T_PIN_GROUP; + +/** + * @brief Open the debug port mode on specific pins + * @param red_table_mask: the pin bit map used in the table debug_pin_table_red. + * @param black_table_mask: the pin bit map used in the table debug_pin_table_black. + * @param debug_mode: debug mode select from T_DEBUG_MODE + * @retval None + */ +extern void (*debug_port_open)(T_DEBUG_MODE debug_mode); + +/** + * @brief Close the debug mode + * @retval None + */ +void debug_port_close(void); + +/** + * @brief set debug mode on specific pins + * @param pin_group: the pin group selected. + * This parameter can be any combination of the following values: + * @arg DBG_PIN_GROUP_A : the digital debug pin bit 0 ~ 31 could be selected as below table. + * ADC_0, ADC_1, ADC_2, ADC_3, P1_0, P1_1, P2_0, P2_1, //digi_debug_0 ~ 7 + * P2_2, P2_3, P2_4, P2_5, P3_2, P3_3, MIC2_P, MIC2_N, //digi_debug_8 ~ 15 + * MICBIAS, LOUT_P, LOUT_N, P10_0 //digi_debug_16 ~ 19 + * @arg DBG_PIN_GROUP_B : the digital debug pin bit 0 ~ 31 could be selected as below table. + * MIC1_P, MIC1_N, //digi_debug_12 ~ 13 + * @retval None + */ +extern void (*debug_port_set_pin_bit_map)(T_PIN_GROUP pin_group, uint32_t dbg_bitmap); + +void debug_port_bt_mac(bool is_set); + +#ifdef __cplusplus +} +#endif + +#endif /* _DEBUG_PORT_H_ */ + diff --git a/bee/rtl87x2g/boot/inc/platform_rtc.h b/bee/rtl87x2g/boot/inc/platform_rtc.h new file mode 100644 index 00000000..498d54aa --- /dev/null +++ b/bee/rtl87x2g/boot/inc/platform_rtc.h @@ -0,0 +1,83 @@ +/** +********************************************************************************************************* +* Copyright(c) 2022, Realtek Semiconductor Corporation. All rights reserved. +********************************************************************************************************** +* @file platform_rtc.h +* @brief Platfrom RTC implementation head file. +* @details +* @author js.lin +* @date 2022-8-31 +* @version v0.1 +********************************************************************************************************* +*/ + +/*============================================================================* + * Define to prevent recursive inclusion + *============================================================================*/ +#ifndef __PLATFORM_RTC_H +#define __PLATFORM_RTC_H + +/*============================================================================* + * Header Files + *============================================================================*/ +#include +#include +#include "rtl876x.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PF_RTC_BIT_NUM BIT64(32) + +/*============================================================================* + * Types + *============================================================================*/ +/** @defgroup PLATFORM_RTC_Exported_Types Platform RTC Exported Types + * @{ + */ + +typedef enum PFRTCComparator +{ + PF_RTC_COMP0 = 0x01, + PF_RTC_COMP1 = 0x02, + PF_RTC_COMP2 = 0x04, + PF_RTC_COMP3 = 0x08, +} PFRTCComparator; + +/** @} */ /* End of group PLATFORM_RTC_Exported_Types */ + +/*============================================================================* + * Functions + *============================================================================*/ +/** @defgroup PLATFORM_RTC_Exported_Functions Platform RTC Exported Functions + * @{ + */ + +extern void (*platform_rtc_aon_init)(void); +extern void (*platform_rtc_non_aon_init)(void); +extern void (*platform_rtc_reset)(void); +extern void (*platform_rtc_run_cmd)(bool); +extern uint32_t (*platform_rtc_get_counter)(void); +extern void (*platform_rtc_set_comp_intr_config)(PFRTCComparator, bool, bool); +extern void (*platform_rtc_set_comp_wk_config)(PFRTCComparator, bool); +extern void (*platform_rtc_set_intr_group_config)(bool); +extern void (*platform_rtc_set_wk_group_config)(bool); +extern bool (*platform_rtc_get_intr_group_config)(void); +extern bool (*platform_rtc_get_wk_group_config)(void); +extern void (*platform_rtc_set_comp)(PFRTCComparator, uint32_t); +extern uint32_t (*platform_rtc_get_comp)(PFRTCComparator); +extern PFRTCComparator(*platform_rtc_get_comp_intr_status)(void); +extern PFRTCComparator(*platform_rtc_get_comp_wk_status)(void); +extern void (*platform_rtc_set_comp_manual_intr)(PFRTCComparator); +extern PFRTCComparator(*platform_rtc_get_comp_manual_intr)(void); +extern void (*platform_rtc_clear_comp_intr_status)(PFRTCComparator); +extern void (*platform_rtc_clear_comp_wk_status)(PFRTCComparator); + +/** @} */ /* End of group PLATFORM_RTC_Exported_Functions */ + +#ifdef __cplusplus +} +#endif + +#endif /* __PLATFORM_RTC_H */ diff --git a/bee/rtl87x2g/boot/inc/power_manager_slave.h b/bee/rtl87x2g/boot/inc/power_manager_slave.h new file mode 100644 index 00000000..f2484d09 --- /dev/null +++ b/bee/rtl87x2g/boot/inc/power_manager_slave.h @@ -0,0 +1,207 @@ +/** +***************************************************************************************** +* Copyright(c) 2022, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file power_manager_slave.h + * @brief Power Manager Slave implementation head file. + * @author George Chang, js.lin + * @date 2022-08-29 + * @version v1.0 + * ************************************************************************************* + */ + +/*============================================================================* + * Define to prevent recursive inclusion + *============================================================================*/ +#ifndef __POWER_MANAGER_SLAVE_H +#define __POWER_MANAGER_SLAVE_H + + +/*============================================================================* + * Header Files +*============================================================================*/ +#include +#include +#include "power_manager_interface.h" +#include "platform_rtc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IRQ_TAG_FILL_WORD ( 0x5a5a5a5aU ) + +#define POWER_MANAGER_GET_TIMESTAMP(stamp) \ + do { \ + stamp = platform_rtc_get_counter(); \ + } while(0) + +#define POWER_MANAGER_GET_TIMEDIFF(start, end, diff) \ + do { \ + diff = clk_cnt_diff(start, end, PF_RTC_BIT_NUM); \ + } while(0) + +#define POWER_MANAGER_UPDATE_EXTREMUM(diff, diff_max, diff_min) \ + do { \ + diff_max = diff > diff_max ? diff : diff_max; \ + diff_min = diff < diff_min ? diff : diff_min; \ + } while(0) + +#define PM_SYSTEM_LEVEL_UNIT_NUM 2 + +/*============================================================================* + * Types +*============================================================================*/ +/** @defgroup POWER_MANAGER_Exported_Types Power Manager Exported Types + * @{ + */ +typedef PMCheckResult(*PMUnitCheckFunc)(uint32_t, uint32_t, uint32_t *); +typedef void (*PMUnitStoreFunc)(void); +typedef void (*PMUnitRestoreFunc)(void); +typedef void (*PMUnitEnterFunc)(void); +typedef void (*PMUnitExitFunc)(void); + +typedef PMCheckResult(*PMCheckWakeupTimeFunc)(uint32_t *); +typedef void (*PMFuncToReturn)(void); + +typedef enum +{ + PROFILING_PM_SLAVE_CHECK = 0, + PROFILING_PM_SLAVE_STORE = 1, + PROFILING_PM_SLAVE_ENTER = 2, + PROFILING_PM_SLAVE_EXIT = 3, + PROFILING_PM_SLAVE_RESTORE = 4, + PROFILING_PM_SLAVE_STAGE_MAX = 5 +} ProfilingPMSlaveStage; + +typedef struct +{ + uint32_t stage_start; + uint32_t stage_end; + uint32_t stage_time[PROFILING_PM_SLAVE_STAGE_MAX]; + uint32_t stage_time_max[PROFILING_PM_SLAVE_STAGE_MAX]; + uint32_t stage_time_min[PROFILING_PM_SLAVE_STAGE_MAX]; +} ProfilingPMSlaveData; + +typedef struct +{ + PMUnitCheckFunc check_func; + PMUnitStoreFunc store_func; + PMUnitRestoreFunc restore_func; + PMUnitEnterFunc enter_func; + PMUnitExitFunc exit_func; + + PMUnitID unit_id; + + ProfilingPMSlaveData *profiling_data; +} PowerManagerSlaveUnit; + +typedef struct +{ + PMUnitID unit_id; + uint32_t pon_delay_time; + uint32_t pon_stage_time; + uint32_t pof_delay_time; + uint32_t pof_stage_time; + uint32_t minimum_sleep_guard_time; +} PowerManagerSlaveRequestResource; + +typedef struct +{ + uint32_t start_time_diff; + uint32_t end_time_diff; +} PMTimePeriod; + +typedef struct +{ + uint32_t cur_clk; + PMTimePeriod occupied; +} PMOccupyTimeInfo; + +typedef struct +{ + PowerManagerSlaveUnit *unit_array[PM_SYSTEM_LEVEL_DEF_MAX][PM_SYSTEM_LEVEL_UNIT_NUM]; + + PMOccupyTimeInfo slave[PM_SYSTEM_LEVEL_DEF_MAX][PM_SYSTEM_LEVEL_UNIT_NUM]; + + uint32_t suspended; + + PMFuncToReturn func_to_return; +} PowerManagerSlaveSystem; + +typedef union +{ + uint8_t value; + struct + { + uint8_t profiling_mode: 1; + uint8_t irq_control_by_master: 1; + uint8_t m2s_inact_msg_dbg: 1; + uint8_t s2m_inact_msg_dbg: 1; + uint8_t m2s_act_msg_dbg: 1; + uint8_t s2m_act_msg_dbg: 1; + uint8_t rsvd: 2; + }; +} PowerManagerSlaveFeatureConfig; + +/*============================================================================* + * Variables +*============================================================================*/ +/** @defgroup POWER_MANAGER_Exported_Variables Power Manager Exported Variables + * @{ + */ +extern PowerManagerSlaveFeatureConfig power_manager_slave_feature_cfg; +extern PowerManagerSlaveSystem power_manager_slave_system; + +extern bool (*power_manager_slave_check)(const PMM2SInactivateMessage *); +extern void (*power_manager_slave_store_and_enter)(const PMM2SInactivateMessage *); +extern void (*power_manager_slave_exit_and_restore)(const PMM2SActivateMessage *); +extern void (*power_manager_slave_cpu_sleep)(bool); +extern bool (*power_manager_slave_check_interrupt_occur)(void); +extern void (*power_manager_slave_handle_irq)(bool, bool *); +extern void (*power_manager_slave_init)(void); + +extern uint32_t (*power_manager_slave_align_clk)(const uint32_t, uint32_t *, uint32_t *, uint32_t); +extern PMCheckResult(*power_manager_slave_align_wakeup_time)(PMCheckWakeupTimeFunc, const uint32_t, + uint32_t *); +extern void (*power_manager_slave_set_occupied_time_descending)(const uint32_t, + const PMOccupyTimeInfo *, PMTimePeriod *, uint8_t); +extern bool (*power_manager_slave_check_occupied_time)(const PMTimePeriod *, const uint8_t, + uint32_t *, uint32_t, uint32_t); +extern void (*power_manager_slave_register_resource)(PMUnitID, uint32_t, uint32_t, uint32_t); +extern void (*power_manager_slave_unregister_resource)(PMUnitID); +extern bool (*power_manager_slave_request_resource)(const uint32_t, uint32_t *, + PowerManagerSlaveRequestResource *); + +extern bool (*power_manager_slave_inact_msg_handler)(const PMM2SInactivateMessage *, bool *); +extern void (*power_manager_slave_act_msg_handler)(const PMM2SActivateMessage *); + +extern void (*power_manager_slave_inact_action_handler)(void); +extern void (*power_manager_slave_act_action_handler)(void); +/** @} */ /* End of group POWER_MANAGER_Exported_Variables */ + +/*============================================================================* + * Functions +*============================================================================*/ + +/** @defgroup POWER_MANAGER_Exported_Functions Power Manager Exported Functions + * @{ + */ +void power_manager_slave_register_function_to_return(PMFuncToReturn func); +void power_manager_slave_register_unit(PMUnitID unit_id, PowerManagerSlaveUnit *p_unit); +PowerManagerSlaveUnit *power_manager_slave_get_unit(PMUnitID unit_id); +void power_manager_slave_suspend_unit(PMUnitID unit_id); +void power_manager_slave_resume_unit(PMUnitID unit_id); +void power_manager_slave_suspend_all(void); +void power_manager_slave_resume_all(void); + +/** @} */ /* End of group POWER_MANAGER_Exported_Functions */ + +/** @} */ /* End of group POWER_MANAGER */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __POWER_MANAGER_SLAVE_H */