THB2/bthome_phy6222/SDK/lib/ble_controller/ll.c
2024-02-10 06:10:35 +03:00

8874 lines
318 KiB
C
Raw Blame History

/*******************************************************************************
Filename: ll.c, updated base on ll.h
Revised:
Revision:
Description: This file contains the Link Layer (LL) API for the Bluetooth
Low Energy (BLE) Controller. It provides the defines, types,
and functions for all supported Bluetooth Low Energy (BLE)
commands.
This API is based on the Bluetooth Core Specification,
V4.0.0, Vol. 6.
SDK_LICENSE
*******************************************************************************/
//#define DEBUG_LL
/*******************************************************************************
INCLUDES
*/
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "ll_buf.h"
#include "ll.h"
#include "ll_def.h"
#include "ll_common.h"
#include "ll_hw_drv.h"
#include "OSAL.h"
#include "OSAL_PwrMgr.h"
#include "osal_bufmgr.h"
#include "bus_dev.h"
#include "jump_function.h"
#include "global_config.h"
#include "ll_debug.h"
#include "ll_enc.h"
#include "rf_phy_driver.h"
#include "time.h"
//#define OWN_PUBLIC_ADDR_POS 0x11004000
extern void clear_timer(AP_TIM_TypeDef* TIMx);
extern uint32_t get_timer_count(AP_TIM_TypeDef* TIMx);
/*******************************************************************************
MACROS
*/
#define LL_COPY_DEV_ADDR_LE( dstPtr, srcPtr ) { \
(dstPtr)[0] = (srcPtr)[0]; \
(dstPtr)[1] = (srcPtr)[1]; \
(dstPtr)[2] = (srcPtr)[2]; \
(dstPtr)[3] = (srcPtr)[3]; \
(dstPtr)[4] = (srcPtr)[4]; \
(dstPtr)[5] = (srcPtr)[5];}
// ALT: COULD USE OSAL COPY.
// osal_memcpy( (dstPtr), (srcPtr), LL_DEVICE_ADDR_LEN );
#define LL_COPY_DEV_ADDR_BE( dstPtr, srcPtr ) { \
(dstPtr)[0] = (srcPtr)[5]; \
(dstPtr)[1] = (srcPtr)[4]; \
(dstPtr)[2] = (srcPtr)[3]; \
(dstPtr)[3] = (srcPtr)[2]; \
(dstPtr)[4] = (srcPtr)[1]; \
(dstPtr)[5] = (srcPtr)[0];}
// ALT: COULD USE OSAL COPY.
// osal_memcpy( (dstPtr), (srcPtr), LL_DEVICE_ADDR_LEN );
#define BDADDR_VALID( bdAddr ) \
( !( \
((bdAddr)[0] == 0xFF) && \
((bdAddr)[1] == 0xFF) && \
((bdAddr)[2] == 0xFF) && \
((bdAddr)[3] == 0xFF) && \
((bdAddr)[4] == 0xFF) && \
((bdAddr)[5] == 0xFF) \
) \
)
// See "Supported States Related" below.
#define LL_SET_SUPPORTED_STATES( state ) \
states[ (state)>>4 ] |= (1<<((state) & 0x0F));
/*******************************************************************************
CONSTANTS
*/
// Bluetooth Version Information
#define LL_VERSION_NUM 0x09 // BT Core Specification V5.0.0, refer to https://www.bluetooth.com/specifications/assigned-numbers/host-controller-interface
#define LL_COMPANY_ID 0x0504 // Phyplus
// Major Version (8 bits) . Minor Version (4 bits) . SubMinor Version (4 bits)
#define LL_SUBVERSION_NUM 0x0208 // Controller v1.0.0. Definition not found in BLE spec
// Connection Window Information
#define LL_WINDOW_SIZE 2 // 2.5ms in 1.25ms ticks
#define LL_WINDOW_OFFSET 0 // 1.25ms + 0
/*******************************************************************************
TYPEDEFS
*/
/*******************************************************************************
LOCAL VARIABLES
*/
/*
** Own Device Address
**
** Note that the device may have a permanently assigned BLE address located in
** the Information Page. However, this address could be overridden with either
** the build flag BDADDR_FROM_FLASH, or by the vendor specific command
** LL_EXT_SET_BDADDR.
*/
// Own Device Public Address
uint8 ownPublicAddr[ LL_DEVICE_ADDR_LEN ]; // index 0..5 is LSO..MSB
// Own Device Random Address
uint8 ownRandomAddr[ LL_DEVICE_ADDR_LEN ]; // index 0..5 is LSO..MSB
// Delay Sleep
static uint16 sleepDelay; // delay sleep for XOSC stabilization upon reset
syncInfo_t syncInfo;
scannerSyncInfo_t scanSyncInfo;
extern uint32 llWaitingIrq;
extern struct buf_rx_desc g_rx_adv_buf;
extern uint8 g_currentLocalRpa[LL_DEVICE_ADDR_LEN];
extern uint8 g_currentPeerRpa[LL_DEVICE_ADDR_LEN];
extern uint8 g_currentPeerAddrType;
extern uint8 g_currentLocalAddrType;
/*******************************************************************************
GLOBAL VARIABLES
*/
advInfo_t adv_param; // Advertiser info
// add for scanner
scanInfo_t scanInfo; // Scanner info
// add for initiator 2018-05-09
initInfo_t initInfo; // Initiator info
extScanInfo_t extScanInfo; // extended Scanner info
extInitInfo_t extInitInfo; // extended initiator info
// BBB new: the memory of LL connect context is allocated by APP
llConnState_t* conn_param = NULL;
uint8 g_maxConnNum = 0;
uint8 g_maxPktPerEventTx = TYP_CONN_BUF_LEN;
uint8 g_maxPktPerEventRx = TYP_CONN_BUF_LEN;
uint8 g_blePktVersion = BLE_PKT_VERSION_4_0;
//move to llConnStat_t
//preChanMapUpdate_t preChanMapUpdate[MAX_NUM_LL_CONN];
llPeriodicScannerInfo_t g_llPeriodAdvSyncInfo[MAX_NUM_LL_PRD_ADV_SYNC];
// A2 multi-connection
llConns_t g_ll_conn_ctx;
uint8 LL_TaskID; // OSAL LL task ID
uint8_t llState; // state of LL ==> to move to per connection context ???
peerInfo_t peerInfo; // peer device's address and address type ==> to move to per connection context???
chanMap_t chanMapUpdate; // channel map for updates
featureSet_t deviceFeatureSet; // feature set for this device
verInfo_t verInfo; // own version information
//llConns_t llConns; // LL connections table
uint8 numComplPkts; // number of completed Tx buffers, use global beacuse we report HCI event when numComplPkts >= numComplPktsLimit
uint8 numComplPktsLimit; // minimum number of completed Tx buffers before event
rfCounters_t rfCounters; // counters for LL RX/TX atomic operation in one connection event
uint8 fastTxRespTime; // flag indicates if fast TX response time feature is enabled/disabled
// ======= add by HZF, for whitelist
peerInfo_t g_llWhitelist[LL_WHITELIST_ENTRY_NUM]; // whitelist table
uint8 g_llWlDeviceNum; // current device number in whitelist list, should not exceed LL_WHITELIST_ENTRY_NUM
// ======= add by HZF, for resolving list
resolvingListInfo_t g_llResolvinglist[LL_RESOLVINGLIST_ENTRY_NUM]; // resolving list table, size to be consider
uint8 g_llRlDeviceNum; // current device number in resolving list, should not exceed LL_RESOLVINGLIST_ENTRY_NUM
uint8 g_llRlEnable = FALSE;
uint16 g_llRlTimeout = 900; // Unit: second
// ============== A1 ROM metal change add
uint32_t g_llHdcDirAdvTime; // for HDC direct adv
//==
//uint8 numComplPktsLimit; // minimum number of completed Tx buffers before event
//uint8 numComplPktsFlush; // flag to indicate send number of completed buffers at end of event
//uint8 fastTxRespTime; // flag indicates if fast TX response time feature is enabled/disabled
//==
// RX Flow Control
uint8 rxFifoFlowCtrl;
//llLinkBuf_t ll_buf;
llGlobalStatistics_t g_pmCounters; // TODO: to divide into per connection counters & global counters
// ===== A2 metal change add
llPduLenManagment_t g_llPduLen; //for dle feature ==> to move to per connection context
//llPhyModeManagment_t g_llPhyModeCtrl;// for phy update ==> to move to per connection context
uint8_t llSecondaryState; // secondary state of LL
// ===== A2 add End
extern l2capSARDbugCnt_t g_sarDbgCnt;
extern struct buf_tx_desc g_tx_adv_buf;
extern struct buf_tx_desc g_tx_ext_adv_buf;
extern struct buf_tx_desc tx_scanRsp_desc;
extern uint32 g_interAuxPduDuration;
// ============ extended advertisement variables
extAdvInfo_t* g_pExtendedAdvInfo = NULL; // extended adv information, note that periodic adv also need it
periodicAdvInfo_t* g_pPeriodicAdvInfo = NULL;
// TODO: add periodic adv set array
uint8 g_extAdvNumber; // number of ext adv set
uint8 g_perioAdvNumber; // number of periodic adv set
uint16 g_advSetMaximumLen; // maximum length of adv data
// extended adv scheduler context, move to a structure?
llAdvScheduleInfo_t* g_pAdvSchInfo;
uint8 g_schExtAdvNum; // current schedule extended adv number
uint8 g_currentExtAdv; // current schedule extended adv index
uint32 g_advPerSlotTick; // us
uint32 g_advSlotPeriodic; // us
uint32 g_currentAdvTimer; // us
// ==== periodic adv scheduler context
llPeriodicAdvScheduleInfo_t* g_pAdvSchInfo_periodic; // periodic adv scheduler info
uint8 g_schExtAdvNum_periodic; // current scheduler periodic adv number
uint8 g_currentExtAdv_periodic; // current scheduler periodic adv index
//
uint8 g_currentTimerTask; // scan or adv
uint32 g_timerExpiryTick; // us
// 2020-02-15 add for connectionless IQ Sample buffer
uint16* g_pLLcteISample=NULL;
uint16* g_pLLcteQSample=NULL;
// according to BQB test case HCI/GEV/BV-02-C & HCI/GEV/BV-03-C,
// mixed legacy advertisement and extended advertisement is disallowed
// mixed legacy scan and extended scan is disallowed
#define LL_MODE_INVALID 0xFF
#define LL_MODE_LEGACY 0x00
#define LL_MODE_EXTENDED 0x01
uint8 g_llScanMode = LL_MODE_INVALID;
uint8 g_llAdvMode = LL_MODE_INVALID;
// RF path compensation
extern int16 g_rfTxPathCompensation, g_rfRxPathCompensation;
// periodic advertiser device list
periodicAdvertiserListInfo_t g_llPeriodicAdvlist[LL_PRD_ADV_ENTRY_NUM];
uint8 g_llPrdAdvDeviceNum; // current periodic advertiser device number
// LOCAL FUNCTIONS
void llPrdAdvDecideNextChn(extAdvInfo_t* pAdvInfo, periodicAdvInfo_t* pPrdAdv);
void llSetupSyncInfo(extAdvInfo_t* pAdvInfo, periodicAdvInfo_t* pPrdAdv);
/*******************************************************************************
@fn LL_Init0
@brief This is the Link Layer task initialization called by OSAL. It
must be called once when the software system is started and
before any other function in the LL API is called.
input parameters
@param taskId - Task identifier assigned by OSAL.
output parameters
@param None.
@return None.
*/
void LL_Init0( uint8 taskId )
{
#ifndef HOST
uint8* p;
#endif
LL_TaskID = taskId;
// set BLE version information
verInfo.verNum = LL_VERSION_NUM;
verInfo.comId = LL_COMPANY_ID;
verInfo.subverNum = LL_SUBVERSION_NUM;
// =========== calculate whiten seed
calculate_whiten_seed(); // must set!!!
#ifdef HOST
// hardcoded address
ownPublicAddr[0] = 0xF3;
ownPublicAddr[1] = 0xE4;
ownPublicAddr[2] = 0xa2;
ownPublicAddr[3] = 0x98;
ownPublicAddr[4] = 0x58;
ownPublicAddr[5] = 0xdf;
#else
// read flash driectly becasue HW has do the address mapping for read Flash operation
p = (uint8*)pGlobal_config[MAC_ADDRESS_LOC];
ownPublicAddr[3] = *(p++);
ownPublicAddr[2] = *(p++);
ownPublicAddr[1] = *(p++);
ownPublicAddr[0] = *(p++);
ownPublicAddr[5] = *(p++);
ownPublicAddr[4] = *(p);
// ownPublicAddr[3] = ReadFlash(address ++);
// ownPublicAddr[2] = ReadFlash(address ++);
// ownPublicAddr[1] = ReadFlash(address ++);
// ownPublicAddr[0] = ReadFlash(address ++);
//
// ownPublicAddr[5] = ReadFlash(address ++);
// ownPublicAddr[4] = ReadFlash(address);
#endif
// set own random address as invalid until one is provided
ownRandomAddr[0] = 0xFF;
ownRandomAddr[1] = 0xFF;
ownRandomAddr[2] = 0xFF;
ownRandomAddr[3] = 0xFF;
ownRandomAddr[4] = 0xFF;
ownRandomAddr[5] = 0xFF;
adv_param.ownAddr[0] = 0xFF;
adv_param.ownAddr[1] = 0xFF;
adv_param.ownAddr[2] = 0xFF;
adv_param.ownAddr[3] = 0xFF;
adv_param.ownAddr[4] = 0xFF;
adv_param.ownAddr[5] = 0xFF;
adv_param.ownAddrType = LL_DEV_ADDR_TYPE_PUBLIC;
adv_param.advMode = LL_ADV_MODE_OFF;
adv_param.advInterval = LL_ADV_INTERVAL_DEFAULT;
adv_param.advEvtType = LL_ADV_NONCONNECTABLE_UNDIRECTED_EVT;
adv_param.advChanMap = LL_ADV_CHAN_MAP_DEFAULT; // correct by HZF, 2-23
adv_param.wlPolicy = LL_ADV_WL_POLICY_ANY_REQ;
adv_param.scaValue = LL_SCA_SLAVE_DEFAULT;
adv_param.advDataLen = 0;
adv_param.scanRspLen = 0;
for (int i = 0; i < g_maxConnNum; i++)
{
conn_param[i].connId = i;
conn_param[i].active = FALSE;
conn_param[i].allocConn = FALSE;
memset((uint8_t*)&conn_param[i].pmCounter, 0, sizeof(llLinkStatistics_t) );
}
// set default Scan values
scanInfo.ownAddr[0] = 0xFF;
scanInfo.ownAddr[1] = 0xFF;
scanInfo.ownAddr[2] = 0xFF;
scanInfo.ownAddr[3] = 0xFF;
scanInfo.ownAddr[4] = 0xFF;
scanInfo.ownAddr[5] = 0xFF;
scanInfo.ownAddrType = LL_DEV_ADDR_TYPE_PUBLIC;
scanInfo.initPending = TRUE;
scanInfo.scanMode = LL_SCAN_STOP;
scanInfo.scanType = LL_SCAN_PASSIVE;
scanInfo.scanInterval = LL_SCAN_INTERVAL_DEFAULT;
scanInfo.scanWindow = LL_SCAN_INTERVAL_DEFAULT;
scanInfo.wlPolicy = LL_SCAN_WL_POLICY_ANY_ADV_PKTS;
scanInfo.filterReports = TRUE;
scanInfo.scanBackoffUL = 1;
scanInfo.nextScanChan = LL_SCAN_ADV_CHAN_37;
scanInfo.numSuccess = 0;
scanInfo.numFailure = 0;
// set default Init values
initInfo.ownAddr[0] = 0xFF;
initInfo.ownAddr[1] = 0xFF;
initInfo.ownAddr[2] = 0xFF;
initInfo.ownAddr[3] = 0xFF;
initInfo.ownAddr[4] = 0xFF;
initInfo.ownAddr[5] = 0xFF;
initInfo.ownAddrType = LL_DEV_ADDR_TYPE_PUBLIC;
initInfo.initPending = TRUE;
initInfo.scanMode = LL_SCAN_STOP;
initInfo.scanInterval = LL_SCAN_INTERVAL_DEFAULT;
initInfo.scanWindow = LL_SCAN_INTERVAL_DEFAULT;
initInfo.nextScanChan = LL_SCAN_ADV_CHAN_37;
initInfo.wlPolicy = LL_INIT_WL_POLICY_USE_PEER_ADDR;
initInfo.connId = 0;
initInfo.scaValue = LL_SCA_MASTER_DEFAULT;
// set default extended Init values
extInitInfo.ownAddr[0] = 0xFF;
extInitInfo.ownAddr[1] = 0xFF;
extInitInfo.ownAddr[2] = 0xFF;
extInitInfo.ownAddr[3] = 0xFF;
extInitInfo.ownAddr[4] = 0xFF;
extInitInfo.ownAddr[5] = 0xFF;
extInitInfo.ownAddrType = LL_DEV_ADDR_TYPE_PUBLIC;
// extInitInfo.initPending = TRUE;
extInitInfo.scanMode = LL_SCAN_STOP;
for (int i = 0; i < LL_MAX_EXTENDED_SCAN_PHYS; i ++) // bug correct 2020-03-09
{
extInitInfo.scanInterval[i] = LL_SCAN_INTERVAL_DEFAULT;
extInitInfo.scanWindow[i] = LL_SCAN_INTERVAL_DEFAULT;
}
extInitInfo.wlPolicy = LL_INIT_WL_POLICY_USE_PEER_ADDR;
extInitInfo.connId = 0;
extInitInfo.scaValue = LL_SCA_MASTER_DEFAULT;
extInitInfo.current_chn = LL_SCAN_ADV_CHAN_37;
scanSyncInfo.valid = FALSE;
// reset the Link Layer
(void)LL_Reset();
// generate true random number for AES-CCM
(void)LL_ENC_GenerateTrueRandNum( cachedTRNGdata, LL_ENC_TRUE_RAND_BUF_SIZE );
numComplPkts = 0;
numComplPktsLimit = 1;
// add by HZF, init globals
fastTxRespTime = LL_EXT_DISABLE_FAST_TX_RESP_TIME; // what is fast TX feature???
llState = LL_STATE_IDLE;
// add in A2
llSecondaryState = LL_SEC_STATE_IDLE;
// initialize this devices Feature Set
// has been called in LL_Reset() , so comended by ZQ
//llInitFeatureSet();
(void)osal_pwrmgr_task_state( LL_TaskID, PWRMGR_CONSERVE );
// default sleep delay
sleepDelay = pGlobal_config[MIN_TIME_TO_STABLE_32KHZ_XOSC];
// delay sleep to allow the 32kHz crystal to stablize
osal_set_event( LL_TaskID, LL_EVT_START_32KHZ_XOSC_DELAY );
}
/*******************************************************************************
@fn LL_ProcessEvent0
@brief This is the Link Layer process event handler called by OSAL.
input parameters
@param taskId - Task identifier assigned by OSAL.
events - Event flags to be processed by this task.
output parameters
@param None.
@return Unprocessed event flags.
*/
uint16 LL_ProcessEvent0( uint8 task_id, uint16 events )
{
(void) task_id;
if ( events & LL_EVT_NEXT_INTERVAL )
{
ll_debug_output(DEBUG_LL_TIMER_EXPIRY_ENTRY);
LL_evt_schedule();
ll_debug_output(DEBUG_LL_TIMER_EXPIRY_EXIT);
return (events ^ LL_EVT_NEXT_INTERVAL );
}
/*
** Directed Advertising results in a Master Connection
*/
if ( events & LL_EVT_MASTER_CONN_CREATED )
{
llConnState_t* connPtr;
connPtr = &conn_param[initInfo.connId];
// if own addr or peer addr using RPA, report enhance connection complete event
// otherwise report legacy connection complete event.
// note that it may not align to below words int the spec
// "If this event is unmasked and the HCI_LE_Connection_Complete event is
// unmasked, only the HCI_LE_Enhanced_Connection_Complete event is sent
// when a new connection has been created"
/*if (g_currentPeerAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC ||
g_currentPeerAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM ||
g_currentLocalAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC ||
g_currentLocalAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM)
{
uint8* local, *peer;
uint8 nil_addr[] = {0, 0, 0, 0, 0, 0};
if (g_currentPeerAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC ||
g_currentPeerAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM )
peer = g_currentPeerRpa;
else
peer = nil_addr;
if (g_currentLocalAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC ||
g_currentLocalAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM )
local = g_currentLocalRpa;
else
local = nil_addr;
LL_EnhConnectionCompleteCback( LL_STATUS_SUCCESS,
(uint16)connPtr->connId, // connection handle
LL_LINK_CONNECT_COMPLETE_MASTER, // role
g_currentPeerAddrType, // peer's address type
peerInfo.peerAddr, // peer's address
local,
peer,
connPtr->curParam.connInterval >> 1, // connection interval, back to 1.25ms units
connPtr->curParam.slaveLatency, // slave latency
connPtr->curParam.connTimeout >> 4, // connection timeout, back to 10ms units
0 ); // sleep clock accurracy not valid for master
}
else */
{
LL_ConnectionCompleteCback( LL_STATUS_SUCCESS, // reasonCode
(uint16)connPtr->connId, // connection handle
LL_LINK_CONNECT_COMPLETE_MASTER, // role
peerInfo.peerAddrType, // peer's address type
peerInfo.peerAddr, // peer's address
connPtr->curParam.connInterval >> 1, // connection interval, back to 1.25ms units
connPtr->curParam.slaveLatency, // slave latency
connPtr->curParam.connTimeout >> 4, // connection timeout, back to 10ms units
0 ); // sleep clock accurracy
}
//if (connPtr->channel_selection == LL_CHN_SEL_ALGORITHM_2)
// LL_ChannelSelectionAlgorithmCback((uint16)connPtr->connId, LL_CHN_SEL_ALGORITHM_2);
return (events ^ LL_EVT_MASTER_CONN_CREATED );
}
/*
** Create Connection Cancel
*/
if ( events & LL_EVT_MASTER_CONN_CANCELLED )
{
// notify Host
LL_ConnectionCompleteCback( LL_STATUS_ERROR_UNKNOWN_CONN_HANDLE, // reasonCode
(uint16)0, // connection handle
LL_LINK_CONNECT_COMPLETE_MASTER, // role
peerInfo.peerAddrType, // peer's address type
peerInfo.peerAddr, // peer's address
0, // connection interval, back to 1.25ms units
0, // slave latency
0, // connection timeout, back to 10ms units
0 ); // sleep clock accurracy not valid for master
return (events ^ LL_EVT_MASTER_CONN_CANCELLED );
}
/*
** Directed Advertising failed to connect
*/
if ( events & LL_EVT_DIRECTED_ADV_FAILED )
{
// notify Host
LL_ConnectionCompleteCback( LL_STATUS_ERROR_DIRECTED_ADV_TIMEOUT, // reasonCode
(uint16)0, // connection handle
LL_LINK_CONNECT_COMPLETE_SLAVE, // role
peerInfo.peerAddrType, // peer's address type
peerInfo.peerAddr, // peer's address
0, // connection interval, back to 1.25ms units
0, // slave latency
0, // connection timeout, back to 10ms units
0 ); // sleep clock accurracy
return (events ^ LL_EVT_DIRECTED_ADV_FAILED );
}
/*
** Directed Advertising results in a Slave Connection
*/
if ( events & LL_EVT_SLAVE_CONN_CREATED )
{
llConnState_t* connPtr;
connPtr = &conn_param[adv_param.connId];
// if own addr or peer addr using RPA, report enhance connection complete event
// otherwise report legacy connection complete event.
// note that it may not align to below words int the spec
// "If this event is unmasked and the HCI_LE_Connection_Complete event is
// unmasked, only the HCI_LE_Enhanced_Connection_Complete event is sent
// when a new connection has been created"
/*if (g_currentPeerAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC ||
g_currentPeerAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM ||
g_currentLocalAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC ||
g_currentLocalAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM)
{
uint8* local, *peer;
uint8 nil_addr[] = {0, 0, 0, 0, 0, 0};
if (g_currentPeerAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC ||
g_currentPeerAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM )
peer = g_currentPeerRpa;
else
peer = nil_addr;
if (g_currentLocalAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC ||
g_currentLocalAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM )
local = g_currentLocalRpa;
else
local = nil_addr;
LL_EnhConnectionCompleteCback( LL_STATUS_SUCCESS,
(uint16)connPtr->connId, // connection handle
LL_LINK_CONNECT_COMPLETE_SLAVE, // role
g_currentPeerAddrType, // peer's address type
peerInfo.peerAddr, // peer's address
local,
peer,
connPtr->curParam.connInterval >> 1, // connection interval, back to 1.25ms units
connPtr->curParam.slaveLatency, // slave latency
connPtr->curParam.connTimeout >> 4, // connection timeout, back to 10ms units
connPtr->sleepClkAccuracy ); // sleep clock accurracy
}
else*/
{
LL_ConnectionCompleteCback( LL_STATUS_SUCCESS, // reasonCode
(uint16)connPtr->connId, // connection handle
LL_LINK_CONNECT_COMPLETE_SLAVE, // role
peerInfo.peerAddrType, // peer's address type
peerInfo.peerAddr, // peer's address
connPtr->curParam.connInterval >> 1, // connection interval, back to 1.25ms units
connPtr->curParam.slaveLatency, // slave latency
connPtr->curParam.connTimeout >> 4, // connection timeout, back to 10ms units
connPtr->sleepClkAccuracy ); // sleep clock accurracy
}
//if (connPtr->channel_selection == LL_CHN_SEL_ALGORITHM_2)
// LL_ChannelSelectionAlgorithmCback((uint16)connPtr->connId, LL_CHN_SEL_ALGORITHM_2);
return (events ^ LL_EVT_SLAVE_CONN_CREATED );
}
/*
** Advertising results in a Slave Connection with an Unacceptable
** Connection Interval
*/
if ( events & LL_EVT_SLAVE_CONN_CREATED_BAD_PARAM )
{
// notify Host
LL_ConnectionCompleteCback( LL_STATUS_ERROR_UNACCEPTABLE_CONN_INTERVAL, // reasonCode
(uint16)0, // connection handle
LL_LINK_CONNECT_COMPLETE_SLAVE, // role
peerInfo.peerAddrType, // peer's address type
peerInfo.peerAddr, // peer's address
0, // connection interval, back to 1.25ms units
0, // slave latency
0, // connection timeout, back to 10ms units
0 ); // sleep clock accurracy
return (events ^ LL_EVT_SLAVE_CONN_CREATED_BAD_PARAM );
}
/*
** Ensure the 32kHz crystal is stable after POR/External Reset/PM3
** before allowing sleep.
** Note: There is nothing in hardware to indicate this to software.
*/
if ( events & LL_EVT_START_32KHZ_XOSC_DELAY )
{
// check if the delay hasn't been disabled
if ( sleepDelay )
{
//osal_pwrmgr_device( PWRMGR_ALWAYS_ON );
(void)osal_pwrmgr_task_state( LL_TaskID, PWRMGR_HOLD );
osal_start_timerEx( LL_TaskID, LL_EVT_32KHZ_XOSC_DELAY, sleepDelay );
}
return (events ^ LL_EVT_START_32KHZ_XOSC_DELAY );
}
/*
** The minimum stabilization delay for the 32kHz crystal has expired.
*/
if ( events & LL_EVT_32KHZ_XOSC_DELAY )
{
// delay over, so allow sleep
//osal_pwrmgr_device( PWRMGR_BATTERY );
(void)osal_pwrmgr_task_state( LL_TaskID, PWRMGR_CONSERVE );
return (events ^ LL_EVT_32KHZ_XOSC_DELAY );
}
/*
** Issue Hard System Reset
*/
if ( events & LL_EVT_RESET_SYSTEM_HARD )
{
// Note: This will break communication with USB dongle.
// Note: No return here after reset.
//SystemReset();
// Note: Unreachable statement generates compiler warning!
//return (events ^ LL_EVT_RESET_SYSTEM_HARD );
}
/*
** Issue Soft System Reset
*/
if ( events & LL_EVT_RESET_SYSTEM_SOFT )
{
// Note: This will not break communication with USB dongle.
// Note: No return here after reset.
//SystemResetSoft();
return (events ^ LL_EVT_RESET_SYSTEM_SOFT );
}
//====== add in A2, for simultaneous connect & adv/scan
if (events & LL_EVT_SECONDARY_ADV)
{
#ifdef DEBUG_LL
LOG("--->\n");
#endif
if (llSecondaryState == LL_SEC_STATE_IDLE
|| llSecondaryState == LL_SEC_STATE_IDLE_PENDING) // adv may be cancel during waiting period, do nothing in this case
;
else
{
// advertise allow decision
if (llSecAdvAllow())
{
llSetupSecAdvEvt();
// if (llSetupSecAdvEvt())
// llSecondaryState = LL_SEC_STATE_ADV;
// else
// llSecondaryState = LL_SEC_STATE_ADV_PENDING;;
//#ifdef DEBUG_LL
// LOG("setup secondary adv event\r\n");
//#endif
}
else
{
llSecondaryState = LL_SEC_STATE_ADV_PENDING;
}
}
return (events ^ LL_EVT_SECONDARY_ADV );
}
// ===== scan
if (events & LL_EVT_SECONDARY_SCAN)
{
if (llSecondaryState == LL_SEC_STATE_IDLE || scanInfo.scanMode == LL_SCAN_STOP) // scan may be cancel during waiting period, do nothing in this case
llSecondaryState = LL_SEC_STATE_IDLE;
else
{
llSetupSecScan(scanInfo.nextScanChan);
}
return (events ^ LL_EVT_SECONDARY_SCAN );
}
// ======= add for A2 multi-conn, init
if (events & LL_EVT_SECONDARY_INIT)
{
if (llSecondaryState == LL_SEC_STATE_IDLE || initInfo.scanMode == LL_SCAN_STOP) // scan may be cancel during waiting period, do nothing in this case
llSecondaryState = LL_SEC_STATE_IDLE;
else
{
llSetupSecInit(initInfo.nextScanChan);
// TODO
}
return (events ^ LL_EVT_SECONDARY_INIT );
}
// ======= RPA re-calculate
if (events & LL_EVT_RPA_TIMEOUT)
{
uint8 resolve_address[6];
uint8* localIrk;
// calculate RPA & update adv_param
if (peerInfo.peerAddrType == LL_DEV_ADDR_TYPE_PUBLIC || peerInfo.peerAddrType == LL_DEV_ADDR_TYPE_RANDOM)
// if (g_currentLocalAddrType == LL_DEV_ADDR_TYPE_PUBLIC || g_currentLocalAddrType == LL_DEV_ADDR_TYPE_RANDOM)
{
// search the resolving list to get local IRK
if ((adv_param.ownAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC || adv_param.ownAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM)
&& (ll_readLocalIRK(&localIrk, peerInfo.peerAddr, peerInfo.peerAddrType) == TRUE))
{
if (!ll_isIrkAllZero(localIrk) &&
ll_CalcRandomAddr(localIrk, resolve_address) == SUCCESS)
{
LL_COPY_DEV_ADDR_LE( adv_param.ownAddr, resolve_address );
SET_BITS(g_tx_adv_buf.txheader, LL_DEV_ADDR_TYPE_RANDOM, TX_ADD_SHIFT, TX_ADD_MASK);
osal_memcpy( &g_tx_adv_buf.data[0], adv_param.ownAddr, 6);
SET_BITS(tx_scanRsp_desc.txheader, LL_DEV_ADDR_TYPE_RANDOM, TX_ADD_SHIFT, TX_ADD_MASK);
osal_memcpy( &tx_scanRsp_desc.data[0], adv_param.ownAddr, 6);
osal_memcpy( &g_currentLocalRpa[0], resolve_address, 6);
// for (int i = 0; i < 6; i ++)
// LOG("%x ", resolve_address[i]);
// LOG("\n");
}
}
// update initA(targetA) for direct ADV
if (adv_param.advEvtType == LL_ADV_CONNECTABLE_LDC_DIRECTED_EVT ||
adv_param.advEvtType == LL_ADV_CONNECTABLE_HDC_DIRECTED_EVT)
{
uint8* peerIrk;
// search the resolving list to get local IRK
if (ll_readPeerIRK(&peerIrk, peerInfo.peerAddr, peerInfo.peerAddrType) == TRUE)
{
if (!ll_isIrkAllZero(peerIrk)) // for all-zero local IRK, not RPA used
{
if (ll_CalcRandomAddr(peerIrk, resolve_address) == SUCCESS)
{
osal_memcpy((uint8_t*) &(g_tx_adv_buf.data[6]), resolve_address, 6);
osal_memcpy( &g_currentPeerRpa[0], resolve_address, 6);
}
}
}
}
}
else // if generate RPA failed, do nothing.
{
}
if (g_llRlDeviceNum > 0)
osal_start_timerEx( LL_TaskID, LL_EVT_RPA_TIMEOUT, g_llRlTimeout * 1000 );
return (events ^ LL_EVT_RPA_TIMEOUT );
}
return 0;
}
/*******************************************************************************
LL API for HCI
*/
/*******************************************************************************
@fn LL_TX_bm_alloc API
@brief This API is used to allocate memory using buffer management.
Note: This function should never be called by the application.
It is only used by HCI and L2CAP_bm_alloc.
input parameters
@param size - Number of bytes to allocate from the heap.
output parameters
@param None.
@return Pointer to buffer, or NULL.
*/
void* LL_TX_bm_alloc( uint16 size )
{
uint8* pBuf;
#if 0
// Note: This is the lowest call for TX buffer management allocation.
// provide padding for encryption
// Note: Potentially wastes up to 15 bytes per packet, but speeds up
// execution.
if ( size <= LL_ENC_BLOCK_LEN )
{
size = LL_ENC_BLOCK_LEN;
}
else
{
size = 2*LL_ENC_BLOCK_LEN;
}
#else
//size = (size+LL_ENC_BLOCK_LEN-1)&0xfff0;//align to 16Byte
#endif
size = (size+LL_ENC_BLOCK_LEN-1)&0xfff0;//align to 16Byte
pBuf = osal_bm_alloc( size +
sizeof(txData_t) +
LL_PKT_HDR_LEN +
LL_PKT_MIC_LEN );
if ( pBuf != NULL )
{
// return pointer to user payload
return( pBuf + ((uint16)sizeof(txData_t)+LL_PKT_HDR_LEN) );
}
return( (void*)NULL );
}
/*******************************************************************************
This API is used to allocate memory using buffer management.
Public function defined in ll.h.
*/
void* LL_RX_bm_alloc( uint16 size )
{
uint8* pBuf;
// Note: This is the lowest call for RX buffer management allocation.
pBuf = osal_bm_alloc( size + HCI_RX_PKT_HDR_SIZE );
if ( pBuf != NULL )
{
// return pointer to user payload
return( pBuf + HCI_RX_PKT_HDR_SIZE );
}
return( (void*)NULL );
}
/*******************************************************************************
This function is used by the HCI to reset and initialize the LL Controller.
Public function defined in ll.h.
*/
llStatus_t LL_Reset0( void )
{
// enter critical section
HAL_ENTER_CRITICAL_SECTION();
// clear the white list table
g_llWlDeviceNum = 0;
for (int i = 0; i < LL_WHITELIST_ENTRY_NUM; i++)
g_llWhitelist[i].peerAddrType = 0xff;
// clear resolving list
g_llRlDeviceNum = 0;
for (int i = 0; i < LL_RESOLVINGLIST_ENTRY_NUM; i++)
{
g_llResolvinglist[i].peerAddrType = 0xff;
g_llResolvinglist[i].privacyMode = NETWORK_PRIVACY_MODE;
}
g_llRlEnable = FALSE;
g_llRlTimeout = 900;
g_llPrdAdvDeviceNum = 0;
for (int i = 0; i < LL_PRD_ADV_ENTRY_NUM; i++)
g_llPeriodicAdvlist[i].addrType = 0xff;
// set the peer's device address type, as allowed for connections by Host
peerInfo.peerAddrType = LL_DEV_ADDR_TYPE_PUBLIC;
// clear the peer's address
peerInfo.peerAddr[0] = 0x00;
peerInfo.peerAddr[1] = 0x00;
peerInfo.peerAddr[2] = 0x00;
peerInfo.peerAddr[3] = 0x00;
peerInfo.peerAddr[4] = 0x00;
peerInfo.peerAddr[5] = 0x00;
// init the Adv parameters to their default values
adv_param.advMode = LL_ADV_MODE_OFF;
// clear the advertising interval
adv_param.advInterval = LL_ADV_INTERVAL_DEFAULT;// Ti set this parameter = 0;
// Note that only the first three bits are used, and note that despite the
// fact that the Host HCI call passes 5 bytes to specify which advertising
// channels are to be used, only the first three bits of the first byte are
// actually used. We'll save four bytes then, and default to all used.
adv_param.advChanMap = LL_ADV_CHAN_ALL;
// set a default type of advertising
adv_param.advEvtType = LL_ADV_NONCONNECTABLE_UNDIRECTED_EVT;
// payload length, including AdvA
adv_param.advDataLen = 0;
#ifdef DEBUG
// clear memory
(void)osal_memset( advInfo.advData, 0, LL_MAX_ADV_DATA_LEN );
// clear memory
(void)osal_memset( advInfo.scanRspData, 0, LL_MAX_SCAN_DATA_LEN );
#endif // DEBUG
// default to no scan response data
adv_param.scanRspLen = 0;
// ===================== for scan/init parameters
// disable scanning
scanInfo.scanMode = LL_SCAN_STOP;
// disable init scanning
initInfo.scanMode = LL_SCAN_STOP;
// initialize this devices Feature Set
llInitFeatureSet();
// initialize default channel map
chanMapUpdate.chanMap[0] = 0xFF;
chanMapUpdate.chanMap[1] = 0xFF;
chanMapUpdate.chanMap[2] = 0xFF;
chanMapUpdate.chanMap[3] = 0xFF;
chanMapUpdate.chanMap[4] = 0x1F;
// set state/role
llState = LL_STATE_IDLE;
ll_debug_output(DEBUG_LL_STATE_IDLE);
// add in A2
llSecondaryState = LL_SEC_STATE_IDLE;
numComplPkts = 0;
numComplPktsLimit = 1;
fastTxRespTime = LL_EXT_DISABLE_FAST_TX_RESP_TIME; // TI default enable it, hzf
rxFifoFlowCtrl = LL_RX_FLOW_CONTROL_DISABLED;
#if 0 // TODO: normally LL should not invoke upper layer function. And gap also invoke these operation???
//add by ZQ 20181030 for DLE feature
L2CAP_SegmentPkt_Reset();
L2CAP_ReassemblePkt_Reset();
osal_memset(&g_sarDbgCnt, 0, sizeof(g_sarDbgCnt));
#endif
llPduLengthManagmentReset();
llPhyModeCtrlReset();
for (int i = 0; i < g_maxConnNum; i ++)
{
conn_param[i].llPhyModeCtrl.def.txPhy = LE_1M_PHY | LE_2M_PHY;
conn_param[i].llPhyModeCtrl.def.rxPhy = LE_1M_PHY | LE_2M_PHY;
conn_param[i].llPhyModeCtrl.def.allPhy=0;
llResetConnId(i);
reset_conn_buf(i);
}
// HZF: add for multi-connection
g_ll_conn_ctx.currentConn = LL_INVALID_CONNECTION_ID;
g_ll_conn_ctx.numLLConns = 0;
g_ll_conn_ctx.numLLMasterConns = 0;
// =========== extended adv/extended scan relate reset
g_llScanMode = LL_MODE_INVALID;
g_llAdvMode = LL_MODE_INVALID;
// clear adv set (extended adv part)
for (int i = 0; i < g_extAdvNumber; i ++)
{
uint8* scanRspData = g_pExtendedAdvInfo[i].scanRspData;
uint8* advData = g_pExtendedAdvInfo[i].data.advertisingData;
memset(&g_pExtendedAdvInfo[i], 0, sizeof(extAdvInfo_t));
g_pExtendedAdvInfo[i].scanRspData = scanRspData;
g_pExtendedAdvInfo[i].data.advertisingData = advData;
g_pExtendedAdvInfo[i].advHandle = LL_INVALID_ADV_SET_HANDLE;
}
// clear adv set (periodic adv part)
for (int i = 0; i < g_perioAdvNumber; i ++)
{
uint8* advData = g_pPeriodicAdvInfo[i].data.advertisingData;
memset(&g_pPeriodicAdvInfo[i], 0, sizeof(periodicAdvInfo_t));
g_pPeriodicAdvInfo[i].data.advertisingData = advData;
g_pPeriodicAdvInfo[i].advHandle = LL_INVALID_ADV_SET_HANDLE;
}
// exit critical section
HAL_EXIT_CRITICAL_SECTION();
// reset all statistics counters
osal_memset(&g_pmCounters, 0, sizeof(g_pmCounters));
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_Disconnect0 API
@brief This API is called by the HCI to terminate a LL connection.
input parameters
@param connId - The LL connection ID on which to send this data.
@param reason - The reason for the Host connection termination.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_INACTIVE_CONNECTION
LL_STATUS_ERROR_CTRL_PROC_ALREADY_ACTIVE
*/
llStatus_t LL_Disconnect0( uint16 connId,
uint8 reason )
{
llStatus_t status;
llConnState_t* connPtr;
// check if the reason code is valid
if ( (reason != LL_DISCONNECT_AUTH_FAILURE) &&
(reason != LL_DISCONNECT_REMOTE_USER_TERM) &&
(reason != LL_DISCONNECT_REMOTE_DEV_LOW_RESOURCES) &&
(reason != LL_DISCONNECT_REMOTE_DEV_POWER_OFF) &&
(reason != LL_DISCONNECT_UNSUPPORTED_REMOTE_FEATURE) &&
(reason != LL_DISCONNECT_KEY_PAIRING_NOT_SUPPORTED) &&
(reason != LL_DISCONNECT_UNACCEPTABLE_CONN_INTERVAL) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// make sure connection ID is valid
if ( (status = LL_ConnActive(connId)) != LL_STATUS_SUCCESS )
{
return( status );
}
// get connection info
connPtr = &conn_param[connId];
// check if any control procedure is already pending
if ( connPtr->ctrlPktInfo.ctrlPktActive == TRUE )
{
// check if a terminate control procedure is already what's pending
if ( connPtr->ctrlPktInfo.ctrlPkts[0] == LL_CTRL_TERMINATE_IND )
{
return( LL_STATUS_ERROR_CTRL_PROC_ALREADY_ACTIVE );
}
else // spec now says a terminate can happen any time
{
// indicate the peer requested this termination
connPtr->termInfo.reason = reason;
// de-activate slave latency to expedite termination
connPtr->slaveLatency = 0;
// override any control procedure that may be in progress
llReplaceCtrlPkt( connPtr, LL_CTRL_TERMINATE_IND );
}
}
else // no control procedure currently active, so set this one up
{
// indicate the peer requested this termination
connPtr->termInfo.reason = reason;
// de-activate slave latency to expedite termination
connPtr->slaveLatency = 0;
// queue control packet for processing
llEnqueueCtrlPkt( connPtr, LL_CTRL_TERMINATE_IND );
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_TxData0 API
@brief This API is called by the HCI to transmit a buffer of data on a
given LL connection. If fragmentation is supported, the HCI must
also indicate whether this is the first Host packet, or a
continuation Host packet. When fragmentation is not supported,
then a start packet should always specified. If the device is in
a connection as a Master and the current connection ID is the
connection for this data, or is in a connection as a Slave, then
the data is written to the TX FIFO (even if the radio is
curerntly active). If this is a Slave connection, and Fast TX is
enabled and Slave Latency is being used, then the amount of time
to the next event is checked. If there's at least a connection
interval plus some overhead, then the next event is re-aligned
to the next event boundary. Otherwise, in all cases, the buffer
pointer will be retained for transmission, and the callback
event LL_TxDataCompleteCback will be generated to the HCI when
the buffer pointer is no longer needed by the LL.
Note: If the return status is LL_STATUS_ERROR_OUT_OF_TX_MEM,
then the HCI must not release the buffer until it receives
the LL_TxDataCompleteCback callback, which indicates the
LL has copied the transmit buffer.
Note: The HCI should not call this routine if a buffer is still
pending from a previous call. This is fatal!
Note: If the connection should be terminated within the LL
before the Host knows, attempts by the HCI to send more
data (after receiving a LL_TxDataCompleteCback) will
fail (LL_STATUS_ERROR_INACTIVE_CONNECTION).
input parameters
@param connId - The LL connection ID on which to send this data.
@param *pBuf - A pointer to the data buffer to transmit.
@param pktLen - The number of bytes to transmit on this connection.
@param fragFlag - LL_DATA_FIRST_PKT_HOST_TO_CTRL:
Indicates buffer is the start of a
Host-to-Controller packet.
LL_DATA_CONTINUATION_PKT:
Indicates buffer is a continuation of a
Host-to-Controller packet.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_INACTIVE_CONNECTION,
LL_STATUS_ERROR_OUT_OF_TX_MEM,
LL_STATUS_ERROR_UNEXPECTED_PARAMETER
*/
llStatus_t LL_TxData0( uint16 connId,
uint8* pBuf,
uint8 pktLen,
uint8 fragFlag )
{
llConnState_t* connPtr;
txData_t* pTxData;
// get the connection info based on the connection ID
connPtr = &conn_param[connId];
// sanity check input parameters
if ( (pBuf == NULL) || (pktLen > connPtr->llPduLen.local.MaxTxOctets/*LL_MAX_LINK_DATA_LEN*/) ||
((fragFlag != LL_DATA_FIRST_PKT_HOST_TO_CTRL) &&
(fragFlag != LL_DATA_CONTINUATION_PKT)) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// make sure connection ID is valid
if ( !conn_param [connId].active )
{
//return( status );
return( LL_STATUS_ERROR_INACTIVE_CONNECTION );
}
// check if the number of available data buffers has been exceeded
if ( getTxBufferFree(connPtr) == 0)
{
return( LL_STATUS_ERROR_OUT_OF_TX_MEM );
}
// adjust pointer to start of packet (i.e. at the header)
pBuf -= LL_PKT_HDR_LEN;
// set the packet length field
// Note: The LLID and Length fields are swapped for the nR (for DMA).
pBuf[0] = pktLen;
// set LLID fragmentation flag in header
// Note: NESN=SN=MD=0 and is handled by RF.
pBuf[1] = (fragFlag==LL_DATA_FIRST_PKT_HOST_TO_CTRL) ?
LL_DATA_PDU_HDR_LLID_DATA_PKT_FIRST : // first pkt
LL_DATA_PDU_HDR_LLID_DATA_PKT_NEXT; // continuation pkt
// ALT: Place check if packet needs to be encrypted and encryption here,
// but be careful about control packets and getting them out of order!
// point to Tx data entry
pTxData = (txData_t*)(pBuf - sizeof(txData_t));
// it does, so queue up this data
llEnqueueDataQ( &connPtr->txDataQ, pTxData );
// check that we are either the master or slave, and if so, that the current
// connection is connId
if ( !(((llState == LL_STATE_CONN_MASTER) || (llState == LL_STATE_CONN_SLAVE))))
//(connId == g_ll_conn_ctx.currentConn)) )
{
// either we are not the master or the slave, or the we are but the connId
// is not the current connection, so just return success as the data is
// queued on the connection
return( LL_STATUS_SUCCESS );
}
// copy any pending data to the TX FIFO
llProcessTxData( connPtr, LL_TX_DATA_CONTEXT_SEND_DATA );
// check if TX FIFO has anything in it
if (getTxBufferSize(connPtr) > 0 )
{
// check if we are a master or slave (i.e. in a connection), and the current
// current connection is connId; check if fast TX is enabled; check if
// slave latency is in use
if ( (fastTxRespTime == LL_EXT_ENABLE_FAST_TX_RESP_TIME) && connPtr->slaveLatency )
{
// <20><><EFBFBD><EFBFBD>Ŀǰ<C4BF><C7B0>֧<EFBFBD><D6A7>fast Tx resp time<6D><65><EFBFBD>feature<72><65><EFBFBD><EFBFBD><EFBFBD>Ҫ֧<D2AA><D6A7><EFBFBD><EFBFBD><EFBFBD>µĴ<C2B5><C4B4><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>޸<EFBFBD>
#if 0
HAL_ENTER_CRITICAL_SECTION(cs);
// taskInfo_t *curTask = llGetCurrentTask();
// uint32 curTime = llGetCurrentTime();
// check if there's enough time before the next event to readjust the
// event timing
// Note: If T2E1 > CT+1, then there is at least two ticks before T2E1.
// Note: TRUE means first parameter T2E1 is not <= second parameter CT,
// thus T2E1 > CT.
if ( llTimeCompare( conn_param [0].next_event_base_time, conn_param [0].next_event_fine_time ) == TRUE )
{
// There is, and at this point, there could be events between now
// and the next connection event. In order to wake on the next
// event boundary, the number of elapsed events is found.
// update current time, and adjust for one event plus some pad
// Note: If we are just before the next event, then there's no
// point to an event re-alignment.
// curTime += connPtr->curParam.connInterval +
// LL_FAST_TX_TICKS_TO_EVT_PAD;
// check if there's enough time before the next event
// Note: If T2E1 > CT+CI+6, then there is at least six ticks
// before the N-1 event.
// Note: TRUE means first parameter T2E1 is not <= second
// parameter CT, thus T2E1 > CT.
if (llTimeCompare( conn_param [0].next_event_base_time, conn_param [0].next_event_fine_time ))
{
// yes, so it is necessary to re-align on an earlier event
// then the next event
uint16 numEventsPast;
uint32 time;
// get the time delta between current time (CT) and last T2E1
// Note: The assumption here is that CT is always ahead of the
// lastT2e1 because T2E1 is always ahead of lastT2e1 and
// CT is at this point behind T2E1. If the call to this
// function happens during a radio event (assuming the
// MCU is not halted) or between the end of a radio
// event and the start of LL processing, then CT will
// still be greater than T2E1 (because T2E1 has not yet
// been updated). If the call is made after LL
// processing, then T2E1 will be updated to the next
// radio event (i.e. ahead of CT), and lastT2e1 will be
// the previous radio event (i.e. behind CT). So if CT
// is behind T2E1, then it is always ahead of lastT2e1.
// Note: The only time CT could be equal to lastT2e1 is
// if radio event ended in less than one software tick
// and this routine is called before the rollover. This
// should never happen given the current radio time and
// post-processing time, but even if it does, this code
// should still be fine as the delta would be zero.
time = calculateTimeDelta(conn_param[0].next_event_base_time, conn_param [0].next_event_fine_time );
// determine the number of events past the last T2E1
// Note: Only quotient in upper halfword needed.
// time = llDivide31By16To16( time, connPtr->curParam.connInterval );
numEventsPast = time / (conn_param [0].curParam.connInterval * 625)+1;
// update next event counter
connPtr->currentEvent = connPtr->lastCurrentEvent;
connPtr->nextEvent = connPtr->currentEvent + numEventsPast;
// update time to next event based on last adjusted AP
// Note: No need to re-calculate the receive window; can
// re-use rxTimeout value previously set by Slave
// post-processing.
// curTask->t2e1.coarse =
// (curTask->lastT2e1.coarse +
// ((uint32)numEventsPast *
// (uint32)connPtr->curParam.connInterval)) & 0x00FFFFFF;
// adjust data channel
connPtr->nextChan = connPtr->currentChan;
llSetNextDataChan( connPtr );
// enable RF event
// llScheduleTask( curTask );
} // else amount of time to next event is less than CI+6
} // else not enough time before next event to make realignment worthwhile
HAL_EXIT_CRITICAL_SECTION();
#endif
} // else delta correction active so queue packet
// M/S, curConn==connID, fast Tx enabled, and SL in use
} // TX FIFO empty
// indicate the packet is sent of buffered
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_SetAdvParam0 API
@brief This API is called by the HCI to set the Advertiser's
parameters.
input parameters
@param advIntervalMin - The minimum Adv interval.
@param advIntervalMax - The maximum Adv interval.
@param advEvtType - The type of advertisment event.
@param ownAddrType - The Adv's address type of public or random.
@param peerAddrType - BLE4.0: Only used for directed advertising. BLE4.2: Peer address type
@param *peerAddr - BLE4.0: Only used for directed advertising (NULL otherwise). BLE4.2: Peer address
@param advChanMap - A byte containing 1 bit per advertising
channel. A bit set to 1 means the channel is
used. The bit positions define the advertising
channels as follows:
Bit 0: 37, Bit 1: 38, Bit 2: 39.
@param advWlPolicy - The Adv white list filter policy.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_NO_ADV_CHAN_FOUND
*/
llStatus_t LL_SetAdvParam0( uint16 advIntervalMin,
uint16 advIntervalMax,
uint8 advEvtType,
uint8 ownAddrType,
uint8 peerAddrType,
uint8* peerAddr,
uint8 advChanMap,
uint8 advWlPolicy )
{
uint8 pduType;
uint8 resolve_address[6];
uint8* localIrk, *peerIrk;
if (g_llAdvMode == LL_MODE_EXTENDED )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_LEGACY;
// sanity check of parameters
if ( ( (advEvtType != LL_ADV_CONNECTABLE_UNDIRECTED_EVT) &&
(advEvtType != LL_ADV_CONNECTABLE_HDC_DIRECTED_EVT) &&
(advEvtType != LL_ADV_NONCONNECTABLE_UNDIRECTED_EVT) &&
(advEvtType != LL_ADV_SCANNABLE_UNDIRECTED_EVT) &&
(advEvtType != LL_ADV_CONNECTABLE_LDC_DIRECTED_EVT) ) ||
( ((advEvtType == LL_ADV_CONNECTABLE_HDC_DIRECTED_EVT) ||
(advEvtType == LL_ADV_CONNECTABLE_LDC_DIRECTED_EVT)) &&
((peerAddr == NULL) ||
((peerAddrType != LL_DEV_ADDR_TYPE_PUBLIC) &&
(peerAddrType != LL_DEV_ADDR_TYPE_RANDOM))) ) ||
( ((advEvtType == LL_ADV_NONCONNECTABLE_UNDIRECTED_EVT) ||
(advEvtType == LL_ADV_SCANNABLE_UNDIRECTED_EVT)) &&
// the minimum interval for nonconnectable Adv is 100ms
((advIntervalMin < LL_ADV_CONN_INTERVAL_MIN) || // should use LL_ADV_NONCONN_INTERVAL_MIN after update it to 20ms
(advIntervalMin > LL_ADV_NONCONN_INTERVAL_MAX) ||
(advIntervalMax < LL_ADV_CONN_INTERVAL_MIN) || // should use LL_ADV_NONCONN_INTERVAL_MIN after update it to 20ms
(advIntervalMax > LL_ADV_NONCONN_INTERVAL_MAX)) ) ||
( (advEvtType == LL_ADV_CONNECTABLE_UNDIRECTED_EVT) &&
// the minimum interval for connectable undirected Adv is 20ms
((advIntervalMin < LL_ADV_CONN_INTERVAL_MIN) ||
(advIntervalMin > LL_ADV_CONN_INTERVAL_MAX) ||
(advIntervalMax < LL_ADV_CONN_INTERVAL_MIN) ||
(advIntervalMax > LL_ADV_CONN_INTERVAL_MAX)) ) ||
( advIntervalMax < advIntervalMin ) ||
( (ownAddrType != LL_DEV_ADDR_TYPE_PUBLIC) &&
(ownAddrType != LL_DEV_ADDR_TYPE_RANDOM) &&
(ownAddrType != LL_DEV_ADDR_TYPE_RPA_PUBLIC) && // BLE 4.2
(ownAddrType != LL_DEV_ADDR_TYPE_RPA_RANDOM)) || // BLE 4.2
( ((ownAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC) ||
(ownAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM)) &&
(peerAddr == NULL)) ||
( (advWlPolicy != LL_ADV_WL_POLICY_ANY_REQ) &&
(advWlPolicy != LL_ADV_WL_POLICY_WL_SCAN_REQ) &&
(advWlPolicy != LL_ADV_WL_POLICY_WL_CONNECT_REQ) &&
(advWlPolicy != LL_ADV_WL_POLICY_WL_ALL_REQ) ) ||
( ((advChanMap & LL_ADV_CHAN_ALL) == 0) ) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// check if advertising is active
if ( adv_param.advMode == LL_ADV_MODE_ON )
{
// yes, so not allowed per the spec
return( LL_STATUS_ERROR_COMMAND_DISALLOWED );
}
adv_param.advEvtType = advEvtType;
adv_param.ownAddrType = ownAddrType;
// save off the advertiser channel map
adv_param.advChanMap = advChanMap & 0x07;
// make sure there's at least one advertising channel that can be used
if ( !adv_param.advChanMap )
{
// force all to be usable in case the error message is ignored
adv_param.advChanMap = LL_ADV_CHAN_ALL;
return( LL_STATUS_ERROR_NO_ADV_CHAN_FOUND );
}
// save the white list policy
adv_param.wlPolicy = advWlPolicy;
// set the advertiser address based on the HCI's address type preference
if ( ownAddrType == LL_DEV_ADDR_TYPE_PUBLIC )
{
// get our address and address type
g_currentLocalAddrType = LL_DEV_ADDR_TYPE_PUBLIC;
LL_COPY_DEV_ADDR_LE( adv_param.ownAddr, ownPublicAddr );
}
else if ( ownAddrType == LL_DEV_ADDR_TYPE_RANDOM )
{
// get our address and address type
g_currentLocalAddrType = LL_DEV_ADDR_TYPE_RANDOM;
LL_COPY_DEV_ADDR_LE( adv_param.ownAddr, ownRandomAddr );
}
// BBB ROM code add
else if ( ownAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC ||
ownAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM)
{
uint8 found = FALSE;
// search the resolving list to get local IRK
if (ll_readLocalIRK(&localIrk, peerAddr, peerAddrType) == TRUE)
{
if (!ll_isIrkAllZero(localIrk)) // for all-zero local IRK, not RPA used
{
if (ll_CalcRandomAddr(localIrk, resolve_address) == SUCCESS)
{
LL_COPY_DEV_ADDR_LE( adv_param.ownAddr, resolve_address );
osal_memcpy( &g_currentLocalRpa[0], resolve_address, 6);
found = TRUE;
g_currentLocalAddrType = LL_DEV_ADDR_TYPE_RANDOM;
}
}
}
if (found == FALSE)
{
if (ownAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC)
{
LL_COPY_DEV_ADDR_LE( adv_param.ownAddr, ownPublicAddr );
g_currentLocalAddrType = LL_DEV_ADDR_TYPE_PUBLIC;
}
else
{
LL_COPY_DEV_ADDR_LE( adv_param.ownAddr, ownRandomAddr );
g_currentLocalAddrType = LL_DEV_ADDR_TYPE_RANDOM;
}
}
}
// save peer address info, to consider whether we need it
if (peerAddr != NULL)
LL_COPY_DEV_ADDR_LE( peerInfo.peerAddr, peerAddr );
peerInfo.peerAddrType = peerAddrType;
// a Connectable Directed Adv event requires Init address info
if ( advEvtType == LL_ADV_CONNECTABLE_HDC_DIRECTED_EVT )
{
// get the Init's address and address type as well
LL_COPY_DEV_ADDR_LE( peerInfo.peerAddr, peerAddr );
// the advertising interval and delay are not used
adv_param.advInterval = 2;//0; // set by HZF, 2 means 1.25ms, so 3 adv channel = 3.75ms, spec require < 3.75ms
}
else if ( advEvtType == LL_ADV_CONNECTABLE_LDC_DIRECTED_EVT )
{
// get the Init's address and address type as well
LL_COPY_DEV_ADDR_LE( peerInfo.peerAddr, peerAddr );
// calculate the advertising interface based on the max/min values
// ALT: COULD UPDATE WITH ALGO IF NEED BE.
adv_param.advInterval = advIntervalMin;
}
else // undirected, discoverable, or non-connectable
{
// calculate the advertising interface based on the max/min values
// ALT: COULD UPDATE WITH ALGO IF NEED BE.
adv_param.advInterval = advIntervalMin;
}
// mapping from adv event type to packet header
switch (adv_param.advEvtType)
{
case LL_ADV_CONNECTABLE_UNDIRECTED_EVT:
pduType = ADV_IND;
break;
case LL_ADV_CONNECTABLE_HDC_DIRECTED_EVT:
case LL_ADV_CONNECTABLE_LDC_DIRECTED_EVT:
pduType = ADV_DIRECT_IND;
break;
case LL_ADV_NONCONNECTABLE_UNDIRECTED_EVT:
pduType = ADV_NONCONN_IND;
break;
case LL_ADV_SCANNABLE_UNDIRECTED_EVT:
pduType = ADV_SCAN_IND;
break;
default:
// should not come here, sanity check in the function start. set default value to suppress warning
pduType = ADV_IND;
break;
}
SET_BITS(g_tx_adv_buf.txheader, pduType, PDU_TYPE_SHIFT, PDU_TYPE_MASK);
SET_BITS(g_tx_adv_buf.txheader, g_currentLocalAddrType, TX_ADD_SHIFT, TX_ADD_MASK);
// SET_BITS(g_tx_adv_buf.txheader, peerInfo.peerAddrType, RX_ADD_SHIFT, RX_ADD_MASK); // RxAdd need't set
if ((advEvtType == LL_ADV_CONNECTABLE_UNDIRECTED_EVT
|| advEvtType == LL_ADV_CONNECTABLE_HDC_DIRECTED_EVT
|| advEvtType == LL_ADV_CONNECTABLE_LDC_DIRECTED_EVT)
&& pGlobal_config[LL_SWITCH] & CONN_CSA2_ALLOW)
SET_BITS(g_tx_adv_buf.txheader, 1, CHSEL_SHIFT, CHSEL_MASK);
osal_memcpy( g_tx_adv_buf.data, adv_param.ownAddr, 6);
SET_BITS(tx_scanRsp_desc.txheader, ADV_SCAN_RSP, PDU_TYPE_SHIFT, PDU_TYPE_MASK);
SET_BITS(tx_scanRsp_desc.txheader, g_currentLocalAddrType, TX_ADD_SHIFT, TX_ADD_MASK);
osal_memcpy( tx_scanRsp_desc.data, adv_param.ownAddr, 6);
// adv length should be set for not direct adv type, 2018-04-05
SET_BITS(g_tx_adv_buf.txheader, (adv_param.advDataLen + 6), LENGTH_SHIFT, LENGTH_MASK);
// for direct adv, copy the peer address to PDU
if(pduType == ADV_DIRECT_IND )
{
uint8 useRpa = FALSE;
SET_BITS(g_tx_adv_buf.txheader, 12, LENGTH_SHIFT, LENGTH_MASK);
// SET_BITS(tx_scanRsp_desc.txheader, peerInfo.peerAddrType, RX_ADD_SHIFT, RX_ADD_MASK);
// search the resolving list to get local IRK
if (ll_readPeerIRK(&peerIrk, peerAddr, peerAddrType) == TRUE)
{
if (!ll_isIrkAllZero(peerIrk)) // for all-zero local IRK, not RPA used
{
if (ll_CalcRandomAddr(peerIrk, resolve_address) == SUCCESS)
{
useRpa = TRUE;
osal_memcpy((uint8_t*) &(g_tx_adv_buf.data[6]), resolve_address, 6);
// osal_memcpy( &g_currentPeerRpa[0], resolve_address, 6);
}
}
}
if (useRpa == FALSE)
osal_memcpy((uint8_t*) &(g_tx_adv_buf.data[6]), peerInfo.peerAddr, 6);
}
// ======================== add by HZF, init ll state so that adv PDU could be changed
if (llState != LL_STATE_CONN_MASTER && llState != LL_STATE_CONN_SLAVE)
{
switch(adv_param .advEvtType)
{
case LL_ADV_CONNECTABLE_UNDIRECTED_EVT:
llState=LL_STATE_ADV_UNDIRECTED;
ll_debug_output(DEBUG_LL_STATE_ADV_UNDIRECTED);
break;
case LL_ADV_CONNECTABLE_HDC_DIRECTED_EVT:
case LL_ADV_CONNECTABLE_LDC_DIRECTED_EVT:
llState=LL_STATE_ADV_DIRECTED;
ll_debug_output(DEBUG_LL_STATE_ADV_DIRECTED);
break;
case LL_ADV_NONCONNECTABLE_UNDIRECTED_EVT:
llState=LL_STATE_ADV_NONCONN;
ll_debug_output(DEBUG_LL_STATE_ADV_NONCONN);
break;
case LL_ADV_SCANNABLE_UNDIRECTED_EVT:
llState=LL_STATE_ADV_SCAN;
ll_debug_output(DEBUG_LL_STATE_ADV_SCAN);
break;
default:
llState=LL_STATE_IDLE;
ll_debug_output(DEBUG_LL_STATE_IDLE);
break;
}
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_SetAdvData0 API
@brief This API is called by the HCI to set the Advertiser's data.
Note: If the Advertiser is restarted without intervening calls
to this routine to make updates, then the previously
defined data will be reused.
Note: If the data happens to be changed while advertising, then
the new data will be sent on the next advertising event.
input parameters
@param advDataLen - The number of scan response bytes: 0..31.
@param advData - Pointer to the advertiser data, or NULL.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_SetAdvData0( uint8 advDataLen,
uint8* advData )
{
if (g_llAdvMode == LL_MODE_EXTENDED )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_LEGACY;
// check that data length isn't greater than the max allowed size
if ( advDataLen > LL_MAX_ADV_DATA_LEN )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// save advertiser data length
adv_param.advDataLen = advDataLen;
// check if there's supposed to be data
if ( advDataLen > 0 )
{
// yes, so make sure we have a valid pointer
if ( advData == NULL )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
else // okay to go
{
// save advertiser data
//osal_memcpy( (uint8_t *)adv_param.advData, advData, adv_param.advDataLen ); // save adv data
osal_memcpy( (uint8_t*) &(g_tx_adv_buf.data[6]), advData, adv_param.advDataLen ); // write adv to tx buffer, change it ?? ... HZF
}
}
// set tx buffer, to be changed
// SET_BITS(g_tx_adv_buf.txheader, peerInfo .peerAddrType, RX_ADD_SHIFT, RX_ADD_MASK);
// osal_memcpy(g_tx_adv_buf.data, adv_param.ownAddr, 6);
SET_BITS(g_tx_adv_buf.txheader, (adv_param.advDataLen+6), LENGTH_SHIFT, LENGTH_MASK);
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This API is called by the HCI to request the Controller to start or stop
advertising.
Public function defined in ll.h.
*/
llStatus_t LL_SetAdvControl0( uint8 advMode )
{
if (g_llAdvMode == LL_MODE_EXTENDED )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_LEGACY;
// check if a direct test mode or modem test is in progress
if ( (llState == LL_STATE_DIRECT_TEST_MODE_TX) ||
(llState == LL_STATE_DIRECT_TEST_MODE_RX) ||
(llState == LL_STATE_MODEM_TEST_TX) ||
(llState == LL_STATE_MODEM_TEST_RX) ||
(llState == LL_STATE_MODEM_TEST_TX_FREQ_HOPPING) )
{
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
}
// sanity checks again to be sure we don't start with bad parameters
if ( ( (adv_param.advEvtType != LL_ADV_CONNECTABLE_UNDIRECTED_EVT) &&
(adv_param.advEvtType != LL_ADV_CONNECTABLE_HDC_DIRECTED_EVT) &&
(adv_param.advEvtType != LL_ADV_NONCONNECTABLE_UNDIRECTED_EVT) &&
(adv_param.advEvtType != LL_ADV_SCANNABLE_UNDIRECTED_EVT) &&
(adv_param.advEvtType != LL_ADV_CONNECTABLE_LDC_DIRECTED_EVT) ) ||
( (adv_param.ownAddrType != LL_DEV_ADDR_TYPE_PUBLIC) &&
(adv_param.ownAddrType != LL_DEV_ADDR_TYPE_RANDOM) &&
(adv_param.ownAddrType != LL_DEV_ADDR_TYPE_RPA_PUBLIC) &&
(adv_param.ownAddrType != LL_DEV_ADDR_TYPE_RPA_RANDOM)) ||
( ((adv_param.advEvtType == LL_ADV_NONCONNECTABLE_UNDIRECTED_EVT) ||
(adv_param.advEvtType == LL_ADV_SCANNABLE_UNDIRECTED_EVT)) &&
(adv_param.advInterval < LL_ADV_CONN_INTERVAL_MIN) ) ) // should use LL_ADV_NONCONN_INTERVAL_MIN after update it to 20ms
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
#ifdef DEBUG_LL
LOG("llState = %d\n", llState);
#endif
// check if we should begin advertising
switch( advMode )
{
// Advertisment Mode is On
case LL_ADV_MODE_ON:
// check if command makes sense
if ( adv_param.advMode == LL_ADV_MODE_ON )
{
// this is unexpected; something is wrong
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
}
// llState changed when configure adv parameters
if (llState == LL_STATE_ADV_UNDIRECTED
|| llState == LL_STATE_ADV_DIRECTED
|| llState == LL_STATE_ADV_NONCONN
|| llState == LL_STATE_ADV_SCAN ) // TODO: check this setting
{
g_llHdcDirAdvTime = 0; // for HDC direct adv
adv_param.advNextChan = LL_ADV_CHAN_LAST + 1; // set adv channel invalid
if ( llSetupAdv() != LL_STATUS_SUCCESS )
{
// indicate advertising is no longer active
adv_param.advMode = LL_ADV_MODE_OFF;
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
}
}
// add in A2, simultaneous conn event & scan/adv event
else if((llState == LL_STATE_CONN_SLAVE
|| llState == LL_STATE_CONN_MASTER)
&& (pGlobal_config[LL_SWITCH] & SIMUL_CONN_ADV_ALLOW))
{
#ifdef DEBUG_LL
LOG("LL_SetAdvControl: start sec adv\r\n");
#endif
if (llSecondaryState != LL_SEC_STATE_IDLE)
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
// adv event check
if (adv_param.advEvtType != LL_ADV_NONCONNECTABLE_UNDIRECTED_EVT
&& adv_param.advEvtType != LL_ADV_SCANNABLE_UNDIRECTED_EVT
&& adv_param.advEvtType != LL_ADV_CONNECTABLE_UNDIRECTED_EVT)
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
// Note: we may need maximum slave number check here. If number of slave reach ceil,
// only no-connectable adv is allowed. The checking could be don't in host
llSecondaryState = LL_SEC_STATE_ADV;
adv_param.advNextChan = LL_ADV_CHAN_LAST + 1; // set adv channel invalid
osal_stop_timerEx( LL_TaskID, LL_EVT_SECONDARY_ADV );
osal_set_event(LL_TaskID, LL_EVT_SECONDARY_ADV); // set adv event
}
else // other state
return (LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE);
// indicate advertising is no longer active
adv_param.advMode = LL_ADV_MODE_ON;
if (g_llRlDeviceNum > 0)
osal_start_timerEx( LL_TaskID, LL_EVT_RPA_TIMEOUT, g_llRlTimeout * 1000 );
break;
case LL_ADV_MODE_OFF:
// check if command makes sense
// if ( adv_param.advMode == LL_ADV_MODE_OFF )
// {
// // this is unexpected; something is wrong
// return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
// }
HAL_ENTER_CRITICAL_SECTION();
// free the associated task block
//llFreeTask( &advInfo.llTask );
// indicate we are no longer actively advertising
adv_param.advMode = LL_ADV_MODE_OFF;
if (llState != LL_STATE_CONN_SLAVE &&
llState != LL_STATE_CONN_MASTER) // no conn + adv case
{
llState = LL_STATE_IDLE; // if not in connect state, set idle to disable advertise
//ZQ 20190912
//stop ll timer when idle, considering the scan-adv interleve case
extern void clear_timer(AP_TIM_TypeDef* TIMx);
clear_timer(AP_TIM1);
ll_debug_output(DEBUG_LL_STATE_IDLE);
}
else // conn + adv case
{
uint8 i;
i = 0;
while (!(adv_param.advChanMap & (1 << i))) i ++; // get the 1st adv channel in the adv channel map
if ((llSecondaryState == LL_SEC_STATE_ADV)
&& (adv_param.advNextChan != (LL_ADV_CHAN_FIRST + i))) // last adv event is not finished
llSecondaryState = LL_SEC_STATE_IDLE_PENDING;
else
{
llSecondaryState = LL_SEC_STATE_IDLE;
osal_stop_timerEx( LL_TaskID, LL_EVT_SECONDARY_ADV ); // stop timer
}
}
HAL_EXIT_CRITICAL_SECTION();
osal_stop_timerEx(LL_TaskID, LL_EVT_RPA_TIMEOUT);
break;
default:
// we have an invalid value for advertisement mode
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This API is called by the LL or HCI to check if a connection given by the
connection handle is active.
Public function defined in ll.h.
*/
llStatus_t LL_ConnActive( uint16 connId )
{
// check if the connection handle is valid
if (connId >= g_maxConnNum )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// check if the connection is active
if ( conn_param[connId].active == FALSE )
{
return( LL_STATUS_ERROR_INACTIVE_CONNECTION );
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This API is called by the HCI to read the peer controller's Version
Information. If the peer's Version Information has already been received by
its request for our Version Information, then this data is already cached and
can be directly returned to the Host. If the peer's Version Information is
not already cached, then it will be requested from the peer, and when
received, returned to the Host via the LL_ReadRemoteVersionInfoCback
callback.
Note: Only one Version Indication is allowed for a connection.
Public function defined in ll.h.
*/
llStatus_t LL_ReadRemoteVersionInfo( uint16 connId )
{
llStatus_t status;
llConnState_t* connPtr;
// make sure connection ID is valid
if ( (status=LL_ConnActive(connId)) != LL_STATUS_SUCCESS )
{
return( status );
}
// get connection info
connPtr = &conn_param[connId];
// first, make sure the connection is still active
if ( !connPtr->active )
{
return( LL_STATUS_ERROR_INACTIVE_CONNECTION );
}
// check if the peer's version information has already been obtained
if ( connPtr->verExchange.peerInfoValid == TRUE )
{
// yes it has, so provide it to the host
LL_ReadRemoteVersionInfoCback( LL_STATUS_SUCCESS,
connId,
connPtr->verInfo.verNum,
connPtr->verInfo.comId,
connPtr->verInfo.subverNum );
}
else // no it hasn't, so...
{
// ...check if the host has already requested this information
if ( connPtr->verExchange.hostRequest == FALSE )
{
// no, so request it by queueing the control packet for processing
llEnqueueCtrlPkt( connPtr, LL_CTRL_VERSION_IND );
// set the flag to indicate the host has requested this information
connPtr->verExchange.hostRequest = TRUE;
}
else // previously requested
{
return( LL_STATUS_ERROR_VER_INFO_REQ_ALREADY_PENDING );
}
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_ClearWhiteList0 API
@brief This API is called by the HCI to clear the White List.
Note: If Scanning is enabled using filtering, and the white
list policy is "Any", then this command will be
disallowed.
input parameters
@param None.
output parameters
@param None.
@return LL_STATUS_SUCCESS
*/
llStatus_t LL_ClearWhiteList0( void )
{
llStatus_t status;
int i, j;
// check that it is okay to use the white list
if ( (status = llCheckWhiteListUsage()) != LL_STATUS_SUCCESS )
{
return( status );
}
// clear number of entries, valid flags, address type flags, and entries
for (i = 0; i < LL_WHITELIST_ENTRY_NUM; i ++)
{
g_llWhitelist[i].peerAddrType = 0xff;
for (j = 0; j < LL_DEVICE_ADDR_LEN; j ++)
g_llWhitelist[i].peerAddr[j] = 0;
}
// set white list number 0
g_llWlDeviceNum = 0;
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_AddWhiteListDevice0 API
@brief This API is called by the HCI to add a device address and its
type to the White List.
input parameters
@param devAddr - Pointer to a 6 byte device address.
@param addrType - Public or Random device address.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_WL_TABLE_FULL
*/
llStatus_t LL_AddWhiteListDevice0( uint8* devAddr,
uint8 addrType )
{
llStatus_t status;
int i, j;
// check that it is okay to use the white list
if ( (status = llCheckWhiteListUsage()) != LL_STATUS_SUCCESS )
{
return( status );
}
// check the WL device address type
if ( (addrType != LL_DEV_ADDR_TYPE_PUBLIC) &&
(addrType != LL_DEV_ADDR_TYPE_RANDOM) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// check if there was room for the entry
if (g_llWlDeviceNum >= LL_WHITELIST_ENTRY_NUM)
return( LL_STATUS_ERROR_WL_TABLE_FULL );
// add the device to a empty record
for (i = 0; i < LL_WHITELIST_ENTRY_NUM; i++)
{
if (g_llWhitelist[i].peerAddrType == 0xff) // empty record
{
g_llWhitelist[i].peerAddrType = addrType;
for (j = 0; j < LL_DEVICE_ADDR_LEN; j++)
g_llWhitelist[i].peerAddr[j] = devAddr[j];
g_llWlDeviceNum ++;
break;
}
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_RemoveWhiteListDevice0 API
@brief This API is called by the HCI to remove a device address and
it's type from the White List.
input parameters
@param devAddr - Pointer to a 6 byte device address.
@param addrType - Public or Random device address.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_WL_TABLE_EMPTY,
LL_STATUS_ERROR_WL_ENTRY_NOT_FOUND
*/
llStatus_t LL_RemoveWhiteListDevice0( uint8* devAddr,
uint8 addrType )
{
llStatus_t status;
int i, j;
// check that it is okay to use the white list
if ( (status = llCheckWhiteListUsage()) != LL_STATUS_SUCCESS )
{
return( status );
}
// check the WL device address type
if ( (addrType != LL_DEV_ADDR_TYPE_PUBLIC) &&
(addrType != LL_DEV_ADDR_TYPE_RANDOM) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// check that there was at least one entry in the table
if ( g_llWlDeviceNum == 0 )
{
return( LL_STATUS_ERROR_WL_TABLE_EMPTY );
}
for (i = 0; i < LL_WHITELIST_ENTRY_NUM; i++)
{
if (g_llWhitelist[i].peerAddrType == addrType)
{
for (j = 0; j < LL_DEVICE_ADDR_LEN; j++) // check whether the address is the same
{
if (g_llWhitelist[i].peerAddr[j] != devAddr[j])
break;
}
if (j == LL_DEVICE_ADDR_LEN) // found it
{
g_llWhitelist[i].peerAddrType = 0xff;
g_llWlDeviceNum --;
break;
}
}
}
if (i == LL_WHITELIST_ENTRY_NUM)
return( LL_STATUS_ERROR_WL_ENTRY_NOT_FOUND );
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This API is called by the HCI to update the Host data channels initiating an
Update Data Channel control procedure.
Note: While it isn't specified, it is assumed that the Host
expects an update channel map on all active connections.
Note: This LL currently only supports one connection.
Public function defined in ll.h.
*/
llStatus_t LL_ChanMapUpdate( uint8* chanMap )
{
uint8 i;
// make sure we're in Master role
if ( llState != LL_STATE_CONN_MASTER )
{
return( LL_STATUS_ERROR_COMMAND_DISALLOWED );
}
// parameter check
if ( chanMap == NULL )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// ensure non-data channels 37..39 are not set and that the Core spec V4.0
// requirement of a minimum of two data channels be used is met
if ( (chanMap[LL_NUM_BYTES_FOR_CHAN_MAP-1] & ~0x1F) ||
(llAtLeastTwoChans( chanMap ) != TRUE) )
{
return( LL_STATUS_ERROR_ILLEGAL_PARAM_COMBINATION );
}
// first need to check if any previous channel map update is still pending
for (i = 0; i < g_maxConnNum; i++)
{
if (conn_param [i].active )
{
llConnState_t* connPtr = &conn_param[i];
// check if an update channel map control procedure is already pending
if ( ((connPtr->ctrlPktInfo.ctrlPktCount > 0) &&
(connPtr->ctrlPktInfo.ctrlPkts[0] == LL_CTRL_CHANNEL_MAP_REQ)) ||
(connPtr->pendingChanUpdate == TRUE) )
{
return( LL_STATUS_ERROR_CTRL_PROC_ALREADY_ACTIVE );
}
}
}
// save the Host's channel map
for (i = 0; i < LL_NUM_BYTES_FOR_CHAN_MAP; i++)
{
chanMapUpdate.chanMap[i] = chanMap[i];
}
// need to issue an update on all active connections, if any
for (i = 0; i < g_maxConnNum; i++)
{
if (conn_param [i].active )
{
llConnState_t* connPtr = &conn_param[i];
// set the relative offset of the number of events for the channel update
// Note: The absolute event number will be determined at the time the
// packet is placed in the TX FIFO.
// Note: The master should allow a minimum of 6 connection events that the
// slave will be listening for before the instant occurs.
connPtr->chanMapUpdateEvent = (connPtr->curParam.slaveLatency+1) +
LL_INSTANT_NUMBER_MIN;
// queue control packet for processing
llEnqueueCtrlPkt( connPtr, LL_CTRL_CHANNEL_MAP_REQ );
}
}
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_PhyUpdate0( uint16 connId )
{
llStatus_t status;
llConnState_t* connPtr;
uint8 phyMode;
// make sure connection ID is valid
if ( (status=LL_ConnActive(connId)) != LL_STATUS_SUCCESS )
{
return( status );
}
// get connection info
connPtr = &conn_param[connId ];
// check if an update control procedure is already pending
if ( ((connPtr->ctrlPktInfo.ctrlPktCount > 0) &&
(connPtr->ctrlPktInfo.ctrlPkts[0] == LL_CTRL_PHY_UPDATE_IND)) ||
(connPtr->pendingPhyModeUpdate == TRUE) )
{
return( LL_STATUS_ERROR_CTRL_PROC_ALREADY_ACTIVE );
}
// we only support symmetric connection
// tx rx phy should be same
if(connPtr->llPhyModeCtrl.req.allPhy==0)
{
phyMode = connPtr->llPhyModeCtrl.req.txPhy & connPtr->llPhyModeCtrl.rsp.txPhy;
phyMode|= connPtr->llPhyModeCtrl.req.rxPhy & connPtr->llPhyModeCtrl.rsp.rxPhy;
}
else if(connPtr->llPhyModeCtrl.req.allPhy==1)
{
phyMode = connPtr->llPhyModeCtrl.req.rxPhy & connPtr->llPhyModeCtrl.rsp.rxPhy;
}
else if(connPtr->llPhyModeCtrl.req.allPhy==2)
{
phyMode = connPtr->llPhyModeCtrl.req.txPhy & connPtr->llPhyModeCtrl.rsp.txPhy;
}
else
{
phyMode=0;
}
if(phyMode==0)
{
//no change case
connPtr->phyUpdateInfo.m2sPhy = 0;
connPtr->phyUpdateInfo.s2mPhy = 0;
}
else if(phyMode&LE_2M_PHY)
{
connPtr->phyUpdateInfo.m2sPhy = LE_2M_PHY;
connPtr->phyUpdateInfo.s2mPhy = LE_2M_PHY;
}
else if(phyMode&LE_CODED_PHY)
{
connPtr->phyUpdateInfo.m2sPhy = LE_CODED_PHY;
connPtr->phyUpdateInfo.s2mPhy = LE_CODED_PHY;
}
else
{
//no perferce can not support the tx/rx same time
connPtr->phyUpdateInfo.m2sPhy = LE_1M_PHY;
connPtr->phyUpdateInfo.s2mPhy = LE_1M_PHY;
}
if(phyMode==0)
{
connPtr->phyModeUpdateEvent = 0;
connPtr->phyUpdateInfo.instant = connPtr->phyModeUpdateEvent;
}
else
{
connPtr->phyModeUpdateEvent = (connPtr->curParam.slaveLatency+1) +
LL_INSTANT_NUMBER_MIN;
connPtr->phyUpdateInfo.instant = connPtr->phyModeUpdateEvent;
}
// queue control packet for processing
llEnqueueCtrlPkt( connPtr, LL_CTRL_PHY_UPDATE_IND );
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_ReadRemoteUsedFeatures API
@brief This API is called by the Master HCI to initiate a feature
setup control process.
input parameters
@param connId - The LL connection ID on which to send this data.
output parameters
@param None.
@return LL_STATUS_SUCCESS
*/
llStatus_t LL_ReadRemoteUsedFeatures0( uint16 connId )
{
llStatus_t status;
llConnState_t* connPtr;
// // make sure we're in Master role // in BLE4.2, it could be send in both master & slave
// if ( llState != LL_STATE_CONN_MASTER )
// {
// return( LL_STATUS_ERROR_COMMAND_DISALLOWED );
// }
// make sure connection ID is valid
if ( (status=LL_ConnActive(connId)) != LL_STATUS_SUCCESS )
{
return( status );
}
// get connection info
connPtr = &conn_param[connId ];
// initiate a Feature Set control procedure
llEnqueueCtrlPkt( connPtr, LL_CTRL_FEATURE_REQ );
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_ReadWlSize0 API
@brief This API is called by the HCI to get the total number of white
list entries that can be stored in the Controller.
input parameters
@param None.
output parameters
@param *numEntries - Total number of available White List entries.
@return LL_STATUS_SUCCESS
*/
llStatus_t LL_ReadWlSize0( uint8* numEntries )
{
*numEntries = LL_WHITELIST_ENTRY_NUM;
return (LL_STATUS_SUCCESS);
}
/*******************************************************************************
@fn LL_NumEmptyWlEntries API
@brief This API is called by the HCI to get the number of White List
entries that are empty.
input parameters
@param None.
output parameters
@param *numEmptyEntries - number of empty entries in the White List.
@return LL_STATUS_SUCCESS
*/
//extern llStatus_t LL_NumEmptyWlEntries( uint8 *numEmptyEntries ); // Not used by TI code
/*******************************************************************************
@fn LL_Encrypt API
@brief This API is called by the HCI to request the LL to encrypt the
data in the command using the key given in the command.
Note: The parameters are byte ordered MSO to LSO.
input parameters
@param *key - A 128 bit key to be used to calculate the
session key.
@param *plaintextData - A 128 bit block that is to be encrypted.
output parameters
@param *encryptedData - A 128 bit block that is encrypted.
@param None.
@return LL_STATUS_SUCCESS
*/
llStatus_t LL_Encrypt0( uint8* key,
uint8* plaintextData,
uint8* encryptedData )
{
// check parameters
if ( (key == NULL ) ||
(plaintextData == NULL) ||
(encryptedData == NULL) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// encrypt on behalf of the host
LL_ENC_AES128_Encrypt( key, plaintextData, encryptedData );
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This API is called by the HCI to request the LL Controller to provide a data
block with random content.
Note: we use different scheme to TI. Below is from TI notes:
The HCI spec indicates that the random number
generation should adhere to one of those specified in FIPS
PUB 140-2. The Core spec refers specifically to the
algorithm specified in FIPS PUB 186-2, Appendix 3.1.
Note that this software only uses the RF hardware to
generate true random numbers. What's more, if the RF is
already in use (i.e. overlapped execution), then the use
of radio to generate true random numbers is prohibited.
In this case, a pseudo-random blocks of numbers will be
returned instead.
Public function defined in ll.h.
*/
llStatus_t LL_Rand( uint8* randData,
uint8 dataLen )
{
uint16 temp_rand;
uint8* pData = randData;
uint32 sysTick;
// check if a DTM or Modem operation is in progress
if ( (llState == LL_STATE_DIRECT_TEST_MODE_TX) ||
(llState == LL_STATE_DIRECT_TEST_MODE_RX) ||
(llState == LL_STATE_MODEM_TEST_TX) ||
(llState == LL_STATE_MODEM_TEST_RX) ||
(llState == LL_STATE_MODEM_TEST_TX_FREQ_HOPPING) )
{
// yes, so sorry, no true random number generation allowed as the radio
// is in continuous use
return( LL_STATUS_ERROR_COMMAND_DISALLOWED );
}
if (dataLen == 0)
{
return (LL_STATUS_ERROR_BAD_PARAMETER);
}
// now use timer3 counter
sysTick = get_timer_count(AP_TIM3);
srand(sysTick);
//srand((unsigned)time(&t));
while (dataLen > 1)
{
temp_rand = (uint16)(rand() & 0xffff);
*(pData ++) = (uint8)((temp_rand & 0xff00) >> 8);
*(pData ++) = (uint8)(temp_rand & 0xff) ;
dataLen -= 2;
}
if (dataLen == 1)
{
temp_rand = (uint16)(rand() & 0xffff);
*(pData) = (uint8)((temp_rand & 0xff00) >> 8);
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This API is a generic interface to get a block of pseudo-random numbers.
Public function defined in ll.h.
*/
llStatus_t LL_PseudoRand( uint8* randData,
uint8 dataLen )
{
return LL_Rand(randData, dataLen);
}
/*******************************************************************************
@fn LL_ReadSupportedStates API
@brief This function is used to provide the HCI with the Link Layer
supported states and supported state/role combinations.
input parameters
@param None.
output parameters
@param *states - Eight byte Bit map of supported states/combos.
@return LL_STATUS_SUCCESS
*/
// the defination of LL_SET_SUPPORTED_STATES implicly using input parameter "states", not good, but it is TI's
llStatus_t LL_ReadSupportedStates( uint8* states )
{
// provide supported state/role combinations
// Note: Upper nibble is byte offset, lower nibble is bit offset.
LL_SET_SUPPORTED_STATES( LL_SCAN_PASSIVE_STATE );
LL_SET_SUPPORTED_STATES( LL_SCAN_ACTIVE_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_NONCONN_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_DISCOV_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_NONCONN_SCAN_PASSIVE_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_DISCOV_SCAN_PASSIVE_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_NONCONN_SCAN_ACTIVE_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_DISCOV_SCAN_ACTIVE_STATE );
LL_SET_SUPPORTED_STATES( LL_INIT_STATE );
LL_SET_SUPPORTED_STATES( LL_INIT_MASTER_STATE );
LL_SET_SUPPORTED_STATES( LL_SCAN_PASSIVE_INIT_STATE );
LL_SET_SUPPORTED_STATES( LL_SCAN_ACTIVE_INIT_STATE );
LL_SET_SUPPORTED_STATES( LL_SCAN_PASSIVE_MASTER_STATE );
LL_SET_SUPPORTED_STATES( LL_SCAN_ACTIVE_MASTER_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_NONCONN_INIT_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_DISCOV_INIT_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_NONCONN_MASTER_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_DISCOV_MASTER_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_UNDIRECT_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_HDC_DIRECT_STATE );
LL_SET_SUPPORTED_STATES( LL_SLAVE_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_LDC_DIRECT_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_UNDIRECT_SCAN_PASSIVE_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_HDC_DIRECT_SCAN_PASSIVE_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_UNDIRECT_SCAN_ACTIVE_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_HDC_DIRECT_SCAN_ACTIVE_STATE );
LL_SET_SUPPORTED_STATES( LL_SCAN_PASSIVE_SLAVE_STATE );
LL_SET_SUPPORTED_STATES( LL_SCAN_ACTIVE_SLAVE_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_LDC_DIRECT_SCAN_PASSIVE_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_LDC_DIRECT_SCAN_ACTIVE_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_NONCONN_SLAVE_STATE );
LL_SET_SUPPORTED_STATES( LL_ADV_DISCOV_SLAVE_STATE );
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_ReadLocalVersionInfo API
@brief This API is called by the HCI to read the controller's
Version information.
input parameters
@param None.
output parameters
@param verNum - Version of the Bluetooth Controller specification.
@param comId - Company identifier of the manufacturer of the
Bluetooth Controller.
@param subverNum - A unique value for each implementation or revision
of an implementation of the Bluetooth Controller.
@return LL_STATUS_SUCCESS
*/
llStatus_t LL_ReadLocalVersionInfo( uint8* verNum,
uint16* comId,
uint16* subverNum )
{
// get the version of this BLE controller
*verNum = verInfo.verNum;
// get the company ID of this BLE controller
*comId = verInfo.comId;
// get the subversion of this BLE controller
*subverNum = verInfo.subverNum;
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_CtrlToHostFlowControl API
@brief This function is used to indicate if the LL enable/disable
receive FIFO processing. This function provides support for
Controller to Host flow control.
input parameters
@param mode: LL_ENABLE_RX_FLOW_CONTROL, LL_DISABLE_RX_FLOW_CONTROL
output parameters
@param None.
@return LL_STATUS_SUCCESS
*/
llStatus_t LL_CtrlToHostFlowControl( uint8 mode )
{
if ( mode == LL_ENABLE_RX_FLOW_CONTROL )
{
// set flag to indicate flow control is enabled
rxFifoFlowCtrl = LL_RX_FLOW_CONTROL_ENABLED;
}
else if ( mode == LL_DISABLE_RX_FLOW_CONTROL )
{
// set flag to indicate flow control is disabled
rxFifoFlowCtrl = LL_RX_FLOW_CONTROL_DISABLED;
}
else // error
{
return( LL_STATUS_ERROR_UNEXPECTED_PARAMETER );
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_ReadTxPowerLevel
@brief This function is used to read a connection's current transmit
power level or the maximum transmit power level.
input parameters
@param connId - The LL connection handle.
@param type - LL_READ_CURRENT_TX_POWER_LEVEL or
LL_READ_MAX_TX_POWER_LEVEL
@param *txPower - A signed value from -30..+20, in dBm.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_PARAM_OUT_OF_RANGE,
LL_STATUS_ERROR_INACTIVE_CONNECTION
*/
llStatus_t LL_ReadTxPowerLevel0( uint8 connId,
uint8 type,
int8* txPower )
{
(void) connId;
uint8_t txPowerIdx=0;
int8 txPowerMaping[18]=
{
RF_PHY_TX_POWER_EXTRA_MAX, 10,
RF_PHY_TX_POWER_MAX, 7,
RF_PHY_TX_POWER_5DBM, 5,
RF_PHY_TX_POWER_0DBM, 0,
RF_PHY_TX_POWER_N5DBM, -5,
RF_PHY_TX_POWER_N10DBM, -10,
RF_PHY_TX_POWER_N15DBM, -15,
RF_PHY_TX_POWER_N20DBM, -20,
RF_PHY_TX_POWER_MIN, -40,//[lastIdx-1] should be zero to end mapping search
};
if ( txPower == NULL )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// determine which type of TX power level is required
switch( type )
{
case LL_READ_CURRENT_TX_POWER_LEVEL:
// search the mapping table
while( txPowerMaping[txPowerIdx]>0
&& txPowerMaping[txPowerIdx]>g_rfPhyTxPower)
{
txPowerIdx=txPowerIdx+2;
}
// return the TX power level based on current setting
*txPower =txPowerMaping[txPowerIdx+1]; // assume when g_rfPhyTxPower = 0x1f, tx power = 10dBm,
// // check if Tx output power is valid
// if ( *txPower == LL_TX_POWER_INVALID )
// {
// return( LL_STATUS_ERROR_PARAM_OUT_OF_RANGE );
// }
break;
case LL_READ_MAX_TX_POWER_LEVEL:
// return max data channel TX power level
*txPower = RF_PHY_TX_POWER_MAX;
break;
default:
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_SetTxPowerLevel
@brief This function is used to set transmit power level
input parameters
@param txPower - The transmit power level to be set
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_INACTIVE_CONNECTION
*/
llStatus_t LL_SetTxPowerLevel0( int8 txPower )
{
// TODO: add tx power range check
// TODO: add tx power mapping
g_rfPhyTxPower = txPower;
rf_phy_set_txPower(g_rfPhyTxPower);
return LL_STATUS_SUCCESS;
}
/*******************************************************************************
@fn LL_ReadChanMap API
@brief This API is called by the HCI to read the channel map that the
LL controller is using for the LL connection.
input parameters
@param connId - The LL connection handle.
output parameters
@param chanMap - A five byte array containing one bit per data channel
where a 1 means the channel is "used" and a 0 means
the channel is "unused".
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_INACTIVE_CONNECTION
*/
llStatus_t LL_ReadChanMap( uint8 connId,
uint8* chanMap )
{
llStatus_t status;
llConnState_t* connPtr;
// make sure connection ID is valid
if ( (status=LL_ConnActive(connId)) != LL_STATUS_SUCCESS )
{
return( status );
}
// get connection info
connPtr = &conn_param[ connId ];
// copy current channel map
chanMap[0] = connPtr->chanMap[0];
chanMap[1] = connPtr->chanMap[1];
chanMap[2] = connPtr->chanMap[2];
chanMap[3] = connPtr->chanMap[3];
chanMap[4] = connPtr->chanMap[4];
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_ReadRssi API
@brief This API is called by the HCI to request RSSI. If there is an
active connection for the given connection ID, then the RSSI of
the last received data packet in the LL will be returned. If a
receiver Modem Test is running, then the RF RSSI for the last
received data will be returned. If no valid RSSI value is
available, then LL_RSSI_NOT_AVAILABLE will be returned.
input parameters
@param connId - The LL connection ID on which to read last RSSI.
output parameters
@param *lastRssi - The last data RSSI received.
Range: -127dBm..+20dBm, 127=Not Available.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_INACTIVE_CONNECTION
*/
llStatus_t LL_ReadRssi0( uint16 connId,
int8* lastRssi )
{
*lastRssi = conn_param[connId].lastRssi ;
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_ReadFoff( uint16 connId,
uint16* foff )
{
*foff = conn_param[connId].foff ;
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_ReadCarrSens( uint16 connId,
uint8* carrSens )
{
*carrSens = conn_param[connId].carrSens ;
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This function is used to initiate a BLE PHY level Transmit Test in Direct
Test Mode where the DUT generates test reference packets at fixed intervals.
This test will make use of the nanoRisc Raw Data Transmit and Receive task.
Note: The BLE device will transmit at maximum power.
Public function defined in ll.h.
*/
llStatus_t LL_DirectTestTxTest0( uint8 txFreq,
uint8 payloadLen,
uint8 payloadType )
{
(void) txFreq;
(void) payloadLen;
(void) payloadType;
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This function is used to initiate a BLE PHY level Receive Test in Direct Test
Mode where the DUT receives test reference packets at fixed intervals. This
test will make use of the nanoRisc Raw Data Transmit and Receive task. The
received packets are verified based on the CRC, and metrics are kept.
Public function defined in ll.h.
*/
llStatus_t LL_DirectTestRxTest0( uint8 rxFreq )
{
(void) rxFreq;
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This function is used to end the Direct Test Transmit or Direct Test Receive
tests executing in Direct Test mode. When the raw task is ended, the
LL_DirectTestEndDoneCback callback is called. If a Direct Test mode operation
is not currently active, an error is returned.
Public function defined in ll.h.
*/
llStatus_t LL_DirectTestEnd0( void )
{
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_EXT_ConnEventNotice Vendor Specific API
@brief This API is called to enable or disable a notification to the
specified task using the specified task event whenever a
Connection event ends. A non-zero taskEvent value is taken to
be "enable", while a zero valued taskEvent is taken to be
"disable".
Note: Currently, only a Slave connection is supported.
input parameters
@param taskID - User's task ID.
@param taskEvent - User's task event.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_INACTIVE_CONNECTION,
LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_ConnEventNotice( uint8 taskID, uint16 taskEvent )
{
(void) taskID;
(void) taskEvent;
return 0;
}
/*******************************************************************************
@fn LL_EXT_DisconnectImmed Vendor Specific API
@brief This function is used to disconnect the connection immediately.
Note: The connection (if valid) is immediately terminated
without notifying the remote device. The Host is still
notified.
input parameters
@param connId - The LL connection ID on which to send this data.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_INACTIVE_CONNECTION
*/
llStatus_t LL_EXT_DisconnectImmed( uint16 connId )
{
(void) connId;
return 0;
}
/*******************************************************************************
@fn LL_EXT_NumComplPktsLimit Vendor Specific API
@brief This API is used to set the minimum number of
completed packets which must be met before a Number of
Completed Packets event is returned. If the limit is not
reach by the end of the connection event, then a Number of
Completed Packets event will be returned (if non-zero) based
on the flushOnEvt flag.
input parameters
@param limit - From 1 to LL_MAX_NUM_DATA_BUFFERS.
@param flushOnEvt - LL_EXT_DISABLE_NUM_COMPL_PKTS_ON_EVENT |
LL_EXT_ENABLE_NUM_COMPL_PKTS_ON_EVENT
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_ERROR_CODE_INVALID_HCI_CMD_PARAMS
*/
llStatus_t LL_EXT_NumComplPktsLimit( uint8 limit,
uint8 flushOnEvt )
{
(void) flushOnEvt;
if ( (limit == 0) || (limit > g_maxPktPerEventTx) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// save the limit
numComplPktsLimit = limit;
return LL_STATUS_SUCCESS;
}
/*******************************************************************************
@fn LL_EXT_OnePacketPerEvent Vendor Specific API
@brief This function is used to enable or disable allowing only one
packet per event.
input parameters
@param control - LL_EXT_ENABLE_ONE_PKT_PER_EVT,
LL_EXT_DISABLE_ONE_PKT_PER_EVT
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_OnePacketPerEvent( uint8 control )
{
(void) control;
return 0;
}
/*******************************************************************************
@fn LL_EXT_OverlappedProcessing Vendor Specific API
@brief This API is used to enable or disable overlapped processing.
input parameters
@param mode - LL_EXT_ENABLE_OVERLAPPED_PROCESSING |
LL_EXT_DISABLE_OVERLAPPED_PROCESSING
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_ERROR_CODE_INVALID_HCI_CMD_PARAMS
*/
llStatus_t LL_EXT_OverlappedProcessing( uint8 mode )
{
(void) mode;
return 0;
}
/*******************************************************************************
@fn LL_EXT_PERbyChan Vendor Specific API
@brief This API is called by the HCI to start or end Packet Error Rate
by Channel counter accumulation for a connection. If the
pointer is not NULL, it is assumed there is sufficient memory
for the PER data, per the type perByChan_t. If NULL, then
the operation is considered disabled.
Note: It is the user's responsibility to make sure there is
sufficient memory for the data, and that the counters
are cleared prior to first use.
Note: The counters are only 16 bits. At the shortest connection
interval, this provides a bit over 8 minutes of data.
input parameters
@param connId - The LL connection ID on which to send this data.
@param perByChan - Pointer to PER by Channel data, or NULL.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_INACTIVE_CONNECTION
*/
llStatus_t LL_EXT_PERbyChan( uint16 connId, perByChan_t* perByChan )
{
(void) connId;
(void) perByChan;
return 0;
}
/*******************************************************************************
@fn LL_ReadAdvChanTxPower
@brief This function is used to read the transmit power level used
for BLE advertising channel packets. Currently, only two
settings are possible, a standard setting of 0 dBm, and a
maximum setting of 4 dBm.
input parameters
@param *txPower - A non-null pointer.
output parameters
@param *txPower - A signed value from -20..+10, in dBm.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_PARAM_OUT_OF_RANGE
*/
llStatus_t LL_ReadAdvChanTxPower0( int8* txPower )
{
(void) txPower;
if (g_llAdvMode == LL_MODE_EXTENDED )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_LEGACY;
return 0;
}
/*******************************************************************************
@fn LL_SetScanParam API
@brief This API is called by the HCI to set the Scanner's parameters.
input parameters
@param scanType - Passive or Active scan type.
@param scanInterval - Time between scan events.
@param scanWindow - Duration of a scan. When the same as the scan
interval, then scan continuously.
@param ownAddrType - Address type (Public or Random) to use in the
SCAN_REQ packet.
@param advWlPolicy - Either allow all Adv packets, or only those that
are in the white list.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_SetScanParam0( uint8 scanType,
uint16 scanInterval,
uint16 scanWindow,
uint8 ownAddrType,
uint8 scanWlPolicy )
{
if (g_llScanMode == LL_MODE_EXTENDED )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_LEGACY;
// sanity check of parameters
if ( ( (scanType != LL_SCAN_PASSIVE) &&
(scanType != LL_SCAN_ACTIVE) ) ||
( (ownAddrType != LL_DEV_ADDR_TYPE_PUBLIC) &&
(ownAddrType != LL_DEV_ADDR_TYPE_RANDOM) &&
(ownAddrType != LL_DEV_ADDR_TYPE_RPA_PUBLIC) &&
(ownAddrType != LL_DEV_ADDR_TYPE_RPA_RANDOM) ) ||
( (scanInterval < LL_SCAN_WINDOW_MIN) ||
(scanInterval > LL_SCAN_WINDOW_MAX) ) ||
( (scanWindow < LL_SCAN_WINDOW_MIN) ||
(scanWindow > LL_SCAN_WINDOW_MAX) ) ||
( (scanWindow > scanInterval) ) ||
( (scanWlPolicy != LL_SCAN_WL_POLICY_ANY_ADV_PKTS) &&
(scanWlPolicy != LL_SCAN_WL_POLICY_USE_WHITE_LIST) ) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// check if scan is active
if ( scanInfo.scanMode == LL_SCAN_START )
{
// yes, so not allowed per the spec
return( LL_STATUS_ERROR_COMMAND_DISALLOWED );
}
// set the scan type
scanInfo.scanType = scanType;
// save white list policy
scanInfo.wlPolicy = scanWlPolicy;
// set the scanner's address based on the HCI's address type preference
if ( ownAddrType == LL_DEV_ADDR_TYPE_PUBLIC )
{
// get our address and address type
scanInfo.ownAddrType = LL_DEV_ADDR_TYPE_PUBLIC;
LL_COPY_DEV_ADDR_LE( scanInfo.ownAddr, ownPublicAddr );
}
else if ( ownAddrType == LL_DEV_ADDR_TYPE_RANDOM )// LL_DEV_ADDR_TYPE_RANDOM
{
// get our address and address type
scanInfo.ownAddrType = LL_DEV_ADDR_TYPE_RANDOM;
LL_COPY_DEV_ADDR_LE( scanInfo.ownAddr, ownRandomAddr );
}
else
{
// for RPAs, scan control not indicate using which RPA list entry, no copy scanA here
scanInfo.ownAddrType = ownAddrType;
}
// set the scan interval
scanInfo.scanInterval = scanInterval;
// set the scan window
scanInfo.scanWindow = scanWindow;
// set the scan filter policy
// if ( scanWlPolicy == LL_SCAN_WL_POLICY_ANY_ADV_PKTS )
// {
//// PHY_SetScanWlPolicy( PHY_SCANNER_ALLOW_ALL_ADV_PKTS );
// }
// else if ( scanWlPolicy == LL_SCAN_WL_POLICY_USE_WHITE_LIST )
// {
//// PHY_SetScanWlPolicy( PHY_SCANNER_USE_WHITE_LIST );
// }
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_SetScanControl API
@brief This API is called by the HCI to start or stop the Scanner. It
also specifies whether the LL will filter duplicate advertising
reports to the Host, or generate a report for each packet
received.
input parameters
@param scanMode - LL_SCAN_START or LL_SCAN_STOP.
@param filterReports - LL_FILTER_REPORTS_DISABLE or
LL_FILTER_REPORTS_ENABLE
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_UNEXPECTED_PARAMETER,
LL_STATUS_ERROR_OUT_OF_TX_MEM,
LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE
*/
llStatus_t LL_SetScanControl0( uint8 scanMode,
uint8 filterReports )
{
if (g_llScanMode == LL_MODE_EXTENDED )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_LEGACY;
// check if a direct test mode or modem test is in progress
if ( (llState == LL_STATE_DIRECT_TEST_MODE_TX) ||
(llState == LL_STATE_DIRECT_TEST_MODE_RX) ||
(llState == LL_STATE_MODEM_TEST_TX) ||
(llState == LL_STATE_MODEM_TEST_RX) ||
(llState == LL_STATE_MODEM_TEST_TX_FREQ_HOPPING) )
{
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
}
// sanity checks again to be sure we don't start with bad parameters
if ( ( (scanInfo.scanType != LL_SCAN_PASSIVE) &&
(scanInfo.scanType != LL_SCAN_ACTIVE)) ||
( (scanInfo.ownAddrType != LL_DEV_ADDR_TYPE_PUBLIC) &&
(scanInfo.ownAddrType != LL_DEV_ADDR_TYPE_RANDOM)) ||
( (scanInfo.scanInterval < LL_SCAN_WINDOW_MIN) ||
(scanInfo.scanInterval > LL_SCAN_WINDOW_MAX)) ||
( (scanInfo.scanWindow < LL_SCAN_WINDOW_MIN) ||
(scanInfo.scanWindow > LL_SCAN_WINDOW_MAX)) ||
( (scanInfo.scanWindow > scanInfo.scanInterval) ) ||
( (filterReports != LL_FILTER_REPORTS_DISABLE) &&
(filterReports != LL_FILTER_REPORTS_ENABLE)) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// check if we should begin scanning
switch( scanMode )
{
// Scanning Mode is On
case LL_SCAN_START:
// check if command makes sense
if ( scanInfo.scanMode == LL_SCAN_START )
{
// this is unexpected; something is wrong
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
}
// get a task block for this BLE state/role
// Note: There will always be a valid pointer, so no NULL check required.
// scanInfo.llTask = llAllocTask( LL_TASK_ID_SCANNER );
// check if no other tasks are currently active
if ( llState == LL_STATE_IDLE )
{
// indicate Scan has not already been initalized
scanInfo.initPending = TRUE;
// save the scan filtering flag
scanInfo.filterReports = filterReports;
// add by HZF
scanInfo.nextScanChan = LL_SCAN_ADV_CHAN_37;
// set LL state
llState = LL_STATE_SCAN;
// Note: llState has been changed.
LL_evt_schedule();
}
else if ((llState == LL_STATE_CONN_SLAVE
|| llState == LL_STATE_CONN_MASTER) // HZF: if we should support adv + scan, add more state here
&& (pGlobal_config[LL_SWITCH] & SIMUL_CONN_SCAN_ALLOW))
{
if (llSecondaryState != LL_SEC_STATE_IDLE)
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
scanInfo.nextScanChan = LL_SCAN_ADV_CHAN_37;
llSecondaryState = LL_SEC_STATE_SCAN;
osal_set_event(LL_TaskID, LL_EVT_SECONDARY_SCAN);
}
else
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
// indicate we are actively scanning
scanInfo.scanMode = LL_SCAN_START;
break;
case LL_SCAN_STOP:
HAL_ENTER_CRITICAL_SECTION();
if (llState == LL_STATE_SCAN) // no conn + scan case
{
llState = LL_STATE_IDLE; // if not in connect state, set idle to disable scan
//ZQ 20190912
//stop ll timer when idle, considering the scan-adv interleve case
clear_timer(AP_TIM1);
ll_debug_output(DEBUG_LL_STATE_IDLE);
}
else if (llState == LL_STATE_CONN_SLAVE
|| llState == LL_STATE_CONN_MASTER) // conn + scan case
{
llSecondaryState = LL_SEC_STATE_IDLE;
}
// indicate we are no longer actively scanning
scanInfo.scanMode = LL_SCAN_STOP;
// A2 multiconn, should we consider current LL state to avoid change master/slave configuration
// now LL slave/master event use same parameter 88
ll_hw_set_rx_timeout(88);
// HZF: should we stop scan task immediately, or wait scan IRQ then stop? Now use option 2.
HAL_EXIT_CRITICAL_SECTION();
break;
default:
// we have an invalid value for advertisement mode
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This API is called by the HCI to set the Advertiser's Scan Response data.
Public function defined in ll.h.
*/
llStatus_t LL_SetScanRspData( uint8 scanRspLen,
uint8* scanRspData )
{
if (g_llAdvMode == LL_MODE_EXTENDED )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_LEGACY;
// check that data length isn't greater than the max allowed size
if ( scanRspLen > LL_MAX_SCAN_DATA_LEN )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// save scan response data length
adv_param.scanRspLen = scanRspLen;
// check if there is any scan response data
if ( scanRspLen > 0 )
{
// yes, so make sure we have a valid pointer
if ( scanRspData == NULL )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
else // okay to go
{
// save scan response data
osal_memcpy( (uint8_t*) &(tx_scanRsp_desc.data[6]), scanRspData, adv_param.scanRspLen );
}
}
SET_BITS(tx_scanRsp_desc.txheader, (adv_param .scanRspLen+6), LENGTH_SHIFT, LENGTH_MASK);
//osal_memcpy( tx_scanRsp_desc.data, adv_param.ownAddr, 6);
return( LL_STATUS_SUCCESS );
}
#ifdef USE_UNPATCHED
/*******************************************************************************
@fn LL_EncLtkReply API
@brief This API is called by the HCI to provide the controller with
the Long Term Key (LTK) for encryption. This command is
actually a reply to the link layer's LL_EncLtkReqCback, which
provided the random number and encryption diversifier received
from the Master during an encryption setup.
Note: The key parameter is byte ordered LSO to MSO.
input parameters
@param connId - The LL connection ID on which to send this data.
@param *key - A 128 bit key to be used to calculate the session key.
output parameters
@param None.
@return LL_STATUS_SUCCESS
*/
llStatus_t LL_EncLtkReply( uint16 connId,
uint8* key )
{
uint8 i;
llStatus_t status;
llConnState_t* connPtr;
// make sure we're in Master role
if ( llState != LL_STATE_CONN_SLAVE )
{
return( LL_STATUS_ERROR_COMMAND_DISALLOWED );
}
// check parameters
if ( key == NULL )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// make sure connection ID is valid
if ( (status=LL_ConnActive(connId)) != LL_STATUS_SUCCESS )
{
return( status );
}
// get connection info
connPtr = &conn_param[ connId ];
// ALT: COULD MAKE THIS PER CONNECTION.
// save LTK
for (i=0; i<LL_ENC_LTK_LEN; i++)
{
// store LTK in MSO..LSO byte order, per FIPS 197 (AES)
connPtr->encInfo.LTK[(LL_ENC_LTK_LEN-i)-1] = key[i];
}
// indicate the host has provided the key
connPtr->encInfo.LTKValid = TRUE;
// got the LTK, so schedule the start of encryption
// ALT: COULD MAKE THIS A REPLACE IF A DUMMY IS SITTING AT THE HEAD OF
// THE QUEUE.
llEnqueueCtrlPkt( connPtr, LL_CTRL_START_ENC_REQ );
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_EncLtkNegReply API
@brief This API is called by the HCI to indicate to the controller
that the Long Term Key (LTK) for encryption can not be provided.
This command is actually a reply to the link layer's
LL_EncLtkReqCback, which provided the random number and
encryption diversifier received from the Master during an
encryption setup. How the LL responds to the negative reply
depends on whether this is part of a start encryption or a
re-start encryption after a pause. For the former, an
encryption request rejection is sent to the peer device. For
the latter, the connection is terminated.
input parameters
@param connId - The LL connection ID on which to send this data.
output parameters
@param None.
@return LL_STATUS_SUCCESS
*/
llStatus_t LL_EncLtkNegReply( uint16 connId )
{
llStatus_t status;
llConnState_t* connPtr;
// make sure we're in Master role
if ( llState != LL_STATE_CONN_SLAVE )
{
return( LL_STATUS_ERROR_COMMAND_DISALLOWED );
}
// make sure connection ID is valid
if ( (status=LL_ConnActive(connId)) != LL_STATUS_SUCCESS )
{
return( status );
}
// get connection info
connPtr = &conn_param[ connId ];
// check if this is during a start or a re-start encryption procedure
if ( connPtr->encInfo.encRestart == TRUE )
{
// indicate the peer requested this termination
connPtr->termInfo.reason = LL_ENC_KEY_REQ_REJECTED;
// queue control packet for processing
// ALT: COULD MAKE THIS A REPLACE IF A DUMMY IS SITTING AT THE HEAD OF
// THE QUEUE.
//llReplaceCtrlPkt( connPtr, LL_CTRL_TERMINATE_IND );
llEnqueueCtrlPkt( connPtr, LL_CTRL_TERMINATE_IND );
}
else // during a start encryption
{
// set the encryption rejection error code
connPtr->encInfo.encRejectErrCode = LL_STATUS_ERROR_PIN_OR_KEY_MISSING; // same as LL_ENC_KEY_REQ_REJECTED
// and reject the encryption request
// ALT: COULD MAKE THIS A REPLACE IF A DUMMY IS SITTING AT THE HEAD OF
// THE QUEUE.
//llReplaceCtrlPkt( connPtr, LL_CTRL_REJECT_IND );
llEnqueueCtrlPkt( connPtr, LL_CTRL_REJECT_IND );
}
return( LL_STATUS_SUCCESS );
}
#endif // USE_UNPATCHED
/*******************************************************************************
This API is called by the HCI to read the controller's own public device
address.
Note: The device's address is stored in NV memory.
Public function defined in ll.h.
*/
llStatus_t LL_ReadBDADDR( uint8* bdAddr )
{
// return own public device address LSO..MSO
bdAddr[0] = ownPublicAddr[0];
bdAddr[1] = ownPublicAddr[1];
bdAddr[2] = ownPublicAddr[2];
bdAddr[3] = ownPublicAddr[3];
bdAddr[4] = ownPublicAddr[4];
bdAddr[5] = ownPublicAddr[5];
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This API is called by the HCI to read the controller's Feature Set. The
Controller indicates which features it supports.
Public function defined in ll.h.
*/
llStatus_t LL_ReadLocalSupportedFeatures( uint8* featureSet )
{
uint8 i;
// copy Feature Set
for (i=0; i<LL_MAX_FEATURE_SET_SIZE; i++)
{
featureSet[i] = deviceFeatureSet.featureSet[i];
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This function is used to save this device's random address. It is provided by
the Host for devices that are unable to store an IEEE assigned public address
in NV memory.
Public function defined in ll.h.
*/
llStatus_t LL_SetRandomAddress( uint8* devAddr )
{
// // add for BQB test 2018-9-20, LL/SEC/ADV/BV-01-C
// // check if advertising is active
// if ( adv_param.advMode == LL_ADV_MODE_ON )
// {
// // yes, so not allowed per the spec
// return( LL_STATUS_ERROR_COMMAND_DISALLOWED );
// }
if ( (llState == LL_STATE_ADV_UNDIRECTED) ||
(llState == LL_STATE_ADV_DIRECTED) ||
(llState == LL_STATE_ADV_SCAN) ||
(llState == LL_STATE_ADV_NONCONN) ||
(llState == LL_STATE_SCAN) ||
(llState == LL_STATE_INIT) ||
(llSecondaryState == LL_SEC_STATE_ADV) ||
(llSecondaryState == LL_SEC_STATE_ADV_PENDING) ||
(llSecondaryState == LL_SEC_STATE_SCAN) ||
(llSecondaryState == LL_SEC_STATE_SCAN_PENDING) ||
(llSecondaryState == LL_SEC_STATE_INIT) ||
(llSecondaryState == LL_SEC_STATE_INIT_PENDING))
{
return( LL_STATUS_ERROR_COMMAND_DISALLOWED );
}
// store our random address LSO..MSO
ownRandomAddr[0] = devAddr[0];
ownRandomAddr[1] = devAddr[1];
ownRandomAddr[2] = devAddr[2];
ownRandomAddr[3] = devAddr[3];
ownRandomAddr[4] = devAddr[4];
ownRandomAddr[5] = devAddr[5];
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This API is called by the HCI to update the connection parameters by
initiating a connection update control procedure.
Public function defined in ll.h.
*/
llStatus_t LL_ConnUpdate( uint16 connId,
uint16 connIntervalMin,
uint16 connIntervalMax,
uint16 connLatency,
uint16 connTimeout,
uint16 minLength,
uint16 maxLength )
{
llStatus_t status;
llConnState_t* connPtr;
// unused input parameter; PC-Lint error 715.
(void)minLength;
(void)maxLength;
// make sure we're in Master role
if ( llState != LL_STATE_CONN_MASTER )
{
return( LL_STATUS_ERROR_COMMAND_DISALLOWED );
}
// sanity checks again to be sure we don't start with bad parameters
if ( LL_INVALID_CONN_TIME_PARAM( connIntervalMin,
connIntervalMax,
connLatency,
connTimeout ) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// make sure connection ID is valid
if ( (status=LL_ConnActive(connId)) != LL_STATUS_SUCCESS )
{
return( status );
}
// get connection info
connPtr = &conn_param[connId];
// check if an updated parameters control procedure is already what's pending
if ( ((connPtr->ctrlPktInfo.ctrlPktCount > 0) &&
(connPtr->ctrlPktInfo.ctrlPkts[0] == LL_CTRL_CONNECTION_UPDATE_REQ)) ||
(connPtr->pendingParamUpdate == TRUE) )
{
return( LL_STATUS_ERROR_CTRL_PROC_ALREADY_ACTIVE );
}
// check if CI/SL/LSTO is valid (i.e. meets the requirements)
// Note: LSTO > (1 + Slave Latency) * (Connection Interval * 2)
// Note: The CI * 2 requirement based on ESR05 V1.0, Erratum 3904.
// Note: LSTO time is normalized to units of 1.25ms (i.e. 10ms = 8 * 1.25ms).
if ( LL_INVALID_CONN_TIME_PARAM_COMBO(connIntervalMax, connLatency, connTimeout) )
{
return( LL_STATUS_ERROR_ILLEGAL_PARAM_COMBINATION );
}
// if there is at least one connection, make sure this connection interval
// is a multiple/divisor of all other active connection intervals; also make
// sure that this connection's interval is not less than the allowed maximum
// connection interval as determined by the maximum number of allowed
// connections times the number of slots per connection.
if ( g_ll_conn_ctx.numLLMasterConns > 0 ) // if ( g_ll_conn_ctx.numLLConns > 0 )
{
uint16 connInterval = (connIntervalMax << 1); // convert to 625us ticks
uint16 minCI = g_ll_conn_ctx.connInterval;
// // first check if this connection interval is even legal
// // Note: The number of active connections is limited by the minCI.
// if ( (minCI / NUM_SLOTS_PER_MASTER) < llConns.numActiveConns )
// {
// return( LL_STATUS_ERROR_UNACCEPTABLE_CONN_INTERVAL );
// }
// // does the CI need to be checked as a multiple of the minCI?
if ( connInterval >= minCI )
{
// check if this connection's CI is valid (i.e. a multiple of minCI)
if ( connInterval % minCI )
{
return( LL_STATUS_ERROR_UNACCEPTABLE_CONN_INTERVAL );
}
}
else
return( LL_STATUS_ERROR_UNACCEPTABLE_CONN_INTERVAL );
}
// no control procedure currently active, so set this one up
// set the window size (units of 1.25ms)
connPtr->paramUpdate.winSize = LL_WINDOW_SIZE;
// set the window offset (units of 1.25ms)
// connPtr->paramUpdate.winOffset = LL_WINDOW_OFFSET;
connPtr->paramUpdate.winOffset = 0; // multiconnection, this value could be 0 or x * old conn interval and should be less than new conn interval
// set the relative offset of the number of events for the parameter update
// Note: The absolute event number will be determined at the time the packet
// is placed in the TX FIFO.
// Note: The master should allow a minimum of 6 connection events that the
// slave will be listening for before the instant occurs.
connPtr->paramUpdateEvent = (connPtr->curParam.slaveLatency+1) +
LL_INSTANT_NUMBER_MIN;
// determine the connection interval based on min and max values
// Note: Range not used, so assume max value.
// Note: minLength and maxLength are informational.
connPtr->paramUpdate.connInterval = connIntervalMax;
// save the new connection slave latency to be used by the peer
connPtr->paramUpdate.slaveLatency = connLatency;
// save the new connection supervisor timeout
connPtr->paramUpdate.connTimeout = connTimeout;
// queue control packet for processing
llEnqueueCtrlPkt( connPtr, LL_CTRL_CONNECTION_UPDATE_REQ );
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This API is called by the HCI to create a connection.
Public function defined in ll.h.
*/
// TODO: check the usage of new enum value of ownAddrType/peerAddrType
llStatus_t LL_CreateConn0( uint16 scanInterval,
uint16 scanWindow,
uint8 initWlPolicy,
uint8 peerAddrType,
uint8* peerAddr,
uint8 ownAddrType,
uint16 connIntervalMin,
uint16 connIntervalMax,
uint16 connLatency,
uint16 connTimeout,
uint16 minLength, // minimum length of connection needed for this LE conn, no use now
uint16 maxLength ) // maximum length of connection needed for this LE conn, no use now
{
uint8 i;
llConnState_t* connPtr;
uint16 txHeader = 0x2205; // header for CONNECT REQ message, length: 0x22, PDU type: 0x5, TxAdd & RxAdd to be set below
if (g_llScanMode == LL_MODE_EXTENDED )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_LEGACY;
//---------------------------------------------------
// protect for the LL_HW re-trigger
// 20190115 ZQ
// if(llWaitingIrq==TRUE) // HZF: temp comment out
// {
// return(LL_STATUS_WARNING_WAITING_LLIRQ);
// }
// ================== sanity check =================
// check if a direct test mode or modem test is in progress
if ( (llState == LL_STATE_DIRECT_TEST_MODE_TX) ||
(llState == LL_STATE_DIRECT_TEST_MODE_RX) ||
(llState == LL_STATE_MODEM_TEST_TX) ||
(llState == LL_STATE_MODEM_TEST_RX) ||
(llState == LL_STATE_MODEM_TEST_TX_FREQ_HOPPING) )
{
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
}
// sanity checks again to be sure we don't start with bad parameters
if ( ( (scanInterval < LL_SCAN_INTERVAL_MIN) ||
(scanInterval > LL_SCAN_INTERVAL_MAX) ) ||
( (scanWindow < LL_SCAN_INTERVAL_MIN) ||
(scanWindow > LL_SCAN_INTERVAL_MAX) ) ||
( (scanWindow > scanInterval) ) ||
( (initWlPolicy != LL_INIT_WL_POLICY_USE_PEER_ADDR) &&
(initWlPolicy != LL_INIT_WL_POLICY_USE_WHITE_LIST) ) ||
( (initWlPolicy == LL_INIT_WL_POLICY_USE_PEER_ADDR) &&
(peerAddr == NULL) ) ||
( (peerAddrType != LL_DEV_ADDR_TYPE_PUBLIC) &&
(peerAddrType != LL_DEV_ADDR_TYPE_RANDOM) &&
(peerAddrType != LL_DEV_ADDR_TYPE_RPA_RANDOM) &&
(peerAddrType != LL_DEV_ADDR_TYPE_RPA_PUBLIC)) ||
( (ownAddrType != LL_DEV_ADDR_TYPE_PUBLIC) &&
(ownAddrType != LL_DEV_ADDR_TYPE_RANDOM) &&
(ownAddrType != LL_DEV_ADDR_TYPE_RPA_RANDOM) &&
(ownAddrType != LL_DEV_ADDR_TYPE_RPA_PUBLIC)) ||
( (maxLength < minLength) ) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// The Host shall not set Peer_Address_Type to either 0x02 or 0x03 if both the Host and the Controller
// support the HCI_LE_Set_Privacy_Mode command. If a Controller that supports the HCI_LE_Set_Privacy_Mode
// command receives the HCI_LE_Create_Connection command with Peer_Address_Type set to either
// 0x02 or 0x03, it may use either device privacy mode or network privacy mode for that peer device.
// if(((peerAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM) ||
// (peerAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC)) ||
// ((ownAddrType == LL_DEV_ADDR_TYPE_RPA_RANDOM) ||
// (ownAddrType == LL_DEV_ADDR_TYPE_RPA_PUBLIC)))
// { // these values shall only be used by the Host if either the Host or the Controller does not support the HCI_LE_Set_Privacy_Mode command
// return( LL_STATUS_ERROR_BAD_PARAMETER );
// }
// sanity checks again to be sure we don't start with bad parameters
if ( LL_INVALID_CONN_TIME_PARAM( connIntervalMin,
connIntervalMax,
connLatency,
connTimeout ) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// check if CI/SL/LSTO is valid (i.e. meets the requirements)
// Note: LSTO > (1 + Slave Latency) * (Connection Interval * 2)
// Note: The CI * 2 requirement based on ESR05 V1.0, Erratum 3904.
// Note: LSTO time is normalized to units of 1.25ms (i.e. 10ms = 8 * 1.25ms).
if ( LL_INVALID_CONN_TIME_PARAM_COMBO(connIntervalMax, connLatency, connTimeout) )
{
return( LL_STATUS_ERROR_ILLEGAL_PARAM_COMBINATION );
}
// multi-connction limits check
if (g_ll_conn_ctx.numLLConns >= g_maxConnNum)
{
return( LL_STATUS_ERROR_OUT_OF_CONN_RESOURCES );
}
// if there is at least one connection, make sure this connection interval
// is a multiple/divisor of all other active connection intervals; also make
// sure that this connection's interval is not less than the allowed maximum
// connection interval as determined by the maximum number of allowed
// connections times the number of slots per connection.
if ( g_ll_conn_ctx.numLLMasterConns > 0 ) // if ( g_ll_conn_ctx.numLLConns > 0 )
{
uint16 connInterval = (connIntervalMax << 1); // convert to 625us ticks
uint16 minCI = g_ll_conn_ctx.connInterval;
// // first check if this connection interval is even legal
// // Note: The number of active connections is limited by the minCI.
// if ( (minCI / NUM_SLOTS_PER_MASTER) < llConns.numActiveConns )
// {
// return( LL_STATUS_ERROR_UNACCEPTABLE_CONN_INTERVAL );
// }
// // does the CI need to be checked as a multiple of the minCI?
if ( connInterval >= minCI )
{
// check if this connection's CI is valid (i.e. a multiple of minCI)
if ( connInterval % minCI )
{
return( LL_STATUS_ERROR_UNACCEPTABLE_CONN_INTERVAL );
}
}
else
return( LL_STATUS_ERROR_UNACCEPTABLE_CONN_INTERVAL );
}
else
{
// TODO: should we consider if there is only slave connection, using the interval of slave as the standard interval of master?
// how could application know the slave interval?
}
// // check if an update channel map control procedure is already pending
// // Note: This is the case where the control procedure is enqueued, but the
// // control packet has not yet been sent OTA. At this point, it is
// // easier to simply reject the new connection until that happens. Once
// // the packet is sent, and the pendingParamUpdate flag is set, the
// // update connection interval is checked by llGetMinCI.
// if ( llPendingUpdateParam() == TRUE )
// {
// return( LL_STATUS_ERROR_UPDATE_CTRL_PROC_PENDING );
// }
// check if the white list policy uses the peer address, it is valid
if ( (initWlPolicy == LL_INIT_WL_POLICY_USE_PEER_ADDR) && (peerAddr == NULL) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
if (llState == LL_STATE_CONN_MASTER || llState == LL_STATE_CONN_SLAVE)
{
if (llSecondaryState == LL_SEC_STATE_IDLE)
llSecondaryState = LL_SEC_STATE_INIT;
else
return LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE;
}
// allocate a connection and assure it is valid
if ( (connPtr = llAllocConnId()) == NULL )
{
llSecondaryState = LL_SEC_STATE_IDLE; // recover llSecondaryState
// exceeded the number of available connection structures
return( LL_STATUS_ERROR_CONNECTION_LIMIT_EXCEEDED );
}
g_ll_conn_ctx.numLLMasterConns ++;
// connId = 0;
// connPtr = &conn_param[connId];
// reset connection parameters
LL_set_default_conn_params(connPtr);
// clear the connection buffer
reset_conn_buf(connPtr->connId);
// save the connection ID with Init
initInfo.connId = connPtr->connId;
// set the scan interval
initInfo.scanInterval = scanInterval;
// set the scan window
initInfo.scanWindow = scanWindow;
// set the Init white list policy
initInfo.wlPolicy = initWlPolicy;
// set the connection channel map
for (i = 0; i < LL_NUM_BYTES_FOR_CHAN_MAP; i++)
{
connPtr->chanMap[i] = chanMapUpdate.chanMap[i];
}
// process connection channel map into the data channel table
llProcessChanMap( connPtr, connPtr->chanMap );
// Core spec V4.0 requires that a minimum of two data channels be used
if ( connPtr->numUsedChans < 2 )
{
// it isn't, so release the resource
llReleaseConnId( connPtr );
g_ll_conn_ctx.numLLMasterConns --;
return( LL_STATUS_ERROR_ILLEGAL_PARAM_COMBINATION );
}
// save the peer address
if (peerAddr != NULL) // bug fixed
LL_COPY_DEV_ADDR_LE( peerInfo.peerAddr, peerAddr );
// save our address type
initInfo.ownAddrType = ownAddrType;
// check the type of own address
if ( ownAddrType == LL_DEV_ADDR_TYPE_PUBLIC )
{
// save our address
LL_COPY_DEV_ADDR_LE( initInfo.ownAddr, ownPublicAddr );
}
else // LL_DEV_ADDR_TYPE_RANDOM
{
// save our address
LL_COPY_DEV_ADDR_LE( initInfo.ownAddr, ownRandomAddr );
}
// save the peer address type
peerInfo.peerAddrType = peerAddrType;
txHeader |= (ownAddrType << TX_ADD_SHIFT & TX_ADD_MASK);
txHeader |= (peerAddrType << RX_ADD_SHIFT & RX_ADD_MASK);
// ramdomly generate a valid 24 bit CRC value
connPtr->initCRC = llGenerateCRC();
// randomly generate a valid, previously unused, 32-bit access address
connPtr->accessAddr = llGenerateValidAccessAddr();
// g_ll_conn_ctx.scheduleInfo[connPtr->allocConn].linkRole = LL_ROLE_MASTER; // will change the role in move_to_master_function
if (g_ll_conn_ctx.numLLMasterConns == 1) // A2 multi-connection, 1st connection, save the connection parameters
{
// determine the connection interval based on min and max values
// Note: Range not used, so assume max value.
// Note: minLength and maxLength are informational.
connPtr->curParam.connInterval = connIntervalMax;
// set the connection timeout
// Note: The spec says this begins at the end of the CONNECT_REQ, but the
// LSTO will be converted into events.
connPtr->curParam.connTimeout = connTimeout;
// set the slave latency
connPtr->curParam.slaveLatency = connLatency;
// save connection parameter as global
g_ll_conn_ctx.connInterval = connPtr->curParam.connInterval; // unit: 1.25ms
g_ll_conn_ctx.slaveLatency = connPtr->curParam.slaveLatency;
g_ll_conn_ctx.connTimeout = connPtr->curParam.connTimeout;
g_ll_conn_ctx.per_slot_time = connPtr->curParam.connInterval * 2 / g_maxConnNum; // unit: 625us
}
else
{
// determine the connection interval based on min and max values
// Note: Range not used, so assume max value.
// Note: minLength and maxLength are informational.
connPtr->curParam.connInterval = g_ll_conn_ctx.connInterval;
// set the connection timeout
// Note: The spec says this begins at the end of the CONNECT_REQ, but the
// LSTO will be converted into events.
connPtr->curParam.connTimeout = g_ll_conn_ctx.connTimeout;
// set the slave latency
connPtr->curParam.slaveLatency = g_ll_conn_ctx.slaveLatency;
}
// set the master's SCA
connPtr->sleepClkAccuracy = initInfo.scaValue;
// set the window size (units of 1.25ms)
// Note: Must be the lesser of 10ms and the connection interval - 1.25ms.
connPtr->curParam.winSize = pGlobal_config[LL_CONN_REQ_WIN_SIZE];//5;//LL_WINDOW_SIZE;
// set the window offset (units of 1.25ms). TO change if we support multiple connections
// Note: Normally, the window offset is managed dynamically so that precise
// connection start times can be achieved (necessary for multiple
// connnections). However, sometimes it is useful to force the window
// offset to something specific for testing. This can be done by here
// when the project is built with the above define.
// Note: This define should only be used for testing one connection and will
// NOT work when multiple connections are attempted!
connPtr->curParam.winOffset = pGlobal_config[LL_CONN_REQ_WIN_OFFSET];//2;//LL_WINDOW_OFFSET;
// set the channel map hop length (5..16)
// Note: 0..255 % 12 = 0..11 + 5 = 5..16.
connPtr->hop = (uint8)( (LL_ENC_GeneratePseudoRandNum() % 12) + 5);
// track advertising channel for advancement at each scan window
initInfo.nextScanChan = LL_SCAN_ADV_CHAN_37;
// enable Init scan
initInfo.scanMode = LL_SCAN_START;
// check if this is the only task
// Note: If there are one or more master connections already running, then
// the Init task will be scheduled when the next connection ends.
{
// ============= construct CONN REQ message payload in g_tx_adv_buf buffer. Using byte copy to avoid
// hardfault cause by no word align reading
uint8 offset = 0;
g_tx_adv_buf.txheader = txHeader;
// setup CONN REQ in connPtr->ll_buf
LL_ReadBDADDR(&g_tx_adv_buf.data[offset]); // initA, Byte 0 ~ 5
offset += 6;
if (peerAddr != NULL)
LL_COPY_DEV_ADDR_LE(&g_tx_adv_buf.data[offset], peerAddr) // AdvA, Byte 6 ~ 11
offset += 6;
// Access Address, Byte 12 ~ 15
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->accessAddr, 4);
offset += 4;
// CRC init, Byte 16 ~ 18
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->initCRC, 3);
offset += 3;
// WinSize, Byte 19
g_tx_adv_buf.data[offset] = connPtr->curParam.winSize;
offset += 1;
// WinOffset, Byte 20 ~ 21
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->curParam.winOffset, 2);
offset += 2;
// Interval, Byte 22 ~ 23
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->curParam.connInterval, 2);
offset += 2;
// Latency, Byte 24 ~ 25
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->curParam.slaveLatency, 2);
offset += 2;
// Timeout, Byte 26 ~ 27
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->curParam.connTimeout, 2);
offset += 2;
// Channel Map, Byte 28 ~ 32
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->chanMap[0], 5);
offset += 5;
// Hop(5bit) + SCA(3bit), Byte 33
g_tx_adv_buf.data[offset] = (connPtr->hop & 0x1f) | ((connPtr->sleepClkAccuracy & 0x7) << 5);
// go ahead and start Init immediately
// llSetupInit( connPtr->connId );
// TODO: calc new connection start time and set dynamic window offset
//llSetupConn();
}
if ( llState == LL_STATE_IDLE )
// go ahead and start Init immediately
llSetupInit( connPtr->connId );
else
osal_set_event(LL_TaskID, LL_EVT_SECONDARY_INIT);
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This API is called by the HCI to cancel a previously given LL connection
creation command that is still pending. This command should only be used
after the LL_CreateConn command as been issued, but before the
LL_ConnComplete callback.
Public function defined in ll.h.
*/
llStatus_t LL_CreateConnCancel0( void )
{
uint8 cancel_now = FALSE;
uint8 connId;
// ensure Init is active
if ( initInfo.scanMode == LL_SCAN_STOP && extInitInfo.scanMode == LL_SCAN_STOP)
{
// no create connection in progress
return( LL_STATUS_ERROR_COMMAND_DISALLOWED );
}
// if scan has not trigger, release conn ID and report. Note: we assume only 1 type of scan could be enabled at the same time
if (initInfo.scanMode == LL_SCAN_START)
connId = initInfo.connId;
else if (extInitInfo.scanMode == LL_SCAN_START)
connId = extInitInfo.connId;
else
return LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE;
if (llState == LL_STATE_INIT && !llWaitingIrq) // primary LL state is init case
{
cancel_now = TRUE;
llState = LL_STATE_IDLE;
}
else if( llSecondaryState == LL_SEC_STATE_INIT && !llWaitingIrq )
{
cancel_now = TRUE;
llSecondaryState = LL_SEC_STATE_IDLE;
}
else if (llSecondaryState == LL_SEC_STATE_INIT_PENDING) // secondary LL state is init case
{
cancel_now = TRUE;
llSecondaryState = LL_SEC_STATE_IDLE;
}
if (extInitInfo.scanMode == LL_SCAN_START && llTaskState != LL_TASK_EXTENDED_INIT) // extended init case
cancel_now = TRUE;
// indicate we are no longer actively scanning
initInfo.scanMode = LL_SCAN_STOP;
extInitInfo.scanMode = LL_SCAN_STOP;
// if the scan is not ongoing, release conn ID
if (cancel_now == TRUE)
{
llReleaseConnId(&conn_param[connId]);
g_ll_conn_ctx.numLLMasterConns --;
(void)osal_set_event( LL_TaskID, LL_EVT_MASTER_CONN_CANCELLED ); // inform high layer
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
This API is called by the Master HCI to setup encryption and to update
encryption keys in the LL connection. If the connection is already in
encryption mode, then this command will first pause the encryption before
subsequently running the encryption setup.
Public function defined in ll.h.
*/
llStatus_t LL_StartEncrypt0( uint16 connId,
uint8* rand,
uint8* eDiv,
uint8* ltk )
{
uint8 i;
llStatus_t status;
llConnState_t* connPtr;
// make sure we're in Master role
if ( llState != LL_STATE_CONN_MASTER )
{
return( LL_STATUS_ERROR_COMMAND_DISALLOWED );
}
// check parameters
if ( (rand == NULL) || (eDiv == NULL) || (ltk == NULL) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// make sure connection ID is valid
if ( (status=LL_ConnActive(connId)) != LL_STATUS_SUCCESS )
{
return( status );
}
// get connection info
connPtr = &conn_param[connId];
// check if a feature response control procedure has taken place
if ( connPtr->featureSetInfo.featureRspRcved == FALSE )
{
// it hasn't so re-load this device's local Feature Set to the
// connection as it may have been changed by the Host with HCI
// extenstion Set Local Feature Set command
for (i=0; i<LL_MAX_FEATURE_SET_SIZE; i++)
{
connPtr->featureSetInfo.featureSet[i] = deviceFeatureSet.featureSet[i];
}
}
// check if encryption is a supported feature set item
if ( (connPtr->featureSetInfo.featureSet[0] & LL_FEATURE_ENCRYPTION) != LL_FEATURE_ENCRYPTION )
{
return( LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED );
}
// cache the master's random vector
// Note: The RAND will be left in LSO..MSO order as this is assumed to be the
// order of the bytes that will be returned to the Host.
for (i=0; i<LL_ENC_RAND_LEN; i++)
{
connPtr->encInfo.RAND[i] = rand[i];
}
// cache the master's encryption diversifier
// Note: The EDIV will be left in LSO..MSO order as this is assumed to be the
// order of the bytes that will be returned to the Host.
connPtr->encInfo.EDIV[0] = eDiv[0];
connPtr->encInfo.EDIV[1] = eDiv[1];
// cache the master's long term key
// Note: The order of the bytes will be maintained as MSO..LSO
// per FIPS 197 (AES).
for (i=0; i<LL_ENC_LTK_LEN; i++)
{
connPtr->encInfo.LTK[(LL_ENC_LTK_LEN-i)-1] = ltk[i];
}
// generate SKDm
// Note: The SKDm LSO is the LSO of the SKD.
// Note: Placement of result forms concatenation of SKDm and SKDs.
// Note: The order of the bytes will be maintained as MSO..LSO
// per FIPS 197 (AES).
LL_ENC_GenDeviceSKD( &connPtr->encInfo.SKD[ LL_ENC_SKD_M_OFFSET ] );
// generate IVm
// Note: The IVm LSO is the LSO of the IV.
// Note: Placement of result forms concatenation of IVm and IVs.
// Note: The order of the bytes will be maintained as MSO..LSO
// per FIPS 197 (AES).
LL_ENC_GenDeviceIV( &connPtr->encInfo.IV[ LL_ENC_IV_M_OFFSET ] );
// schedule a cache update of FIPS TRNG values for next SKD/IV usage
// postRfOperations |= LL_POST_RADIO_CACHE_RANDOM_NUM;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>cachedTRNGdata<74><61><EFBFBD><EFBFBD>ʵ<EFBFBD><CAB5>masterʱ<72><CAB1><EFBFBD><EFBFBD><EFBFBD>
(void)LL_ENC_GenerateTrueRandNum( cachedTRNGdata, LL_ENC_TRUE_RAND_BUF_SIZE );
// set flag to stop all outgoing transmissions
connPtr->txDataEnabled = FALSE;
// invalidate the existing session key, if any
connPtr->encInfo.SKValid = FALSE;
// indicate the LTK is not valid
connPtr->encInfo.LTKValid = FALSE;
// check if we are already in encryption mode
if ( connPtr->encEnabled == TRUE )
{
// set a flag to indicate this is a restart (i.e. pause-then-start)
connPtr->encInfo.encRestart = TRUE;
// setup a pause encryption control procedure
llEnqueueCtrlPkt( connPtr, LL_CTRL_PAUSE_ENC_REQ );
}
else // no, so...
{
// clear flag to indicate this is an encryption setup
connPtr->encInfo.encRestart = FALSE;
// setup an encryption control procedure
llEnqueueCtrlPkt( connPtr, LL_CTRL_ENC_REQ );
}
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_WriteSuggestedDefaultDataLength(uint16 TxOctets,uint16 TxTime)
{
if(TxOctets > LL_PDU_LENGTH_SUPPORTED_MAX_TX_OCTECTS
|| TxTime > LL_PDU_LENGTH_SUPPORTED_MAX_TX_TIME
|| TxOctets < LL_PDU_LENGTH_INITIAL_MAX_TX_OCTECTS
|| TxTime < LL_PDU_LENGTH_INITIAL_MAX_TX_TIME)
{
return(LL_STATUS_ERROR_PARAM_OUT_OF_RANGE);
}
else
{
g_llPduLen.suggested.MaxTxOctets= TxOctets;
g_llPduLen.suggested.MaxRxTime = TxTime;
return(LL_STATUS_SUCCESS);
}
}
llStatus_t LL_SetDataLengh0( uint16 connId,uint16 TxOctets,uint16 TxTime )
{
uint8 i;
llStatus_t status;
llConnState_t* connPtr;
if(TxOctets > LL_PDU_LENGTH_SUPPORTED_MAX_TX_OCTECTS
|| TxTime > LL_PDU_LENGTH_SUPPORTED_MAX_TX_TIME
|| TxOctets < LL_PDU_LENGTH_INITIAL_MAX_TX_OCTECTS
|| TxTime < LL_PDU_LENGTH_INITIAL_MAX_TX_TIME)
{
return(LL_STATUS_ERROR_PARAM_OUT_OF_RANGE);
}
// make sure connection ID is valid
if ( (status=LL_ConnActive(connId)) != LL_STATUS_SUCCESS )
{
return( status );
}
// get connection info
connPtr = &conn_param[connId];
// check if a feature response control procedure has taken place
if ( connPtr->featureSetInfo.featureRspRcved == FALSE )
{
// it hasn't so re-load this device's local Feature Set to the
// connection as it may have been changed by the Host with HCI
// extenstion Set Local Feature Set command
for (i=0; i<LL_MAX_FEATURE_SET_SIZE; i++)
{
connPtr->featureSetInfo.featureSet[i] = deviceFeatureSet.featureSet[i];
}
}
// check if dle is a supported feature set item
if ( (connPtr->featureSetInfo.featureSet[0] & LL_FEATURE_DATA_LENGTH_EXTENSION) != LL_FEATURE_DATA_LENGTH_EXTENSION )
{
return( LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED );
}
// check if an updated parameters control procedure is already what's pending
if ( ((connPtr->ctrlPktInfo.ctrlPktCount > 0) &&
(connPtr->ctrlPktInfo.ctrlPkts[0] == LL_CTRL_LENGTH_REQ)) ||
(connPtr->llPduLen.isProcessingReq == TRUE) || (connPtr->llPduLen.isWatingRsp == TRUE) )
{
return( LL_STATUS_ERROR_CTRL_PROC_ALREADY_ACTIVE );
}
// g_llPduLen.suggested.MaxTxOctets = TxOctets; // remove by HZF, suggested value is from host, should not change in controller
// g_llPduLen.suggested.MaxTxTime = TxTime;
// setup an LL_LENGTH_REQ
llEnqueueCtrlPkt( connPtr, LL_CTRL_LENGTH_REQ );
return(LL_STATUS_SUCCESS);
}
llStatus_t LL_SetDefaultPhyMode( uint16 connId,uint8 allPhy,uint8 txPhy, uint8 rxPhy)
{
conn_param[connId].llPhyModeCtrl.def.allPhy = allPhy;
conn_param[connId].llPhyModeCtrl.def.txPhy = txPhy;
conn_param[connId].llPhyModeCtrl.def.rxPhy = rxPhy;
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_SetPhyMode0( uint16 connId,uint8 allPhy,uint8 txPhy, uint8 rxPhy,uint16 phyOptions)
{
uint8 i;
llStatus_t status;
llConnState_t* connPtr;
// make sure connection ID is valid
if ( (status=LL_ConnActive(connId)) != LL_STATUS_SUCCESS )
{
return( status );
}
// get connection info
connPtr = &conn_param[connId];
// check if a feature response control procedure has taken place
if ( connPtr->featureSetInfo.featureRspRcved == FALSE )
{
// it hasn't so re-load this device's local Feature Set to the
// connection as it may have been changed by the Host with HCI
// extenstion Set Local Feature Set command
for (i=0; i<LL_MAX_FEATURE_SET_SIZE; i++)
{
connPtr->featureSetInfo.featureSet[i] = deviceFeatureSet.featureSet[i];
}
}
// check if dle is a supported feature set item
if( ( (connPtr->featureSetInfo.featureSet[1] & LL_FEATURE_2M_PHY) != LL_FEATURE_2M_PHY )
&& ( (connPtr->featureSetInfo.featureSet[1] & LL_FEATURE_CODED_PHY) != LL_FEATURE_CODED_PHY ) )
{
return( LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED );
}
// check if an updated parameters control procedure is already what's pending
if ( ((connPtr->ctrlPktInfo.ctrlPktCount > 0) &&
(connPtr->ctrlPktInfo.ctrlPkts[0] == LL_CTRL_PHY_REQ)) ||
(connPtr->pendingPhyModeUpdate== TRUE) ||
(connPtr->llPhyModeCtrl.isWatingRsp == TRUE) || (connPtr->llPhyModeCtrl.isProcessingReq == TRUE) )
{
return( LL_STATUS_ERROR_CTRL_PROC_ALREADY_ACTIVE );
}
//support Symmetric Only
if(allPhy==0 &&(txPhy!=rxPhy))
{
return( LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED );
}
// how to check the required param?
//LL_TS_5.0.3 Table 4.43: PDU payload contents for each case variation for LE 2M PHY
connPtr->llPhyModeCtrl.req.allPhy = allPhy;
if(connPtr->llPhyModeCtrl.req.allPhy==0)
{
connPtr->llPhyModeCtrl.req.txPhy = txPhy;
connPtr->llPhyModeCtrl.req.rxPhy = txPhy;
}
else if(connPtr->llPhyModeCtrl.req.allPhy==1)
{
connPtr->llPhyModeCtrl.req.txPhy = 0;
connPtr->llPhyModeCtrl.req.rxPhy = txPhy;
}
else if(connPtr->llPhyModeCtrl.req.allPhy==2)
{
connPtr->llPhyModeCtrl.req.txPhy = txPhy;
connPtr->llPhyModeCtrl.req.rxPhy = 0;
}
else
{
//no prefer on both phy
connPtr->llPhyModeCtrl.req.txPhy = 0;
connPtr->llPhyModeCtrl.req.rxPhy = 0;
}
connPtr->llPhyModeCtrl.phyOptions = phyOptions;
// setup an LL_LENGTH_REQ
llEnqueueCtrlPkt( connPtr, LL_CTRL_PHY_REQ );
return(LL_STATUS_SUCCESS);
}
// add by HZF for resolving list
llStatus_t LL_AddResolvingListLDevice( uint8 addrType,
uint8* devAddr,
uint8* peerIrk,
uint8* localIrk)
{
int i;
if ( (llState == LL_STATE_ADV_UNDIRECTED) ||
(llState == LL_STATE_ADV_DIRECTED) ||
(llState == LL_STATE_ADV_SCAN) ||
(llState == LL_STATE_ADV_NONCONN) ||
(llState == LL_STATE_SCAN) ||
(llState == LL_STATE_INIT) ||
(llSecondaryState == LL_SEC_STATE_ADV) ||
(llSecondaryState == LL_SEC_STATE_ADV_PENDING) ||
(llSecondaryState == LL_SEC_STATE_SCAN) ||
(llSecondaryState == LL_SEC_STATE_SCAN_PENDING) ||
(llSecondaryState == LL_SEC_STATE_INIT) ||
(llSecondaryState == LL_SEC_STATE_INIT_PENDING))
{
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
}
if (extInitInfo.scanMode == LL_SCAN_START)
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
// check the RL device address type
if ( (addrType != LL_DEV_ADDR_TYPE_PUBLIC) &&
(addrType != LL_DEV_ADDR_TYPE_RANDOM) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// check if there was room for the entry
if (g_llRlDeviceNum >= LL_RESOLVINGLIST_ENTRY_NUM)
return( LL_STATUS_ERROR_RL_TABLE_FULL );
// add the device to a empty record
for (i = 0; i < LL_RESOLVINGLIST_ENTRY_NUM; i++)
{
if (g_llResolvinglist[i].peerAddrType == 0xff) // empty record
{
g_llResolvinglist[i].peerAddrType = addrType;
osal_memcpy(&g_llResolvinglist[i].peerAddr[0], &devAddr[0], LL_DEVICE_ADDR_LEN);
// TODO: should we revert the byte order???
osal_memcpy(&g_llResolvinglist[i].localIrk[0], &localIrk[0], LL_ENC_IRK_LEN);
osal_memcpy(&g_llResolvinglist[i].peerIrk[0], &peerIrk[0], LL_ENC_IRK_LEN);
g_llRlDeviceNum ++;
break;
}
}
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_RemoveResolvingListDevice( uint8* devAddr,
uint8 addrType )
{
int i, j;
if ( (llState == LL_STATE_ADV_UNDIRECTED) ||
(llState == LL_STATE_ADV_DIRECTED) ||
(llState == LL_STATE_ADV_SCAN) ||
(llState == LL_STATE_ADV_NONCONN) ||
(llState == LL_STATE_SCAN) ||
(llState == LL_STATE_INIT) ||
(llSecondaryState == LL_SEC_STATE_ADV) ||
(llSecondaryState == LL_SEC_STATE_ADV_PENDING) ||
(llSecondaryState == LL_SEC_STATE_SCAN) ||
(llSecondaryState == LL_SEC_STATE_SCAN_PENDING) ||
(llSecondaryState == LL_SEC_STATE_INIT) ||
(llSecondaryState == LL_SEC_STATE_INIT_PENDING))
{
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
}
if (extInitInfo.scanMode == LL_SCAN_START)
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
// check the RL device address type
if ( (addrType != LL_DEV_ADDR_TYPE_PUBLIC) &&
(addrType != LL_DEV_ADDR_TYPE_RANDOM) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// check that there was at least one entry in the table
if ( g_llRlDeviceNum == 0 )
{
return( LL_STATUS_ERROR_RL_TABLE_EMPTY );
}
for (i = 0; i < LL_RESOLVINGLIST_ENTRY_NUM; i++)
{
if (g_llResolvinglist[i].peerAddrType == addrType)
{
for (j = 0; j < LL_DEVICE_ADDR_LEN; j++) // check whether the address is the same
{
if (g_llResolvinglist[i].peerAddr[j] != devAddr[j])
break;
}
if (j == LL_DEVICE_ADDR_LEN) // found it
{
g_llResolvinglist[i].peerAddrType = 0xff;
g_llResolvinglist[i].privacyMode = NETWORK_PRIVACY_MODE;
g_llRlDeviceNum --;
break;
}
}
}
if (i == LL_RESOLVINGLIST_ENTRY_NUM)
return( LL_STATUS_ERROR_RL_ENTRY_NOT_FOUND );
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_ClearResolvingList( void )
{
int i;
if ( (llState == LL_STATE_ADV_UNDIRECTED) ||
(llState == LL_STATE_ADV_DIRECTED) ||
(llState == LL_STATE_ADV_SCAN) ||
(llState == LL_STATE_ADV_NONCONN) ||
(llState == LL_STATE_SCAN) ||
(llState == LL_STATE_INIT) ||
(llSecondaryState == LL_SEC_STATE_ADV) ||
(llSecondaryState == LL_SEC_STATE_ADV_PENDING) ||
(llSecondaryState == LL_SEC_STATE_SCAN) ||
(llSecondaryState == LL_SEC_STATE_SCAN_PENDING) ||
(llSecondaryState == LL_SEC_STATE_INIT) ||
(llSecondaryState == LL_SEC_STATE_INIT_PENDING))
{
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
}
if (extInitInfo.scanMode == LL_SCAN_START)
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
// clear number of entries, valid flags, address type flags, and entries
for (i = 0; i < LL_RESOLVINGLIST_ENTRY_NUM; i ++)
{
g_llResolvinglist[i].peerAddrType = 0xff;
g_llResolvinglist[i].privacyMode = NETWORK_PRIVACY_MODE;
memset(&g_llResolvinglist[i].peerAddr[0], 0, LL_DEVICE_ADDR_LEN);
}
// set white list number 0
g_llRlDeviceNum = 0;
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_ReadResolvingListSize( uint8* numEntries )
{
*numEntries = LL_RESOLVINGLIST_ENTRY_NUM;
return (LL_STATUS_SUCCESS);
}
llStatus_t LL_ReadPeerResolvableAddress( uint8* peerRpa )
{
peerRpa[0] = g_currentPeerRpa[0];
peerRpa[1] = g_currentPeerRpa[1];
peerRpa[2] = g_currentPeerRpa[2];
peerRpa[3] = g_currentPeerRpa[3];
peerRpa[4] = g_currentPeerRpa[4];
peerRpa[5] = g_currentPeerRpa[5];
return (LL_STATUS_SUCCESS);
}
llStatus_t LL_ReadLocalResolvableAddress( uint8* localRpa )
{
localRpa[0] = g_currentPeerRpa[0];
localRpa[1] = g_currentPeerRpa[1];
localRpa[2] = g_currentPeerRpa[2];
localRpa[3] = g_currentPeerRpa[3];
localRpa[4] = g_currentPeerRpa[4];
localRpa[5] = g_currentPeerRpa[5];
return (LL_STATUS_SUCCESS);
}
// enable : 1 - enable, 0 - disable
llStatus_t LL_SetAddressResolutionEnable( uint8 enable )
{
if ( (llState == LL_STATE_ADV_UNDIRECTED) ||
(llState == LL_STATE_ADV_DIRECTED) ||
(llState == LL_STATE_ADV_SCAN) ||
(llState == LL_STATE_ADV_NONCONN) ||
(llState == LL_STATE_SCAN) ||
(llState == LL_STATE_INIT) ||
(llSecondaryState == LL_SEC_STATE_ADV) ||
(llSecondaryState == LL_SEC_STATE_ADV_PENDING) ||
(llSecondaryState == LL_SEC_STATE_SCAN) ||
(llSecondaryState == LL_SEC_STATE_SCAN_PENDING) ||
(llSecondaryState == LL_SEC_STATE_INIT) ||
(llSecondaryState == LL_SEC_STATE_INIT_PENDING))
{
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
}
if (extInitInfo.scanMode == LL_SCAN_START)
return( LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE );
g_llRlEnable = enable;
// if (g_llRlEnable == TRUE)
// osal_start_timerEx( LL_TaskID, LL_EVT_RPA_TIMEOUT, g_llRlTimeout * 1000 );
// else // FALSE
// osal_stop_timerEx(LL_TaskID, LL_EVT_RPA_TIMEOUT);
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_SetResolvablePrivateAddressTimeout( uint16 rpaTimeout )
{
g_llRlTimeout = rpaTimeout;
// if (g_llRlEnable == TRUE)
// osal_start_timerEx( LL_TaskID, LL_EVT_RPA_TIMEOUT, g_llRlTimeout * 1000 );
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_Set_Privacy_Mode(uint8 peerIdType,
uint8* peerIdAddr,
uint8 privacyMode)
{
int i, j;
// search the peer address in the resolving address list
if ( g_llRlDeviceNum == 0 )
{
return( LL_STATUS_ERROR_RL_TABLE_EMPTY );
}
for (i = 0; i < LL_RESOLVINGLIST_ENTRY_NUM; i++)
{
if (g_llResolvinglist[i].peerAddrType == peerIdType)
{
for (j = 0; j < LL_DEVICE_ADDR_LEN; j++) // check whether the address is the same
{
if (g_llResolvinglist[i].peerAddr[j] != peerIdAddr[j])
break;
}
if (j == LL_DEVICE_ADDR_LEN) // found it
break;
}
}
if (i == LL_RESOLVINGLIST_ENTRY_NUM)
return( LL_STATUS_ERROR_UNKNOWN_CONN_HANDLE );
g_llResolvinglist[i].privacyMode = privacyMode;
return LL_STATUS_SUCCESS;
}
// extend advertisement function
/*******************************************************************************
@fn LL_SetExtAdvSetRandomAddress
@brief This function is used to set random address for a advertisement set
input parameters
@param adv_handle - advertisement set handler
random_address - random address
output parameters
@param none
@return LL_STATUS_SUCCESS, error code(TBD)
*/
llStatus_t LL_SetExtAdvSetRandomAddress( uint8 adv_handle,
uint8* random_address
)
{
int i;
// extended advertiser memory is allocate by application, return fail if not
if (g_pExtendedAdvInfo == NULL)
return LL_STATUS_ERROR_OUT_OF_HEAP;
// search advertisement handler list
for (i = 0; i < g_extAdvNumber; i ++)
{
if (g_pExtendedAdvInfo[i].advHandle == adv_handle)
break;
}
// if not found, then adv parameter has not been set
if (i == g_extAdvNumber)
return LL_STATUS_ERROR_UNKNOWN_ADV_ID;
g_pExtendedAdvInfo[i].parameter.isOwnRandomAddressSet = TRUE;
memcpy(&g_pExtendedAdvInfo[i].parameter.ownRandomAddress[0], random_address, LL_DEVICE_ADDR_LEN);
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_SetExtAdvParam
@brief This function is used to set extend advertiser parameters
input parameters
@param adv_handle - advertisement set handler
output parameters
@param none
@return LL_STATUS_SUCCESS, error code(TBD)
*/
llStatus_t LL_SetExtAdvParam( uint8 adv_handle,
uint16 adv_event_properties,
uint32 primary_advertising_interval_Min, // 3 octets
uint32 primary_advertising_interval_Max, // 3 octets
uint8 primary_advertising_channel_map,
uint8 own_address_type,
uint8 peer_address_type,
uint8* peer_address,
uint8 advertising_filter_policy,
int8 advertising_tx_power,
uint8 primary_advertising_PHY,
uint8 secondary_advertising_max_skip,
uint8 secondary_advertising_PHY,
uint8 advertising_SID,
uint8 scan_request_notification_enable,
int8* selectTxPwr
)
{
int i;
// TODO: add parameters range checking
if (g_llAdvMode == LL_MODE_LEGACY)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_EXTENDED;
if ((adv_event_properties & LE_ADV_PROP_LEGACY_BITMASK) // Legacy Adv
&& (adv_event_properties != LL_EXT_ADV_PROP_ADV_IND)
&& (adv_event_properties != LL_EXT_ADV_PROP_ADV_LDC_ADV)
&& (adv_event_properties != LL_EXT_ADV_PROP_ADV_HDC_ADV)
&& (adv_event_properties != LL_EXT_ADV_PROP_ADV_SCAN_IND)
&& (adv_event_properties != LL_EXT_ADV_PROP_ADV_NOCONN_IND))
return LL_STATUS_ERROR_BAD_PARAMETER;
if (primary_advertising_interval_Max < primary_advertising_interval_Min
|| primary_advertising_interval_Min < 0x20
|| primary_advertising_interval_Max < 0x20)
return LL_STATUS_ERROR_BAD_PARAMETER;
// check whether aux adv pdu resource period is OK for max skip & adv interval setting
if (!(adv_event_properties & LE_ADV_PROP_LEGACY_BITMASK) // not legacy Adv
&& primary_advertising_interval_Max * (secondary_advertising_max_skip + 1) * 625 < g_advSlotPeriodic)
return LL_STATUS_ERROR_BAD_PARAMETER;
// extended advertiser memory is allocate by application, return fail if not
if (g_pExtendedAdvInfo == NULL)
return LL_STATUS_ERROR_OUT_OF_HEAP;
// search advertisement handler list
for (i = 0; i < g_extAdvNumber; i ++)
{
if (g_pExtendedAdvInfo[i].advHandle == adv_handle)
break;
}
// if not found, search the 1st available slot
if (i == g_extAdvNumber)
{
for (i = 0; i < g_extAdvNumber; i ++)
{
if (g_pExtendedAdvInfo[i].advHandle == LL_INVALID_ADV_SET_HANDLE)
{
uint8* scanRspData = g_pExtendedAdvInfo[i].scanRspData;
uint8* advData = g_pExtendedAdvInfo[i].data.advertisingData;
memset(&g_pExtendedAdvInfo[i], 0, sizeof(extAdvInfo_t));
g_pExtendedAdvInfo[i].scanRspData = scanRspData;
g_pExtendedAdvInfo[i].data.advertisingData = advData;
break;
}
}
}
if (i == g_extAdvNumber)
return LL_STATUS_ERROR_OUT_OF_HEAP;
// save the advertisement parameters
g_pExtendedAdvInfo[i].advHandle = adv_handle;
g_pExtendedAdvInfo[i].parameter.advEventProperties = adv_event_properties;
g_pExtendedAdvInfo[i].parameter.priAdvIntMin = primary_advertising_interval_Min;
g_pExtendedAdvInfo[i].parameter.priAdvgIntMax = primary_advertising_interval_Max;
g_pExtendedAdvInfo[i].parameter.priAdvChnMap = primary_advertising_channel_map;
g_pExtendedAdvInfo[i].parameter.ownAddrType = own_address_type;
g_pExtendedAdvInfo[i].parameter.peerAddrType = peer_address_type;
memcpy(g_pExtendedAdvInfo[i].parameter.peerAddress, peer_address, LL_DEVICE_ADDR_LEN);
g_pExtendedAdvInfo[i].parameter.wlPolicy = advertising_filter_policy;
g_pExtendedAdvInfo[i].parameter.advTxPower = advertising_tx_power;
g_pExtendedAdvInfo[i].parameter.primaryAdvPHY = primary_advertising_PHY;
g_pExtendedAdvInfo[i].parameter.secondaryAdvPHY = secondary_advertising_PHY;
g_pExtendedAdvInfo[i].parameter.secondaryAdvMaxSkip= secondary_advertising_max_skip;
g_pExtendedAdvInfo[i].parameter.advertisingSID = advertising_SID;
g_pExtendedAdvInfo[i].parameter.scanReqNotificationEnable = scan_request_notification_enable;
// =========== controller select parameters, TBD
// decide primary advertising interval. The aux PDU period is decided by global_config, the primary adv interval
// should be set considering the maximum skip AUX PDU requirement
if (primary_advertising_interval_Min * (secondary_advertising_max_skip + 1) * 625 > g_advSlotPeriodic)
g_pExtendedAdvInfo[i].primary_advertising_interval = primary_advertising_interval_Min * 625; // bug fiex 04-08, * 1250 -> * 625
else
g_pExtendedAdvInfo[i].primary_advertising_interval = primary_advertising_interval_Max * 625;
// select Tx power and return
g_pExtendedAdvInfo[i].tx_power = advertising_tx_power;
*selectTxPwr = advertising_tx_power;
g_pExtendedAdvInfo[i].isPeriodic = FALSE;
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_SetExtAdvData
@brief This function is used to set extend advertiser set data
input parameters
@param adv_handle - advertisement set handler
output parameters
@param none
@return LL_STATUS_SUCCESS, error code(TBD)
*/
llStatus_t LL_SetExtAdvData( uint8 adv_handle,
uint8 operation,
uint8 fragment_preference,
uint8 advertising_data_length,
uint8* advertising_data
)
{
int i;
// TODO: add parameters range checking
if (g_llAdvMode == LL_MODE_LEGACY)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_EXTENDED;
if (operation > BLE_EXT_ADV_OP_UNCHANGED_DATA)
return LL_STATUS_ERROR_BAD_PARAMETER;
// extended advertiser memory is allocate by application, return fail if not
if (g_pExtendedAdvInfo == NULL)
return LL_STATUS_ERROR_OUT_OF_HEAP;
// search advertisement handler list
for (i = 0; i < g_extAdvNumber; i ++)
{
if (g_pExtendedAdvInfo[i].advHandle == adv_handle)
break;
}
// if not found, then adv parameter has not been set
if (i == g_extAdvNumber)
return LL_STATUS_ERROR_UNKNOWN_ADV_ID;
// comment out 04-10, set adv data should be received after set adv parameter
// {
// for (i = 0; i < g_extAdvNumber; i ++)
// {
// if (g_pExtendedAdvInfo[i].advHandle == LL_INVALID_ADV_SET_HANDLE)
// break;
// }
// if (i == g_extAdvNumber)
// return LL_STATUS_ERROR_OUT_OF_HEAP;
//
// // set DID when 1st create the data set
// LL_Rand((uint8*)&g_pExtendedAdvInfo[i].data.DIDInfo, 2);
// }
// check legacy ADV data length should <= 31
if (ll_isLegacyAdv(&g_pExtendedAdvInfo[i])
&& (operation != BLE_EXT_ADV_OP_COMPLETE_DATA
|| advertising_data_length > 31))
return LL_STATUS_ERROR_BAD_PARAMETER;
if (operation == BLE_EXT_ADV_OP_FIRST_FRAG ||
operation == BLE_EXT_ADV_OP_COMPLETE_DATA)
g_pExtendedAdvInfo[i].data.advertisingDataLength = 0;
if (g_pExtendedAdvInfo[i].data.advertisingDataLength + advertising_data_length > g_advSetMaximumLen)
{
g_pExtendedAdvInfo[i].data.advertisingDataLength = 0;
return LL_STATUS_ERROR_OUT_OF_HEAP;
}
// fill advertising set
g_pExtendedAdvInfo[i].advHandle = adv_handle;
g_pExtendedAdvInfo[i].data.fragmentPreference = fragment_preference;
memcpy(&g_pExtendedAdvInfo[i].data.advertisingData[g_pExtendedAdvInfo[i].data.advertisingDataLength],
advertising_data, advertising_data_length);
g_pExtendedAdvInfo[i].data.advertisingDataLength += advertising_data_length;
// last fragment or 1 segment adv data
if (operation == BLE_EXT_ADV_OP_LAST_FRAG ||
operation == BLE_EXT_ADV_OP_COMPLETE_DATA )
g_pExtendedAdvInfo[i].data.dataComplete = TRUE;
if (operation == BLE_EXT_ADV_OP_LAST_FRAG ||
operation == BLE_EXT_ADV_OP_COMPLETE_DATA ||
operation == BLE_EXT_ADV_OP_UNCHANGED_DATA ) // unchange data, just update DID
{
// update DID
g_pExtendedAdvInfo[i].data.DIDInfo = ll_generateExtAdvDid(g_pExtendedAdvInfo[i].data.DIDInfo);
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_SetExtScanRspData
@brief This function is used to set extend scan response data
input parameters
@param adv_handle - advertisement set handler
output parameters
@param none
@return LL_STATUS_SUCCESS, error code(TBD)
*/
llStatus_t LL_SetExtScanRspData( uint8 adv_handle,
uint8 operation,
uint8 fragment_preference,
uint8 scan_rsp_data_length,
uint8* scan_rsp_data
)
{
(void) fragment_preference;
int i;
if (g_llAdvMode == LL_MODE_LEGACY)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_EXTENDED;
if (operation != BLE_EXT_ADV_OP_COMPLETE_DATA)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
// extended advertiser memory is allocate by application, return fail if not
if (g_pExtendedAdvInfo == NULL)
return LL_STATUS_ERROR_OUT_OF_HEAP;
// search advertisement handler list
for (i = 0; i < g_extAdvNumber; i ++)
{
if (g_pExtendedAdvInfo[i].advHandle == adv_handle)
break;
}
// if not found, then adv parameter has not been set
if (i == g_extAdvNumber)
return LL_STATUS_ERROR_UNKNOWN_ADV_ID;
// check legacy ADV scan rsp data length should <= 31
if (ll_isLegacyAdv(&g_pExtendedAdvInfo[i])
&& (operation != BLE_EXT_ADV_OP_COMPLETE_DATA
|| scan_rsp_data_length > 31))
return LL_STATUS_ERROR_BAD_PARAMETER;
// // if not found, search the 1st available slot
// if (i == g_extAdvNumber)
// {
// for (i = 0; i < g_extAdvNumber; i ++)
// {
// if (g_pExtendedAdvInfo[i].advHandle == LL_INVALID_ADV_SET_HANDLE)
// break;
// }
// if (i == g_extAdvNumber)
// return LL_STATUS_ERROR_OUT_OF_HEAP;
// }
if (g_pExtendedAdvInfo[i].scanRspData == NULL || g_advSetMaximumLen < scan_rsp_data_length)
return LL_STATUS_ERROR_OUT_OF_HEAP;
memcpy(&g_pExtendedAdvInfo[i].scanRspData[0], scan_rsp_data, scan_rsp_data_length);
g_pExtendedAdvInfo[i].scanRspMaxLength = scan_rsp_data_length;
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_SetExtAdvEnable
@brief This function is used to enable/disable extend advertise
input parameters
@param enable - enable/disable
output parameters
@param none
@return LL_STATUS_SUCCESS, error code(TBD)
*/
llStatus_t LL_SetExtAdvEnable( uint8 enable,
uint8 number_of_sets,
uint8* advertising_handle,
uint16* duration,
uint8* max_extended_advertising_events)
{
int i, j;
extAdvInfo_t* pExtAdv[64];
periodicAdvInfo_t* pPrdAdv[64];
// TODO: add more sanity checking to align to the spec
if (g_llAdvMode == LL_MODE_LEGACY)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_EXTENDED;
if (number_of_sets == 0 && enable == TRUE)
return LL_STATUS_ERROR_UNEXPECTED_PARAMETER;
// the adv parameter should be present for the adv_handler, otherwise, it will return Unknown Advertising Identifier(0x42)
for (i = 0; i < number_of_sets; i++)
{
pExtAdv[i] = NULL;
pPrdAdv[i] = NULL;
for (j = 0; j < g_extAdvNumber; j++)
{
if (g_pExtendedAdvInfo[j].advHandle == advertising_handle[i])
{
pExtAdv[i] = &g_pExtendedAdvInfo[j];
break;
}
}
if (pExtAdv[i] == NULL) // adv handle not found
return LL_STATUS_ERROR_UNKNOWN_ADV_ID;
if (ll_isLegacyAdv(pExtAdv[i]))
{
if (pExtAdv[i]->data.advertisingDataLength > 31)
return LL_STATUS_ERROR_ILLEGAL_PARAM_COMBINATION;
}
if ((pExtAdv[i]->parameter.advEventProperties & LE_ADV_PROP_SCAN_BITMASK)
&& (pExtAdv[i]->scanRspData == NULL || pExtAdv[i]->scanRspMaxLength == 0))
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
if (pExtAdv[i]->parameter.ownAddrType == LL_DEV_ADDR_TYPE_RANDOM
&& pExtAdv[i]->parameter.isOwnRandomAddressSet == FALSE)
return LL_STATUS_ERROR_BAD_PARAMETER;
// TODO: If the advertising set's Own_Address_Type parameter is set to 0x03, the
// controller's resolving list did not contain a matching entry, and the random
// address for the advertising set has not been initialized, the Controller shall
// return the error code Invalid HCI Command Parameters (0x12).
// pExtAdv[i]->isPeriodic = FALSE;
// // if periodic adv is support, check whether the adv set is periodic adv
// if (g_pPeriodicAdvInfo != NULL)
// {
// for (j = 0; j < g_perioAdvNumber; j ++)
// {
// if (g_pPeriodicAdvInfo[j].advHandle == pExtAdv[i]->advHandle)
// {
// pExtAdv[i]->isPeriodic = TRUE;
// index = j;
// break;
// }
// }
// }
// for periodic adv, search the period adv context
if (pExtAdv[i]->isPeriodic == TRUE)
{
for (j = 0; j < g_perioAdvNumber; j ++)
{
if (g_pPeriodicAdvInfo[j].advHandle == pExtAdv[i]->advHandle)
{
pPrdAdv[i] = &g_pPeriodicAdvInfo[j];
break;
}
}
if (pPrdAdv[i] == NULL)
return LL_STATUS_ERROR_UNKNOWN_ADV_ID;
}
// for enable adv case, the adv set data should be complete,
// otherwise, it shall return Command Disallowed(0x0C)
if (enable == TRUE)
{
if ((pExtAdv[i]->isPeriodic == FALSE && pExtAdv[i]->data.dataComplete == FALSE)
|| (pExtAdv[i]->isPeriodic == TRUE && pPrdAdv[i]->data.dataComplete == FALSE))
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
}
}
// check OK, save the enable info
for (i = 0; i < number_of_sets; i++)
{
pExtAdv[i]->active = enable;
if (enable == TRUE)
{
// only save parameters in enable case
pExtAdv[i]->duration = duration[i] * 10000; // 10000: unit 10ms, convert to us
pExtAdv[i]->maxExtAdvEvents = max_extended_advertising_events[i];
}
}
// ====== update extend advertiser scheduler to apply new parameters
if (enable == FALSE)
{
// case 1: number of set is 0, disable all adv case. Remove the adv from adv schduler
if (number_of_sets == 0)
{
for (i = 0; i < g_extAdvNumber; i ++) // bug fixed 04-07
{
if (g_pAdvSchInfo[i].adv_handler == LL_INVALID_ADV_SET_HANDLE)
continue;
ll_delete_adv_task(i);
}
// periodic adv case, disable all EXT_ADV_IND + AUX_ADV_IND of periodic adv. It should be OK
// by set the active flag to FALSE. TO BE TEST.
for (i = 0; i < g_perioAdvNumber; i ++)
{
if (g_pAdvSchInfo_periodic[i].adv_handler == LL_INVALID_ADV_SET_HANDLE)
continue;
g_pAdvSchInfo_periodic[i].pAdvInfo->active = FALSE;
}
}
// case 2: disable the adv in the adv set list
else
{
for (i = 0; i < number_of_sets; i ++)
{
// search the adv in the scheduler list and disable it
for (j = 0; j < g_schExtAdvNum; j++)
{
if (g_pAdvSchInfo[j].adv_handler == pExtAdv[i]->advHandle)
{
ll_delete_adv_task(j);
break;
}
}
// for periodic adv, only de-active the extended adv part.
// for extended adv, the active flag have already been set by ll_delete_adv_task()
// but it is harmless to set it again
pExtAdv[i]->active = FALSE;
}
}
}
else // enable case
{
for (i = 0; i < number_of_sets; i++)
{
for (j = 0; j < g_schExtAdvNum; j++)
{
// check whether the adv already started
if (g_pAdvSchInfo[j].adv_handler == pExtAdv[i]->advHandle) // the adv in the scheduler list
{
// TODO: adv already enable, it should:
// If the HCI_LE_Set_Extended_Advertising_Enable command is sent again for
// an advertising set while that set is enabled, the timer used for the duration and
// the number of events counter are reset and any change to the random address
// shall take effect
break;
}
}
// new extended adv case
if (j == g_schExtAdvNum && pExtAdv[i]->isPeriodic == FALSE)
ll_add_adv_task(pExtAdv[i]);
// new periodic adv case
if (j == g_schExtAdvNum && pExtAdv[i]->isPeriodic == TRUE)
{
// check whether the corresponding periodic adv is enable
// 1. enable, start periodic adv
// 2. disable, do nothing
if (pPrdAdv[i]->active == TRUE)
ll_add_adv_task_periodic(pPrdAdv[i], pExtAdv[i]);
}
}
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_ReadMaximumAdvDataLength
@brief This function is used to read the maximum adv set data length support by controller
input parameters
@param adv_handle - advertisement set handler
output parameters
@param length - pointer to the variable of maximum data length support
@return LL_STATUS_SUCCESS, error code(TBD)
*/
llStatus_t LL_ReadMaximumAdvDataLength( uint16* length )
{
*length = LL_MAX_ADVERTISER_SET_LENGTH; // TBD.
if (g_llAdvMode == LL_MODE_LEGACY)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_EXTENDED;
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_ReadNumberOfSupportAdvSet
@brief This function is used to read number of adv set supported by controller
input parameters
@param adv_handle - advertisement set handler
output parameters
@param none
@return LL_STATUS_SUCCESS, error code(TBD)
*/
llStatus_t LL_ReadNumberOfSupportAdvSet( uint8* number )
{
if (g_llAdvMode == LL_MODE_LEGACY)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_EXTENDED;
*number = 240; // TBD
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_RemoveAdvSet
@brief This function is used to remove advertisement set
input parameters
@param adv_handle - advertisement set handler
output parameters
@param none
@return LL_STATUS_SUCCESS, error code(TBD)
*/
llStatus_t LL_RemoveAdvSet( uint8 adv_handle)
{
uint8 i;
uint8 extIndex = 0xFF, prdIndex = 0xFF;
if (g_llAdvMode == LL_MODE_LEGACY)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_EXTENDED;
// search extendedadvertisement handler list
for (i = 0; i < g_extAdvNumber; i ++)
{
if (g_pExtendedAdvInfo[i].advHandle == adv_handle)
{
extIndex = i;
break;
}
}
// search advertisement handler list
for (i = 0; i < g_perioAdvNumber; i ++)
{
if (g_pPeriodicAdvInfo[i].advHandle == adv_handle)
{
prdIndex = i;
break;
}
}
if (extIndex == 0xFF && prdIndex == 0xFF)
return LL_STATUS_ERROR_UNKNOWN_ADV_ID;
if ((extIndex != 0xFF && g_pExtendedAdvInfo[extIndex].active == TRUE)
|| (prdIndex != 0xFF && g_pPeriodicAdvInfo[prdIndex].active == TRUE))
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
if (extIndex != 0xFF)
{
uint8* scanRspData = g_pExtendedAdvInfo[extIndex].scanRspData;
uint8* advData = g_pExtendedAdvInfo[extIndex].data.advertisingData;
memset(&g_pExtendedAdvInfo[extIndex], 0, sizeof(extAdvInfo_t));
g_pExtendedAdvInfo[extIndex].scanRspData = scanRspData;
g_pExtendedAdvInfo[extIndex].data.advertisingData = advData;
g_pExtendedAdvInfo[extIndex].advHandle = LL_INVALID_ADV_SET_HANDLE;
}
if (prdIndex != 0xFF)
{
uint8* advData = g_pPeriodicAdvInfo[prdIndex].data.advertisingData;
memset(&g_pPeriodicAdvInfo[prdIndex], 0, sizeof(periodicAdvInfo_t));
g_pPeriodicAdvInfo[prdIndex].data.advertisingData = advData;
g_pPeriodicAdvInfo[prdIndex].advHandle = LL_INVALID_ADV_SET_HANDLE;
}
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_ClearAdvSets
@brief This function is used to clear the stored advertisement sets in controller
input parameters
@param none
output parameters
@param none
@return LL_STATUS_SUCCESS, error code(TBD)
*/
llStatus_t LL_ClearAdvSets(void)
{
uint8 i;
if (g_llAdvMode == LL_MODE_LEGACY)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_EXTENDED;
// check extendedadvertisement handler list
for (i = 0; i < g_extAdvNumber; i ++)
{
if (g_pExtendedAdvInfo[i].advHandle != LL_INVALID_ADV_SET_HANDLE
&& g_pExtendedAdvInfo[i].active == TRUE)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
}
// check periodic advertisement handler list
for (i = 0; i < g_perioAdvNumber; i ++)
{
if (g_pPeriodicAdvInfo[i].advHandle != LL_INVALID_ADV_SET_HANDLE
&& g_pPeriodicAdvInfo[i].active == TRUE)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
}
// clear adv set (extended adv part)
for (i = 0; i < g_extAdvNumber; i ++)
{
uint8* scanRspData = g_pExtendedAdvInfo[i].scanRspData;
uint8* advData = g_pExtendedAdvInfo[i].data.advertisingData;
memset(&g_pExtendedAdvInfo[i], 0, sizeof(extAdvInfo_t));
g_pExtendedAdvInfo[i].scanRspData = scanRspData;
g_pExtendedAdvInfo[i].data.advertisingData = advData;
g_pExtendedAdvInfo[i].advHandle = LL_INVALID_ADV_SET_HANDLE;
}
// clear adv set (periodic adv part)
for (i = 0; i < g_perioAdvNumber; i ++)
{
uint8* advData = g_pPeriodicAdvInfo[i].data.advertisingData;
memset(&g_pPeriodicAdvInfo[i], 0, sizeof(periodicAdvInfo_t));
g_pPeriodicAdvInfo[i].data.advertisingData = advData;
g_pPeriodicAdvInfo[i].advHandle = LL_INVALID_ADV_SET_HANDLE;
}
return( LL_STATUS_SUCCESS );
}
// ===================== extended scan/init
llStatus_t LL_SetExtendedScanParameters(uint8 own_address_type,
uint8 scanning_filter_policy,
uint8 scanning_PHYs,
uint8* scan_type,
uint16* scan_interval,
uint16* scan_window)
{
uint8 number_phys, i;
// TODO: sanity checking
if (g_llScanMode == LL_MODE_LEGACY )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_EXTENDED;
// If the Host specifies a PHY that is not supported by the Controller, including a bit that is reserved for future use,
// it should return the error code Unsupported Feature or Parameter Value(0x11).
if ((scanning_PHYs & (~(LL_SCAN_PHY_1M_BITMASK | LL_SCAN_PHY_CODED_BITMASK))) != 0)
return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED;
extScanInfo.ownAddrType = own_address_type;
extScanInfo.wlPolicy = scanning_filter_policy;
number_phys = 0;
if (scanning_PHYs & LL_SCAN_PHY_1M_BITMASK)
{
extScanInfo.scanPHYs[number_phys] = PKT_FMT_BLE1M;
number_phys ++;
}
if (scanning_PHYs & LL_SCAN_PHY_CODED_BITMASK)
{
extScanInfo.scanPHYs[number_phys] = PKT_FMT_BLR125K;
number_phys ++;
}
for (i = 0; i < number_phys; i ++)
{
extScanInfo.scanType[i] = scan_type[i];
extScanInfo.scanInterval[i] = scan_interval[i];
extScanInfo.scanWindow[i] = scan_window[i];
}
extScanInfo.numOfScanPHY = number_phys;
return LL_STATUS_SUCCESS;
}
llStatus_t LL_SetExtendedScanEnable(uint8 enable,
uint8 filter_duplicates,
uint16 duration,
uint16 period)
{
// TODO: sanity checking
if (g_llScanMode == LL_MODE_LEGACY )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_EXTENDED;
extScanInfo.enable = enable;
extScanInfo.filterDuplicate = filter_duplicates;
extScanInfo.duration = duration;
extScanInfo.period = period;
// trigger scan task
if (enable == TRUE)
{
extScanInfo.current_index = 0;
extScanInfo.current_chn = LL_ADV_CHAN_FIRST;
extScanInfo.current_scan_PHY = extScanInfo.scanPHYs[extScanInfo.current_index];
extScanInfo.adv_data_offset = 0;
llSetupExtScan(extScanInfo.current_chn);
}
else
{
}
return LL_STATUS_SUCCESS;
}
#define DBG_DISABLE_LATENCY 0
llStatus_t LL_PLUS_DisableSlaveLatency0(uint8 connId)
{
llConnState_t* connPtr;
uint16 next_event, elapse_event,chanMapDltEvt;
uint32 remain_time1, remain_time2;
uint32 old_timerDrift;
uint32 time_advance;
// get connection information
connPtr = &conn_param[connId]; // only 1 connection now, HZF
// check whether it should recover from latency
if (FALSE == connPtr->active
|| llState != LL_STATE_CONN_SLAVE
|| llSecondaryState != LL_SEC_STATE_IDLE)
{
//LOG("==1==\n\r");
return(LL_STATUS_DISABLE_LATENCY_INACTIVE_CONN);
}
// TODO: if latency already disable , return
pGlobal_config[LL_SWITCH] &= ~SLAVE_LATENCY_ALLOW;
connPtr->slaveLatencyAllowed = FALSE;
if ((AP_TIM1->ControlReg & 0x1) == 0 // timer1 not running
|| llWaitingIrq
|| connPtr->slaveLatency == 0)
{
//LOG("==2==%d,%d,%d\n\r",(CP_TIM1->ControlReg & 0x1),llWaitingIrq,connPtr->slaveLatency);
return (LL_STATUS_DISABLE_LATENCY_DISABLED);
}
remain_time1 = read_LL_remainder_time();
if (remain_time1 <=connPtr->lastTimeToNextEvt*625* 3) // the timer will expiry soon, so not adjust it
{
//LOG("==3==\n\r");
return(LL_STATUS_DISABLE_LATENCY_PENDING);
}
uint16 remain_event;
remain_event=(remain_time1+connPtr->timerDrift)/(connPtr->lastTimeToNextEvt * 625);
//20190805 ZQ:
// considering the event wrap case
elapse_event= llEventDelta(connPtr->nextEvent,connPtr->currentEvent)-remain_event-(uint8)1;
// elapse_time=elapse_event*(connPtr->lastTimeToNextEvt * 625);
next_event= connPtr->currentEvent + elapse_event + (uint8)2; // additional 2 connect event for some margin
old_timerDrift = connPtr->timerDrift;
llCalcTimerDrift(connPtr->lastTimeToNextEvt,
elapse_event + 1,
connPtr->sleepClkAccuracy,
(uint32*)&(connPtr->timerDrift));
// timer should expiry early, consider: 1. less timer drift 2. less latency event
//time_advance = connPtr->lastTimeToNextEvt * (connPtr->nextEvent - next_event) * 625 - (old_timerDrift - connPtr->timerDrift)+550;
time_advance = connPtr->lastTimeToNextEvt * (llEventDelta(connPtr->nextEvent,next_event) ) * 625 - (old_timerDrift - connPtr->timerDrift)+550;
// apply the timing advance
remain_time2 = AP_TIM1->CurrentCount >> 2;
if(remain_time2<time_advance)
{
//LOG("==4==\n\r");
// pGlobal_config[LL_SWITCH] |= SLAVE_LATENCY_ALLOW;
// connPtr->slaveLatencyAllowed = TRUE;
return (LL_STATUS_DISABLE_LATENCY_MISS_EVT);
}
set_timer(AP_TIM1,remain_time2 - time_advance);
// LOG("currentEvent = %d\r\n", connPtr->currentEvent);
// LOG("old next_event = %d\r\n", connPtr->nextEvent);
// LOG("new next_event = %d\r\n", next_event);
// update connection context
connPtr->currentEvent += elapse_event;
//connPtr->currentChan = old_chn;
//ZQ 20191209 : should use unmap channel
connPtr->currentChan = connPtr->lastCurrentChan;
//ZQ 20190805
// Max latency is 500, preChanMapUdate restore should be trigged within 500 evt
if(connPtr->preChanMapUpdate.chanMapUpdated==TRUE)
{
chanMapDltEvt = llEventDelta( connPtr->preChanMapUpdate.chanMapUpdateEvent, (connPtr->currentEvent + (uint8) 2));
//20190806 ZQ:
//only process the revert chanMap when chanMap Delta event with in (0 500)
if(chanMapDltEvt>0 && chanMapDltEvt<500)
{
connPtr->pendingChanUpdate = TRUE;
osal_memcpy(&(connPtr->chanMap[0]),&(connPtr->preChanMapUpdate.chanMap[0]),5);
llProcessChanMap(connPtr, connPtr->chanMap ); // 16MHz clk, cost 116us!
#if (DBG_DISABLE_LATENCY)
LOG("[Hit preChnMap] %d\n\r",connPtr->preChanMapUpdate.chanMapUpdateEvent);
#endif
}
}
if (connPtr->channel_selection == LL_CHN_SEL_ALGORITHM_1)
connPtr->currentChan = llGetNextDataChan(connPtr, elapse_event + 2 );
else
{
// channel selection algorithm 2
connPtr->currentChan = llGetNextDataChanCSA2(next_event,
(( ( connPtr->accessAddr & 0xFFFF0000 )>> 16 ) ^ ( connPtr->accessAddr & 0x0000FFFF)),
connPtr->chanMap,
connPtr->chanMapTable,
connPtr->numUsedChans);
}
//connPtr->slaveLatency -= (connPtr->nextEvent - next_event);
connPtr->slaveLatency -= llEventDelta(connPtr->nextEvent, next_event);
connPtr->nextEvent = next_event;
//
// LOG("==5==\n\r");
//
//
#if (DBG_DISABLE_LATENCY)
LOG("new timer = %d\r\n", remain_time2 - time_advance);
LOG("old Drift = %d\,new Drift = %d \r\n", old_timerDrift,connPtr->timerDrift);
LOG("cur_event=%d,elapse_event=%d, next_event=%d,\r\n",connPtr->currentEvent, elapse_event,next_event);
LOG("RFCHN n=%d o=%d\r\n",connPtr->currentChan,connPtr->lastCurrentChan);
#endif
return(LL_STATUS_SUCCESS);
}
llStatus_t LL_PLUS_EnableSlaveLatency0(uint8 connId)
{
llConnState_t* connPtr;
// get connection information
connPtr = &conn_param[connId]; // only 1 connection now.
// check whether it should enable from disable_latency
if (FALSE == connPtr->active
|| llState != LL_STATE_CONN_SLAVE
|| llSecondaryState != LL_SEC_STATE_IDLE)
{
//LOG("==e1==\n\r");
return(LL_STATUS_DISABLE_LATENCY_INACTIVE_CONN);
}
// // check whether it should enable from disable_latency
// if ( connPtr->pendingChanUpdate == TRUE
// || connPtr->pendingParamUpdate == TRUE
// || connPtr->pendingPhyModeUpdate == TRUE)
// {
// LOG("==e2==\n\r");
// return;
// }
pGlobal_config[LL_SWITCH] |= SLAVE_LATENCY_ALLOW;
connPtr->slaveLatencyAllowed = TRUE;
return(LL_STATUS_SUCCESS);
}
llStatus_t LL_ExtendedCreateConnection(uint8 initiator_filter_policy,
uint8 own_address_type,
uint8 peer_address_type,
uint8* peer_address,
uint8 initiating_PHYs,
uint16* scan_interval,
uint16* scan_window,
uint16* conn_interval_min,
uint16* conn_interval_max,
uint16* conn_latency,
uint16* supervision_timeout,
uint16* minimum_CE_length,
uint16* maximum_CE_length)
{
uint8 number_phys, i;
llConnState_t* connPtr;
uint16 txHeader = 0x2205;
if (g_llScanMode == LL_MODE_LEGACY )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_EXTENDED;
// TODO: more sanity checking
if (((initiating_PHYs & (~(LL_SCAN_PHY_1M_BITMASK | LL_CONN_PHY_2M_BITMASK | LL_SCAN_PHY_CODED_BITMASK))) != 0))
return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED;
// ===== allocate a connection and assure it is valid
if ( (connPtr = llAllocConnId()) == NULL )
{
llSecondaryState = LL_SEC_STATE_IDLE; // recover llSecondaryState
// exceeded the number of available connection structures
return( LL_STATUS_ERROR_CONNECTION_LIMIT_EXCEEDED );
}
// ================== save init parameters
extInitInfo.ownAddrType = own_address_type;
extInitInfo.wlPolicy = initiator_filter_policy;
number_phys = 0;
i = 0;
if (initiating_PHYs & LL_SCAN_PHY_1M_BITMASK)
{
extInitInfo.initPHYs[number_phys] = PKT_FMT_BLE1M;
extInitInfo.scanInterval[number_phys] = scan_interval[i];
extInitInfo.scanWindow[number_phys] = scan_window[i];
extInitInfo.conn_latency[number_phys] = conn_latency[i];
extInitInfo.supervision_timeout[number_phys] = supervision_timeout[i];
extInitInfo.conn_interval_min[number_phys] = conn_interval_min[i];
extInitInfo.conn_interval_max[number_phys] = conn_interval_max[i];
extInitInfo.minimum_CE_length[number_phys] = minimum_CE_length[i];
extInitInfo.maximum_CE_length[number_phys] = maximum_CE_length[i];
number_phys ++;
i ++;
}
if (initiating_PHYs & LL_CONN_PHY_2M_BITMASK)
{
extInitInfo.is_2M_parameter_present = TRUE;
// no scan parameters for 2M PHY, the HCI parameters are skipped
extInitInfo.conn_latency_2Mbps = conn_latency[i];
extInitInfo.supervision_timeout_2Mbps = supervision_timeout[i];
extInitInfo.conn_interval_min_2Mbps = conn_interval_min[i];
extInitInfo.conn_interval_max_2Mbps = conn_interval_max[i];
extInitInfo.minimum_CE_length_2Mbps = minimum_CE_length[i];
extInitInfo.maximum_CE_length_2Mbps = maximum_CE_length[i];
i ++;
}
if (initiating_PHYs & LL_SCAN_PHY_CODED_BITMASK)
{
extInitInfo.initPHYs[number_phys] = PKT_FMT_BLR125K;
extInitInfo.scanInterval[number_phys] = scan_interval[i];
extInitInfo.scanWindow[number_phys] = scan_window[i];
extInitInfo.conn_latency[number_phys] = conn_latency[i];
extInitInfo.supervision_timeout[number_phys] = supervision_timeout[i];
extInitInfo.conn_interval_min[number_phys] = conn_interval_min[i];
extInitInfo.conn_interval_max[number_phys] = conn_interval_max[i];
extInitInfo.minimum_CE_length[number_phys] = minimum_CE_length[i];
extInitInfo.maximum_CE_length[number_phys] = maximum_CE_length[i];
number_phys ++;
}
extInitInfo.numOfScanPHY = number_phys;
// save the peer address type
peerInfo.peerAddrType = peer_address_type;
// save the peer address
if (peer_address != NULL) // bug fixed
LL_COPY_DEV_ADDR_LE( peerInfo.peerAddr, peer_address );
// save our address type
extInitInfo.ownAddrType = own_address_type;
// check the type of own address
if ( own_address_type == LL_DEV_ADDR_TYPE_PUBLIC )
{
// save our address
LL_COPY_DEV_ADDR_LE( extInitInfo.ownAddr, ownPublicAddr );
}
else // LL_DEV_ADDR_TYPE_RANDOM
{
// save our address
LL_COPY_DEV_ADDR_LE( extInitInfo.ownAddr, ownRandomAddr );
}
// ========== update connection context
g_ll_conn_ctx.numLLMasterConns ++;
// reset connection parameters
LL_set_default_conn_params(connPtr);
// clear the connection buffer
reset_conn_buf(connPtr->connId);
// save the connection ID with Init
extInitInfo.connId = connPtr->connId;
// set the connection channel map
for (i = 0; i < LL_NUM_BYTES_FOR_CHAN_MAP; i++)
{
connPtr->chanMap[i] = chanMapUpdate.chanMap[i];
}
// process connection channel map into the data channel table
llProcessChanMap( connPtr, connPtr->chanMap );
// ramdomly generate a valid 24 bit CRC value
connPtr->initCRC = llGenerateCRC();
// randomly generate a valid, previously unused, 32-bit access address
connPtr->accessAddr = llGenerateValidAccessAddr();
// ============= below connection context depends on PHY index, will be selected when scan OK
#if 0
if (g_ll_conn_ctx.numLLMasterConns == 1) // A2 multi-connection, 1st connection, save the connection parameters
{
// determine the connection interval based on min and max values
// Note: Range not used, so assume max value.
// Note: minLength and maxLength are informational.
connPtr->curParam.connInterval = conn_interval_max;
// set the connection timeout
// Note: The spec says this begins at the end of the CONNECT_REQ, but the
// LSTO will be converted into events.
connPtr->curParam.connTimeout = supervision_timeout;
// set the slave latency
connPtr->curParam.slaveLatency = conn_latency;
// save connection parameter as global
g_ll_conn_ctx.connInterval = connPtr->curParam.connInterval; // unit: 1.25ms
g_ll_conn_ctx.slaveLatency = connPtr->curParam.slaveLatency;
g_ll_conn_ctx.connTimeout = connPtr->curParam.connTimeout;
g_ll_conn_ctx.per_slot_time = connPtr->curParam.connInterval * 2 / g_maxConnNum; // unit: 625us
}
else
{
// determine the connection interval based on min and max values
// Note: Range not used, so assume max value.
// Note: minLength and maxLength are informational.
connPtr->curParam.connInterval = g_ll_conn_ctx.connInterval;
// set the connection timeout
// Note: The spec says this begins at the end of the CONNECT_REQ, but the
// LSTO will be converted into events.
connPtr->curParam.connTimeout = g_ll_conn_ctx.connTimeout;
// set the slave latency
connPtr->curParam.slaveLatency = g_ll_conn_ctx.slaveLatency;
}
#endif
// set the master's SCA
connPtr->sleepClkAccuracy = extInitInfo.scaValue;
// set the window size (units of 1.25ms)
// Note: Must be the lesser of 10ms and the connection interval - 1.25ms.
connPtr->curParam.winSize = pGlobal_config[LL_CONN_REQ_WIN_SIZE];
// set the window offset (units of 1.25ms). TO change if we support multiple connections
// Note: Normally, the window offset is managed dynamically so that precise
// connection start times can be achieved (necessary for multiple
// connnections). However, sometimes it is useful to force the window
// offset to something specific for testing. This can be done by here
// when the project is built with the above define.
// Note: This define should only be used for testing one connection and will
// NOT work when multiple connections are attempted!
connPtr->curParam.winOffset = pGlobal_config[LL_CONN_REQ_WIN_OFFSET];//2;//LL_WINDOW_OFFSET;
// set the channel map hop length (5..16)
// Note: 0..255 % 12 = 0..11 + 5 = 5..16.
connPtr->hop = (uint8)( (LL_ENC_GeneratePseudoRandNum() % 12) + 5);
// =============== fill AUX_CONNECT_REQ PDU, some connection parameters depend on init PHY
{
uint8 offset = 0;
g_tx_adv_buf.txheader = txHeader;
// setup CONN REQ in connPtr->ll_buf
LL_ReadBDADDR(&g_tx_adv_buf.data[offset]); // initA, Byte 0 ~ 5
offset += 6;
if (peer_address != NULL)
LL_COPY_DEV_ADDR_LE(&g_tx_adv_buf.data[offset], peer_address) // AdvA, Byte 6 ~ 11
offset += 6;
// Access Address, Byte 12 ~ 15
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->accessAddr, 4);
offset += 4;
// CRC init, Byte 16 ~ 18
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->initCRC, 3);
offset += 3;
// WinSize, Byte 19
g_tx_adv_buf.data[offset] = connPtr->curParam.winSize;
offset += 1;
// WinOffset, Byte 20 ~ 21
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->curParam.winOffset, 2);
offset += 2;
// Interval, Byte 22 ~ 23
// memcpy((uint8 *)&g_tx_adv_buf.data[offset], (uint8 *)&connPtr->curParam.connInterval, 2);
offset += 2;
// Latency, Byte 24 ~ 25
// memcpy((uint8 *)&g_tx_adv_buf.data[offset], (uint8 *)&connPtr->curParam.slaveLatency, 2);
offset += 2;
// Timeout, Byte 26 ~ 27
// memcpy((uint8 *)&g_tx_adv_buf.data[offset], (uint8 *)&connPtr->curParam.connTimeout, 2);
offset += 2;
// Channel Map, Byte 28 ~ 32
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->chanMap[0], 5);
offset += 5;
// Hop(5bit) + SCA(3bit), Byte 33
g_tx_adv_buf.data[offset] = (connPtr->hop & 0x1f) | ((connPtr->sleepClkAccuracy & 0x7) << 5);
}
// =============== init scan
// if ( llState == LL_STATE_IDLE )
// // go ahead and start Init immediately
extInitInfo.current_index = 0;
extInitInfo.current_chn = LL_ADV_CHAN_FIRST;
extInitInfo.current_scan_PHY = extInitInfo.initPHYs[extInitInfo.current_index];
// enable Init scan
extInitInfo.scanMode = LL_SCAN_START;
llSetupExtInit();
// else
// osal_set_event(LL_TaskID, LL_EVT_SECONDARY_INIT);
return LL_STATUS_SUCCESS;
}
// ========================= Periodic Advertiser
llStatus_t LL_SetPeriodicAdvParameter(uint8 adv_handle,
uint16 interval_min,
uint16 interval_max,
uint16 adv_event_properties)
{
int i;
// TODO: add parameters range checking
if (g_llAdvMode == LL_MODE_LEGACY)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_EXTENDED;
if (interval_max * 1250 < g_advSlotPeriodic)
return LL_STATUS_ERROR_OUT_OF_CONN_RESOURCES;
// extended advertiser memory is allocate by application, return fail if not
if (g_pPeriodicAdvInfo == NULL)
return LL_STATUS_ERROR_OUT_OF_HEAP;
// search advertisement handler list
for (i = 0; i < g_perioAdvNumber; i ++)
{
if (g_pPeriodicAdvInfo[i].advHandle == adv_handle)
break;
}
// if not found, search the 1st available slot
if (i == g_perioAdvNumber)
{
for (i = 0; i < g_perioAdvNumber; i ++)
{
if (g_pPeriodicAdvInfo[i].advHandle == LL_INVALID_ADV_SET_HANDLE)
{
memset(&g_pPeriodicAdvInfo[i], 0, sizeof(periodicAdvInfo_t));
break;
}
}
}
if (i == g_perioAdvNumber)
return LL_STATUS_ERROR_OUT_OF_HEAP;
// search extended advertisement handler list
for (i = 0; i < g_extAdvNumber; i ++)
{
if (g_pExtendedAdvInfo[i].advHandle == adv_handle)
break;
}
// if not found, report error. refer Vol 4, Part E. 7.8.61 LE Set Periodic Advertising Parameters command
// The Advertising_Handle parameter identifies the advertising set whose
// periodic advertising parameters are being configured. If the corresponding
// advertising set does not already exist, then the Controller shall return the error
// code Unknown Advertising Identifier (0x42).
if (i == g_extAdvNumber)
{
return LL_STATUS_ERROR_UNKNOWN_ADV_ID;
}
else
g_pExtendedAdvInfo[i].isPeriodic = TRUE;
// save the advertisement parameters
g_pPeriodicAdvInfo[i].advHandle = adv_handle;
g_pPeriodicAdvInfo[i].adv_event_properties = adv_event_properties;
g_pPeriodicAdvInfo[i].adv_interval_max = interval_max;
g_pPeriodicAdvInfo[i].adv_interval_min = interval_min;
// =========== controller select parameters, TBD
// decide periodic advertising interval
g_pPeriodicAdvInfo[i].adv_interval = g_advSlotPeriodic;
return LL_STATUS_SUCCESS;
}
llStatus_t LL_SetPeriodicAdvData(uint8 adv_handle,
uint8 operation,
uint8 advertising_data_length,
uint8* advertising_data)
{
int i;
// TODO: add parameters range checking
if (g_llAdvMode == LL_MODE_LEGACY)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_EXTENDED;
if (operation > BLE_EXT_ADV_OP_UNCHANGED_DATA)
return LL_STATUS_ERROR_BAD_PARAMETER;
// extended advertiser memory is allocate by application, return fail if not
if (g_pPeriodicAdvInfo == NULL)
return LL_STATUS_ERROR_OUT_OF_HEAP;
// search advertisement handler list
for (i = 0; i < g_perioAdvNumber; i ++)
{
if (g_pPeriodicAdvInfo[i].advHandle == adv_handle)
break;
}
// if not found, return error
if (i == g_perioAdvNumber)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
if (operation == BLE_EXT_ADV_OP_FIRST_FRAG ||
operation == BLE_EXT_ADV_OP_COMPLETE_DATA)
{
g_pPeriodicAdvInfo[i].data.advertisingDataLength = 0;
g_pPeriodicAdvInfo[i].data.dataComplete = FALSE;
}
if (g_pPeriodicAdvInfo[i].data.advertisingDataLength + advertising_data_length > g_advSetMaximumLen)
{
g_pPeriodicAdvInfo[i].data.advertisingDataLength = 0;
return LL_STATUS_ERROR_OUT_OF_HEAP;
}
// fill advertising set
g_pPeriodicAdvInfo[i].advHandle = adv_handle;
memcpy(&g_pPeriodicAdvInfo[i].data.advertisingData[g_pPeriodicAdvInfo[i].data.advertisingDataLength],
advertising_data, advertising_data_length);
g_pPeriodicAdvInfo[i].data.advertisingDataLength += advertising_data_length;
// last fragment or 1 segment adv data
if (operation == BLE_EXT_ADV_OP_LAST_FRAG ||
operation == BLE_EXT_ADV_OP_COMPLETE_DATA )
g_pPeriodicAdvInfo[i].data.dataComplete = TRUE;
return LL_STATUS_SUCCESS;
}
llStatus_t LL_SetPeriodicAdvEnable(uint8 enable,
uint8 advertising_handle)
{
int i;
periodicAdvInfo_t* pPrdAdv = NULL;
extAdvInfo_t* pExtAdv = NULL;
if (g_llAdvMode == LL_MODE_LEGACY)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llAdvMode = LL_MODE_EXTENDED;
if (g_pPeriodicAdvInfo == NULL)
return LL_STATUS_ERROR_OUT_OF_HEAP;
// search advertisement handler list
for (i = 0; i < g_perioAdvNumber; i ++)
{
if (g_pPeriodicAdvInfo[i].advHandle == advertising_handle)
{
pPrdAdv = &g_pPeriodicAdvInfo[i];
break;
}
}
// if not found, return error
if (i == g_perioAdvNumber)
return LL_STATUS_ERROR_UNKNOWN_ADV_ID;
if (g_pPeriodicAdvInfo[i].data.dataComplete == FALSE
&& enable == TRUE)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
// TODO: check whether the adv data could be filled in the adv period(error code LL_STATUS_ERROR_PACKET_TOO_LONG)
// ====== periodic advertiser scheduler process
if (enable == FALSE)
{
if (pPrdAdv->active == FALSE) // already disable, do nothing
return LL_STATUS_SUCCESS;
// disable periodic adv process
// case 1: extended adv is already disable
// case 2: extended adv is not disable, it will also stop extended adv
// search the adv in the scheduler list and disable it
for (i = 0; i < g_schExtAdvNum_periodic; i++)
{
if (g_pAdvSchInfo_periodic[i].adv_handler == pPrdAdv[i].advHandle)
{
g_pAdvSchInfo_periodic[i].pAdvInfo->active = FALSE;
ll_delete_adv_task_periodic(i);
break;
}
}
}
else // enable case
{
if (pPrdAdv->active == TRUE)
{
// TODO: Enabling periodic advertising when it is already enabled can cause the
// random address to change
return LL_STATUS_SUCCESS;
}
// search advertisement handler list
for (i = 0; i < g_extAdvNumber; i ++)
{
if (g_pExtendedAdvInfo[i].advHandle == advertising_handle)
{
pExtAdv = &g_pExtendedAdvInfo[i];
break;
}
}
// initial periodic adv sync info context
// ramdomly generate a valid 24 bit CRC value
pPrdAdv->crcInit = llGenerateCRC();
// randomly generate a valid, previously unused, 32-bit access address
pPrdAdv->AA = llGenerateValidAccessAddr();
pPrdAdv->sca = LL_SCA_MASTER_DEFAULT;
pPrdAdv->chn_map[0] = 0xFF;
pPrdAdv->chn_map[1] = 0xFF;
pPrdAdv->chn_map[2] = 0xFF;
pPrdAdv->chn_map[3] = 0xFF;
pPrdAdv->chn_map[4] = 0x1F;
// 2020-1-7 add for CSA2
// the used channel map uses 1 bit per data channel, or 5 bytes for 37 chans
for (i = 0; i < LL_NUM_BYTES_FOR_CHAN_MAP; i++)
{
// replace the current channel map with the update channel map
// Note: This needs to be done so that llGetNextDataChan can more
// efficiently determine if the next channel is used.
// connPtr->chanMap[i] = chanMap[i];
// for each channel given by a bit in each of the bytes
// Note: When i is on the last byte, only 5 bits need to be checked, but
// it is easier here to check all 8 with the assumption that the rest
// of the reserved bits are zero.
for (uint8 j = 0; j < 8; j++)
{
// check if the channel is used; only interested in used channels
if ( (pPrdAdv->chn_map[i] >> j) & 1 )
{
// sequence used channels in ascending order
pPrdAdv->chanMapTable[pPrdAdv->numUsedChans] = (i * 8U) + j;
// count it
pPrdAdv->numUsedChans++;
}
}
}
// case 1: extended adv is not enable, not trigger periodic adv
if (pExtAdv == NULL || pExtAdv->active == FALSE)
{
}
// case 2: extende adv is enable, start extended adv + periodic adv
else
{
pExtAdv->data.DIDInfo = ll_generateExtAdvDid(pExtAdv->data.DIDInfo);
ll_add_adv_task_periodic(pPrdAdv, pExtAdv);
}
}
pPrdAdv->active = enable;
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_PeriodicAdvertisingCreateSync(uint8 options,
uint8 advertising_SID,
uint8 advertiser_Address_Type,
uint8* advertiser_Address,
uint16 skip,
uint16 sync_Timeout,
uint8 sync_CTE_Type)
{
if (g_llScanMode == LL_MODE_LEGACY )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_EXTENDED;
// TODO: add more sanity checking
if (scanSyncInfo.valid == TRUE)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
if (options & LL_PERIODIC_ADV_CREATE_SYNC_INIT_RPT_DISABLE_BITMASK) // we not support this mode
return LL_STATUS_ERROR_CONN_FAILED_TO_BE_ESTABLISHED;
scanSyncInfo.options = options;
scanSyncInfo.advertising_SID = advertising_SID;
scanSyncInfo.advertiser_Address_Type = advertiser_Address_Type;
if (advertiser_Address != NULL)
memcpy(scanSyncInfo.advertiser_Address, advertiser_Address, LL_DEVICE_ADDR_LEN);
scanSyncInfo.skip = skip;
scanSyncInfo.sync_Timeout = sync_Timeout;
scanSyncInfo.sync_CTE_Type = sync_CTE_Type;
scanSyncInfo.valid = TRUE;
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_PeriodicAdvertisingCreateSyncCancel(void)
{
if (g_llScanMode == LL_MODE_LEGACY )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_EXTENDED;
if (scanSyncInfo.valid == FALSE)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
scanSyncInfo.valid = FALSE;
return( LL_STATUS_SUCCESS );
}
// TODO
llStatus_t LL_PeriodicAdvertisingTerminateSync( uint16 sync_handle)
{
if (g_llScanMode == LL_MODE_LEGACY )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_EXTENDED;
// TODO: search periodic scanner list and remove it from schedule list
llDeleteSyncHandle(sync_handle);
return( LL_STATUS_SUCCESS );
}
// ==========
llStatus_t LL_AddDevToPeriodicAdvList(uint8 addrType,
uint8* devAddr,
uint8 sid)
{
int i;
if (g_llScanMode == LL_MODE_LEGACY )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_EXTENDED;
// create Sync pending
if (scanSyncInfo.valid == TRUE)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
// check the device address type
if ( (addrType != LL_DEV_ADDR_TYPE_PUBLIC) &&
(addrType != LL_DEV_ADDR_TYPE_RANDOM) )
{
return( LL_STATUS_ERROR_BAD_PARAMETER );
}
// check if there was room for the entry
if (g_llPrdAdvDeviceNum >= LL_PRD_ADV_ENTRY_NUM)
return( LL_STATUS_ERROR_PAL_TABLE_FULL );
// if the entry already exist, return 0x12 error code
for (i = 0; i < LL_PRD_ADV_ENTRY_NUM; i++)
{
if (g_llPeriodicAdvlist[i].addrType == addrType
&& g_llPeriodicAdvlist[i].sid == sid
&& (g_llPeriodicAdvlist[i].addr[0] == devAddr[0]
&& g_llPeriodicAdvlist[i].addr[1] == devAddr[1]
&& g_llPeriodicAdvlist[i].addr[2] == devAddr[2]
&& g_llPeriodicAdvlist[i].addr[3] == devAddr[3]
&& g_llPeriodicAdvlist[i].addr[4] == devAddr[4]
&& g_llPeriodicAdvlist[i].addr[5] == devAddr[5]))
return LL_STATUS_ERROR_BAD_PARAMETER;
}
// add the device to a empty record
for (i = 0; i < LL_PRD_ADV_ENTRY_NUM; i++)
{
if (g_llPeriodicAdvlist[i].addrType == 0xff) // empty record
{
g_llPeriodicAdvlist[i].addrType = addrType;
osal_memcpy(&g_llPeriodicAdvlist[i].addr[0], &devAddr[0], LL_DEVICE_ADDR_LEN);
g_llPeriodicAdvlist[i].sid = sid;
g_llPrdAdvDeviceNum ++;
break;
}
}
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_RemovePeriodicAdvListDevice(uint8 addrType,
uint8* devAddr,
uint8 sid)
{
int i;
if (g_llScanMode == LL_MODE_LEGACY )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_EXTENDED;
// create Sync pending
if (scanSyncInfo.valid == TRUE)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
// search the device
for (i = 0; i < LL_PRD_ADV_ENTRY_NUM; i++)
{
if (g_llPeriodicAdvlist[i].addrType == addrType
&& g_llPeriodicAdvlist[i].sid == sid
&& (g_llPeriodicAdvlist[i].addr[0] == devAddr[0]
&& g_llPeriodicAdvlist[i].addr[1] == devAddr[1]
&& g_llPeriodicAdvlist[i].addr[2] == devAddr[2]
&& g_llPeriodicAdvlist[i].addr[3] == devAddr[3]
&& g_llPeriodicAdvlist[i].addr[4] == devAddr[4]
&& g_llPeriodicAdvlist[i].addr[5] == devAddr[5]))
break;
}
if (i == LL_PRD_ADV_ENTRY_NUM
|| g_llPrdAdvDeviceNum == 0)
return LL_STATUS_ERROR_UNKNOWN_ADV_ID;
g_llPeriodicAdvlist[i].addrType = 0xff;
memset(g_llPeriodicAdvlist[i].addr, 0, LL_DEVICE_ADDR_LEN);
g_llPrdAdvDeviceNum --;
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_ClearPeriodicAdvList(void)
{
int i;
if (g_llScanMode == LL_MODE_LEGACY )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_EXTENDED;
// create Sync pending
if (scanSyncInfo.valid == TRUE)
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
for (i = 0; i < LL_PRD_ADV_ENTRY_NUM; i++)
{
g_llPeriodicAdvlist[i].addrType = 0xff;
memset(g_llPeriodicAdvlist[i].addr, 0, LL_DEVICE_ADDR_LEN);
}
g_llPrdAdvDeviceNum = 0;
return( LL_STATUS_SUCCESS );
}
llStatus_t LL_ReadPeriodicAdvListSize(uint8* numEntries)
{
if (g_llScanMode == LL_MODE_LEGACY )
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
g_llScanMode = LL_MODE_EXTENDED;
*numEntries = LL_PRD_ADV_ENTRY_NUM;
return( LL_STATUS_SUCCESS );
}
// ==================
llStatus_t LL_ConnectionlessCTE_TransmitParam0( uint8 advertising_handle,
uint8 len,
uint8 type,
uint8 count,
uint8 Pattern_LEN,
uint8* AnaIDs)
{
int i,j;
uint8 suppCTEType=0;
periodicAdvInfo_t* pPrdAdv = NULL;
if (g_pPeriodicAdvInfo == NULL)
return LL_STATUS_ERROR_OUT_OF_HEAP;
// search advertisement handler list
for (i = 0; i < g_perioAdvNumber; i ++)
{
if (g_pPeriodicAdvInfo[i].advHandle == advertising_handle)
{
pPrdAdv = &g_pPeriodicAdvInfo[i];
break;
}
}
// if period advertising set not cteate , return unknown advertising Identifier
if( i == g_perioAdvNumber )
{
return LL_STATUS_ERROR_UNKNOWN_ADV_ID;
}
// return error code Command Disallowed when CTE hasve been enabled
if( pPrdAdv->PrdCTEInfo.enable == TRUE )
{
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
}
// check Controller support CTE Type
suppCTEType = deviceFeatureSet.featureSet[LL_CTE_FEATURE_IDX] & 0x60;
pPrdAdv->PrdCTEInfo.CTE_Type = type;
switch (type)
{
case CONNLESS_CTE_TYPE_AOA:
// ignore Pattern_LEN and AnaIDs
if( !( suppCTEType & LL_AOA_SUPPORT ) )
{
return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED;
}
break;
case CONNLESS_CTE_TYPE_AOD_1us:
case CONNLESS_CTE_TYPE_AOD_2us:
// TODO
// should check the Controller support AOD 1us or 2us
if( !( suppCTEType & LL_AOD_SUPPORT ) || ( Pattern_LEN > LL_CTE_MAX_PATTERN_LEN ) )
{
return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED;
}
else
{
pPrdAdv->PrdCTEInfo.pattern_LEN = Pattern_LEN;
for( j = 0; j < Pattern_LEN; j++)
{
if( AnaIDs[j] > LL_CTE_MAX_ANT_ID )
return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED;
}
osal_memcpy(pPrdAdv->PrdCTEInfo.AntID, AnaIDs, LL_CTE_MAX_PATTERN_LEN );
}
break;
default:
// unsupported CTE Type
// return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED;
break;
}
// parameter len in 8us units shall not greater than LL_CTE_MAX_SUPP_LEN
// max CTE_Count check in each PA Event
if(( len < LL_CTE_MIN_SUPP_LEN ) || ( len > LL_CTE_MAX_SUPP_LEN ) || ( count > LL_CTE_MAX_PA_INTV_CNT ))
{
return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED;
}
else
{
pPrdAdv->PrdCTEInfo.CTE_Length = len;
pPrdAdv->PrdCTEInfo.CTE_Count = count;
pPrdAdv->PrdCTEInfo.CTE_Count_Idx = 0;
}
return LL_STATUS_SUCCESS;
}
llStatus_t LL_ConnectionlessCTE_TransmitEnable0( uint8 advertising_handle,uint8 enable)
{
int i,j;
uint32 ant1=0,ant0=0;
periodicAdvInfo_t* pPrdAdv = NULL;
if (g_pPeriodicAdvInfo == NULL)
return LL_STATUS_ERROR_OUT_OF_HEAP;
// search advertisement handler list
for (i = 0; i < g_perioAdvNumber; i ++)
{
// equal advertising_handle indicate that it has already issued LL_SetPeriodicAdvParameter command
if (g_pPeriodicAdvInfo[i].advHandle == advertising_handle)
{
pPrdAdv = &g_pPeriodicAdvInfo[i];
break;
}
}
// advertising set not exist return error code
if( i == g_perioAdvNumber )
{
return LL_STATUS_ERROR_UNKNOWN_ADV_ID;
}
// should enable periodic advertising first
// check g_pPeriodicAdvInfo[i].active means : if periodic advertising set disabled that should not enable CTE Tx
// 2020-02-10 comment
// TODO :: only check if PA Parameter is set before CTE Transmit enable
// if( ( g_pPeriodicAdvInfo[i].active != TRUE ) )
// {
// return LL_STATUS_ERROR_COMMAND_DISALLOWED;
// }
// means the host has not issue LL_ConnectionlessCTE_TransmitParam before this command
if( 0 == pPrdAdv->PrdCTEInfo.CTE_Length )
{
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
}
// check secondary advertising PHY allow CTE
if( pPrdAdv->secondaryAdvPHY > LL_SECOND_ADV_PHY_2M )
{
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
}
switch ( enable )
{
case LL_CTE_ENABLE:
pPrdAdv->PrdCTEInfo.enable = LL_CTE_ENABLE;
switch ( pPrdAdv->PrdCTEInfo.CTE_Type )
{
case CONNLESS_CTE_TYPE_AOA:
// AOA transmit not switching Antenna , shall not config switch timing
// ll_hw_set_ant_switch_timing(8, 40);
ll_hw_set_ant_switch_mode( LL_HW_ANT_SW_CTE_OFF );
break;
case CONNLESS_CTE_TYPE_AOD_1us:
case CONNLESS_CTE_TYPE_AOD_2us:
// combination ant pattern
for( j = 0 ; j < pPrdAdv->PrdCTEInfo.pattern_LEN ; j++ )
{
// PHY SDK 4 bit represents an Antenna ID, so uint32 represents max 8 antenna
if( j < 8 )
{
ant0 |= ( ((uint32)pPrdAdv->PrdCTEInfo.AntID[j]) << (4*j) );
}
else
{
ant1 |= ( ((uint32)pPrdAdv->PrdCTEInfo.AntID[j]) << (4*(j-8)) );
}
}
ll_hw_set_ant_pattern( ant1, ant0 );
// antWin : 8, 1us switching + 1us sampling 8 *0.25us unit = 2us
// antWin : 16,2us switching + 2us sampling 16*0.25us unit = 4us
ll_hw_set_ant_switch_timing((pPrdAdv->PrdCTEInfo.CTE_Type==CONNLESS_CTE_TYPE_AOD_1us?8:16), 40);
ll_hw_set_ant_switch_mode( LL_HW_ANT_SW_TX_MANU );
break;
default:
break;
}
// 2020-02-10 comment
// TODO: cte_txSupp instruction executed here, whether all packet contain CTEInfo field ,
// and those that shall not include CTEInfo filed , eg AUX_EXT_IND
// ll_hw_set_cte_txSupp( CTE_SUPP_LEN_SET | g_pPeriodicAdvInfo[i].PrdCTEInfo.CTE_Length );
break;
case LL_CTE_DISABLE:
pPrdAdv->PrdCTEInfo.enable = LL_CTE_DISABLE;
break;
default:
break;
}
return LL_STATUS_SUCCESS;
}
llStatus_t LL_ConnectionlessIQ_SampleEnable0( uint16 sync_handle,
uint8 enable,
uint8 slot_Duration,
uint8 MaxSampledCTEs,
uint8 pattern_len,
uint8* AnaIDs)
{
int i,j;
uint32 ant1=0,ant0=0;
for( i = 0; i < MAX_NUM_LL_PRD_ADV_SYNC ; i++ )
{
if( g_llPeriodAdvSyncInfo[i].syncHandler == sync_handle )
break;
}
// only support 1 periodic advertising , so only support 1 IQ Sampling
// TODO
// Logic issus : JIRA BBBBLESTAC-10 issue 4
if( ( scanSyncInfo.valid != TRUE ) || ( i == MAX_NUM_LL_PRD_ADV_SYNC ) )
{
return LL_STATUS_ERROR_UNKNOWN_ADV_ID;
}
if( MaxSampledCTEs > LL_CTE_MAX_IQ_SAMP_CNT)
{
return LL_STATUS_ERROR_UNEXPECTED_PARAMETER;
}
else
{
g_llPeriodAdvSyncInfo[i].IQSampleInfo.CTE_Count = MaxSampledCTEs;
}
// PHY SDK default support 1us and 2us slot dureation, so no condition determination
g_llPeriodAdvSyncInfo[i].IQSampleInfo.slot_Duration = slot_Duration;
switch ( enable )
{
case LL_IQ_SAMP_ENABLE:
if( g_llPeriodAdvSyncInfo[i].IQSampleInfo.enable )
{
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
}
else
{
g_llPeriodAdvSyncInfo[i].IQSampleInfo.enable = LL_IQ_SAMP_ENABLE;
switch ( g_llPeriodAdvSyncInfo[i].IQSampleInfo.CTE_Type )
{
case CONNLESS_CTE_TYPE_AOA:
g_llPeriodAdvSyncInfo[i].IQSampleInfo.pattern_LEN = pattern_len;
for( j = 0; j < pattern_len; j++)
{
if( AnaIDs[j] > LL_CTE_MAX_ANT_ID )
return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED;
}
osal_memcpy(g_llPeriodAdvSyncInfo[i].IQSampleInfo.AntID, AnaIDs, LL_CTE_MAX_PATTERN_LEN );
ll_hw_set_ant_switch_mode( LL_HW_ANT_SW_RX_MANU );
// combination ant pattern
for( j = 0 ; j < g_llPeriodAdvSyncInfo[i].IQSampleInfo.pattern_LEN ; j++ )
{
// PHY SDK 4 bit represents an Antenna ID, so uint32 represents max 8 antenna
if( j < 8 )
{
ant0 |= ( ((uint32)g_llPeriodAdvSyncInfo[i].IQSampleInfo.AntID[j]) << (4*j) );
}
else
{
ant1 |= ( ((uint32)g_llPeriodAdvSyncInfo[i].IQSampleInfo.AntID[j]) << (4*(j-8)) );
}
}
ll_hw_set_ant_pattern( ant1, ant0 );
break;
case CONNLESS_CTE_TYPE_AOD_1us:
case CONNLESS_CTE_TYPE_AOD_2us:
// AOD receiver not switching Antenna
ll_hw_set_ant_switch_mode( LL_HW_ANT_SW_CTE_OFF );
ll_hw_set_ant_pattern( 0, 0 );
break;
default:
break;
}
// antWin : 8, 1us switching + 1us sampling 8 *0.25us unit = 2us
// antWin : 16,2us switching + 2us sampling 16*0.25us unit = 4us
ll_hw_set_ant_switch_timing((slot_Duration==LL_IQ_SW_SAMP_1US?8:16), 40);
ll_hw_set_cte_rxSupp( CTE_SUPP_LEN_SET | g_pPeriodicAdvInfo[i].PrdCTEInfo.CTE_Length );
}
break;
case LL_IQ_SAMP_DISABLE:
g_llPeriodAdvSyncInfo[i].IQSampleInfo.enable = LL_IQ_SAMP_ENABLE;
break;
default:
break;
}
return LL_STATUS_SUCCESS;
}
llStatus_t LL_Set_ConnectionCTE_ReceiveParam0( uint16 connHandle,
uint8 enable,
uint8 slot_Duration,
uint8 pattern_len,
uint8* AnaIDs)
{
uint8 i,j;
uint32 ant1=0,ant0=0;
// llConnState_t.connid means connHandle
llConnState_t* connPtr;
for( i = 0; i< g_maxConnNum; i++)
{
connPtr = &conn_param[i];
if( connPtr->connId == connHandle )
{
break;
}
}
if( g_maxConnNum == i )
{
return LL_STATUS_ERROR_INACTIVE_CONNECTION;
}
switch( enable )
{
case LL_CONN_IQSAMP_ENABLE:
if( connPtr->llConnCTE.enable == LL_CONN_IQSAMP_ENABLE )
{
// current connection CTE already enabled, return error
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
}
else
{
connPtr->llConnCTE.slot_Duration = slot_Duration;
connPtr->llConnCTE.enable = LL_CONN_IQSAMP_ENABLE;
connPtr->llConnCTE.pattern_LEN = pattern_len;
for( j = 0; j < pattern_len; j++)
{
if( AnaIDs[j] > LL_CTE_MAX_ANT_ID )
return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED;
}
osal_memcpy(connPtr->llConnCTE.AntID, AnaIDs, LL_CTE_MAX_PATTERN_LEN );
for( j = 0 ; j < connPtr->llConnCTE.pattern_LEN ; j++ )
{
// PHY SDK 4 bit represents an Antenna ID, so uint32 represents max 8 antenna
if( j < 8 )
{
ant0 |= ( ((uint32)connPtr->llConnCTE.AntID[j]) << (4*j) );
}
else
{
ant1 |= ( ((uint32)connPtr->llConnCTE.AntID[j]) << (4*(j-8)) );
}
}
ll_hw_set_ant_pattern( ant1, ant0 );
ll_hw_set_ant_switch_mode( LL_HW_ANT_SW_CTE_AUTO );
// antWin : 8, 1us switching + 1us sampling 8 *0.25us unit = 2us
// antWin : 16,2us switching + 2us sampling 16*0.25us unit = 4us
ll_hw_set_ant_switch_timing((slot_Duration==LL_IQ_SW_SAMP_1US?8:16), 40);
// 2020-02-12 comment receive parameter set rxSupp len = 0
ll_hw_set_cte_rxSupp(CTE_SUPP_NULL);
// ll_hw_set_cte_rxSupp( CTE_SUPP_LEN_SET | g_pPeriodicAdvInfo[i].PrdCTEInfo.CTE_Length );
}
break;
case LL_CONN_IQSAMP_DISENABLE:
connPtr->llConnCTE.enable = LL_CONN_IQSAMP_DISENABLE;
break;
default:
break;
}
return LL_STATUS_SUCCESS;
}
llStatus_t LL_Connection_CTE_Request_Enable0( uint16 connHandle,
uint8 enable,
uint16 Interval,
uint8 len,
uint8 type)
{
uint8 i;
// uint32 ant1=0,ant0=0;
// llConnState_t.connid means connHandle
llConnState_t* connPtr;
for( i = 0; i< g_maxConnNum; i++)
{
connPtr = &conn_param[i];
if( connPtr->connId == connHandle )
{
break;
}
}
if( g_maxConnNum == i )
{
return LL_STATUS_ERROR_INACTIVE_CONNECTION;
}
// check if peer device feature set support CTE Response
if( ( connPtr->featureSetInfo.featureSet[LL_CTE_FEATURE_IDX] & LL_CONN_CTE_RSP ) != LL_CONN_CTE_RSP )
{
return LL_STATUS_ERROR_UNSUPPORTED_REMOTE_FEATURE;
}
switch ( enable )
{
case LL_CONN_CTE_REQ_ENABLE:
// if Controller already enable ,return error code LL_STATUS_ERROR_COMMAND_DISALLOWED
// if the Host issue this command before issue the LL_Set_ConnectionCTE_ReceiveParam command at least
// once on the connection , the Controller shall return error code LL_STATUS_ERROR_COMMAND_DISALLOWED
if( ( LL_CONN_CTE_REQ_ENABLE == connPtr->llCTE_ReqFlag ) || \
!( LL_CONN_IQSAMP_ENABLE == connPtr->llConnCTE.enable ))
{
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
}
connPtr->llCTE_ReqFlag = LL_CONN_CTE_REQ_ENABLE;
if(( len < LL_CTE_MIN_SUPP_LEN ) || ( len > LL_CTE_MAX_SUPP_LEN ))
{
return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED;
}
else
{
connPtr->llConnCTE.CTE_Length = len;
}
connPtr->llConnCTE.CTE_Type = type;
// TODO
// check connection PHY support CTE ?
// if( connPtr->llPhyModeCtrl.local )
// {
// return LL_STATUS_ERROR_COMMAND_DISALLOWED
// }
if( 0 == Interval)
{
// CTE Request Interval = 0 , means Initiate the CTE request procedure once
}
else
{
// TODO
// shall verify when disabled slaveLatency case
if( connPtr->slaveLatencyAllowed )
{
if( Interval <= connPtr->slaveLatency )
{
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
}
}
}
connPtr->llConnCTE.CTE_Request_Intv = Interval;
llEnqueueCtrlPkt( connPtr, LL_CTRL_CTE_REQ );
break;
case LL_CONN_CTE_REQ_DISENABLE:
connPtr->llCTE_ReqFlag = LL_CONN_CTE_REQ_DISENABLE;
break;
}
return LL_STATUS_SUCCESS;
}
llStatus_t LL_Set_ConnectionCTE_TransmitParam0( uint16 connHandle,
uint8 type,
uint8 pattern_len,
uint8* AnaIDs)
{
uint8 i,j;
uint32 ant1=0,ant0=0;
// llConnState_t.connid means connHandle
llConnState_t* connPtr;
for( i = 0; i< g_maxConnNum; i++)
{
connPtr = &conn_param[i];
if( connPtr->connId == connHandle )
{
break;
}
}
if( g_maxConnNum == i )
{
return LL_STATUS_ERROR_INACTIVE_CONNECTION;
}
if( connPtr->llConnCTE.enable )
{
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
}
// TODO:
// if CTE Type parameter has a bit set for CTE that the controller does not support ,
// the controller controller shall return error code
// return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED
// pattern len check
if( pattern_len > LL_CTE_MAX_PATTERN_LEN )
{
return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED;
}
else
{
connPtr->llConnCTE.pattern_LEN = pattern_len;
for( j = 0; j < pattern_len; j++)
{
if( AnaIDs[j] > LL_CTE_MAX_ANT_ID )
return LL_STATUS_ERROR_FEATURE_NOT_SUPPORTED;
}
osal_memcpy(connPtr->llConnCTE.AntID, AnaIDs, LL_CTE_MAX_PATTERN_LEN );
}
switch ( type )
{
case CONN_CTE_TYPE_AOA:
ll_hw_set_ant_pattern( 0, 0 );
ll_hw_set_ant_switch_mode( LL_HW_ANT_SW_CTE_OFF );
break;
case CONN_CTE_TYPE_AOD_1us:
case CONN_CTE_TYPE_AOD_2us:
// combination ant pattern
for( j = 0 ; j < connPtr->llConnCTE.pattern_LEN ; j++ )
{
// PHY SDK 4 bit represents an Antenna ID, so uint32 represents max 8 antenna
if( j < 8 )
{
ant0 |= ( ((uint32)connPtr->llConnCTE.AntID[j]) << (4*j) );
}
else
{
ant1 |= ( ((uint32)connPtr->llConnCTE.AntID[j]) << (4*(j-8)) );
}
}
ll_hw_set_ant_pattern( ant1, ant0 );
ll_hw_set_ant_switch_timing((type==CONN_CTE_TYPE_AOD_1us?8:16), 40);
ll_hw_set_ant_switch_mode( LL_HW_ANT_SW_CTE_AUTO );
break;
default:
break;
}
// ll_hw_set_cte_txSupp( CTE_SUPP_NULL);
// ll_hw_set_cte_txSupp( CTE_SUPP_LEN_SET | connPtr->llConnCTE.pattern_LEN );
// indicate Connection CTE Transmit parameter has been set
connPtr->llConnCTE.enable = TRUE;
return LL_STATUS_SUCCESS;
}
llStatus_t LL_Connection_CTE_Response_Enable0( uint16 connHandle,uint8 enable)
{
uint8 i;
// uint32 ant1=0,ant0=0;
// llConnState_t.connid means connHandle
llConnState_t* connPtr;
for( i = 0; i< g_maxConnNum; i++)
{
connPtr = &conn_param[i];
if( connPtr->connId == connHandle )
{
break;
}
}
if( g_maxConnNum == i )
{
return LL_STATUS_ERROR_INACTIVE_CONNECTION;
}
switch ( enable )
{
case LL_CONN_CTE_RSP_ENABLE:
// if the host issue this command before LL_Set_ConnectionCTE_TransmitParam at least once on the connection
// the controller shall return error code: LL_STATUS_ERROR_COMMAND_DISALLOWED
if( !connPtr->llConnCTE.enable )
{
return LL_STATUS_ERROR_COMMAND_DISALLOWED;
}
// TODO
// check connection PHY support CTE ?
// if( connPtr->llPhyModeCtrl.local )
// {
// return LL_STATUS_ERROR_COMMAND_DISALLOWED
// }
connPtr->llCTE_RspFlag = LL_CONN_CTE_RSP_ENABLE;
break;
case LL_CONN_CTE_RSP_DISENABLE:
connPtr->llCTE_RspFlag = LL_CONN_CTE_RSP_DISENABLE;
break;
default:
break;
}
return LL_STATUS_SUCCESS;
}
llStatus_t LL_READ_Anatenna_Info( uint8* param )
{
// PHY SDK default support
// switching_sampling_rate
param[0] = LL_CONTROLLER_SUPP_1US_AOD_TX | \
LL_CONTROLLER_SUPP_1US_AOD_SAMP | \
LL_CONTROLLER_SUPP_1US_AOA_TX_SAMP;
// Antenna length
param[1] = LL_CTE_MAX_ANTENNA_LEN;
// MAX Length of switch pattern
param[2] = LL_CTE_MAX_PATTERN_LEN;
// MAX CTE Length
param[3] = LL_CTE_MAX_SUPP_LEN;
return LL_STATUS_SUCCESS;
}
// power compensation
llStatus_t LL_Read_Rf_Path_Compensation(uint8* param)
{
param[0] = LO_UINT16(g_rfTxPathCompensation);
param[1] = HI_UINT16(g_rfTxPathCompensation);
param[2] = LO_UINT16(g_rfRxPathCompensation);
param[3] = HI_UINT16(g_rfRxPathCompensation);
return LL_STATUS_SUCCESS;
}
llStatus_t LL_Write_Rf_Path_Compensation(int16 tx_compensation, int16 rx_compensation)
{
g_rfTxPathCompensation = tx_compensation;
g_rfRxPathCompensation = rx_compensation;
return LL_STATUS_SUCCESS;
}
llStatus_t LL_Read_Transmit_Power( uint8* param)
{
int8 minTxPwr, maxTxPwr;
// temporary set as protocol range
minTxPwr = -20;
maxTxPwr = 20;
param[1] = LO_UINT16(minTxPwr);
param[2] = HI_UINT16(maxTxPwr);
return LL_STATUS_SUCCESS;
}
//////////////////////////////////////// Vendor specific command API //////////////////////////////////////
/*
** Vendor Specific Command API
*/
/*******************************************************************************
@fn LL_EXT_SetRxGain Vendor Specific API
@brief This function is used to to set the RF RX gain.
input parameters
@param rxGain - LL_EXT_RX_GAIN_STD, LL_EXT_RX_GAIN_HIGH
output parameters
@param cmdComplete - Boolean to indicate the command is still pending.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_SetRxGain( uint8 rxGain,
uint8* cmdComplete )
{
(void) rxGain;
(void) cmdComplete;
return 0;
}
/*******************************************************************************
@fn LL_EXT_SetTxPower0 Vendor Specific API
@brief This function is used to to set the RF TX power.
input parameters
@param txPower - LL_EXT_TX_POWER_0_DBM, LL_EXT_TX_POWER_4_DBM
output parameters
@param cmdComplete - Boolean to indicate the command is still pending.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_SetTxPower0( uint8 txPower,
uint8* cmdComplete )
{
uint32 temp;
*cmdComplete = TRUE;
temp = *(volatile uint32*)(0x400302b8);
*(volatile uint32*)(0x400300b8) = (temp & 0xfffe0fff) | ((txPower & 0x1f) << 12);
return ( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_EXT_ClkDivOnHalt Vendor Specific API
@brief This function is used to enable or disable dividing down the
system clock while halted.
Note: This command is disallowed if haltDuringRf is not defined.
input parameters
@param control - LL_EXT_ENABLE_CLK_DIVIDE_ON_HALT,
LL_EXT_DISABLE_CLK_DIVIDE_ON_HALT
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_COMMAND_DISALLOWED
*/
llStatus_t LL_EXT_ClkDivOnHalt( uint8 control )
{
(void) control;
return 0;
}
/*******************************************************************************
@fn LL_EXT_DeclareNvUsage Vendor Specific API
@brief This HCI Extension API is used to indicate to the Controller
whether or not the Host will be using the NV memory during BLE
operations.
input parameters
@param mode - HCI_EXT_NV_IN_USE, HCI_EXT_NV_NOT_IN_USE
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_COMMAND_DISALLOWED
*/
llStatus_t LL_EXT_DeclareNvUsage( uint8 mode )
{
(void) mode;
return 0;
}
/*******************************************************************************
@fn LL_EXT_Decrypt API
@brief This API is called by the HCI to request the LL to decrypt the
data in the command using the key given in the command.
Note: The parameters are byte ordered MSO to LSO.
input parameters
@param *key - A 128 bit key to be used to calculate the
session key.
@param *encryptedData - A 128 bit block that is encrypted.
output parameters
@param *plaintextData - A 128 bit block that is to be encrypted.
@param None.
@return LL_STATUS_SUCCESS
*/
llStatus_t LL_EXT_Decrypt( uint8* key,
uint8* encryptedData,
uint8* plaintextData )
{
(void) key;
(void) encryptedData;
(void) plaintextData;
return 0;
}
/*******************************************************************************
@fn LL_EXT_SetLocalSupportedFeatures API
@brief This API is called by the HCI to indicate to the Controller
which features can or can not be used.
Note: Not all features indicated by the Host to the Controller
are valid. If invalid, they shall be ignored.
input parameters
@param featureSet - A pointer to the Feature Set where each bit:
0: Feature shall not be used.
1: Feature can be used.
output parameters
@param None.
@return LL_STATUS_SUCCESS
*/
llStatus_t LL_EXT_SetLocalSupportedFeatures( uint8* featureSet )
{
(void) featureSet;
return 0;
}
/*******************************************************************************
@fn LL_EXT_ModemTestTx
@brief This API is used start a continuous transmitter modem test,
using either a modulated or unmodulated carrier wave tone, at
the frequency that corresponds to the specified RF channel. Use
LL_EXT_EndModemTest command to end the test.
Note: A LL reset will be issued by LL_EXT_EndModemTest!
Note: The BLE device will transmit at maximum power.
Note: This API can be used to verify this device meets Japan's
TELEC regulations.
input parameters
@param cwMode - LL_EXT_TX_MODULATED_CARRIER,
LL_EXT_TX_UNMODULATED_CARRIER
txFreq - Transmit RF channel k=0..39, where BLE F=2402+(k*2MHz).
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE
*/
llStatus_t LL_EXT_ModemTestTx( uint8 cwMode,
uint8 txFreq )
{
(void) cwMode;
(void) txFreq;
return 0;
}
/*******************************************************************************
@fn LL_EXT_ModemHopTestTx
@brief This API is used to start a continuous transmitter direct test
mode test using a modulated carrier wave and transmitting a
37 byte packet of Pseudo-Random 9-bit data. A packet is
transmitted on a different frequency (linearly stepping through
all RF channels 0..39) every 625us. Use LL_EXT_EndModemTest
command to end the test.
Note: A LL reset will be issued by LL_EXT_EndModemTest!
Note: The BLE device will transmit at maximum power.
Note: This API can be used to verify this device meets Japan's
TELEC regulations.
input parameters
@param None.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE
*/
llStatus_t LL_EXT_ModemHopTestTx( void )
{
return 0;
}
/*******************************************************************************
@fn LL_EXT_ModemTestRx
@brief This API is used to start a continuous receiver modem test
using a modulated carrier wave tone, at the frequency that
corresponds to the specific RF channel. Any received data is
discarded. Receiver gain may be adjusted using the
LL_EXT_SetRxGain command. RSSI may be read during this test by
using the LL_ReadRssi command. Use LL_EXT_EndModemTest command
to end the test.
Note: A LL reset will be issued by LL_EXT_EndModemTest!
Note: The BLE device will transmit at maximum power.
input parameters
@param rxFreq - Receiver RF channel k=0..39, where BLE F=2402+(k*2MHz).
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE
*/
llStatus_t LL_EXT_ModemTestRx( uint8 rxFreq )
{
(void) rxFreq;
return 0;
}
/*******************************************************************************
@fn LL_EXT_EndModemTest
@brief This API is used to shutdown a modem test. A complete link
layer reset will take place.
input parameters
@param None.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_UNEXPECTED_STATE_ROLE
*/
llStatus_t LL_EXT_EndModemTest( void )
{
return 0;
}
/*******************************************************************************
@fn LL_EXT_SetFreqTune
@brief This API is used to set the Frequncy Tuning up or down. If the
current setting is already at the max/min value, then no
update is performed.
Note: This is a Production Test Mode only command!
input parameters
@param step - LL_EXT_SET_FREQ_TUNE_UP or LL_EXT_SET_FREQ_TUNE_DOWN
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_SetFreqTune( uint8 step )
{
(void) step;
return 0;
}
/*******************************************************************************
@fn LL_EXT_SaveFreqTune
@brief This API is used to save the current Frequency Tuning value to
flash memory. It is restored on reboot or wake from sleep.
Note: This is a Production Test Mode only command!
input parameters
@param None.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_COMMAND_DISALLOWED
*/
llStatus_t LL_EXT_SaveFreqTune( void )
{
return 0;
}
/*******************************************************************************
@fn LL_EXT_SetMaxDtmTxPower Vendor Specific API
@brief This function is used to set the max RF TX power to be used
when using Direct Test Mode.
input parameters
@param txPower - LL_EXT_TX_POWER_MINUS_23_DBM,
LL_EXT_TX_POWER_MINUS_6_DBM,
LL_EXT_TX_POWER_0_DBM,
LL_EXT_TX_POWER_4_DBM
output parameters
@param cmdComplete - Boolean to indicate the command is still pending.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_SetMaxDtmTxPower( uint8 txPower )
{
(void) txPower;
return 0;
}
/*******************************************************************************
@fn LL_EXT_MapPmIoPort Vendor Specific API
@brief This function is used to configure and map a CC254x I/O Port as
a General Purpose I/O (GPIO) output signal that reflects the
Power Management (PM) state of the CC254x device. The GPIO
output will be High on Wake, and Low upon entering Sleep. This
feature can be disabled by specifying LL_EXT_PM_IO_PORT_NONE for
the ioPort (ioPin is then ignored). The system default value
upon hardware reset is disabled. This command can be used to
control an external DC-DC Converter (its actual intent) such has
the TI TPS62730 (or any similar converter that works the same
way). This command should be used with extreme care as it will
override how the Port/Pin was previously configured! This
includes the mapping of Port 0 pins to 32kHz clock output,
Analog I/O, UART, Timers; Port 1 pins to Observables, Digital
Regulator status, UART, Timers; Port 2 pins to an external 32kHz
XOSC. The selected Port/Pin will be configured as an output GPIO
with interrupts masked. Careless use can result in a
reconfiguration that could disrupt the system. It is therefore
the user's responsibility to ensure the selected Port/Pin does
not cause any conflicts in the system.
Note: Only Pins 0, 3 and 4 are valid for Port 2 since Pins 1
and 2 are mapped to debugger signals DD and DC.
Note: Port/Pin signal change will only occur when Power Savings
is enabled.
input parameters
@param ioPort - LL_EXT_PM_IO_PORT_P0,
LL_EXT_PM_IO_PORT_P1,
LL_EXT_PM_IO_PORT_P2,
LL_EXT_PM_IO_PORT_NONE
@param ioPin - LL_EXT_PM_IO_PORT_PIN0,
LL_EXT_PM_IO_PORT_PIN1,
LL_EXT_PM_IO_PORT_PIN2,
LL_EXT_PM_IO_PORT_PIN3,
LL_EXT_PM_IO_PORT_PIN4,
LL_EXT_PM_IO_PORT_PIN5,
LL_EXT_PM_IO_PORT_PIN6,
LL_EXT_PM_IO_PORT_PIN7
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_COMMAND_DISALLOWED
*/
llStatus_t LL_EXT_MapPmIoPort( uint8 ioPort, uint8 ioPin )
{
(void) ioPort;
(void) ioPin;
return 0;
}
/*******************************************************************************
@fn LL_EXT_ExtendRfRange Vendor Specific API
@brief This function is used to Extend Rf Range using the TI CC2590
2.4 GHz RF Front End device.
input parameters
@param cmdComplete - Pointer to get indicatin if command is done.
output parameters
@param cmdComplete - Boolean to indicate the command is still pending.
@return LL_STATUS_SUCCESS
*/
//llStatus_t LL_EXT_ExtendRfRange( uint8 *cmdComplete )
//{
// return 0;
//}
/*******************************************************************************
@fn LL_EXT_HaltDuringRf Vendor Specfic API
@brief This function is used to enable or disable halting the
CPU during RF. The system defaults to enabled.
input parameters
@param mode - LL_EXT_HALT_DURING_RF_ENABLE,
LL_EXT_HALT_DURING_RF_DISABLE
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_COMMAND_DISALLOWED,
LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_HaltDuringRf( uint8 mode )
{
(void) mode;
return 0;
}
/*******************************************************************************
@fn LL_EXT_BuildRevision Vendor Specific API
@brief This API is used to to set a user revision number or read the
build revision number.
input parameters
@param mode - LL_EXT_SET_USER_REVISION |
LL_EXT_READ_BUILD_REVISION
@param userRevNum - A 16 bit value the user can set as their own
revision number
output parameters
@param buildRev - Pointer to returned build revision, if any.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_BuildRevision( uint8 mode, uint16 userRevNum, uint8* buildRev )
{
(void) mode;
(void) userRevNum;
(void) buildRev;
return 0;
}
/*******************************************************************************
@fn LL_EXT_DelaySleep Vendor Specific API
@brief This API is used to to set the sleep delay.
input parameters
@param delay - 0 .. 1000, in milliseconds.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_DelaySleep( uint16 delay )
{
(void) delay;
return 0;
}
/*******************************************************************************
@fn LL_EXT_ResetSystem Vendor Specific API
@brief This API is used to to issue a soft or hard system reset.
input parameters
@param mode - LL_EXT_RESET_SYSTEM_HARD | LL_EXT_RESET_SYSTEM_SOFT
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_ResetSystem( uint8 mode )
{
(void) mode;
return 0;
}
/*******************************************************************************
@fn LL_EXT_SetFastTxResponseTime API
@brief This API is used to enable or disable the fast TX response
time feature. This can be helpful when a short connection
interval is used in combination with slave latency. In such
a scenario, the response time for sending the TX data packet
can effectively shorten or eliminate slave latency, thereby
increasing power consumption. By disabling, this feature
trades fast response time for less power consumption.
input parameters
@param control - LL_EXT_ENABLE_FAST_TX_RESP_TIME,
LL_EXT_DISABLE_FAST_TX_RESP_TIME
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_COMMAND_DISALLOWED,
LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_SetFastTxResponseTime( uint8 control )
{
(void) control;
return 0;
}
/*******************************************************************************
@fn LL_EXT_SetSCA
@brief This API is used to set this device's Sleep Clock Accuracy.
Note: For a slave device, this value is directly used, but only
if power management is enabled. For a master device, this
value is converted into one of eight ordinal values
representing a SCA range, as specified in Table 2.2,
Vol. 6, Part B, Section 2.3.3.1 of the Core specification.
Note: This command is only allowed when the device is not in a
connection.
Note: The device's SCA value remains unaffected by a HCI_Reset.
input parameters
@param scaInPPM - This device's SCA in PPM from 0..500.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER,
LL_STATUS_ERROR_COMMAND_DISALLOWED
*/
llStatus_t LL_EXT_SetSCA( uint16 scaInPPM )
{
(void) scaInPPM;
return 0;
}
/*******************************************************************************
@fn LL_EXT_SetSlaveLatencyOverride API
@brief This API is used to enable or disable the suspention of slave
latency. This can be helpful when the Slave application knows
it will soon receive something that needs to be handled without
delay.
input parameters
@param control - LL_EXT_DISABLE_SL_OVERRIDE,
LL_EXT_ENABLE_SL_OVERRIDE
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_COMMAND_DISALLOWED,
LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_SetSlaveLatencyOverride( uint8 control )
{
(void) control;
return 0;
}
/*******************************************************************************
@fn LL_EXT_AdvEventNotice Vendor Specific API
@brief This API is called to enable or disable a notification to the
specified task using the specified task event whenever a Adv
event ends. A non-zero taskEvent value is taken to be "enable",
while a zero valued taskEvent is taken to be "disable".
input parameters
@param taskID - User's task ID.
@param taskEvent - User's task event.
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_BAD_PARAMETER
*/
llStatus_t LL_EXT_AdvEventNotice( uint8 taskID, uint16 taskEvent )
{
(void) taskID;
(void) taskEvent;
return 0;
}
/*******************************************************************************
@fn LL_EXT_PacketErrorRate Vendor Specific API
@brief This function is used to Reset or Read the Packet Error Rate
counters for a connection. When Reset, the counters are cleared;
when Read, the total number of packets received, the number of
packets received with a CRC error, the number of events, and the
number of missed events are returned via a callback.
Note: The counters are only 16 bits. At the shortest connection
interval, this provides a bit over 8 minutes of data.
input parameters
@param connId - The LL connection ID on which to send this data.
@param command - LL_EXT_PER_RESET, LL_EXT_PER_READ
output parameters
@param None.
@return LL_STATUS_SUCCESS, LL_STATUS_ERROR_INACTIVE_CONNECTION
*/
llStatus_t LL_EXT_PacketErrorRate( uint16 connId, uint8 command )
{
(void) connId;
(void) command;
return 0;
}
/*
** LL Callbacks to HCI
*/
/*******************************************************************************
@fn LL_EXT_PacketErrorRateCback Callback
@brief This Callback is used by the LL to notify the HCI that the
Packet Error Rate Read command has been completed.
Note: The counters are only 16 bits. At the shortest connection
interval, this provides a bit over 8 minutes of data.
input parameters
@param numPkts - Number of Packets received.
@param numCrcErr - Number of Packets received with a CRC error.
@param numEvents - Number of Connection Events.
@param numPkts - Number of Missed Connection Events.
output parameters
@param None.
@return None.
*/
//void LL_EXT_PacketErrorRateCback( uint16 numPkts,
// uint16 numCrcErr,
// uint16 numEvents,
// uint16 numMissedEvts )
//{
// return;
//}
llStatus_t LL_EXT_SetBDADDR( uint8* bdAddr )
{
(void) bdAddr;
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_InitConnectContext
@brief This function initialize the LL connection-orient context
input parameters
@param pConnContext - connection-orient context, the memory is allocated by application
maxConnNum - the size of connect-orient context
maxPktPerEvent - number of packets transmit/receive per connection event
output parameters
@param None.
@return None.
*/
llStatus_t LL_InitConnectContext(llConnState_t* pConnContext,
uint8* pConnBuffer,
uint8 maxConnNum,
uint8 maxPktPerEventTx,
uint8 maxPktPerEventRx,
uint8 blePktVersion)
{
int i, j;
int pktLen = BLE_PKT40_LEN;
uint8* p;
int total = 0;
if (maxConnNum > MAX_NUM_LL_CONN)
return LL_STATUS_ERROR_BAD_PARAMETER;
if (pConnContext == NULL)
return LL_STATUS_ERROR_BAD_PARAMETER;
if (pConnBuffer == NULL)
return LL_STATUS_ERROR_BAD_PARAMETER;
if (blePktVersion == BLE_PKT_VERSION_4_0) // BLE4.0
pktLen = BLE_PKT40_LEN;
else if (blePktVersion == BLE_PKT_VERSION_5_1) // BLE5.1
pktLen = BLE_PKT51_LEN;
pktLen += 6; // header
g_maxConnNum = maxConnNum;
conn_param = pConnContext;
g_maxPktPerEventTx = maxPktPerEventTx;
g_maxPktPerEventRx = maxPktPerEventRx;
g_blePktVersion = blePktVersion;
p = pConnBuffer;
for (i = 0; i < maxConnNum; i++)
{
memset(&conn_param[i], 0, sizeof(llConnState_t));
for (j = 0; j < maxPktPerEventTx; j++)
{
conn_param[i].ll_buf.tx_conn_desc[j] = (struct ll_pkt_desc*)p;
p += pktLen;
total += pktLen;
}
for (j = 0; j < maxPktPerEventRx; j++)
{
conn_param[i].ll_buf.rx_conn_desc[j] = (struct ll_pkt_desc*)p;
p += pktLen;
total += pktLen;
}
conn_param[i].ll_buf.tx_not_ack_pkt = (struct ll_pkt_desc*)p;
p += pktLen;
total += pktLen;
for (j = 0; j < maxPktPerEventTx; j++)
{
conn_param[i].ll_buf.tx_ntrm_pkts[j] = (struct ll_pkt_desc*)p;
p += pktLen;
total += pktLen;
}
}
// LOG("total = %d\n", total);
return( LL_STATUS_SUCCESS );
}
// necessary?
/*******************************************************************************
@fn LL_InitExtendedScan
@brief This function initialize the extended scan context
input parameters
@param scanDataBuffer -
scanDataBufferLength -
output parameters
@param None.
@return None.
*/
llStatus_t LL_InitExtendedScan(uint8* scanDataBuffer,
uint16 scanDataBufferLength)
{
extScanInfo.adv_data = scanDataBuffer;
extScanInfo.adv_data_buf_len = scanDataBufferLength;
return( LL_STATUS_SUCCESS );
}
// TODO: need better interface for user
/*******************************************************************************
@fn LL_InitExtendedAdv
@brief The capability of extend advertiser is decided by application.
Application should allocate the memory for extend adv and init
LL ext advertiser
input parameters
@param extAdvInfo - pointer to memory block for extended adv info
@param extAdvNumber - size of the memory block for extended adv info
@param advSetMaxLen - maximum adv data set length
output parameters
@param None.
@return None.
*/
llStatus_t LL_InitExtendedAdv( extAdvInfo_t* extAdvInfo,
uint8 extAdvSetNumber,
uint16 advSetMaxLen)
{
int i;
g_pExtendedAdvInfo = extAdvInfo;
g_extAdvNumber = extAdvSetNumber;
g_advSetMaximumLen = advSetMaxLen;
for (i = 0; i < g_extAdvNumber; i ++)
{
g_pExtendedAdvInfo[i].advHandle = LL_INVALID_ADV_SET_HANDLE;
g_pExtendedAdvInfo[i].parameter.isOwnRandomAddressSet = FALSE;
// g_pExtendedAdvInfo[i].parameter.advertisingSID = LL_INVALID_ADV_SET_HANDLE;
g_pExtendedAdvInfo[i].adv_event_counter = 0;
g_pExtendedAdvInfo[i].data.fragmentPreference = 0xFF;
g_pExtendedAdvInfo[i].data.advertisingDataLength = 0; // no adv data
g_pExtendedAdvInfo[i].data.dataComplete = FALSE;
g_pExtendedAdvInfo[i].scanRspMaxLength = 0; // no scan rsp data
g_pExtendedAdvInfo[i].isPeriodic = FALSE;
}
// init adv scheduler
g_pAdvSchInfo = (llAdvScheduleInfo_t*)osal_mem_alloc(sizeof(llAdvScheduleInfo_t) * g_extAdvNumber);
if (g_pAdvSchInfo == NULL)
return (LL_STATUS_ERROR_OUT_OF_HEAP);
for (i = 0; i < g_extAdvNumber; i ++)
{
g_pAdvSchInfo[i].adv_handler = LL_INVALID_ADV_SET_HANDLE;
g_pAdvSchInfo[i].pAdvInfo = NULL;
}
g_schExtAdvNum = 0;
g_currentExtAdv = 0xFF; // invalid
// temp set, change to global_config
g_advSlotPeriodic = pGlobal_config[LL_EXT_ADV_RSC_PERIOD]; // 1 second
g_advPerSlotTick = pGlobal_config[LL_EXT_ADV_RSC_SLOT_DURATION]; // 10ms
llTaskState = LL_TASK_INVALID;
g_interAuxPduDuration = pGlobal_config[LL_EXT_ADV_INTER_SEC_CHN_INT];
return( LL_STATUS_SUCCESS );
}
/*******************************************************************************
@fn LL_InitPediodicAdv
@brief The capability of periodic advertiser is decided by application.
Application should allocate the memory for extend adv and init
LL ext advertiser
input parameters
@param extAdvInfo - pointer to memory block for extended adv info
@param extAdvNumber - size of the memory block for extended adv info
@param advSetMaxLen - maximum adv data set length
output parameters
@param None.
@return None.
*/
llStatus_t LL_InitPeriodicAdv(extAdvInfo_t* extAdvInfo,
periodicAdvInfo_t* periodicAdvInfo,
uint8 periodicAdvSetNumber,
uint16 advSetMaxLen)
{
int i;
g_pExtendedAdvInfo = extAdvInfo;
g_extAdvNumber = periodicAdvSetNumber;
g_pPeriodicAdvInfo = periodicAdvInfo;
g_perioAdvNumber = periodicAdvSetNumber;
g_advSetMaximumLen = advSetMaxLen;
for (i = 0; i < g_extAdvNumber; i ++)
{
g_pExtendedAdvInfo[i].advHandle = LL_INVALID_ADV_SET_HANDLE;
g_pExtendedAdvInfo[i].parameter.isOwnRandomAddressSet = FALSE;
g_pExtendedAdvInfo[i].parameter.advertisingSID = LL_INVALID_ADV_SET_HANDLE;
g_pExtendedAdvInfo[i].adv_event_counter = 0;
g_pExtendedAdvInfo[i].data.fragmentPreference = 0xFF;
g_pExtendedAdvInfo[i].data.advertisingDataLength = 0; // no data
g_pExtendedAdvInfo[i].data.dataComplete = FALSE;
g_pExtendedAdvInfo[i].data.advertisingData = NULL; // should not be filled
g_pExtendedAdvInfo[i].isPeriodic = FALSE;
}
for (i = 0; i < g_perioAdvNumber; i ++)
{
g_pPeriodicAdvInfo[i].advHandle = LL_INVALID_ADV_SET_HANDLE;
g_pPeriodicAdvInfo[i].active = FALSE;
g_pPeriodicAdvInfo[i].periodic_adv_event_counter = 0;
g_pPeriodicAdvInfo[i].currentAdvOffset = 0;
g_pPeriodicAdvInfo[i].data.advertisingDataLength = 0; // no data
g_pPeriodicAdvInfo[i].data.dataComplete = FALSE;
}
// =============== init adv scheduler
g_pAdvSchInfo_periodic = (llPeriodicAdvScheduleInfo_t*)osal_mem_alloc(sizeof(llPeriodicAdvScheduleInfo_t) * periodicAdvSetNumber);
if (g_pAdvSchInfo_periodic == NULL)
return (LL_STATUS_ERROR_OUT_OF_HEAP);
for (i = 0; i < g_perioAdvNumber; i ++)
{
g_pAdvSchInfo_periodic[i].adv_handler = LL_INVALID_ADV_SET_HANDLE;
g_pAdvSchInfo_periodic[i].pAdvInfo = NULL;
}
g_schExtAdvNum_periodic = 0;
g_currentExtAdv_periodic = 0xFF; // invalid
// temp set, change to global_config
g_advSlotPeriodic = pGlobal_config[LL_PRD_ADV_RSC_PERIOD]; // 1 second
g_advPerSlotTick = pGlobal_config[LL_PRD_ADV_RSC_SLOT_DURATION]; // 10ms
// the IFS time between 2 continuous AUX PDU in the same periodic/extended adv event
g_interAuxPduDuration = pGlobal_config[LL_EXT_ADV_INTER_SEC_CHN_INT];
llTaskState = LL_TASK_INVALID;
return( LL_STATUS_SUCCESS );
}
// ======= extended adv/periodic adv PDU construction functions
//#pragma O0
//#define LL_PERIOD_ADV_EXT_PART_DURATION 20000
// ADV_EXT_IND PDU construction function
void llSetupAdvExtIndPDU0(extAdvInfo_t* pAdvInfo, periodicAdvInfo_t* pPrdAdv)
{
uint8 advMode, extHeaderFlag, length, extHdrLength;
uint8 offset = 0;
// set AdvMode
if (pAdvInfo->parameter.advEventProperties & LE_ADV_PROP_CONN_BITMASK)
advMode = LL_EXT_ADV_MODE_CONN;
else if (pAdvInfo->parameter.advEventProperties & LE_ADV_PROP_SCAN_BITMASK)
advMode = LL_EXT_ADV_MODE_SC;
else
advMode = LL_EXT_ADV_MODE_NOCONN_NOSC;
length = 0;
// adv PDU header
g_tx_ext_adv_buf.txheader = 0;
// PDU type, 4 bits
SET_BITS(g_tx_ext_adv_buf.txheader, ADV_EXT_TYPE, PDU_TYPE_SHIFT, PDU_TYPE_MASK);
// RFU, ChSel, TxAdd, RxAdd
SET_BITS(g_tx_ext_adv_buf.txheader, pAdvInfo->parameter.ownAddrType, TX_ADD_SHIFT, TX_ADD_MASK);
// === step 1. decide the extended header fields
extHdrLength = 0;
// extended header
extHeaderFlag = 0;
extHdrLength ++; // flag
if (pAdvInfo->isPeriodic == FALSE && (pAdvInfo->data.dataComplete == TRUE && pAdvInfo->data.advertisingDataLength == 0)) // no aux PDU case
{
extHeaderFlag |= LE_EXT_HDR_ADVA_PRESENT_BITMASK;
extHdrLength += 6;
if (pAdvInfo->parameter.advEventProperties & LE_ADV_PROP_DIRECT_BITMASK)
{
extHeaderFlag |= LE_EXT_HDR_TARGETA_PRESENT_BITMASK;
extHdrLength += 6;
}
}
else // with auxilary PDU case
{
extHeaderFlag |= LE_EXT_HDR_ADI_PRESENT_BITMASK | LE_EXT_HDR_AUX_PTR_PRESENT_BITMASK;
extHdrLength += 5;
}
if (pAdvInfo->parameter.advEventProperties & LE_ADV_PROP_TX_POWER_BITMASK)
{
extHeaderFlag |= LE_EXT_HDR_TX_PWR_PRESENT_BITMASK;
extHdrLength ++;
}
length = 1 + extHdrLength; // 1: extended header len(6bits) + advMode(2bit)
// Length
SET_BITS(g_tx_ext_adv_buf.txheader, length, LENGTH_SHIFT, LENGTH_MASK);
// === step 2. fill extended header
offset = 0;
// Extended header length + AdvMode(1 octet)
g_tx_ext_adv_buf.data[offset] = ((advMode & 0x3) << 6) | (extHdrLength & 0x3F);
offset ++;
g_tx_ext_adv_buf.data[offset] = extHeaderFlag;
offset ++;
// AdvA (6 octets)
if (extHeaderFlag & LE_EXT_HDR_ADVA_PRESENT_BITMASK)
{
if (pAdvInfo->parameter.ownAddrType == LL_DEV_ADDR_TYPE_RANDOM && pAdvInfo->parameter.isOwnRandomAddressSet == TRUE)
memcpy(&g_tx_ext_adv_buf.data[offset], pAdvInfo->parameter.ownRandomAddress, LL_DEVICE_ADDR_LEN);
else // public address
memcpy(&g_tx_ext_adv_buf.data[offset], ownPublicAddr, LL_DEVICE_ADDR_LEN);
offset += LL_DEVICE_ADDR_LEN;
}
// TargetA(6 octets)
if (extHeaderFlag & LE_EXT_HDR_TARGETA_PRESENT_BITMASK)
{
// TODO: peer addr type process check
memcpy(&g_tx_ext_adv_buf.data[offset], pAdvInfo->parameter.peerAddress, LL_DEVICE_ADDR_LEN);
offset += LL_DEVICE_ADDR_LEN;
}
// CTEInfo(1 octets), always not present
// if (extHeaderFlag & LE_EXT_HDR_CTE_INFO_PRESENT_BITMASK)
// {
// offset += 1;
// }
// AdvDataInfo(ADI)(2 octets)
if (extHeaderFlag & LE_EXT_HDR_ADI_PRESENT_BITMASK)
{
uint16 adi;
adi = ((pAdvInfo->parameter.advertisingSID & 0x0F) << 12) | (pAdvInfo->data.DIDInfo & 0x0FFF);
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&adi, 2);
offset += 2;
}
// AuxPtr(3 octets)
if (extHeaderFlag & LE_EXT_HDR_AUX_PTR_PRESENT_BITMASK)
{
uint8 chn_idx, ca, offset_unit, aux_phy;
uint16 aux_offset;
uint32 temp = 0;
chn_idx = 3 + (pAdvInfo->advHandle & 0x0F); // temp set
ca = 0; // 50-500ppm
aux_phy = pAdvInfo->parameter.secondaryAdvPHY - 1; // HCI & LL using different enum
// for extenede adv case, the offset is calculated by auxPduRemainder
if (pAdvInfo->isPeriodic == FALSE)
{
if (g_pAdvSchInfo[g_currentExtAdv].auxPduRemainder >= 245700)
offset_unit = 1; // 300us, for aux offset >= 245700us
else
offset_unit = 0; // 30us, for aux offset < 245700us
// calculate elapse time since last timer trigger
//aux_offset = (g_pAdvSchInfo[g_currentExtAdv].auxPduRemainder - elapse_time) / ((offset_unit == 1) ? 300 : 30);
aux_offset = g_pAdvSchInfo[g_currentExtAdv].auxPduRemainder / ((offset_unit == 1) ? 300 : 30);
}
// for periodic adv case, the offset is fixed 1500us + 5000 * adv channel left
else
{
uint8_t temp, number;
temp = 1 << (pPrdAdv->currentChn - LL_ADV_CHAN_FIRST); // current bit mask
temp = ~(temp | (temp - 1));
temp = pAdvInfo->parameter.priAdvChnMap & temp; // channel in the chan map to be broadcast
number = (temp & 0x0001) +
((temp & 0x0002) >> 1) +
((temp & 0x0004) >> 2);
// the interval between chan 37<->38, 38<->39 is 5000us, primary adv -> aux adv chn is 1500us
offset_unit = 0; // 30us, for aux offset < 245700us
aux_offset = (number * pGlobal_config[LL_EXT_ADV_INTER_PRI_CHN_INT] + pGlobal_config[LL_EXT_ADV_PRI_2_SEC_CHN_INT]) / 30;
}
// LOG("#%d#%d# ", g_pAdvSchInfo[g_currentExtAdv].auxPduRemainder - elapse_time, aux_offset);
temp |= (chn_idx & LL_AUX_PTR_CHN_IDX_MASK) << LL_AUX_PTR_CHN_IDX_SHIFT;
temp |= (ca & LL_AUX_PTR_CA_MASK) << LL_AUX_PTR_CA_SHIFT;
temp |= (offset_unit & LL_AUX_PTR_OFFSET_UNIT_MASK) << LL_AUX_PTR_OFFSET_UNIT_SHIFT;
temp |= (aux_offset & LL_AUX_PTR_AUX_OFFSET_MASK) << LL_AUX_PTR_AUX_OFFSET_SHIFT;
temp |= (aux_phy & LL_AUX_PTR_AUX_PHY_MASK) << LL_AUX_PTR_AUX_PHY_SHIFT;
temp &= 0x00FFFFFF;
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&temp, 3);
pAdvInfo->auxChn = chn_idx; // save secondary channel index
pAdvInfo->currentAdvOffset = 0; // reset offset in adv data set
offset += 3;
}
// SyncInfo(18 octets)
// if (extHeaderFlag & LE_EXT_HDR_SYNC_INFO_PRESENT_BITMASK)
// {
// // TODO
// offset += 18;
// }
// TxPower(1 octets)
if (extHeaderFlag & LE_EXT_HDR_TX_PWR_PRESENT_BITMASK) // Tx power is optional, could we only filled it in AUX_ADV_IND?
{
int16 radia_pwr;
radia_pwr = pAdvInfo->tx_power * 10 + g_rfTxPathCompensation;
if (radia_pwr > 1270) radia_pwr = 1270;
if (radia_pwr < -1270) radia_pwr = -1270;
g_tx_ext_adv_buf.data[offset] = (uint8)(radia_pwr / 10);
offset += 1;
}
// ACAD(varies)
// init adv data offset
pAdvInfo->currentAdvOffset = 0;
}
void llSetupAuxAdvIndPDU0(extAdvInfo_t* pAdvInfo, periodicAdvInfo_t* pPrdAdv)
{
uint8 advMode, extHeaderFlag, length, extHdrLength, advDataLen;
uint8 offset = 0;
// uint32 T2, elapse_time;
// set AdvMode
if (pAdvInfo->parameter.advEventProperties & LE_ADV_PROP_CONN_BITMASK)
advMode = LL_EXT_ADV_MODE_CONN;
else if (pAdvInfo->parameter.advEventProperties & LE_ADV_PROP_SCAN_BITMASK)
advMode = LL_EXT_ADV_MODE_SC;
else
advMode = LL_EXT_ADV_MODE_NOCONN_NOSC;
length = 0;
// adv PDU header
g_tx_ext_adv_buf.txheader = 0;
// PDU type, 4 bits
SET_BITS(g_tx_ext_adv_buf.txheader, ADV_EXT_TYPE, PDU_TYPE_SHIFT, PDU_TYPE_MASK);
// RFU, ChSel, TxAdd, RxAdd
SET_BITS(g_tx_ext_adv_buf.txheader, pAdvInfo->parameter.ownAddrType, TX_ADD_SHIFT, TX_ADD_MASK);
if (pGlobal_config[LL_SWITCH] & CONN_CSA2_ALLOW)
SET_BITS(g_tx_adv_buf.txheader, 1, CHSEL_SHIFT, CHSEL_MASK);
extHdrLength = 0;
// == step 1. decide what fields should be present in extended header
// extended header
extHeaderFlag = 0;
extHdrLength ++;
extHeaderFlag |= LE_EXT_HDR_ADI_PRESENT_BITMASK;
extHdrLength += 2;
if (pAdvInfo->parameter.advEventProperties & LE_ADV_PROP_DIRECT_BITMASK)
{
extHeaderFlag |= LE_EXT_HDR_TARGETA_PRESENT_BITMASK;
extHdrLength += 6;
}
// if (advMode != LL_EXT_ADV_MODE_NOCONN_NOSC) // This field is C4 for LL_EXT_ADV_MODE_NOCONN_NOSC, we will not send AdvA in EXT_ADV_IND, so it is mandatory here
// {
extHeaderFlag |= LE_EXT_HDR_ADVA_PRESENT_BITMASK;
extHdrLength += 6;
// }
if (pAdvInfo->parameter.advEventProperties & LE_ADV_PROP_TX_POWER_BITMASK)
{
extHeaderFlag |= LE_EXT_HDR_TX_PWR_PRESENT_BITMASK;
extHdrLength ++;
}
if (pAdvInfo->isPeriodic == TRUE)
{
extHeaderFlag |= LE_EXT_HDR_SYNC_INFO_PRESENT_BITMASK;
extHdrLength += 18;
}
if (pAdvInfo->data.dataComplete == TRUE)
{
if (advMode == LL_EXT_ADV_MODE_NOCONN_NOSC
&& (pAdvInfo->isPeriodic == FALSE)
&& (pAdvInfo->data.advertisingDataLength > 255 - 1 - extHdrLength))
{
extHeaderFlag |= LE_EXT_HDR_AUX_PTR_PRESENT_BITMASK;
extHdrLength += 3;
// maximum payload length = 255: header len (= 1), ext header len(extHdrLength), adv data(advDataLen)
advDataLen = 255 - 1 - extHdrLength; // adv data length. TODO: check spec
}
else
advDataLen = pAdvInfo->data.advertisingDataLength; // put all adv data in field "Adv Data"
}
else // update 04-13, consider update adv data case
advDataLen = 0;
length = 1 + extHdrLength + advDataLen; // 1: extended header len(6bits) + advMode(2bit)
// Length
SET_BITS(g_tx_ext_adv_buf.txheader, length, LENGTH_SHIFT, LENGTH_MASK);
// === step 2. fill AUX_ADV_IND PDU
// Extended header length + AdvMode(1 octet)
g_tx_ext_adv_buf.data[offset] = ((advMode & 0x3) << 6) | (extHdrLength & 0x3F);
offset ++;
g_tx_ext_adv_buf.data[offset] = extHeaderFlag;
offset ++;
// AdvA (6 octets)
if (extHeaderFlag & LE_EXT_HDR_ADVA_PRESENT_BITMASK)
{
if (pAdvInfo->parameter.ownAddrType == LL_DEV_ADDR_TYPE_RANDOM && pAdvInfo->parameter.isOwnRandomAddressSet == TRUE)
memcpy(&g_tx_ext_adv_buf.data[offset], pAdvInfo->parameter.ownRandomAddress, LL_DEVICE_ADDR_LEN);
else // public address
memcpy(&g_tx_ext_adv_buf.data[offset], ownPublicAddr, LL_DEVICE_ADDR_LEN);
offset += LL_DEVICE_ADDR_LEN;
}
// TargetA(6 octets)
if (extHeaderFlag & LE_EXT_HDR_TARGETA_PRESENT_BITMASK)
{
memcpy(&g_tx_ext_adv_buf.data[offset], pAdvInfo->parameter.peerAddress, LL_DEVICE_ADDR_LEN);
offset += LL_DEVICE_ADDR_LEN;
}
// CTEInfo(1 octets), not present for AUX_ADV_IND
if (extHeaderFlag & LE_EXT_HDR_CTE_INFO_PRESENT_BITMASK)
{
// offset += 1;
}
// AdvDataInfo(ADI)(2 octets)
if (extHeaderFlag & LE_EXT_HDR_ADI_PRESENT_BITMASK)
{
uint16 adi;
adi = ((pAdvInfo->parameter.advertisingSID & 0x0F) << 12) | (pAdvInfo->data.DIDInfo & 0x0FFF);
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&adi, 2);
offset += 2;
}
// AuxPtr(3 octets)
if (extHeaderFlag & LE_EXT_HDR_AUX_PTR_PRESENT_BITMASK)
{
uint8 chn_idx, ca, offset_unit, aux_phy;
uint16 aux_offset;
uint32 temp = 0;
// if (pAdvInfo->isPeriodic == FALSE) // no periodic adv case
// {
chn_idx = llGetNextAuxAdvChn(pAdvInfo->currentChn);
ca = 0; // 50-500ppm
offset_unit = 0; // 30us, for aux offset < 245700us
aux_phy = pAdvInfo->parameter.secondaryAdvPHY - 1; // HCI & LL using different enum
aux_offset = g_interAuxPduDuration / 30;
pAdvInfo->currentChn = chn_idx;
// }
// else // AUX_PTR field is not required for periodic adv AUX_ADV_IND
// {
// }
temp |= (chn_idx & LL_AUX_PTR_CHN_IDX_MASK) << LL_AUX_PTR_CHN_IDX_SHIFT;
temp |= (ca & LL_AUX_PTR_CA_MASK) << LL_AUX_PTR_CA_SHIFT;
temp |= (offset_unit & LL_AUX_PTR_OFFSET_UNIT_MASK) << LL_AUX_PTR_OFFSET_UNIT_SHIFT;
temp |= (aux_offset & LL_AUX_PTR_AUX_OFFSET_MASK) << LL_AUX_PTR_AUX_OFFSET_SHIFT;
temp |= (aux_phy & LL_AUX_PTR_AUX_PHY_MASK) << LL_AUX_PTR_AUX_PHY_SHIFT;
temp &= 0x00FFFFFF;
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&temp, 3);
offset += 3;
}
else if (pAdvInfo->isPeriodic == FALSE) // only applicable to extended adv case
{
// no more aux PDU, update next channel number
int i = 0;
while ((i < 3) && !(pAdvInfo->parameter.priAdvChnMap & (1 << i))) i ++;
pAdvInfo->currentChn = LL_ADV_CHAN_FIRST + i;
}
else // periodic adv case
{
llPrdAdvDecideNextChn(pAdvInfo, pPrdAdv);
}
// SyncInfo(18 octets)
if (extHeaderFlag & LE_EXT_HDR_SYNC_INFO_PRESENT_BITMASK)
{
// TODO
llSetupSyncInfo(pAdvInfo, pPrdAdv);
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&syncInfo, 18);
offset += 18;
}
// TxPower(1 octets)
if (extHeaderFlag & LE_EXT_HDR_TX_PWR_PRESENT_BITMASK) // Tx power is optional, could we only filled it in AUX_ADV_IND?
{
int16 radia_pwr;
radia_pwr = pAdvInfo->tx_power * 10 + g_rfTxPathCompensation;
if (radia_pwr > 1270) radia_pwr = 1270;
if (radia_pwr < -1270) radia_pwr = -1270;
g_tx_ext_adv_buf.data[offset] = (uint8)(radia_pwr / 10);
offset += 1;
}
// ACAD(varies), not present
// copy adv data
if (pAdvInfo->isPeriodic == FALSE)
{
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&pAdvInfo->data.advertisingData[pAdvInfo->currentAdvOffset], advDataLen);
pAdvInfo->currentAdvOffset += advDataLen;
}
}
//#pragma O0
void llSetupAuxChainIndPDU0(extAdvInfo_t* pAdvInfo, periodicAdvInfo_t* pPrdAdv)
{
uint8 advMode, extHeaderFlag, length, extHdrLength, advDataLen;
uint8 offset = 0;
// set AdvMode
advMode = 0;
length = 0;
// adv PDU header
g_tx_ext_adv_buf.txheader = 0;
// PDU type, 4 bits
SET_BITS(g_tx_ext_adv_buf.txheader, ADV_EXT_TYPE, PDU_TYPE_SHIFT, PDU_TYPE_MASK);
// RFU, ChSel, TxAdd, RxAdd
SET_BITS(g_tx_ext_adv_buf.txheader, pAdvInfo->parameter.ownAddrType, TX_ADD_SHIFT, TX_ADD_MASK);
extHdrLength = 0;
// extended header
extHeaderFlag = 0;
extHdrLength ++;
// 2020-02-10 add for Connectionless CTE Info
// CTEInfo field is C5: Optional
if( pPrdAdv->PrdCTEInfo.enable == LL_CTE_ENABLE )
{
if( pPrdAdv->PrdCTEInfo.CTE_Count > pPrdAdv->PrdCTEInfo.CTE_Count_Idx )
{
extHeaderFlag |= LE_EXT_HDR_CTE_INFO_PRESENT_BITMASK;
extHdrLength ++;
}
}
// ADI field is C3, it is present in our implementation
if (pAdvInfo->isPeriodic == FALSE)
{
extHeaderFlag |= LE_EXT_HDR_ADI_PRESENT_BITMASK;
extHdrLength += 2;
}
// comment out because for periodic adv, tx pwr field is in AUX_SYNC_IND. for extended adv, txPwr field in AUX_ADV_IND
// if (((pAdvInfo->isPeriodic == FALSE) && (pAdvInfo->parameter.advEventProperties & LE_ADV_PROP_TX_POWER_BITMASK))
// || ((pAdvInfo->isPeriodic == TRUE) && (pPrdAdv->adv_event_properties & LE_ADV_PROP_TX_POWER_BITMASK)))
// {
// extHeaderFlag |= LE_EXT_HDR_TX_PWR_PRESENT_BITMASK;
// extHdrLength ++;
// }
// if Adv Data could not be sent completely in this PDU, need AuxPtr
if (pAdvInfo->isPeriodic == FALSE)
{
if (pAdvInfo->data.dataComplete == TRUE)
{
if (pAdvInfo->data.advertisingDataLength - pAdvInfo->currentAdvOffset > 255 - 1 - extHdrLength)
{
extHeaderFlag |= LE_EXT_HDR_AUX_PTR_PRESENT_BITMASK;
extHdrLength += 3;
// maximum payload length = 255, header len = 1, ADI len = 2, AUX_PTR len = 2
advDataLen = 255 - 1 - extHdrLength;;
}
else
advDataLen = pAdvInfo->data.advertisingDataLength - pAdvInfo->currentAdvOffset; // put all remain adv data in field "Adv Data"
}
else // update 04-13, adv data may be reconfigured during advertising, include no data in such case
advDataLen = 0;
}
else
{
if (pPrdAdv->data.dataComplete == TRUE)
{
if (pPrdAdv->data.advertisingDataLength - pPrdAdv->currentAdvOffset > 255 - 1 - extHdrLength)
{
extHeaderFlag |= LE_EXT_HDR_AUX_PTR_PRESENT_BITMASK;
extHdrLength += 3;
// maximum payload length = 255, header len = 1, ADI len = 2, AUX_PTR len = 2
advDataLen = 255 - 1 - extHdrLength;;
}
else
advDataLen = pPrdAdv->data.advertisingDataLength - pPrdAdv->currentAdvOffset; // put all remain adv data in field "Adv Data"
}
else // update 04-13, adv data may be reconfigured during advertising, include no data in such case
advDataLen = 0;
}
length = 1 + extHdrLength + advDataLen; // 1: extended header len(6bits) + advMode(2bit)
// Length
SET_BITS(g_tx_ext_adv_buf.txheader, length, LENGTH_SHIFT, LENGTH_MASK);
// fill extended header
offset = 0;
// Extended header length + AdvMode(1 octet)
g_tx_ext_adv_buf.data[offset] = ((advMode & 0x3) << 6) | (extHdrLength & 0x3F);
offset ++;
g_tx_ext_adv_buf.data[offset] = extHeaderFlag;
offset ++;
// CTEInfo(1 octets), not present for AUX_ADV_IND
if (extHeaderFlag & LE_EXT_HDR_CTE_INFO_PRESENT_BITMASK)
{
//LOG("\n SyC \n");
g_tx_ext_adv_buf.data[offset] = ( ( pPrdAdv->PrdCTEInfo.CTE_Type << 6 ) | \
( pPrdAdv->PrdCTEInfo.CTE_Length));
pPrdAdv->PrdCTEInfo.CTE_Count_Idx ++;
offset += 1;
}
// AdvDataInfo(ADI)(2 octets)
if (extHeaderFlag & LE_EXT_HDR_ADI_PRESENT_BITMASK)
{
uint16 adi;
adi = ((pAdvInfo->parameter.advertisingSID & 0x0F) << 12) | (pAdvInfo->data.DIDInfo & 0x0FFF);
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&adi, 2);
offset += 2;
}
// AuxPtr(3 octets)
if (extHeaderFlag & LE_EXT_HDR_AUX_PTR_PRESENT_BITMASK)
{
uint8 chn_idx, ca, offset_unit, aux_phy;
uint16 aux_offset;
uint32 temp = 0;
chn_idx = llGetNextAuxAdvChn(pAdvInfo->currentChn);
ca = 0; // 50-500ppm
offset_unit = 0; // 30us, for aux offset < 245700us
aux_phy = pAdvInfo->parameter.secondaryAdvPHY - 1; // HCI & LL using different enum
aux_offset = g_interAuxPduDuration / 30;
temp |= (chn_idx & LL_AUX_PTR_CHN_IDX_MASK) << LL_AUX_PTR_CHN_IDX_SHIFT;
temp |= (ca & LL_AUX_PTR_CA_MASK) << LL_AUX_PTR_CA_SHIFT;
temp |= (offset_unit & LL_AUX_PTR_OFFSET_UNIT_MASK) << LL_AUX_PTR_OFFSET_UNIT_SHIFT;
temp |= (aux_offset & LL_AUX_PTR_AUX_OFFSET_MASK) << LL_AUX_PTR_AUX_OFFSET_SHIFT;
temp |= (aux_phy & LL_AUX_PTR_AUX_PHY_MASK) << LL_AUX_PTR_AUX_PHY_SHIFT;
temp &= 0x00FFFFFF;
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&temp, 3);
pAdvInfo->currentChn = chn_idx; // save secondary channel index
pPrdAdv->currentChn = chn_idx;
offset += 3;
}
else
{
// no more aux PDU, update next channel number
if (pAdvInfo->isPeriodic == FALSE)
{
int i = 0;
while ((i < 3) && !(pAdvInfo->parameter.priAdvChnMap & (1 << i))) i ++;
pAdvInfo->currentChn = LL_ADV_CHAN_FIRST + i;
}
else // periodic adv case
{
llPrdAdvDecideNextChn(pAdvInfo, pPrdAdv);
}
}
// // TxPower(1 octets)
// if (extHeaderFlag & LE_EXT_HDR_TX_PWR_PRESENT_BITMASK)
// {
// // TODO
// offset += 1;
// }
// ACAD(varies), not present
// copy adv data
if (pAdvInfo == FALSE)
{
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&pAdvInfo->data.advertisingData[pAdvInfo->currentAdvOffset], advDataLen);
pAdvInfo->currentAdvOffset += advDataLen;
}
else // periodic adv case
{
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&pPrdAdv->data.advertisingData[pPrdAdv->currentAdvOffset], advDataLen);
pPrdAdv->currentAdvOffset += advDataLen;
// if finish broad current periodic adv event, revert the read pointer of adv data
if (pPrdAdv->currentAdvOffset == pPrdAdv->data.advertisingDataLength)
{
// 2020-02-10 add logic for CTE info
if( pPrdAdv->PrdCTEInfo.enable == LL_CTE_ENABLE )
{
if( pPrdAdv->PrdCTEInfo.CTE_Count_Idx >= pPrdAdv->PrdCTEInfo.CTE_Count )
{
// already send all CTE info, then reset pPrdAdv->currentAdvOffset
pPrdAdv->currentAdvOffset = 0;
pPrdAdv->PrdCTEInfo.CTE_Count_Idx = 0;
// length set to zero means stop CTE
ll_hw_set_cte_txSupp( CTE_SUPP_LEN_SET | 0x0 );
}
}
else
{
// 2020-02-21 bug fix: If CTE is not enabled, then
// subsequent packets will not be sent
pPrdAdv->currentAdvOffset = 0;
}
}
}
}
void llSetupAuxSyncIndPDU0(extAdvInfo_t* pAdvInfo, periodicAdvInfo_t* pPrdAdv)
{
uint8 advMode, extHeaderFlag, length, extHdrLength, advDataLen;
uint8 offset = 0;
// set AdvMode
advMode = 0;
length = 0;
// adv PDU header
g_tx_ext_adv_buf.txheader = 0;
// PDU type, 4 bits
SET_BITS(g_tx_ext_adv_buf.txheader, ADV_EXT_TYPE, PDU_TYPE_SHIFT, PDU_TYPE_MASK);
// RFU, ChSel, TxAdd, RxAdd
SET_BITS(g_tx_ext_adv_buf.txheader, 0, TX_ADD_SHIFT, TX_ADD_MASK); // it seems TxAdd is ignored in spec
extHdrLength = 0;
// extended header
extHeaderFlag = 0; // for AUX_SYNC_IND PDU: CTE info, AuxPtr, Tx power, ACAD, Adv Data are optional, other fields are absent
extHdrLength ++;
//extHeaderFlag |= LE_EXT_HDR_CTE_INFO_PRESENT_BITMASK;
// 2020-02-10 add for CTE transmit
// if periodic advertising CTE Enable
if( pPrdAdv->PrdCTEInfo.enable == LL_CTE_ENABLE )
{
if( pPrdAdv->PrdCTEInfo.CTE_Count > 0 )
{
extHeaderFlag |= LE_EXT_HDR_CTE_INFO_PRESENT_BITMASK;
extHdrLength ++;
}
}
if (pPrdAdv->adv_event_properties & LE_ADV_PROP_TX_POWER_BITMASK)
{
extHeaderFlag |= LE_EXT_HDR_TX_PWR_PRESENT_BITMASK;
extHdrLength ++;
}
// if Adv Data could not be sent completely in this PDU, need AuxPtr
if (pPrdAdv->data.dataComplete == TRUE)
{
if (pPrdAdv->data.advertisingDataLength - pPrdAdv->currentAdvOffset > 255 - 1 - extHdrLength)
{
extHeaderFlag |= LE_EXT_HDR_AUX_PTR_PRESENT_BITMASK;
extHdrLength += 3;
// maximum payload length = 255, header len = 1, ADI len = 2, AUX_PTR len = 2
advDataLen = 255 - 1 - extHdrLength;
}
else
advDataLen = pPrdAdv->data.advertisingDataLength - pPrdAdv->currentAdvOffset; // put all remain adv data in field "Adv Data"
}
else // update 04-13, adv data may be reconfigured during advertising, include no data in such case
advDataLen = 0;
length = 1 + extHdrLength + advDataLen; // 1: extended header len(6bits) + advMode(2bit)
// Length
SET_BITS(g_tx_ext_adv_buf.txheader, length, LENGTH_SHIFT, LENGTH_MASK);
// fill extended header
offset = 0;
// Extended header length + AdvMode(1 octet)
g_tx_ext_adv_buf.data[offset] = ((advMode & 0x3) << 6) | (extHdrLength & 0x3F);
offset ++;
g_tx_ext_adv_buf.data[offset] = extHeaderFlag;
offset ++;
// CTEInfo(1 octets), not present for AUX_ADV_IND
if (extHeaderFlag & LE_EXT_HDR_CTE_INFO_PRESENT_BITMASK)
{
g_tx_ext_adv_buf.data[offset] = ( ( pPrdAdv->PrdCTEInfo.CTE_Type << 6 ) | \
( pPrdAdv->PrdCTEInfo.CTE_Length));
pPrdAdv->PrdCTEInfo.CTE_Count_Idx ++;
ll_hw_set_cte_txSupp( CTE_SUPP_LEN_SET | pPrdAdv->PrdCTEInfo.CTE_Length );
offset += 1;
// LOG("\n Sy \n");
}
// AuxPtr(3 octets)
if (extHeaderFlag & LE_EXT_HDR_AUX_PTR_PRESENT_BITMASK)
{
uint8 chn_idx, ca, offset_unit, aux_phy;
uint16 aux_offset;
uint32 temp = 0;
chn_idx = llGetNextAuxAdvChn(pPrdAdv->currentChn);
// fixed interval between periodic PDUs now
ca = 0; // 50-500ppm
offset_unit = 0; // 30us, for aux offset < 245700us
aux_offset = g_interAuxPduDuration / 30;
aux_phy = pAdvInfo->parameter.secondaryAdvPHY - 1; // HCI & LL using different enum
temp |= (chn_idx & LL_AUX_PTR_CHN_IDX_MASK) << LL_AUX_PTR_CHN_IDX_SHIFT;
temp |= (ca & LL_AUX_PTR_CA_MASK) << LL_AUX_PTR_CA_SHIFT;
temp |= (offset_unit & LL_AUX_PTR_OFFSET_UNIT_MASK) << LL_AUX_PTR_OFFSET_UNIT_SHIFT;
temp |= (aux_offset & LL_AUX_PTR_AUX_OFFSET_MASK) << LL_AUX_PTR_AUX_OFFSET_SHIFT;
temp |= (aux_phy & LL_AUX_PTR_AUX_PHY_MASK) << LL_AUX_PTR_AUX_PHY_SHIFT;
temp &= 0x00FFFFFF;
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&temp, 3);
pPrdAdv->currentChn = chn_idx; // save secondary channel index
offset += 3;
}
else
{
// no more aux PDU, update next channel number
if (pAdvInfo->isPeriodic == FALSE)
{
int i = 0;
while ((i < 3) && !(pAdvInfo->parameter.priAdvChnMap & (1 << i))) i ++;
pAdvInfo->currentChn = LL_ADV_CHAN_FIRST + i;
}
else // periodic adv case
{
llPrdAdvDecideNextChn(pAdvInfo, pPrdAdv);
}
}
// TxPower(1 octets)
if (extHeaderFlag & LE_EXT_HDR_TX_PWR_PRESENT_BITMASK) // Tx power is optional, could we only filled it in AUX_ADV_IND?
{
int16 radia_pwr;
radia_pwr = pAdvInfo->tx_power * 10 + g_rfTxPathCompensation;
if (radia_pwr > 1270) radia_pwr = 1270;
if (radia_pwr < -1270) radia_pwr = -1270;
g_tx_ext_adv_buf.data[offset] = (uint8)(radia_pwr / 10);
offset += 1;
}
// ACAD(varies), not present
// copy adv data
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&pPrdAdv->data.advertisingData[pPrdAdv->currentAdvOffset], advDataLen);
pPrdAdv->currentAdvOffset += advDataLen;
// if finish broad current periodic adv event, revert the read pointer of adv data
if (pPrdAdv->currentAdvOffset == pPrdAdv->data.advertisingDataLength)
{
// 2020-02-10 add logic for CTE info
if( pPrdAdv->PrdCTEInfo.enable == LL_CTE_ENABLE )
{
if( pPrdAdv->PrdCTEInfo.CTE_Count_Idx >= pPrdAdv->PrdCTEInfo.CTE_Count )
{
// already send all CTE info, then reset pPrdAdv->currentAdvOffset
pPrdAdv->currentAdvOffset = 0;
// length set to zero means stop CTE
ll_hw_set_cte_txSupp( CTE_SUPP_LEN_SET | 0x0 );
}
}
else
{
// 2020-02-21 bug fix: If CTE is not enabled, then
// subsequent packets will not be sent
pPrdAdv->currentAdvOffset = 0;
}
}
}
void llSetupAuxScanRspPDU0(extAdvInfo_t* pAdvInfo)
{
uint8 advMode, extHeaderFlag, length, extHdrLength, advDataLen;
uint8 offset = 0;
// set AdvMode
advMode = 0;
length = 0;
// adv PDU header
g_tx_ext_adv_buf.txheader = 0;
// PDU type, 4 bits
SET_BITS(g_tx_ext_adv_buf.txheader, ADV_EXT_TYPE, PDU_TYPE_SHIFT, PDU_TYPE_MASK);
// RFU, ChSel, TxAdd, RxAdd
SET_BITS(g_tx_ext_adv_buf.txheader, 0, TX_ADD_SHIFT, TX_ADD_MASK); // it seems TxAdd is ignored in spec
extHdrLength = 0;
// extended header
extHeaderFlag = 0; // for AUX_SYNC_IND PDU: CTE info, AuxPtr, Tx power, ACAD, Adv Data are optional, other fields are absent
extHdrLength ++;
extHeaderFlag |= LE_EXT_HDR_ADVA_PRESENT_BITMASK;
extHdrLength += 6;
advDataLen = pAdvInfo->scanRspMaxLength;
length = 1 + extHdrLength + advDataLen; // 1: extended header len(6bits) + advMode(2bit)
// Length
SET_BITS(g_tx_ext_adv_buf.txheader, length, LENGTH_SHIFT, LENGTH_MASK);
// fill extended header
offset = 0;
// Extended header length + AdvMode(1 octet)
g_tx_ext_adv_buf.data[offset] = ((advMode & 0x3) << 6) | (extHdrLength & 0x3F);
offset ++;
g_tx_ext_adv_buf.data[offset] = extHeaderFlag;
offset ++;
// AdvA (6 octets)
if (extHeaderFlag & LE_EXT_HDR_ADVA_PRESENT_BITMASK)
{
if (pAdvInfo->parameter.ownAddrType == LL_DEV_ADDR_TYPE_RANDOM && pAdvInfo->parameter.isOwnRandomAddressSet == TRUE)
memcpy(&g_tx_ext_adv_buf.data[offset], pAdvInfo->parameter.ownRandomAddress, LL_DEVICE_ADDR_LEN);
else // public address
memcpy(&g_tx_ext_adv_buf.data[offset], ownPublicAddr, LL_DEVICE_ADDR_LEN);
offset += LL_DEVICE_ADDR_LEN;
}
// copy adv data
memcpy(&g_tx_ext_adv_buf.data[offset], (uint8*)&pAdvInfo->scanRspData[0], pAdvInfo->scanRspMaxLength);
}
//#pragma O2
void llSetupAuxConnectReqPDU0(void)
{
uint8 offset;
llConnState_t* connPtr;
connPtr = &conn_param[extInitInfo.connId];
if ((extInitInfo.current_scan_PHY == PKT_FMT_BLE1M)
|| (extInitInfo.current_scan_PHY == PKT_FMT_BLR125K))
{
connPtr->curParam.connInterval = extInitInfo.conn_interval_max[extInitInfo.current_index];
connPtr->curParam.slaveLatency = extInitInfo.conn_latency[extInitInfo.current_index];
connPtr->curParam.connTimeout = extInitInfo.supervision_timeout[extInitInfo.current_index];
}
else // 2Mbps case
{
connPtr->curParam.connInterval = extInitInfo.conn_interval_max_2Mbps;
connPtr->curParam.slaveLatency = extInitInfo.conn_latency_2Mbps;
connPtr->curParam.connTimeout = extInitInfo.supervision_timeout_2Mbps;
}
offset = 22;
// Interval, Byte 22 ~ 23
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->curParam.connInterval, 2);
offset += 2;
// Latency, Byte 24 ~ 25
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->curParam.slaveLatency, 2);
offset += 2;
// Timeout, Byte 26 ~ 27
memcpy((uint8*)&g_tx_adv_buf.data[offset], (uint8*)&connPtr->curParam.connTimeout, 2);
}
void llSetupAuxConnectRspPDU0(extAdvInfo_t* pAdvInfo)
{
uint8 advMode, extHeaderFlag, length, extHdrLength;
uint8 offset = 0;
length = 14;
// adv PDU header
g_tx_adv_buf.txheader = 0;
// PDU type, 4 bits
SET_BITS(g_tx_adv_buf.txheader, ADV_AUX_CONN_RSP, PDU_TYPE_SHIFT, PDU_TYPE_MASK);
// RFU, ChSel, TxAdd, RxAdd
SET_BITS(g_tx_adv_buf.txheader, pAdvInfo->parameter.ownAddrType, TX_ADD_SHIFT, TX_ADD_MASK);
// Length
SET_BITS(g_tx_adv_buf.txheader, length, LENGTH_SHIFT, LENGTH_MASK);
offset = 0;
extHdrLength = 13; // ext header flag(1byte) + advA(6 octets) + targetA(6octets)
// set AdvMode
advMode = LL_EXT_ADV_MODE_AUX_CONN_RSP;
g_tx_adv_buf.data[offset] = ((advMode & 0x3) << 6) | (extHdrLength & 0x3F);
offset ++;
// extended header
extHeaderFlag = LE_EXT_HDR_ADVA_PRESENT_BITMASK | LE_EXT_HDR_TARGETA_PRESENT_BITMASK;
g_tx_adv_buf.data[offset] = extHeaderFlag;
offset ++;
if (pAdvInfo->parameter.ownAddrType == LL_DEV_ADDR_TYPE_RANDOM && pAdvInfo->parameter.isOwnRandomAddressSet == TRUE)
memcpy(&g_tx_adv_buf.data[offset], pAdvInfo->parameter.ownRandomAddress, LL_DEVICE_ADDR_LEN);
else // public address
memcpy(&g_tx_adv_buf.data[offset], ownPublicAddr, LL_DEVICE_ADDR_LEN);
offset += LL_DEVICE_ADDR_LEN;
// TargetA(6 octets)
osal_memcpy(&g_tx_adv_buf.data[offset], g_rx_adv_buf.data, 6);
offset += LL_DEVICE_ADDR_LEN;
}
// for periodic adv, when finish extended adv part broadcast, or finish periodic adv part broadcase
// controller should decide next broadcast channel and PDU.
// extended adv part: ADV_EXT_IND + AUX_ADV_IND
// periodic adv part: AUX_SYNC_IND + AUX_CHAIN_IND(optional, for long adv data)
void llPrdAdvDecideNextChn(extAdvInfo_t* pAdvInfo, periodicAdvInfo_t* pPrdAdv)
{
llPeriodicAdvScheduleInfo_t* p_scheduler = NULL;
p_scheduler = &g_pAdvSchInfo_periodic[g_currentExtAdv_periodic];
if (p_scheduler->nextEventRemainder == LL_INVALID_TIME)
{
pPrdAdv->currentChn = llGetNextDataChanCSA2(pPrdAdv->periodic_adv_event_counter,\
(( ( pPrdAdv->AA & 0xFFFF0000 )>> 16 ) ^ ( pPrdAdv->AA & 0x0000FFFF)),\
pPrdAdv->chn_map,\
pPrdAdv->chanMapTable,\
pPrdAdv->numUsedChans);
pPrdAdv->pa_current_chn = pPrdAdv->currentChn;
return;
}
if (p_scheduler->auxPduRemainder > p_scheduler->nextEventRemainder + pGlobal_config[LL_EXT_ADV_TASK_DURATION])
{
int i = 0;
while ((i < 3) && !(pAdvInfo->parameter.priAdvChnMap & (1 << i))) i ++;
pPrdAdv->currentChn = LL_ADV_CHAN_FIRST + i;
}
else if (p_scheduler->auxPduRemainder > p_scheduler->nextEventRemainder) // no enough margin for ext adv
{
// skip 1 primary adv event
p_scheduler->nextEventRemainder += pAdvInfo->primary_advertising_interval;
pAdvInfo->adv_event_counter ++;
pAdvInfo->adv_event_duration += pAdvInfo->primary_advertising_interval;
// to simplify the process, here not check ext adv duration & counter, TO be add if required
pPrdAdv->currentChn = llGetNextDataChanCSA2(pPrdAdv->periodic_adv_event_counter,\
(( ( pPrdAdv->AA & 0xFFFF0000 )>> 16 ) ^ ( pPrdAdv->AA & 0x0000FFFF)),\
pPrdAdv->chn_map,\
pPrdAdv->chanMapTable,\
pPrdAdv->numUsedChans);
pPrdAdv->pa_current_chn = pPrdAdv->currentChn;
}
else
{
pPrdAdv->currentChn = llGetNextDataChanCSA2(pPrdAdv->periodic_adv_event_counter,\
(( ( pPrdAdv->AA & 0xFFFF0000 )>> 16 ) ^ ( pPrdAdv->AA & 0x0000FFFF)),\
pPrdAdv->chn_map,\
pPrdAdv->chanMapTable,\
pPrdAdv->numUsedChans);
pPrdAdv->pa_current_chn = pPrdAdv->currentChn;
}
}
void llSetupSyncInfo(extAdvInfo_t* pAdvInfo, periodicAdvInfo_t* pPrdAdv)
{
(void) pPrdAdv;
(void) pAdvInfo;
uint32 T2, elapse_time, remainder;
llPeriodicAdvScheduleInfo_t* p_scheduler = NULL;
p_scheduler = &g_pAdvSchInfo_periodic[g_currentExtAdv_periodic];
memcpy(syncInfo.chn_map, pPrdAdv->chn_map, 4);
syncInfo.chn_map4.chn_map = pPrdAdv->chn_map[4];
syncInfo.chn_map4.sca = pPrdAdv->sca;
syncInfo.AA[0] = pPrdAdv->AA & 0xff;
syncInfo.AA[1] = (pPrdAdv->AA >> 8) & 0xff;
syncInfo.AA[2] = (pPrdAdv->AA >> 16) & 0xff;
syncInfo.AA[3] = (pPrdAdv->AA >> 24) & 0xff;
syncInfo.crcInit[0] = pPrdAdv->crcInit & 0xff;
syncInfo.crcInit[1] = (pPrdAdv->crcInit >> 8) & 0xff;
syncInfo.crcInit[2] = (pPrdAdv->crcInit >> 16) & 0xff;
syncInfo.offset.rfu = 0;
syncInfo.interval = pPrdAdv->adv_interval_max;// adv_interval;
syncInfo.event_counter = pPrdAdv->periodic_adv_event_counter;
if (p_scheduler->auxPduRemainder >= 245700)
syncInfo.offset.offsetUnit = 1; // 300us, for aux offset >= 245700us
else
syncInfo.offset.offsetUnit = 0; // 30us, for aux offset < 245700us
syncInfo.offset.offsetAdj = 0;
// calculate elapse time since last timer trigger
T2 = read_current_fine_time();
elapse_time = LL_TIME_DELTA(g_timerExpiryTick, T2);
if (p_scheduler->auxPduRemainder > 2457600 + elapse_time) // if > 2.4576 second
{
remainder = p_scheduler->auxPduRemainder - 2457600;
syncInfo.offset.offsetAdj = 1;
}
else
remainder = p_scheduler->auxPduRemainder;
syncInfo.offset.syncPacketOffset = (remainder - elapse_time) / ((syncInfo.offset.offsetUnit == 1) ? 300 : 30);
}
void LL_EXT_Init_IQ_pBuff(uint16* ibuf,uint16* qbuf)
{
if( ( ibuf != NULL ) && ( qbuf != NULL) )
{
g_pLLcteISample = ibuf;
g_pLLcteQSample = qbuf;
}
}