This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC1352R: Reset BLE Stack does not work

Part Number: CC1352R

Hi,

I am trying to reset the BLE stack in SDK 4.10. SP OAD off-chip example project. Unfortunately, it does not work.

What I do:

On a certain occasion I call HCI_ResetCmd. It returns SUCCESS.

I then wait for the HCI_RESET event, which comes up right after HCI_ResetCmd call.

Here I call GAP_DeviceInit, which returns bleIncorrectMode.

I got this solution from here and here.

In my understanding, GAP_DeviceInit returns bleIncorrectMode, when HCI_ResetCmd has NOT been called before.

What is the problem here?

most interesting code snippets:

Complete Code:

/******************************************************************************

 @file  simple_peripheral_oad_offchip.c

 @brief This file contains the Simple Peripheral sample application for use
        with the CC2650 Bluetooth Low Energy Protocol Stack.

 Group: WCS, BTS
 Target Device: cc13x2_26x2

 ******************************************************************************
 
 Copyright (c) 2013-2020, Texas Instruments Incorporated
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 *  Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

 *  Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

 *  Neither the name of Texas Instruments Incorporated nor the names of
    its contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 ******************************************************************************
 
 
 *****************************************************************************/

/*********************************************************************
 * INCLUDES
 */
#include <string.h>

#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/knl/Event.h>
#include <ti/sysbios/knl/Queue.h>

#include <ti/display/Display.h>

#if !(defined __TI_COMPILER_VERSION__)
#include <intrinsics.h>
#endif

#include <ti/drivers/utils/List.h>

#include <icall.h>
#include "util.h"
#include "onboard.h"
#include <bcomdef.h>
/* This Header file contains all BLE API and icall structure definition */
#include <icall_ble_api.h>

#include <devinfoservice.h>
#include <simple_gatt_profile.h>

#ifdef USE_RCOSC
#include <rcosc_calibration.h>
#endif //USE_RCOSC

#include <ti_drivers_config.h>
#include <board_key.h>

#include <menu/two_btn_menu.h>

#include "ti_ble_config.h"
#ifdef LED_DEBUG
#include <ti/drivers/PIN.h>
#endif //LED_DEBUG

#include "simple_peripheral_oad_offchip_menu.h"
#include "simple_peripheral_oad_offchip.h"

// Used for imgHdr_t structure
#include <common/cc26xx/oad/oad_image_header.h>
#include "oad.h"

/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */
// How often to perform periodic event (in ms)
#define SP_PERIODIC_EVT_PERIOD               5000

// Offset into the scanRspData string the software version info is stored
#define OAD_SOFT_VER_OFFSET                   15

// Task configuration
#define SP_TASK_PRIORITY                     1

#ifndef SP_TASK_STACK_SIZE
#define SP_TASK_STACK_SIZE                   1408
#endif

// Application events
#define SP_STATE_CHANGE_EVT                  0
#define SP_CHAR_CHANGE_EVT                   1
#define SP_KEY_CHANGE_EVT                    2
#define SP_ADV_EVT                           3
#define SP_PAIR_STATE_EVT                    4
#define SP_PASSCODE_EVT                      5
#define SP_PERIODIC_EVT                      6
#define SP_READ_RPA_EVT                      7
#define SP_SEND_PARAM_UPDATE_EVT             8
#define SP_CONN_EVT                          9
#define SP_TEST_EVT                          10

#define SP_OAD_QUEUE_EVT                     OAD_QUEUE_EVT       // Event_Id_01
#define SP_OAD_COMPLETE_EVT                  OAD_DL_COMPLETE_EVT // Event_Id_02
#define SP_OAD_NO_MEM_EVT                    OAD_OUT_OF_MEM_EVT  // Event_Id_03

// Internal Events for RTOS application
#define SP_ICALL_EVT                         ICALL_MSG_EVENT_ID  // Event_Id_31
#define SP_QUEUE_EVT                         UTIL_QUEUE_EVENT_ID // Event_Id_30

// Bitwise OR of all RTOS events to pend on
#define SP_ALL_EVENTS                        (SP_ICALL_EVT             | \
                                              SP_QUEUE_EVT             | \
                                              SP_OAD_QUEUE_EVT         | \
                                              SP_OAD_COMPLETE_EVT      | \
                                              SP_OAD_NO_MEM_EVT)

// Size of string-converted device address ("0xXXXXXXXXXXXX")
#define SP_ADDR_STR_SIZE     15

// Row numbers for two-button menu
#define SP_ROW_SEPARATOR_1   (TBM_ROW_APP + 0)
#define SP_ROW_STATUS_1      (TBM_ROW_APP + 1)
#define SP_ROW_STATUS_2      (TBM_ROW_APP + 2)
#define SP_ROW_CONNECTION    (TBM_ROW_APP + 3)
#define SP_ROW_ADVSTATE      (TBM_ROW_APP + 4)
#define SP_ROW_RSSI          (TBM_ROW_APP + 5)
#define SP_ROW_IDA           (TBM_ROW_APP + 6)
#define SP_ROW_RPA           (TBM_ROW_APP + 7)
#define SP_ROW_DEBUG         (TBM_ROW_APP + 8)

// For storing the active connections
#define SP_RSSI_TRACK_CHNLS        1            // Max possible channels can be GAP_BONDINGS_MAX
#define SP_MAX_RSSI_STORE_DEPTH    5
#define SP_INVALID_HANDLE          0xFFFF
#define RSSI_2M_THRSHLD           -30           // -80 dB rssi
#define RSSI_1M_THRSHLD           -40           // -90 dB rssi
#define RSSI_S2_THRSHLD           -50           // -100 dB rssi
#define RSSI_S8_THRSHLD           -60           // -120 dB rssi
#define SP_PHY_NONE                LL_PHY_NONE  // No PHY set
#define AUTO_PHY_UPDATE            0xFF

// Spin if the expression is not true
#define SIMPLEPERIPHERAL_ASSERT(expr) if (!(expr)) simple_peripheral_spin();
/*********************************************************************
 * TYPEDEFS
 */

// App event passed from stack modules. This type is defined by the application
// since it can queue events to itself however it wants.
typedef struct
{
  uint8_t event;                // event type
  void    *pData;               // pointer to message
} spEvt_t;

// Container to store passcode data when passing from gapbondmgr callback
// to app event. See the pfnPairStateCB_t documentation from the gapbondmgr.h
// header file for more information on each parameter.
typedef struct
{
  uint8_t state;
  uint16_t connHandle;
  uint8_t status;
} spPairStateData_t;

// Container to store passcode data when passing from gapbondmgr callback
// to app event. See the pfnPasscodeCB_t documentation from the gapbondmgr.h
// header file for more information on each parameter.
typedef struct
{
  uint8_t deviceAddr[B_ADDR_LEN];
  uint16_t connHandle;
  uint8_t uiInputs;
  uint8_t uiOutputs;
  uint32_t numComparison;
} spPasscodeData_t;

// Container to store advertising event data when passing from advertising
// callback to app event. See the respective event in GapAdvScan_Event_IDs
// in gap_advertiser.h for the type that pBuf should be cast to.
typedef struct
{
  uint32_t event;
  void *pBuf;
} spGapAdvEventData_t;

// Container to store information from clock expiration using a flexible array
// since data is not always needed
typedef struct
{
  uint8_t event;                //
  uint8_t data[];
} spClockEventData_t;

// List element for parameter update and PHY command status lists
typedef struct
{
  List_Elem elem;
  uint16_t  connHandle;
} spConnHandleEntry_t;

// Connected device information
typedef struct
{
  uint16_t         connHandle;                        // Connection Handle
  Clock_Struct*    pUpdateClock;                      // pointer to clock struct
  int8_t           rssiArr[SP_MAX_RSSI_STORE_DEPTH];
  uint8_t          rssiCntr;
  int8_t           rssiAvg;
  bool             phyCngRq;                          // Set to true if PHY change request is in progress
  uint8_t          currPhy;
  uint8_t          rqPhy;
  uint8_t          phyRqFailCnt;                      // PHY change request count
  bool             isAutoPHYEnable;                   // Flag to indicate auto phy change
} spConnRec_t;

/*********************************************************************
 * GLOBAL VARIABLES
 */

// Display Interface
Display_Handle dispHandle = NULL;
extern const imgHdr_t _imgHdr;

// Task configuration
Task_Struct spTask;
#if defined __TI_COMPILER_VERSION__
#pragma DATA_ALIGN(spTaskStack, 8)
#else
#pragma data_alignment=8
#endif
uint8_t spTaskStack[SP_TASK_STACK_SIZE];

/*********************************************************************
 * LOCAL VARIABLES
 */

// Entity ID globally used to check for source and/or destination of messages
static ICall_EntityID selfEntity;

// Event globally used to post local events and pend on system and
// local events.
static ICall_SyncHandle syncEvent;

// Queue object used for app messages
static Queue_Struct appMsgQueue;
static Queue_Handle appMsgQueueHandle;

// Clock instance for internal periodic events. Only one is needed since
// GattServApp will handle notifying all connected GATT clients
static Clock_Struct clkPeriodic;
// Clock instance for RPA read events.
static Clock_Struct clkRpaRead;

// Memory to pass periodic event ID to clock handler
spClockEventData_t argPeriodic =
{ .event = SP_PERIODIC_EVT };

// Memory to pass RPA read event ID to clock handler
spClockEventData_t argRpaRead =
{ .event = SP_READ_RPA_EVT };

// Per-handle connection info
static spConnRec_t connList[MAX_NUM_BLE_CONNS];

// Current connection handle as chosen by menu
static uint16_t menuConnHandle = LINKDB_CONNHANDLE_INVALID;

// List to store connection handles for set phy command status's
static List_List setPhyCommStatList;

// List to store connection handles for queued param updates
static List_List paramUpdateList;

// Variable used to store the number of messages pending once OAD completes
// The application cannot reboot until all pending messages are sent
static uint8_t numPendingMsgs = 0;
static bool oadWaitReboot = false;

// Flag to be stored in NV that tracks whether service changed
// indications needs to be sent out
static uint32_t  sendSvcChngdOnNextBoot = FALSE;

#ifdef LED_DEBUG
// State variable for debugging LEDs
static PIN_State sbpLedState;

// Pin table for LED debug pins
static const PIN_Config sbpLedPins[] = {
    CONFIG_PIN_RLED | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    CONFIG_PIN_GLED | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    PIN_TERMINATE
};
#endif //LED_DEBUG

// Advertising handles
static uint8 advHandleLegacy;
static uint8 advHandleLongRange;

// Address mode
static GAP_Addr_Modes_t addrMode = DEFAULT_ADDRESS_MODE;

// Current Random Private Address
static uint8 rpa[B_ADDR_LEN] = {0};

/** clock module instance data structure */
static Clock_Struct clockStruct;

/** clock module instance handle */
static Clock_Handle clockHandle;

/** clock module instance parameters */
static Clock_Params clockParams;
/*********************************************************************
 * LOCAL FUNCTIONS
 */

static void SimplePeripheral_init( void );
static void SimplePeripheral_taskFxn(UArg a0, UArg a1);

static uint8_t SimplePeripheral_processStackMsg(ICall_Hdr *pMsg);
static uint8_t SimplePeripheral_processGATTMsg(gattMsgEvent_t *pMsg);
static void SimplePeripheral_processGapMessage(gapEventHdr_t *pMsg);
static void SimplePeripheral_advCallback(uint32_t event, void *pBuf, uintptr_t arg);
static void SimplePeripheral_processAdvEvent(spGapAdvEventData_t *pEventData);
static void SimplePeripheral_processAppMsg(spEvt_t *pMsg);
static void SimplePeripheral_processCharValueChangeEvt(uint8_t paramId);
static void SimplePeripheral_performPeriodicTask(void);
static void SimplePeripheral_updateRPA(void);
static void SimplePeripheral_clockHandler(UArg arg);
static void SimplePeripheral_passcodeCb(uint8_t *pDeviceAddr, uint16_t connHandle,
                                        uint8_t uiInputs, uint8_t uiOutputs,
                                        uint32_t numComparison);
static void SimplePeripheral_pairStateCb(uint16_t connHandle, uint8_t state,
                                         uint8_t status);
static void SimplePeripheral_processPairState(spPairStateData_t *pPairState);
static void SimplePeripheral_processPasscode(spPasscodeData_t *pPasscodeData);
static void SimplePeripheral_charValueChangeCB(uint8_t paramId);
static status_t SimplePeripheral_enqueueMsg(uint8_t event, void *pData);
static void SimplePeripheral_processOadWriteCB(uint8_t event, uint16_t arg);
static void SimplePeripheral_keyChangeHandler(uint8 keys);
static void SimplePeripheral_handleKeys(uint8_t keys);
static void SimplePeripheral_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg);
static void SimplePeripheral_initPHYRSSIArray(void);
static void SimplePeripheral_updatePHYStat(uint16_t eventCode, uint8_t *pMsg);
static uint8_t SimplePeripheral_addConn(uint16_t connHandle);
static uint8_t SimplePeripheral_getConnIndex(uint16_t connHandle);
static uint8_t SimplePeripheral_removeConn(uint16_t connHandle);
static void SimplePeripheral_processParamUpdate(uint16_t connHandle);
static uint8_t SimplePeripheral_processL2CAPMsg(l2capSignalEvent_t *pMsg);
static status_t SimplePeripheral_startAutoPhyChange(uint16_t connHandle);
static status_t SimplePeripheral_stopAutoPhyChange(uint16_t connHandle);
static status_t SimplePeripheral_setPhy(uint16_t connHandle, uint8_t allPhys,
                                        uint8_t txPhy, uint8_t rxPhy,
                                        uint16_t phyOpts);
static uint8_t SimplePeripheral_clearConnListEntry(uint16_t connHandle);
static void SimplePeripheral_menuSwitchCb(tbmMenuObj_t* pMenuObjCurr,
                                          tbmMenuObj_t* pMenuObjNext);
static void SimplePeripheral_connEvtCB(Gap_ConnEventRpt_t *pReport);
static void SimplePeripheral_processConnEvt(Gap_ConnEventRpt_t *pReport);
static void ClockEventHandler(UArg arg);

/*********************************************************************
 * EXTERN FUNCTIONS
 */
extern void AssertHandler(uint8 assertCause, uint8 assertSubcause);

/*********************************************************************
 * PROFILE CALLBACKS
 */

// GAP Bond Manager Callbacks
static gapBondCBs_t SimplePeripheral_BondMgrCBs =
{
  SimplePeripheral_passcodeCb,       // Passcode callback
  SimplePeripheral_pairStateCb       // Pairing/Bonding state Callback
};

// Simple GATT Profile Callbacks
static simpleProfileCBs_t SimplePeripheral_simpleProfileCBs =
{
  SimplePeripheral_charValueChangeCB // Simple GATT Characteristic value change callback
};

static oadTargetCBs_t SimplePeripheral_oadCBs =
{
  SimplePeripheral_processOadWriteCB // Write Callback.
};

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      simple_peripheral_spin
 *
 * @brief   Spin forever
 *
 * @param   none
 */
static void simple_peripheral_spin(void)
{
  volatile uint8_t x = 0;

  while(1)
  {
    x++;
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_createTask
 *
 * @brief   Task creation function for the Simple Peripheral OAD User App.
 */
void SimplePeripheral_createTask(void)
{
  Task_Params taskParams;

  // Configure task
  Task_Params_init(&taskParams);
  taskParams.stack = spTaskStack;
  taskParams.stackSize = SP_TASK_STACK_SIZE;
  taskParams.priority = SP_TASK_PRIORITY;

  Task_construct(&spTask, SimplePeripheral_taskFxn, &taskParams, NULL);
}

/*********************************************************************
 * @fn      SimplePeripheral_init
 *
 * @brief   Called during initialization and contains application
 *          specific initialization (ie. hardware initialization/setup,
 *          table initialization, power up notification, etc), and
 *          profile initialization/setup.
 */
static void SimplePeripheral_init(void)
{
  // Create the menu
  SimplePeripheral_buildMenu();
  // ******************************************************************
  // N0 STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
  // ******************************************************************
  // Register the current thread as an ICall dispatcher application
  // so that the application can send and receive messages.
  ICall_registerApp(&selfEntity, &syncEvent);

  /* initialize clock */
  Clock_Params_init(&clockParams);    /* init clock params for clock to be one-shot and halted */
  clockParams.startFlag = true;
  Clock_construct(&clockStruct,
                  ClockEventHandler,
                  10000000/Clock_tickPeriod, /* 10 seconds */
                  &clockParams);
  clockHandle = Clock_handle(&clockStruct);

#ifdef LED_DEBUG
  // Open the LED debug pins
  if (!PIN_open(&sbpLedState, sbpLedPins))
  {
    Display_print0(dispHandle, 0, 0, "Debug PINs failed to open");
  }
  else
  {
    PIN_Id activeLed;
    uint8_t blinkCnt = 15;

    PIN_setOutputValue(&sbpLedState, CONFIG_PIN_RLED, 0);
    PIN_setOutputValue(&sbpLedState, CONFIG_PIN_GLED, 0);

    if (blinkCnt < 12)
    {
      activeLed = CONFIG_PIN_RLED;
    }
    else
    {
      activeLed = CONFIG_PIN_GLED;
    }

    for(uint8_t numBlinks = 0; numBlinks < blinkCnt; ++numBlinks)
    {
      PIN_setOutputValue(&sbpLedState, activeLed, !PIN_getOutputValue(activeLed));

      // Sleep for 100ms, sys-tick for BLE-Stack is 10us,
      // Task sleep is in # of ticks
      Task_sleep(10000);
    }

    // Close the pins after using
    PIN_close(&sbpLedState);
  }
#endif //LED_DEBUG

#ifdef USE_RCOSC
  // Set device's Sleep Clock Accuracy
#if ( HOST_CONFIG & ( CENTRAL_CFG | PERIPHERAL_CFG ) )
  HCI_EXT_SetSCACmd(500);
#endif // (CENTRAL_CFG | PERIPHERAL_CFG)
  RCOSC_enableCalibration();
#endif // USE_RCOSC

  // Create an RTOS queue for message from profile to be sent to app.
  appMsgQueueHandle = Util_constructQueue(&appMsgQueue);

  // Create one-shot clock for internal periodic events.
  Util_constructClock(&clkPeriodic, SimplePeripheral_clockHandler,
                      SP_PERIODIC_EVT_PERIOD, 0, false, (UArg)&argPeriodic);

   // Read in the OAD Software version
  uint8_t swVer[OAD_SW_VER_LEN];
  OAD_getSWVersion(swVer, OAD_SW_VER_LEN);

  // Set the Device Name characteristic in the GAP GATT Service
  // For more information, see the section in the User's Guide:
  // http://software-dl.ti.com/lprf/ble5stack-latest/
  GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);

  // Configure GAP
  {
    uint16_t paramUpdateDecision = DEFAULT_PARAM_UPDATE_REQ_DECISION;

    // Pass all parameter update requests to the app for it to decide
    GAP_SetParamValue(GAP_PARAM_LINK_UPDATE_DECISION, paramUpdateDecision);
  }

  // Setup the GAP Bond Manager. For more information see the GAP Bond Manager
  // section in the User's Guide
  setBondManagerParameters();

  // Initialize GATT attributes
  GGS_AddService(GATT_ALL_SERVICES);           // GAP GATT Service
  GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT Service
  DevInfo_AddService();                        // Device Information Service
  SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile

  // Setup the SimpleProfile Characteristic Values
  // For more information, see the GATT and GATTServApp sections in the User's Guide:
  // http://software-dl.ti.com/lprf/ble5stack-latest/
  {
    uint8_t charValue1 = 1;
    uint8_t charValue2 = 2;
    uint8_t charValue3 = 3;
    uint8_t charValue4 = 4;
    uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 };

    SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, sizeof(uint8_t),
                               &charValue1);
    SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, sizeof(uint8_t),
                               &charValue2);
    SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, sizeof(uint8_t),
                               &charValue3);
    SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t),
                               &charValue4);
    SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN,
                               charValue5);
  }

  // Register callback with SimpleGATTprofile
  SimpleProfile_RegisterAppCBs(&SimplePeripheral_simpleProfileCBs);

  // Start Bond Manager and register callback
  VOID GAPBondMgr_Register(&SimplePeripheral_BondMgrCBs);

  // Register with GAP for HCI/Host messages. This is needed to receive HCI
  // events. For more information, see the HCI section in the User's Guide:
  // http://software-dl.ti.com/lprf/ble5stack-latest/
  GAP_RegisterForMsgs(selfEntity);

  // Register for GATT local events and ATT Responses pending for transmission
  GATT_RegisterForMsgs(selfEntity);

  // Set default values for Data Length Extension
  // Extended Data Length Feature is already enabled by default
  {
    // Set initial values to maximum, RX is set to max. by default(251 octets, 2120us)
    // Some brand smartphone is essentially needing 251/2120, so we set them here.
    #define APP_SUGGESTED_PDU_SIZE 251 //default is 27 octets(TX)
    #define APP_SUGGESTED_TX_TIME 2120 //default is 328us(TX)

    // This API is documented in hci.h
    // See the LE Data Length Extension section in the BLE5-Stack User's Guide for information on using this command:
    // http://software-dl.ti.com/lprf/ble5stack-latest/
    HCI_LE_WriteSuggestedDefaultDataLenCmd(APP_SUGGESTED_PDU_SIZE, APP_SUGGESTED_TX_TIME);
  }

  // Initialize GATT Client
  GATT_InitClient();

  // Init key debouncer
  Board_initKeys(SimplePeripheral_keyChangeHandler);

  // Initialize Connection List
  SimplePeripheral_clearConnListEntry(LINKDB_CONNHANDLE_ALL);

  //Initialize GAP layer for Peripheral role and register to receive GAP events
  GAP_DeviceInit(GAP_PROFILE_PERIPHERAL, selfEntity, addrMode, &pRandomAddress);

  // Initialize array to store connection handle and RSSI values
  SimplePeripheral_initPHYRSSIArray();

  // The type of display is configured based on the BOARD_DISPLAY_USE...
  // preprocessor definitions
  dispHandle = Display_open(Display_Type_ANY, NULL);

  // Initialize Two-Button Menu module
  TBM_SET_TITLE(&spMenuMain, "Simple Peripheral");
  tbm_setItemStatus(&spMenuMain, SP_ITEM_SELECT_OAD_DBG, SP_ITEM_SELECT_CONN);

  tbm_initTwoBtnMenu(dispHandle, &spMenuMain, 4, SimplePeripheral_menuSwitchCb);
  Display_printf(dispHandle, SP_ROW_SEPARATOR_1, 0, "====================");


  uint8_t versionStr[OAD_SW_VER_LEN + 1];

  memcpy(versionStr, swVer, OAD_SW_VER_LEN);

  // Add in Null terminator
  versionStr[OAD_SW_VER_LEN] = NULL;

  // Display Image version
  Display_print1(dispHandle, 0, 0, "SBP Off-chip OAD v%s",
                 versionStr);


  // Open the OAD module and add the OAD service to the application
  if(OAD_SUCCESS != OAD_open(OAD_DEFAULT_INACTIVITY_TIME))
  {
    Display_print0(dispHandle, 0, 0, "OAD failed to open");
  }
  else
  {
    // Resiter the OAD callback with the application
    OAD_register(&SimplePeripheral_oadCBs);
  }

}

/*********************************************************************
 * @fn      SimplePeripheral_taskFxn
 *
 * @brief   Application task entry point for the Simple Peripheral.
 *
 * @param   a0, a1 - not used.
 */
static void SimplePeripheral_taskFxn(UArg a0, UArg a1)
{
  // Initialize application
  SimplePeripheral_init();

  // Application main loop
  for (;;)
  {
    uint32_t events;

    // Waits for an event to be posted associated with the calling thread.
    // Note that an event associated with a thread is posted when a
    // message is queued to the message receive queue of the thread
    events = Event_pend(syncEvent, Event_Id_NONE, SP_ALL_EVENTS,
                        ICALL_TIMEOUT_FOREVER);

    if (events)
    {
      ICall_EntityID dest;
      ICall_ServiceEnum src;
      ICall_HciExtEvt *pMsg = NULL;

      // Fetch any available messages that might have been sent from the stack
      if (ICall_fetchServiceMsg(&src, &dest,
                                (void **)&pMsg) == ICALL_ERRNO_SUCCESS)
      {
        uint8 safeToDealloc = TRUE;

        if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
        {
          ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg;

          // Check for BLE stack events first
          if (pEvt->signature != 0xffff)
          {
            // Process inter-task message
            safeToDealloc = SimplePeripheral_processStackMsg((ICall_Hdr *)pMsg);
          }
        }

        if (pMsg && safeToDealloc)
        {
          ICall_freeMsg(pMsg);
        }
      }

      // If RTOS queue is not empty, process app message.
      if (events & SP_QUEUE_EVT)
      {
        while (!Queue_empty(appMsgQueueHandle))
        {
          spEvt_t *pMsg = (spEvt_t *)Util_dequeueMsg(appMsgQueueHandle);
          if (pMsg)
          {
            // Process message.
            SimplePeripheral_processAppMsg(pMsg);

            // Free the space from the message.
            ICall_free(pMsg);
          }
        }
      }
       // OAD events
      if(events & SP_OAD_NO_MEM_EVT)
      {
        // The OAD module is unable to allocate memory, print failure, cancel OAD
        Display_print0(dispHandle, SP_ROW_STATUS_1, 0,
                        "OAD malloc fail, cancelling OAD");
        OAD_cancel();

#ifdef LED_DEBUG
        // Diplay is not enabled in persist app so use LED
        if(PIN_open(&sbpLedState, sbpLedPins))
        {
          PIN_setOutputValue(&sbpLedState, CONFIG_PIN_RLED, 1);
        }
#endif //LED_DEBUG
      }
      // OAD queue processing
      if(events & SP_OAD_QUEUE_EVT)
      {
        // Process the OAD Message Queue
        uint8_t status = OAD_processQueue();

        // If the OAD state machine encountered an error, print it
        // Return codes can be found in oad_constants.h
        if(status == OAD_DL_COMPLETE)
        {
          Display_print0(dispHandle, SP_ROW_STATUS_1, 0, "OAD DL Complete, wait for Enable");
        }
        else if(status == OAD_IMG_ID_TIMEOUT)
        {
          Display_print0(dispHandle, SP_ROW_STATUS_1, 0, "ImgID Timeout, disconnecting");

          // This may be an attack, terminate the link,
          // Note HCI_DISCONNECT_REMOTE_USER_TERM seems to most closet reason for
          // termination at this state
          MAP_GAP_TerminateLinkReq(OAD_getactiveCxnHandle(), HCI_DISCONNECT_REMOTE_USER_TERM);
        }
        else if(status != OAD_SUCCESS)
        {
          Display_print1(dispHandle, SP_ROW_STATUS_1, 0, "OAD Error: %d", status);
        }

      }

      if(events & SP_OAD_COMPLETE_EVT)
      {
        // Register for L2CAP Flow Control Events
        L2CAP_RegisterFlowCtrlTask(selfEntity);
      }

    }
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_processStackMsg
 *
 * @brief   Process an incoming stack message.
 *
 * @param   pMsg - message to process
 *
 * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
 */
static uint8_t SimplePeripheral_processStackMsg(ICall_Hdr *pMsg)
{
  // Always dealloc pMsg unless set otherwise
  uint8_t safeToDealloc = TRUE;

  switch (pMsg->event)
  {
    case GAP_MSG_EVENT:
      SimplePeripheral_processGapMessage((gapEventHdr_t*) pMsg);
      break;

    case GATT_MSG_EVENT:
      // Process GATT message
      safeToDealloc = SimplePeripheral_processGATTMsg((gattMsgEvent_t *)pMsg);
      break;

    case HCI_GAP_EVENT_EVENT:
    {
      // Process HCI message
      switch(pMsg->status)
      {
        case HCI_COMMAND_COMPLETE_EVENT_CODE:
        // Process HCI Command Complete Events here
        {
          SimplePeripheral_processCmdCompleteEvt((hciEvt_CmdComplete_t *) pMsg);
          break;
        }

        case HCI_BLE_HARDWARE_ERROR_EVENT_CODE:
          AssertHandler(HAL_ASSERT_CAUSE_HARDWARE_ERROR,0);
          break;

        // HCI Commands Events
        case HCI_COMMAND_STATUS_EVENT_CODE:
        {
          hciEvt_CommandStatus_t *pMyMsg = (hciEvt_CommandStatus_t *)pMsg;
          switch ( pMyMsg->cmdOpcode )
          {
            case HCI_LE_SET_PHY:
            {
              if (pMyMsg->cmdStatus == HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE)
              {
                Display_printf(dispHandle, SP_ROW_STATUS_1, 0,
                        "PHY Change failure, peer does not support this");
              }
              else
              {
                Display_printf(dispHandle, SP_ROW_STATUS_1, 0,
                               "PHY Update Status Event: 0x%x",
                               pMyMsg->cmdStatus);
              }

              SimplePeripheral_updatePHYStat(HCI_LE_SET_PHY, (uint8_t *)pMsg);
              break;
            }

            default:
              break;
          }
          break;
        }

        // LE Events
        case HCI_LE_EVENT_CODE:
        {
          hciEvt_BLEPhyUpdateComplete_t *pPUC =
            (hciEvt_BLEPhyUpdateComplete_t*) pMsg;

          // A Phy Update Has Completed or Failed
          if (pPUC->BLEEventCode == HCI_BLE_PHY_UPDATE_COMPLETE_EVENT)
          {
            if (pPUC->status != SUCCESS)
            {
              Display_printf(dispHandle, SP_ROW_STATUS_1, 0,
                             "PHY Change failure");
            }
            else
            {
              // Only symmetrical PHY is supported.
              // rxPhy should be equal to txPhy.
              Display_printf(dispHandle, SP_ROW_STATUS_2, 0,
                             "PHY Updated to %s",
                             (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_1M) ? "1M" :
                             (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_2M) ? "2M" :
                             (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_CODED) ? "CODED" : "Unexpected PHY Value");
            }

            SimplePeripheral_updatePHYStat(HCI_BLE_PHY_UPDATE_COMPLETE_EVENT, (uint8_t *)pMsg);
          }
          break;
        }

        default:
          break;
      }

      break;
    }

    case L2CAP_SIGNAL_EVENT:
      // Process L2CAP signal
      safeToDealloc = SimplePeripheral_processL2CAPMsg((l2capSignalEvent_t *)pMsg);
      break;

    default:
      // do nothing
      break;
  }

  return (safeToDealloc);
}

/*********************************************************************
 * @fn      SimplePeripheral_processL2CAPMsg
 *
 * @brief   Process L2CAP messages and events.
 *
 * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
 */
static uint8_t SimplePeripheral_processL2CAPMsg(l2capSignalEvent_t *pMsg)
{
  uint8_t safeToDealloc = TRUE;
  static bool firstRun = TRUE;

  switch (pMsg->opcode)
  {
    case L2CAP_NUM_CTRL_DATA_PKT_EVT:
    {
      /*
      * We cannot reboot the device immediately after receiving
      * the enable command, we must allow the stack enough time
      * to process and respond to the OAD_EXT_CTRL_ENABLE_IMG
      * command. This command will determine the number of
      * packets currently queued up by the LE controller.
      * BIM var is already set via OadPersistApp_processOadWriteCB
      */
      if(firstRun)
      {
        firstRun = false;

        // We only want to set the numPendingMsgs once
        numPendingMsgs = MAX_NUM_PDU - pMsg->cmd.numCtrlDataPktEvt.numDataPkt;

        // Wait until all PDU have been sent on cxn events
        Gap_RegisterConnEventCb(SimplePeripheral_connEvtCB,
                                  GAP_CB_REGISTER,
                                  OAD_getactiveCxnHandle());

        /* Set the flag so that the connection event callback will
         * be processed in the context of a pending OAD reboot
         */
        oadWaitReboot = true;
      }

      break;
    }

    default:
      // do nothing
      break;
  }

  return (safeToDealloc);
}

/*********************************************************************
 * @fn      SimplePeripheral_processGATTMsg
 *
 * @brief   Process GATT messages and events.
 *
 * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
 */
static uint8_t SimplePeripheral_processGATTMsg(gattMsgEvent_t *pMsg)
{
  if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
  {
    // ATT request-response or indication-confirmation flow control is
    // violated. All subsequent ATT requests or indications will be dropped.
    // The app is informed in case it wants to drop the connection.

    // Display the opcode of the message that caused the violation.
    Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode);
  }
  else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
  {
    // MTU size updated
    OAD_setBlockSize(pMsg->msg.mtuEvt.MTU);
    Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "MTU Size: %d", pMsg->msg.mtuEvt.MTU);
  }

  // Free message payload. Needed only for ATT Protocol messages
  GATT_bm_free(&pMsg->msg, pMsg->method);

  // It's safe to free the incoming message
  return (TRUE);
}

/*********************************************************************
 * @fn      SimplePeripheral_processAppMsg
 *
 * @brief   Process an incoming callback from a profile.
 *
 * @param   pMsg - message to process
 *
 * @return  None.
 */
static void SimplePeripheral_processAppMsg(spEvt_t *pMsg)
{
  bool dealloc = TRUE;

  switch (pMsg->event)
  {
    case SP_CHAR_CHANGE_EVT:
      SimplePeripheral_processCharValueChangeEvt(*(uint8_t*)(pMsg->pData));
      break;

    case SP_KEY_CHANGE_EVT:
      SimplePeripheral_handleKeys(*(uint8_t*)(pMsg->pData));
      break;

    case SP_ADV_EVT:
      SimplePeripheral_processAdvEvent((spGapAdvEventData_t*)(pMsg->pData));
      break;

    case SP_PAIR_STATE_EVT:
      SimplePeripheral_processPairState((spPairStateData_t*)(pMsg->pData));
      break;

    case SP_PASSCODE_EVT:
      SimplePeripheral_processPasscode((spPasscodeData_t*)(pMsg->pData));
      break;

    case SP_PERIODIC_EVT:
      SimplePeripheral_performPeriodicTask();
      break;

    case SP_READ_RPA_EVT:
      SimplePeripheral_updateRPA();
      break;

    case SP_SEND_PARAM_UPDATE_EVT:
    {
      // Extract connection handle from data
      uint16_t connHandle = *(uint16_t *)(((spClockEventData_t *)pMsg->pData)->data);

      SimplePeripheral_processParamUpdate(connHandle);

      // This data is not dynamically allocated
      dealloc = FALSE;
      break;
    }

    case SP_CONN_EVT:
      SimplePeripheral_processConnEvt((Gap_ConnEventRpt_t *)(pMsg->pData));
      break;

    case SP_TEST_EVT:
    {
        Status_t status = SUCCESS;
        status = HCI_ResetCmd();

        if (status != SUCCESS)
        {
            /* ohoh... very bad... consider a sysReset */
        }
    }
    break;

    default:
      // Do nothing.
      break;
  }

  // Free message data if it exists and we are to dealloc
  if ((dealloc == TRUE) && (pMsg->pData != NULL))
  {
    ICall_free(pMsg->pData);
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_processGapMessage
 *
 * @brief   Process an incoming GAP event.
 *
 * @param   pMsg - message to process
 */
static void SimplePeripheral_processGapMessage(gapEventHdr_t *pMsg)
{
  switch(pMsg->opcode)
  {
    case GAP_DEVICE_INIT_DONE_EVENT:
    {
      bStatus_t status = FAILURE;

      gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;

      if(pPkt->hdr.status == SUCCESS)
      {
        // Store the system ID
        uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];

        // use 6 bytes of device address for 8 bytes of system ID value
        systemId[0] = pPkt->devAddr[0];
        systemId[1] = pPkt->devAddr[1];
        systemId[2] = pPkt->devAddr[2];

        // set middle bytes to zero
        systemId[4] = 0x00;
        systemId[3] = 0x00;

        // shift three bytes up
        systemId[7] = pPkt->devAddr[5];
        systemId[6] = pPkt->devAddr[4];
        systemId[5] = pPkt->devAddr[3];

        // Set Device Info Service Parameter
        DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);

        Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Initialized");

        // Setup and start Advertising
        // For more information, see the GAP section in the User's Guide:
        // http://software-dl.ti.com/lprf/ble5stack-latest/

        // Create Advertisement set #1 and assign handle
        status = GapAdv_create(&SimplePeripheral_advCallback, &advParams1,
                               &advHandleLegacy);
        SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

        // Load advertising data for set #1 that is statically allocated by the app
        status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_ADV,
                                     sizeof(advData1), advData1);
        SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

        // Load scan response data for set #1 that is statically allocated by the app
        status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_SCAN_RSP,
                                     sizeof(scanResData1), scanResData1);
        SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

        // Set event mask for set #1
        status = GapAdv_setEventMask(advHandleLegacy,
                                     GAP_ADV_EVT_MASK_START_AFTER_ENABLE |
                                     GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
                                     GAP_ADV_EVT_MASK_SET_TERMINATED);

        // Enable legacy advertising for set #1
        status = GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
        SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

        // Create Advertisement set #2 and assign handle
        status = GapAdv_create(&SimplePeripheral_advCallback, &advParams2,
                               &advHandleLongRange);
        SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

        // Load advertising data for set #2 that is statically allocated by the app
        status = GapAdv_loadByHandle(advHandleLongRange, GAP_ADV_DATA_TYPE_ADV,
                                     sizeof(advData2), advData2);
        SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

        // Set event mask for set #2
        status = GapAdv_setEventMask(advHandleLongRange,
                                     GAP_ADV_EVT_MASK_START_AFTER_ENABLE |
                                     GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
                                     GAP_ADV_EVT_MASK_SET_TERMINATED);

        // Enable long range advertising for set #2
        status = GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
        SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

        // Display device address
        Display_printf(dispHandle, SP_ROW_IDA, 0, "%s Addr: %s",
                       (addrMode <= ADDRMODE_RANDOM) ? "Dev" : "ID",
                       Util_convertBdAddr2Str(pPkt->devAddr));

        if (addrMode > ADDRMODE_RANDOM)
        {
          SimplePeripheral_updateRPA();

          // Create one-shot clock for RPA check event.
          Util_constructClock(&clkRpaRead, SimplePeripheral_clockHandler,
                              READ_RPA_PERIOD, 0, true,
                              (UArg) &argRpaRead);
        }
      }

      break;
    }

    case GAP_LINK_ESTABLISHED_EVENT:
    {
      gapEstLinkReqEvent_t *pPkt = (gapEstLinkReqEvent_t *)pMsg;

      // Display the amount of current connections
      uint8_t numActive = linkDB_NumActive();
      Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Num Conns: %d",
                     (uint16_t)numActive);

      if (pPkt->hdr.status == SUCCESS)
      {
        // Add connection to list and start RSSI
        SimplePeripheral_addConn(pPkt->connectionHandle);

        // Display the address of this connection
        Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connected to %s",
                       Util_convertBdAddr2Str(pPkt->devAddr));

        // Enable connection selection option
        tbm_setItemStatus(&spMenuMain, SP_ITEM_SELECT_CONN, TBM_ITEM_NONE);

        // Start Periodic Clock.
        Util_startClock(&clkPeriodic);
      }

      if (numActive < MAX_NUM_BLE_CONNS)
      {
        // Start advertising since there is room for more connections
        GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
        GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
      }
      else
      {
        // Stop advertising since there is no room for more connections
        GapAdv_disable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
        GapAdv_disable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
      }

      break;
    }

    case GAP_LINK_TERMINATED_EVENT:
    {
      gapTerminateLinkEvent_t *pPkt = (gapTerminateLinkEvent_t *)pMsg;

      // Display the amount of current connections
      uint8_t numActive = linkDB_NumActive();
      Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Device Disconnected!");
      Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Num Conns: %d",
                     (uint16_t)numActive);

      // Remove the connection from the list and disable RSSI if needed
      SimplePeripheral_removeConn(pPkt->connectionHandle);

      // If no active connections
      if (numActive == 0)
      {
        // Stop periodic clock
        Util_stopClock(&clkPeriodic);

        // Disable Connection Selection option
        tbm_setItemStatus(&spMenuMain, TBM_ITEM_NONE, SP_ITEM_SELECT_CONN);
      }

      // Start advertising since there is room for more connections
      GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
      GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);

      // Clear remaining lines
      Display_clearLine(dispHandle, SP_ROW_CONNECTION);

      // Cancel the OAD if one is going on
      // A disconnect forces the peer to re-identify
      OAD_cancel();

      break;
    }

    case GAP_UPDATE_LINK_PARAM_REQ_EVENT:
    {
      gapUpdateLinkParamReqReply_t rsp;
      gapUpdateLinkParamReqEvent_t *pReq = (gapUpdateLinkParamReqEvent_t *)pMsg;

      rsp.connectionHandle = pReq->req.connectionHandle;
      rsp.signalIdentifier = pReq->req.signalIdentifier;

      // Only accept connection intervals with slave latency of 0
      // This is just an example of how the application can send a response
      if(pReq->req.connLatency == 0)
      {
        rsp.intervalMin = pReq->req.intervalMin;
        rsp.intervalMax = pReq->req.intervalMax;
        rsp.connLatency = pReq->req.connLatency;
        rsp.connTimeout = pReq->req.connTimeout;
        rsp.accepted = TRUE;
      }
      else
      {
        rsp.accepted = FALSE;
      }

      // Send Reply
      VOID GAP_UpdateLinkParamReqReply(&rsp);

      break;
    }

    case GAP_LINK_PARAM_UPDATE_EVENT:
    {
      gapLinkUpdateEvent_t *pPkt = (gapLinkUpdateEvent_t *)pMsg;

      // Get the address from the connection handle
      linkDBInfo_t linkInfo;
      linkDB_GetInfo(pPkt->connectionHandle, &linkInfo);

      if(pPkt->status == SUCCESS)
      {
        // Display the address of the connection update
        Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Link Param Updated: %s",
                       Util_convertBdAddr2Str(linkInfo.addr));
      }
      else
      {
        // Display the address of the connection update failure
        Display_printf(dispHandle, SP_ROW_STATUS_2, 0,
                       "Link Param Update Failed 0x%x: %s", pPkt->opcode,
                       Util_convertBdAddr2Str(linkInfo.addr));
      }

      // Check if there are any queued parameter updates
      spConnHandleEntry_t *connHandleEntry = (spConnHandleEntry_t *)List_get(&paramUpdateList);
      if (connHandleEntry != NULL)
      {
        // Attempt to send queued update now
        SimplePeripheral_processParamUpdate(connHandleEntry->connHandle);

        // Free list element
        ICall_free(connHandleEntry);
      }

      break;
    }

#if defined ( NOTIFY_PARAM_UPDATE_RJCT )
    case GAP_LINK_PARAM_UPDATE_REJECT_EVENT:
    {
      linkDBInfo_t linkInfo;
      gapLinkUpdateEvent_t *pPkt = (gapLinkUpdateEvent_t *)pMsg;

      // Get the address from the connection handle
      linkDB_GetInfo(pPkt->connectionHandle, &linkInfo);

      // Display the address of the connection update failure
      Display_printf(dispHandle, SP_ROW_STATUS_2, 0,
                     "Peer Device's Update Request Rejected 0x%x: %s", pPkt->opcode,
                     Util_convertBdAddr2Str(linkInfo.addr));

      break;
    }
#endif
    default:
      Display_clearLines(dispHandle, SP_ROW_STATUS_1, SP_ROW_STATUS_2);
      break;
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_charValueChangeCB
 *
 * @brief   Callback from Simple Profile indicating a characteristic
 *          value change.
 *
 * @param   paramId - parameter Id of the value that was changed.
 *
 * @return  None.
 */
static void SimplePeripheral_charValueChangeCB(uint8_t paramId)
{
  uint8_t *pValue = ICall_malloc(sizeof(uint8_t));

  if (pValue)
  {
    *pValue = paramId;

    if (SimplePeripheral_enqueueMsg(SP_CHAR_CHANGE_EVT, pValue) != SUCCESS)
    {
      ICall_free(pValue);
    }
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_processOadWriteCB
 *
 * @brief   Process a write request to the OAD reset service
 *
 * @param   connHandle - the connection Handle this request is from.
 * @param   bim_var    - bim_var to set before resetting.
 *
 * @return  None.
 */
void SimplePeripheral_processOadWriteCB(uint8_t event, uint16_t arg)
{
  Event_post(syncEvent, event);
}

/*********************************************************************
 * @fn      SimplePeripheral_processCharValueChangeEvt
 *
 * @brief   Process a pending Simple Profile characteristic value change
 *          event.
 *
 * @param   paramID - parameter ID of the value that was changed.
 */
static void SimplePeripheral_processCharValueChangeEvt(uint8_t paramId)
{
  uint8_t newValue;

  switch(paramId)
  {
    case SIMPLEPROFILE_CHAR1:
      SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue);

      Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Char 1: %d", (uint16_t)newValue);
      break;

    case SIMPLEPROFILE_CHAR3:
      SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &newValue);

      Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Char 3: %d", (uint16_t)newValue);
      break;

    default:
      // should not reach here!
      break;
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_performPeriodicTask
 *
 * @brief   Perform a periodic application task. This function gets called
 *          every five seconds (SP_PERIODIC_EVT_PERIOD). In this example,
 *          the value of the third characteristic in the SimpleGATTProfile
 *          service is retrieved from the profile, and then copied into the
 *          value of the the fourth characteristic.
 *
 * @param   None.
 *
 * @return  None.
 */
static void SimplePeripheral_performPeriodicTask(void)
{
  uint8_t valueToCopy;

  // Call to retrieve the value of the third characteristic in the profile
  if (SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &valueToCopy) == SUCCESS)
  {
    // Call to set that value of the fourth characteristic in the profile.
    // Note that if notifications of the fourth characteristic have been
    // enabled by a GATT client device, then a notification will be sent
    // every time this function is called.
    SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t),
                               &valueToCopy);
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_updateRPA
 *
 * @brief   Read the current RPA from the stack and update display
 *          if the RPA has changed.
 *
 * @param   None.
 *
 * @return  None.
 */
static void SimplePeripheral_updateRPA(void)
{
  // Read the current RPA.
  // The parameters for the call to HCI_LE_ReadLocalResolvableAddressCmd
  // are not needed to be accurate to retrieve the local resolvable address.
  // The 1st parameter can be any of ADDRMODE_PUBLIC and ADDRMODE_RANDOM.
  // The 2nd parameter only has to be not NULL.
  // The result will come with HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS
  // complete event.
  HCI_LE_ReadLocalResolvableAddressCmd(0, rpa);
}

/*********************************************************************
 * @fn      SimplePeripheral_clockHandler
 *
 * @brief   Handler function for clock timeouts.
 *
 * @param   arg - event type
 *
 * @return  None.
 */
static void SimplePeripheral_clockHandler(UArg arg)
{
  spClockEventData_t *pData = (spClockEventData_t *)arg;

  if (pData->event == SP_PERIODIC_EVT)
  {
    // Start the next period
    Util_startClock(&clkPeriodic);

    // Post event to wake up the application
    SimplePeripheral_enqueueMsg(SP_PERIODIC_EVT, NULL);
  }
  else if (pData->event == SP_READ_RPA_EVT)
  {
    // Start the next period
    Util_startClock(&clkRpaRead);

    // Post event to read the current RPA
    SimplePeripheral_enqueueMsg(SP_READ_RPA_EVT, NULL);
  }
  else if (pData->event == SP_SEND_PARAM_UPDATE_EVT)
  {
    // Send message to app
    SimplePeripheral_enqueueMsg(SP_SEND_PARAM_UPDATE_EVT, pData);
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_keyChangeHandler
 *
 * @brief   Key event handler function
 *
 * @param   keys - bitmap of pressed keys
 *
 * @return  none
 */
static void SimplePeripheral_keyChangeHandler(uint8_t keys)
{
  uint8_t *pValue = ICall_malloc(sizeof(uint8_t));

  if (pValue)
  {
    *pValue = keys;

    if(SimplePeripheral_enqueueMsg(SP_KEY_CHANGE_EVT, pValue) != SUCCESS)
    {
      ICall_free(pValue);
    }
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_handleKeys
 *
 * @brief   Handles all key events for this device.
 *
 * @param   keys - bit field for key events. Valid entries:
 *                 KEY_LEFT
 *                 KEY_RIGHT
 */
static void SimplePeripheral_handleKeys(uint8_t keys)
{
  if (keys & KEY_LEFT)
  {
    // Check if the key is still pressed. Workaround for possible bouncing.
    if (PIN_getInputValue(CONFIG_PIN_BTN1) == 0)
    {
      tbm_buttonLeft();
    }
  }
  else if (keys & KEY_RIGHT)
  {
    // Check if the key is still pressed. Workaround for possible bouncing.
    if (PIN_getInputValue(CONFIG_PIN_BTN2) == 0)
    {
      tbm_buttonRight();
    }
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_doSetConnPhy
 *
 * @brief   Set PHY preference.
 *
 * @param   index - 0: 1M PHY
 *                  1: 2M PHY
 *                  2: 1M + 2M PHY
 *                  3: CODED PHY (Long range)
 *                  4: 1M + 2M + CODED PHY
 *
 * @return  always true
 */
bool SimplePeripheral_doSetConnPhy(uint8 index)
{
  bool status = TRUE;

  static uint8_t phy[] = {
    HCI_PHY_1_MBPS, HCI_PHY_2_MBPS, HCI_PHY_1_MBPS | HCI_PHY_2_MBPS,
    HCI_PHY_CODED, HCI_PHY_1_MBPS | HCI_PHY_2_MBPS | HCI_PHY_CODED,
    AUTO_PHY_UPDATE
  };

  uint8_t connIndex = SimplePeripheral_getConnIndex(menuConnHandle);
  SIMPLEPERIPHERAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);

  // Set Phy Preference on the current connection. Apply the same value
  // for RX and TX.
  // If auto PHY update is not selected and if auto PHY update is enabled, then
  // stop auto PHY update
  // Note PHYs are already enabled by default in build_config.opt in stack project.
  if(phy[index] != AUTO_PHY_UPDATE)
  {
    // Cancel RSSI reading  and auto phy changing
    SimplePeripheral_stopAutoPhyChange(connList[connIndex].connHandle);

    SimplePeripheral_setPhy(menuConnHandle, 0, phy[index], phy[index], 0);

    Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "PHY preference: %s",
                   TBM_GET_ACTION_DESC(&spMenuConnPhy, index));
  }
  else
  {
    // Start RSSI read for auto PHY update (if it is disabled)
    SimplePeripheral_startAutoPhyChange(menuConnHandle);
  }

  return status;
}
/*********************************************************************
 * @fn      SimplePeripheral_advCallback
 *
 * @brief   GapAdv module callback
 *
 * @param   pMsg - message to process
 */
static void SimplePeripheral_advCallback(uint32_t event, void *pBuf, uintptr_t arg)
{
  spGapAdvEventData_t *pData = ICall_malloc(sizeof(spGapAdvEventData_t));

  if (pData)
  {
    pData->event = event;
    pData->pBuf = pBuf;

    if(SimplePeripheral_enqueueMsg(SP_ADV_EVT, pData) != SUCCESS)
    {
      ICall_free(pData);
    }
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_processAdvEvent
 *
 * @brief   Process advertising event in app context
 *
 * @param   pEventData
 */
static void SimplePeripheral_processAdvEvent(spGapAdvEventData_t *pEventData)
{
  switch (pEventData->event)
  {
    case GAP_EVT_ADV_START_AFTER_ENABLE:
      Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d Enabled",
                     *(uint8_t *)(pEventData->pBuf));
      break;

    case GAP_EVT_ADV_END_AFTER_DISABLE:
      Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d Disabled",
                     *(uint8_t *)(pEventData->pBuf));
      break;

    case GAP_EVT_ADV_START:
      break;

    case GAP_EVT_ADV_END:
      break;

    case GAP_EVT_ADV_SET_TERMINATED:
    {
#ifndef Display_DISABLE_ALL
      GapAdv_setTerm_t *advSetTerm = (GapAdv_setTerm_t *)(pEventData->pBuf);

      Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d disabled after conn %d",
                     advSetTerm->handle, advSetTerm->connHandle );
#endif
    }
    break;

    case GAP_EVT_SCAN_REQ_RECEIVED:
      break;

    case GAP_EVT_INSUFFICIENT_MEMORY:
      break;

    default:
      break;
  }

  // All events have associated memory to free except the insufficient memory
  // event
  if (pEventData->event != GAP_EVT_INSUFFICIENT_MEMORY)
  {
    ICall_free(pEventData->pBuf);
  }
}


/*********************************************************************
 * @fn      SimplePeripheral_pairStateCb
 *
 * @brief   Pairing state callback.
 *
 * @return  none
 */
static void SimplePeripheral_pairStateCb(uint16_t connHandle, uint8_t state,
                                         uint8_t status)
{
  spPairStateData_t *pData = ICall_malloc(sizeof(spPairStateData_t));

  // Allocate space for the event data.
  if (pData)
  {
    pData->state = state;
    pData->connHandle = connHandle;
    pData->status = status;

    // Queue the event.
    if(SimplePeripheral_enqueueMsg(SP_PAIR_STATE_EVT, pData) != SUCCESS)
    {
      ICall_free(pData);
    }
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_passcodeCb
 *
 * @brief   Passcode callback.
 *
 * @return  none
 */
static void SimplePeripheral_passcodeCb(uint8_t *pDeviceAddr,
                                        uint16_t connHandle,
                                        uint8_t uiInputs,
                                        uint8_t uiOutputs,
                                        uint32_t numComparison)
{
  spPasscodeData_t *pData = ICall_malloc(sizeof(spPasscodeData_t));

  // Allocate space for the passcode event.
  if (pData )
  {
    pData->connHandle = connHandle;
    memcpy(pData->deviceAddr, pDeviceAddr, B_ADDR_LEN);
    pData->uiInputs = uiInputs;
    pData->uiOutputs = uiOutputs;
    pData->numComparison = numComparison;

    // Enqueue the event.
    if(SimplePeripheral_enqueueMsg(SP_PASSCODE_EVT, pData) != SUCCESS)
    {
      ICall_free(pData);
    }
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_processPairState
 *
 * @brief   Process the new paring state.
 *
 * @return  none
 */
static void SimplePeripheral_processPairState(spPairStateData_t *pPairData)
{
  uint8_t state = pPairData->state;
  uint8_t status = pPairData->status;

  switch (state)
  {
    case GAPBOND_PAIRING_STATE_STARTED:
      Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Pairing started");
      break;

    case GAPBOND_PAIRING_STATE_COMPLETE:
      if (status == SUCCESS)
      {
        Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Pairing success");
      }
      else
      {
        Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Pairing fail: %d", status);
      }
      break;

    case GAPBOND_PAIRING_STATE_ENCRYPTED:
      if (status == SUCCESS)
      {
        Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Encryption success");
      }
      else
      {
        Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Encryption failed: %d", status);
      }
      break;

    case GAPBOND_PAIRING_STATE_BOND_SAVED:
      if (status == SUCCESS)
      {
        Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Bond save success");
      }
      else
      {
        Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Bond save failed: %d", status);
      }
      break;

    default:
      break;
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_processPasscode
 *
 * @brief   Process the Passcode request.
 *
 * @return  none
 */
static void SimplePeripheral_processPasscode(spPasscodeData_t *pPasscodeData)
{
  // Display passcode to user
  if (pPasscodeData->uiOutputs != 0)
  {
    Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Passcode: %d",
                   B_APP_DEFAULT_PASSCODE);
  }

  // Send passcode response
  GAPBondMgr_PasscodeRsp(pPasscodeData->connHandle , SUCCESS,
                         B_APP_DEFAULT_PASSCODE);
}

/*********************************************************************
 * @fn      SimplePeripheral_connEvtCB
 *
 * @brief   Connection event callback.
 *
 * @param pReport pointer to connection event report
 */
static void SimplePeripheral_connEvtCB(Gap_ConnEventRpt_t *pReport)
{
  // Enqueue the event for processing in the app context.
  if(SimplePeripheral_enqueueMsg(SP_CONN_EVT, pReport) != SUCCESS)
  {
    ICall_free(pReport);
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_processConnEvt
 *
 * @brief   Process connection event.
 *
 * @param pReport pointer to connection event report
 */
static void SimplePeripheral_processConnEvt(Gap_ConnEventRpt_t *pReport)
{
  /* If we are waiting for an OAD Reboot, process connection events to ensure
   * that we are not waiting to send data before restarting
   */
  if(oadWaitReboot)
  {
    // Wait until all pending messages are sent
    if(numPendingMsgs == 0)
    {
      // Store the flag to indicate that a service changed IND will
      // be sent at the next boot
      sendSvcChngdOnNextBoot = TRUE;

      uint8_t status = osal_snv_write(BLE_NVID_CUST_START,
                                      sizeof(sendSvcChngdOnNextBoot),
                                      (uint8 *)&sendSvcChngdOnNextBoot);
      if(status != SUCCESS)
      {
        Display_print1(dispHandle, 5, 0, "SNV WRITE FAIL: %d", status);
      }

      // Reset the system
      SystemReset();
    }
    else
    {
      numPendingMsgs--;
    }
  }
  else
  {
    // Get index from handle
    uint8_t connIndex = SimplePeripheral_getConnIndex(pReport->handle);

    // If auto phy change is enabled
    if (connList[connIndex].isAutoPHYEnable == TRUE)
    {
      // Read the RSSI
      HCI_ReadRssiCmd(pReport->handle);
    }
  }
}

/*********************************************************************
 * @fn      SimplePeripheral_enqueueMsg
 *
 * @brief   Creates a message and puts the message in RTOS queue.
 *
 * @param   event - message event.
 * @param   state - message state.
 */
static status_t SimplePeripheral_enqueueMsg(uint8_t event, void *pData)
{
  uint8_t success;
  spEvt_t *pMsg = ICall_malloc(sizeof(spEvt_t));

  // Create dynamic pointer to message.
  if(pMsg)
  {
    pMsg->event = event;
    pMsg->pData = pData;

    // Enqueue the message.
    success = Util_enqueueMsg(appMsgQueueHandle, syncEvent, (uint8_t *)pMsg);
    return (success) ? SUCCESS : FAILURE;
  }

  return(bleMemAllocError);
}

/*********************************************************************
 * @fn      SimplePeripheral_doSelectConn
 *
 * @brief   Select a connection to communicate with
 *
 * @param   index - item index from the menu
 *
 * @return  always true
 */
bool SimplePeripheral_doSelectConn(uint8_t index)
{
  menuConnHandle = connList[index].connHandle;

  // Set the menu title and go to this connection's context
  TBM_SET_TITLE(&spMenuPerConn, TBM_GET_ACTION_DESC(&spMenuSelectConn, index));

  // Clear non-connection-related message
  Display_clearLine(dispHandle, SP_ROW_CONNECTION);

  tbm_goTo(&spMenuPerConn);

  return (true);
}

/*********************************************************************
 * @fn      SimplePeripheral_addConn
 *
 * @brief   Add a device to the connected device list
 *
 * @return  index of the connected device list entry where the new connection
 *          info is put in.
 *          if there is no room, MAX_NUM_BLE_CONNS will be returned.
 */
static uint8_t SimplePeripheral_addConn(uint16_t connHandle)
{
  uint8_t i;
  uint8_t status = bleNoResources;
  spClockEventData_t *paramUpdateEventData;

  // Try to find an available entry
  for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
  {
    if (connList[i].connHandle == LINKDB_CONNHANDLE_INVALID)
    {
      // Found available entry to put a new connection info in
      connList[i].connHandle = connHandle;

      // Allocate data to send through clock handler
      paramUpdateEventData = ICall_malloc(sizeof(spClockEventData_t) +
                                          sizeof (uint16_t));
      if(paramUpdateEventData)
      {
        paramUpdateEventData->event = SP_SEND_PARAM_UPDATE_EVT;
        *((uint16_t *)paramUpdateEventData->data) = connHandle;

        // Create a clock object and start
        connList[i].pUpdateClock
          = (Clock_Struct*) ICall_malloc(sizeof(Clock_Struct));

        if (connList[i].pUpdateClock)
        {
          Util_constructClock(connList[i].pUpdateClock,
                              SimplePeripheral_clockHandler,
                              SEND_PARAM_UPDATE_DELAY, 0, true,
                              (UArg) paramUpdateEventData);
        }
      }
      else
      {
        status = bleMemAllocError;
      }

      // Set default PHY to 1M
      connList[i].currPhy = HCI_PHY_1_MBPS;

      break;
    }
  }

  return status;
}

/*********************************************************************
 * @fn      SimplePeripheral_getConnIndex
 *
 * @brief   Find index in the connected device list by connHandle
 *
 * @return  the index of the entry that has the given connection handle.
 *          if there is no match, MAX_NUM_BLE_CONNS will be returned.
 */
static uint8_t SimplePeripheral_getConnIndex(uint16_t connHandle)
{
  uint8_t i;

  for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
  {
    if (connList[i].connHandle == connHandle)
    {
      return i;
    }
  }

  return(MAX_NUM_BLE_CONNS);
}

/*********************************************************************
 * @fn      SimplePeripheral_getConnIndex
 *
 * @brief   Find index in the connected device list by connHandle
 *
 * @return  SUCCESS if connHandle found valid index or bleInvalidRange
 *          if index wasn't found. LINKDB_CONNHANDLE_ALL will always succeed.
 */
static uint8_t SimplePeripheral_clearConnListEntry(uint16_t connHandle)
{
  uint8_t i;
  // Set to invalid connection index initially
  uint8_t connIndex = MAX_NUM_BLE_CONNS;

  if(connHandle != LINKDB_CONNHANDLE_ALL)
  {
    // Get connection index from handle
    connIndex = SimplePeripheral_getConnIndex(connHandle);
    if(connIndex >= MAX_NUM_BLE_CONNS)
	{
	  return(bleInvalidRange);
	}
  }

  // Clear specific handle or all handles
  for(i = 0; i < MAX_NUM_BLE_CONNS; i++)
  {
    if((connIndex == i) || (connHandle == LINKDB_CONNHANDLE_ALL))
    {
      connList[i].connHandle = LINKDB_CONNHANDLE_INVALID;
      connList[i].currPhy = 0;
      connList[i].phyCngRq = 0;
      connList[i].phyRqFailCnt = 0;
      connList[i].rqPhy = 0;
      memset(connList[i].rssiArr, 0, SP_MAX_RSSI_STORE_DEPTH);
      connList[i].rssiAvg = 0;
      connList[i].rssiCntr = 0;
      connList[i].isAutoPHYEnable = FALSE;
    }
  }

  return(SUCCESS);
}

/*********************************************************************
 * @fn      SimplePeripheral_removeConn
 *
 * @brief   Remove a device from the connected device list
 *
 * @return  index of the connected device list entry where the new connection
 *          info is removed from.
 *          if connHandle is not found, MAX_NUM_BLE_CONNS will be returned.
 */
static uint8_t SimplePeripheral_removeConn(uint16_t connHandle)
{
  uint8_t connIndex = SimplePeripheral_getConnIndex(connHandle);

  if(connIndex != MAX_NUM_BLE_CONNS)
  {
    Clock_Struct* pUpdateClock = connList[connIndex].pUpdateClock;

    if (pUpdateClock != NULL)
    {
      // Stop and destruct the RTOS clock if it's still alive
      if (Util_isActive(pUpdateClock))
      {
        Util_stopClock(pUpdateClock);
      }

      // Destruct the clock object
      Clock_destruct(pUpdateClock);
      // Free clock struct
      ICall_free(pUpdateClock);
    }
    // Stop Auto PHY Change
    SimplePeripheral_stopAutoPhyChange(connHandle);
    // Clear Connection List Entry
    SimplePeripheral_clearConnListEntry(connHandle);
  }

  return connIndex;
}

/*********************************************************************
 * @fn      SimplePeripheral_processParamUpdate
 *
 * @brief   Remove a device from the connected device list
 *
 * @return  index of the connected device list entry where the new connection
 *          info is removed from.
 *          if connHandle is not found, MAX_NUM_BLE_CONNS will be returned.
 */
static void SimplePeripheral_processParamUpdate(uint16_t connHandle)
{
  gapUpdateLinkParamReq_t req;
  uint8_t connIndex;

  req.connectionHandle = connHandle;
  req.connLatency = DEFAULT_DESIRED_SLAVE_LATENCY;
  req.connTimeout = DEFAULT_DESIRED_CONN_TIMEOUT;
  req.intervalMin = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
  req.intervalMax = DEFAULT_DESIRED_MAX_CONN_INTERVAL;

  connIndex = SimplePeripheral_getConnIndex(connHandle);
  SIMPLEPERIPHERAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);

  // Deconstruct the clock object
  Clock_destruct(connList[connIndex].pUpdateClock);
  // Free clock struct
  ICall_free(connList[connIndex].pUpdateClock);
  connList[connIndex].pUpdateClock = NULL;

  // Send parameter update
  bStatus_t status = GAP_UpdateLinkParamReq(&req);

  // If there is an ongoing update, queue this for when the udpate completes
  if (status == bleAlreadyInRequestedMode)
  {
    spConnHandleEntry_t *connHandleEntry = ICall_malloc(sizeof(spConnHandleEntry_t));
    if (connHandleEntry)
    {
      connHandleEntry->connHandle = connHandle;

      List_put(&paramUpdateList, (List_Elem *)&connHandleEntry);
    }
  }
}

/*********************************************************************
 * @fn      SimpleCentral_processCmdCompleteEvt
 *
 * @brief   Process an incoming OSAL HCI Command Complete Event.
 *
 * @param   pMsg - message to process
 *
 * @return  none
 */
static void SimplePeripheral_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg)
{
  uint8_t status = pMsg->pReturnParam[0];

  //Find which command this command complete is for
  switch (pMsg->cmdOpcode)
  {
    case HCI_READ_RSSI:
    {
      // Display RSSI value, if RSSI is higher than threshold, change to faster PHY
      if (status == SUCCESS)
      {
        uint16_t handle = BUILD_UINT16(pMsg->pReturnParam[1], pMsg->pReturnParam[2]);

        uint8_t index = SimplePeripheral_getConnIndex(handle);
        SIMPLEPERIPHERAL_ASSERT(index < MAX_NUM_BLE_CONNS);

        connList[index].rssiArr[connList[index].rssiCntr++] =
                                                  (int8_t)pMsg->pReturnParam[3];
        connList[index].rssiCntr %= SP_MAX_RSSI_STORE_DEPTH;

        int16_t sum_rssi = 0;
        for(uint8_t cnt=0; cnt<SP_MAX_RSSI_STORE_DEPTH; cnt++)
        {
          sum_rssi += connList[index].rssiArr[cnt];
        }
        connList[index].rssiAvg = (uint32_t)(sum_rssi/SP_MAX_RSSI_STORE_DEPTH);

        Display_printf(dispHandle, SP_ROW_RSSI, 0,
                       "RSSI:-%d, AVG RSSI:-%d",
                       (uint32_t)(-(int8_t)pMsg->pReturnParam[3]),
                       (uint32_t)(-sum_rssi/SP_MAX_RSSI_STORE_DEPTH));

        uint8_t phyRq = SP_PHY_NONE;
        uint8_t phyRqS = SP_PHY_NONE;
        uint8_t phyOpt = LL_PHY_OPT_NONE;

        if(connList[index].phyCngRq == FALSE)
        {
          if((connList[index].rssiAvg >= RSSI_2M_THRSHLD) &&
             (connList[index].currPhy != HCI_PHY_2_MBPS) &&
             (connList[index].currPhy != SP_PHY_NONE))
          {
            // try to go to higher data rate
            phyRqS = phyRq = HCI_PHY_2_MBPS;
          }
          else if((connList[index].rssiAvg < RSSI_2M_THRSHLD) &&
                  (connList[index].rssiAvg >= RSSI_1M_THRSHLD) &&
                  (connList[index].currPhy != HCI_PHY_1_MBPS) &&
                  (connList[index].currPhy != SP_PHY_NONE))
          {
            // try to go to legacy regular data rate
            phyRqS = phyRq = HCI_PHY_1_MBPS;
          }
          else if((connList[index].rssiAvg >= RSSI_S2_THRSHLD) &&
                  (connList[index].rssiAvg < RSSI_1M_THRSHLD) &&
                  (connList[index].currPhy != SP_PHY_NONE))
          {
            // try to go to lower data rate S=2(500kb/s)
            phyRqS = HCI_PHY_CODED;
            phyOpt = LL_PHY_OPT_S2;
            phyRq = BLE5_CODED_S2_PHY;
          }
          else if(connList[index].rssiAvg < RSSI_S2_THRSHLD )
          {
            // try to go to lowest data rate S=8(125kb/s)
            phyRqS = HCI_PHY_CODED;
            phyOpt = LL_PHY_OPT_S8;
            phyRq = BLE5_CODED_S8_PHY;
          }
          if((phyRq != SP_PHY_NONE) &&
             // First check if the request for this phy change is already not honored then don't request for change
             (((connList[index].rqPhy == phyRq) &&
               (connList[index].phyRqFailCnt < 2)) ||
              (connList[index].rqPhy != phyRq)))
          {
            //Initiate PHY change based on RSSI
            SimplePeripheral_setPhy(connList[index].connHandle, 0,
                                    phyRqS, phyRqS, phyOpt);
            connList[index].phyCngRq = TRUE;

            // If it a request for different phy than failed request, reset the count
            if(connList[index].rqPhy != phyRq)
            {
              // then reset the request phy counter and requested phy
              connList[index].phyRqFailCnt = 0;
            }

            if(phyOpt == LL_PHY_OPT_NONE)
            {
              connList[index].rqPhy = phyRq;
            }
            else if(phyOpt == LL_PHY_OPT_S2)
            {
              connList[index].rqPhy = BLE5_CODED_S2_PHY;
            }
            else
            {
              connList[index].rqPhy = BLE5_CODED_S8_PHY;
            }

          }
        } // end of if(connList[index].phyCngRq == FALSE)
      } // end of if (status == SUCCESS)
      break;
    }

    case HCI_LE_READ_PHY:
    {
      if (status == SUCCESS)
      {
        Display_printf(dispHandle, SP_ROW_RSSI + 2, 0, "RXPh: %d, TXPh: %d",
                       pMsg->pReturnParam[3], pMsg->pReturnParam[4]);
      }
      break;
    }

    case HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS:
    {
      uint8_t* pRpaNew = &(pMsg->pReturnParam[1]);

      if (memcmp(pRpaNew, rpa, B_ADDR_LEN))
      {
        // If the RPA has changed, update the display
        Display_printf(dispHandle, SP_ROW_RPA, 0, "RP Addr: %s",
                       Util_convertBdAddr2Str(pRpaNew));
        memcpy(rpa, pRpaNew, B_ADDR_LEN);
      }
      break;
    }

    case HCI_RESET:
    {
        Status_t status = SUCCESS;
        //Initialize GAP layer for Peripheral role and register to receive GAP events
        status = GAP_DeviceInit(GAP_PROFILE_PERIPHERAL, selfEntity, addrMode, &pRandomAddress);

        if (status != SUCCESS)
        {
            /* ohoh, very bad :( */
        }
    }
    break;

    default:
      break;
  } // end of switch (pMsg->cmdOpcode)
}

/*********************************************************************
* @fn      SimplePeripheral_initPHYRSSIArray
*
* @brief   Initializes the array of structure/s to store data related
*          RSSI based auto PHy change
*
* @param   connHandle - the connection handle
*
* @param   addr - pointer to device address
*
* @return  index of connection handle
*/
static void SimplePeripheral_initPHYRSSIArray(void)
{
  //Initialize array to store connection handle and RSSI values
  memset(connList, 0, sizeof(connList));
  for (uint8_t index = 0; index < MAX_NUM_BLE_CONNS; index++)
  {
    connList[index].connHandle = SP_INVALID_HANDLE;
  }
}
/*********************************************************************
      // Set default PHY to 1M
 * @fn      SimplePeripheral_startAutoPhyChange
 *
 * @brief   Start periodic RSSI reads on a link.
 *
 * @param   connHandle - connection handle of link
 * @param   devAddr - device address
 *
 * @return  SUCCESS: Terminate started
 *          bleIncorrectMode: No link
 *          bleNoResources: No resources
 */
static status_t SimplePeripheral_startAutoPhyChange(uint16_t connHandle)
{
  status_t status = FAILURE;

  // Get connection index from handle
  uint8_t connIndex = SimplePeripheral_getConnIndex(connHandle);
  SIMPLEPERIPHERAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);

  // Start Connection Event notice for RSSI calculation
  Gap_RegisterConnEventCb(SimplePeripheral_connEvtCB, GAP_CB_REGISTER, connHandle);

  // Flag in connection info if successful
  if (status == SUCCESS)
  {
    connList[connIndex].isAutoPHYEnable = TRUE;
  }

  return status;
}

/*********************************************************************
 * @fn      SimplePeripheral_stopAutoPhyChange
 *
 * @brief   Cancel periodic RSSI reads on a link.
 *
 * @param   connHandle - connection handle of link
 *
 * @return  SUCCESS: Operation successful
 *          bleIncorrectMode: No link
 */
static status_t SimplePeripheral_stopAutoPhyChange(uint16_t connHandle)
{
  // Get connection index from handle
  uint8_t connIndex = SimplePeripheral_getConnIndex(connHandle);
  SIMPLEPERIPHERAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);

  // Stop connection event notice
  Gap_RegisterConnEventCb(NULL, GAP_CB_UNREGISTER, connHandle);

  // Also update the phychange request status for active RSSI tracking connection
  connList[connIndex].phyCngRq = FALSE;
  connList[connIndex].isAutoPHYEnable = FALSE;

  return SUCCESS;
}

/*********************************************************************
 * @fn      SimplePeripheral_setPhy
 *
 * @brief   Call the HCI set phy API and and add the handle to a
 *          list to match it to an incoming command status event
 */
static status_t SimplePeripheral_setPhy(uint16_t connHandle, uint8_t allPhys,
                                        uint8_t txPhy, uint8_t rxPhy,
                                        uint16_t phyOpts)
{
  // Allocate list entry to store handle for command status
  spConnHandleEntry_t *connHandleEntry = ICall_malloc(sizeof(spConnHandleEntry_t));

  if (connHandleEntry)
  {
    connHandleEntry->connHandle = connHandle;

    // Add entry to the phy command status list
    List_put(&setPhyCommStatList, (List_Elem *)connHandleEntry);

    // Send PHY Update
    HCI_LE_SetPhyCmd(connHandle, allPhys, txPhy, rxPhy, phyOpts);
  }

  return SUCCESS;
}

/*********************************************************************
* @fn      SimplePeripheral_updatePHYStat
*
* @brief   Update the auto phy update state machine
*
* @param   connHandle - the connection handle
*
* @return  None
*/
static void SimplePeripheral_updatePHYStat(uint16_t eventCode, uint8_t *pMsg)
{
  uint8_t connIndex;

  switch (eventCode)
  {
    case HCI_LE_SET_PHY:
    {
      // Get connection handle from list
      spConnHandleEntry_t *connHandleEntry =
                           (spConnHandleEntry_t *)List_get(&setPhyCommStatList);

      if (connHandleEntry)
      {
        // Get index from connection handle
        connIndex = SimplePeripheral_getConnIndex(connHandleEntry->connHandle);

        ICall_free(connHandleEntry);

        // Is this connection still valid?
        if (connIndex < MAX_NUM_BLE_CONNS)
        {
          hciEvt_CommandStatus_t *pMyMsg = (hciEvt_CommandStatus_t *)pMsg;

          if (pMyMsg->cmdStatus == HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE)
          {
            // Update the phychange request status for active RSSI tracking connection
            connList[connIndex].phyCngRq = FALSE;
            connList[connIndex].phyRqFailCnt++;
          }
        }
      }
      break;
    }

    // LE Event - a Phy update has completed or failed
    case HCI_BLE_PHY_UPDATE_COMPLETE_EVENT:
    {
      hciEvt_BLEPhyUpdateComplete_t *pPUC =
                                     (hciEvt_BLEPhyUpdateComplete_t*) pMsg;

      if(pPUC)
      {
        // Get index from connection handle
        connIndex = SimplePeripheral_getConnIndex(pPUC->connHandle);

        // Is this connection still valid?
        if (connIndex < MAX_NUM_BLE_CONNS)
        {
          // Update the phychange request status for active RSSI tracking connection
          connList[connIndex].phyCngRq = FALSE;

          if (pPUC->status == SUCCESS)
          {
            connList[connIndex].currPhy = pPUC->rxPhy;
          }
          if(pPUC->rxPhy != connList[connIndex].rqPhy)
          {
            connList[connIndex].phyRqFailCnt++;
          }
          else
          {
            // Reset the request phy counter and requested phy
            connList[connIndex].phyRqFailCnt = 0;
            connList[connIndex].rqPhy = 0;
          }
        }
      }

      break;
    }

    default:
      break;
  } // end of switch (eventCode)
}

/*********************************************************************
 * @fn      SimplePeripheral_menuSwitchCb
 *
 * @brief   Detect menu context switching
 *
 * @param   pMenuObjCurr - the current menu object
 * @param   pMenuObjNext - the menu object the context is about to switch to
 *
 * @return  none
 */
static void SimplePeripheral_menuSwitchCb(tbmMenuObj_t* pMenuObjCurr,
                                       tbmMenuObj_t* pMenuObjNext)
{
  uint8_t NUMB_ACTIVE_CONNS = linkDB_NumActive();

  // interested in only the events of
  // entering scMenuConnect, spMenuSelectConn, and scMenuMain for now
  if (pMenuObjNext == &spMenuSelectConn)
  {
    static uint8_t* pAddrs;
    uint8_t* pAddrTemp;

    if (pAddrs != NULL)
    {
      ICall_free(pAddrs);
    }

    // Allocate buffer to display addresses
    pAddrs = ICall_malloc(NUMB_ACTIVE_CONNS * SP_ADDR_STR_SIZE);

    if (pAddrs == NULL)
    {
      TBM_SET_NUM_ITEM(&spMenuSelectConn, 0);
    }
    else
    {
      uint8_t i;

      TBM_SET_NUM_ITEM(&spMenuSelectConn, MAX_NUM_BLE_CONNS);

      pAddrTemp = pAddrs;

      // Add active connection info to the menu object
      for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
      {
        if (connList[i].connHandle != LINKDB_CONNHANDLE_INVALID)
        {
          // Get the address from the connection handle
          linkDBInfo_t linkInfo;
          linkDB_GetInfo(connList[i].connHandle, &linkInfo);
          // This connection is active. Set the corresponding menu item with
          // the address of this connection and enable the item.
          memcpy(pAddrTemp, Util_convertBdAddr2Str(linkInfo.addr),
                 SP_ADDR_STR_SIZE);
          TBM_SET_ACTION_DESC(&spMenuSelectConn, i, pAddrTemp);
          tbm_setItemStatus(&spMenuSelectConn, (1 << i), SP_ITEM_NONE);
          pAddrTemp += SP_ADDR_STR_SIZE;
        }
        else
        {
          // This connection is not active. Disable the corresponding menu item.
          tbm_setItemStatus(&spMenuSelectConn, SP_ITEM_NONE, (1 << i));
        }
      }
    }
  }
  else if (pMenuObjNext == &spMenuMain)
  {
    // Now we are not in a specific connection's context

    // Clear connection-related message
    Display_clearLine(dispHandle, SP_ROW_CONNECTION);
  }
}

/*******************************************************************************
 * @fn          ClockEventHandler
 *
 * @brief       Gets calles by clock after timeout - icall reinit test helper
 *
 * input parameters
 *
 * @param       arg - create time user argument
 *
 * output parameters
 *
 * @param       None.
 *
 * @return      None.
 */
static void ClockEventHandler(UArg arg)
{
    SimplePeripheral_enqueueMsg(SP_TEST_EVT, NULL);
}
/*********************************************************************
*********************************************************************/

  • Also, I could not find any convincing docu on this topic, besides the 2 forum posts, metioned above.

    Any resource hint would be very appreaciated!

  • Hi Gabiel,

    Does waiting a bit (10-100 mS?) makes a difference in the behavior of GAP_DeviceInit?

    Regards,

    Arthur

  • Hi Arthur,

    thanks for your answer! I'll give this one a try...

  • Hi Arthur,

    I have waited up to 1 seconds, using Task_sleep:

        case HCI_RESET:
        {
            Status_t status = SUCCESS;
    
            /* sleep 100 ms, as suggested in https://e2e.ti.com/support/wireless-connectivity/bluetooth-group/bluetooth/f/bluetooth-forum/1137466/cc1352r-reset-ble-stack-does-not-work/ */
            Task_sleep(1000000u / Clock_tickPeriod);
    
            //Initialize GAP layer for Peripheral role and register to receive GAP events
            status = GAP_DeviceInit(GAP_PROFILE_PERIPHERAL, selfEntity, addrMode, &pRandomAddress);
    
            if (status != SUCCESS)
            {
                /* ohoh, very bad :( */
            }
        }

    It behaves just the same, unfortunately

    Kind regards

    Gabriel

  • btw. you can also try it on current 6.20 SDK - it is just the exact same.

    Just copy the code below into simple_peripheral_oad_offchip.c...

    /******************************************************************************
    
     @file  simple_peripheral_oad_offchip.c
    
     @brief This file contains the Simple Peripheral sample application for use
            with the CC2650 Bluetooth Low Energy Protocol Stack.
    
     Group: WCS, BTS
     Target Device: cc13xx_cc26xx
    
     ******************************************************************************
     
     Copyright (c) 2013-2022, Texas Instruments Incorporated
     All rights reserved.
    
     Redistribution and use in source and binary forms, with or without
     modification, are permitted provided that the following conditions
     are met:
    
     *  Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
    
     *  Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
    
     *  Neither the name of Texas Instruments Incorporated nor the names of
        its contributors may be used to endorse or promote products derived
        from this software without specific prior written permission.
    
     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    
     ******************************************************************************
     
     
     *****************************************************************************/
    
    /*********************************************************************
     * INCLUDES
     */
    #include <string.h>
    
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Clock.h>
    #include <ti/sysbios/knl/Event.h>
    #include <ti/sysbios/knl/Queue.h>
    
    #include <ti/display/Display.h>
    
    #if (!(defined __TI_COMPILER_VERSION__) && !(defined __GNUC__))
    #include <intrinsics.h>
    #endif
    
    #include <ti/drivers/utils/List.h>
    
    #include <icall.h>
    #include "util.h"
    #include "onboard.h"
    #include <bcomdef.h>
    /* This Header file contains all BLE API and icall structure definition */
    #include <icall_ble_api.h>
    
    #include <devinfoservice.h>
    #include <simple_gatt_profile.h>
    
    #ifdef USE_RCOSC
    #include <rcosc_calibration.h>
    #endif //USE_RCOSC
    
    #include <ti_drivers_config.h>
    #include <board_key.h>
    
    #include <menu/two_btn_menu.h>
    
    #include "ti_ble_config.h"
    #ifdef LED_DEBUG
    #include <ti/drivers/GPIO.h>
    #endif //LED_DEBUG
    
    #include "simple_peripheral_oad_offchip_menu.h"
    #include "simple_peripheral_oad_offchip.h"
    
    // Used for imgHdr_t structure
    #include <common/cc26xx/oad/oad_image_header.h>
    #include "oad.h"
    
    /*********************************************************************
     * MACROS
     */
    
    /*********************************************************************
     * CONSTANTS
     */
    // How often to perform periodic event (in ms)
    #define SP_PERIODIC_EVT_PERIOD               5000
    
    // Offset into the scanRspData string the software version info is stored
    #define OAD_SOFT_VER_OFFSET                   15
    
    // Task configuration
    #define SP_TASK_PRIORITY                     1
    
    #ifndef SP_TASK_STACK_SIZE
    #define SP_TASK_STACK_SIZE                   1408
    #endif
    
    // Application events
    #define SP_STATE_CHANGE_EVT                  0
    #define SP_CHAR_CHANGE_EVT                   1
    #define SP_KEY_CHANGE_EVT                    2
    #define SP_ADV_EVT                           3
    #define SP_PAIR_STATE_EVT                    4
    #define SP_PASSCODE_EVT                      5
    #define SP_PERIODIC_EVT                      6
    #define SP_READ_RPA_EVT                      7
    #define SP_SEND_PARAM_UPDATE_EVT             8
    #define SP_CONN_EVT                          9
    #define SP_TEST_EVT                          10
    
    #define SP_OAD_QUEUE_EVT                     OAD_QUEUE_EVT       // Event_Id_01
    #define SP_OAD_COMPLETE_EVT                  OAD_DL_COMPLETE_EVT // Event_Id_02
    #define SP_OAD_NO_MEM_EVT                    OAD_OUT_OF_MEM_EVT  // Event_Id_03
    
    // Internal Events for RTOS application
    #define SP_ICALL_EVT                         ICALL_MSG_EVENT_ID  // Event_Id_31
    #define SP_QUEUE_EVT                         UTIL_QUEUE_EVENT_ID // Event_Id_30
    
    // Bitwise OR of all RTOS events to pend on
    #define SP_ALL_EVENTS                        (SP_ICALL_EVT             | \
                                                  SP_QUEUE_EVT             | \
                                                  SP_OAD_QUEUE_EVT         | \
                                                  SP_OAD_COMPLETE_EVT      | \
                                                  SP_OAD_NO_MEM_EVT)
    
    // Size of string-converted device address ("0xXXXXXXXXXXXX")
    #define SP_ADDR_STR_SIZE     15
    
    // Row numbers for two-button menu
    #define SP_ROW_SEPARATOR_1   (TBM_ROW_APP + 0)
    #define SP_ROW_STATUS_1      (TBM_ROW_APP + 1)
    #define SP_ROW_STATUS_2      (TBM_ROW_APP + 2)
    #define SP_ROW_CONNECTION    (TBM_ROW_APP + 3)
    #define SP_ROW_ADVSTATE      (TBM_ROW_APP + 4)
    #define SP_ROW_RSSI          (TBM_ROW_APP + 5)
    #define SP_ROW_IDA           (TBM_ROW_APP + 6)
    #define SP_ROW_RPA           (TBM_ROW_APP + 7)
    #define SP_ROW_DEBUG         (TBM_ROW_APP + 8)
    
    // For storing the active connections
    #define SP_RSSI_TRACK_CHNLS        1            // Max possible channels can be GAP_BONDINGS_MAX
    #define SP_MAX_RSSI_STORE_DEPTH    5
    #define SP_INVALID_HANDLE          0xFFFF
    #define RSSI_2M_THRSHLD           -30           // -80 dB rssi
    #define RSSI_1M_THRSHLD           -40           // -90 dB rssi
    #define RSSI_S2_THRSHLD           -50           // -100 dB rssi
    #define RSSI_S8_THRSHLD           -60           // -120 dB rssi
    #define SP_PHY_NONE                LL_PHY_NONE  // No PHY set
    #define AUTO_PHY_UPDATE            0xFF
    
    // Spin if the expression is not true
    #define SIMPLEPERIPHERAL_ASSERT(expr) if (!(expr)) simple_peripheral_spin();
    /*********************************************************************
     * TYPEDEFS
     */
    
    // App event passed from stack modules. This type is defined by the application
    // since it can queue events to itself however it wants.
    typedef struct
    {
      uint8_t event;                // event type
      void    *pData;               // pointer to message
    } spEvt_t;
    
    // Container to store passcode data when passing from gapbondmgr callback
    // to app event. See the pfnPairStateCB_t documentation from the gapbondmgr.h
    // header file for more information on each parameter.
    typedef struct
    {
      uint8_t state;
      uint16_t connHandle;
      uint8_t status;
    } spPairStateData_t;
    
    // Container to store passcode data when passing from gapbondmgr callback
    // to app event. See the pfnPasscodeCB_t documentation from the gapbondmgr.h
    // header file for more information on each parameter.
    typedef struct
    {
      uint8_t deviceAddr[B_ADDR_LEN];
      uint16_t connHandle;
      uint8_t uiInputs;
      uint8_t uiOutputs;
      uint32_t numComparison;
    } spPasscodeData_t;
    
    // Container to store advertising event data when passing from advertising
    // callback to app event. See the respective event in GapAdvScan_Event_IDs
    // in gap_advertiser.h for the type that pBuf should be cast to.
    typedef struct
    {
      uint32_t event;
      void *pBuf;
    } spGapAdvEventData_t;
    
    // Container to store information from clock expiration using a flexible array
    // since data is not always needed
    typedef struct
    {
      uint8_t event;                //
      uint8_t data[];
    } spClockEventData_t;
    
    // List element for parameter update and PHY command status lists
    typedef struct
    {
      List_Elem elem;
      uint16_t  connHandle;
    } spConnHandleEntry_t;
    
    // Connected device information
    typedef struct
    {
      uint16_t         connHandle;                        // Connection Handle
      Clock_Struct*    pUpdateClock;                      // pointer to clock struct
      int8_t           rssiArr[SP_MAX_RSSI_STORE_DEPTH];
      uint8_t          rssiCntr;
      int8_t           rssiAvg;
      bool             phyCngRq;                          // Set to true if PHY change request is in progress
      uint8_t          currPhy;
      uint8_t          rqPhy;
      uint8_t          phyRqFailCnt;                      // PHY change request count
      bool             isAutoPHYEnable;                   // Flag to indicate auto phy change
    } spConnRec_t;
    
    /*********************************************************************
     * GLOBAL VARIABLES
     */
    
    // Display Interface
    Display_Handle dispHandle = NULL;
    extern const imgHdr_t _imgHdr;
    
    // Task configuration
    Task_Struct spTask;
    #if defined __TI_COMPILER_VERSION__
    #pragma DATA_ALIGN(spTaskStack, 8)
    #elif defined(__GNUC__) || defined(__clang__)
    __attribute__ ((aligned (8)))
    #else
    #pragma data_alignment=8
    #endif
    uint8_t spTaskStack[SP_TASK_STACK_SIZE];
    
    /*********************************************************************
     * LOCAL VARIABLES
     */
    
    // Entity ID globally used to check for source and/or destination of messages
    static ICall_EntityID selfEntity;
    
    // Event globally used to post local events and pend on system and
    // local events.
    static ICall_SyncHandle syncEvent;
    
    // Queue object used for app messages
    static Queue_Struct appMsgQueue;
    static Queue_Handle appMsgQueueHandle;
    
    // Clock instance for internal periodic events. Only one is needed since
    // GattServApp will handle notifying all connected GATT clients
    static Clock_Struct clkPeriodic;
    // Clock instance for RPA read events.
    static Clock_Struct clkRpaRead;
    
    // Memory to pass periodic event ID to clock handler
    spClockEventData_t argPeriodic =
    { .event = SP_PERIODIC_EVT };
    
    // Memory to pass RPA read event ID to clock handler
    spClockEventData_t argRpaRead =
    { .event = SP_READ_RPA_EVT };
    
    // Per-handle connection info
    static spConnRec_t connList[MAX_NUM_BLE_CONNS];
    
    // Current connection handle as chosen by menu
    static uint16_t menuConnHandle = LINKDB_CONNHANDLE_INVALID;
    
    // List to store connection handles for set phy command status's
    static List_List setPhyCommStatList;
    
    // List to store connection handles for queued param updates
    static List_List paramUpdateList;
    
    // Variable used to store the number of messages pending once OAD completes
    // The application cannot reboot until all pending messages are sent
    static uint8_t numPendingMsgs = 0;
    static bool oadWaitReboot = false;
    
    // Flag to be stored in NV that tracks whether service changed
    // indications needs to be sent out
    static uint32_t  sendSvcChngdOnNextBoot = FALSE;
    
    // Advertising handles
    static uint8 advHandleLegacy;
    static uint8 advHandleLongRange;
    
    // Address mode
    static GAP_Addr_Modes_t addrMode = DEFAULT_ADDRESS_MODE;
    
    // Current Random Private Address
    static uint8 rpa[B_ADDR_LEN] = {0};
    
    /** clock module instance data structure */
    static Clock_Struct clockStruct;
    
    /** clock module instance handle */
    static Clock_Handle clockHandle;
    
    /** clock module instance parameters */
    static Clock_Params clockParams;
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    
    static void SimplePeripheral_init( void );
    static void SimplePeripheral_taskFxn(UArg a0, UArg a1);
    
    static uint8_t SimplePeripheral_processStackMsg(ICall_Hdr *pMsg);
    static uint8_t SimplePeripheral_processGATTMsg(gattMsgEvent_t *pMsg);
    static void SimplePeripheral_processGapMessage(gapEventHdr_t *pMsg);
    static void SimplePeripheral_advCallback(uint32_t event, void *pBuf, uintptr_t arg);
    static void SimplePeripheral_processAdvEvent(spGapAdvEventData_t *pEventData);
    static void SimplePeripheral_processAppMsg(spEvt_t *pMsg);
    static void SimplePeripheral_processCharValueChangeEvt(uint8_t paramId);
    static void SimplePeripheral_performPeriodicTask(void);
    static void SimplePeripheral_updateRPA(void);
    static void SimplePeripheral_clockHandler(UArg arg);
    static void SimplePeripheral_passcodeCb(uint8_t *pDeviceAddr, uint16_t connHandle,
                                            uint8_t uiInputs, uint8_t uiOutputs,
                                            uint32_t numComparison);
    static void SimplePeripheral_pairStateCb(uint16_t connHandle, uint8_t state,
                                             uint8_t status);
    static void SimplePeripheral_processPairState(spPairStateData_t *pPairState);
    static void SimplePeripheral_processPasscode(spPasscodeData_t *pPasscodeData);
    static void SimplePeripheral_charValueChangeCB(uint8_t paramId);
    static status_t SimplePeripheral_enqueueMsg(uint8_t event, void *pData);
    static void SimplePeripheral_processOadWriteCB(uint8_t event, uint16_t arg);
    static void SimplePeripheral_keyChangeHandler(uint8 keys);
    static void SimplePeripheral_handleKeys(uint8_t keys);
    static void SimplePeripheral_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg);
    static void SimplePeripheral_initPHYRSSIArray(void);
    static void SimplePeripheral_updatePHYStat(uint16_t eventCode, uint8_t *pMsg);
    static uint8_t SimplePeripheral_addConn(uint16_t connHandle);
    static uint8_t SimplePeripheral_getConnIndex(uint16_t connHandle);
    static uint8_t SimplePeripheral_removeConn(uint16_t connHandle);
    static void SimplePeripheral_processParamUpdate(uint16_t connHandle);
    static uint8_t SimplePeripheral_processL2CAPMsg(l2capSignalEvent_t *pMsg);
    static status_t SimplePeripheral_startAutoPhyChange(uint16_t connHandle);
    static status_t SimplePeripheral_stopAutoPhyChange(uint16_t connHandle);
    static status_t SimplePeripheral_setPhy(uint16_t connHandle, uint8_t allPhys,
                                            uint8_t txPhy, uint8_t rxPhy,
                                            uint16_t phyOpts);
    static uint8_t SimplePeripheral_clearConnListEntry(uint16_t connHandle);
    static void SimplePeripheral_menuSwitchCb(tbmMenuObj_t* pMenuObjCurr,
                                              tbmMenuObj_t* pMenuObjNext);
    static void SimplePeripheral_connEvtCB(Gap_ConnEventRpt_t *pReport);
    static void SimplePeripheral_processConnEvt(Gap_ConnEventRpt_t *pReport);
    static void ClockEventHandler(UArg arg);
    
    /*********************************************************************
     * EXTERN FUNCTIONS
     */
    extern void AssertHandler(uint8 assertCause, uint8 assertSubcause);
    
    /*********************************************************************
     * PROFILE CALLBACKS
     */
    
    // GAP Bond Manager Callbacks
    static gapBondCBs_t SimplePeripheral_BondMgrCBs =
    {
      SimplePeripheral_passcodeCb,       // Passcode callback
      SimplePeripheral_pairStateCb       // Pairing/Bonding state Callback
    };
    
    // Simple GATT Profile Callbacks
    static simpleProfileCBs_t SimplePeripheral_simpleProfileCBs =
    {
      SimplePeripheral_charValueChangeCB // Simple GATT Characteristic value change callback
    };
    
    static oadTargetCBs_t SimplePeripheral_oadCBs =
    {
      SimplePeripheral_processOadWriteCB // Write Callback.
    };
    
    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    
    /*********************************************************************
     * @fn      simple_peripheral_spin
     *
     * @brief   Spin forever
     *
     * @param   none
     */
    static void simple_peripheral_spin(void)
    {
      volatile uint8_t x = 0;
    
      while(1)
      {
        x++;
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_createTask
     *
     * @brief   Task creation function for the Simple Peripheral OAD User App.
     */
    void SimplePeripheral_createTask(void)
    {
      Task_Params taskParams;
    
      // Configure task
      Task_Params_init(&taskParams);
      taskParams.stack = spTaskStack;
      taskParams.stackSize = SP_TASK_STACK_SIZE;
      taskParams.priority = SP_TASK_PRIORITY;
    
      Task_construct(&spTask, SimplePeripheral_taskFxn, &taskParams, NULL);
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_init
     *
     * @brief   Called during initialization and contains application
     *          specific initialization (ie. hardware initialization/setup,
     *          table initialization, power up notification, etc), and
     *          profile initialization/setup.
     */
    static void SimplePeripheral_init(void)
    {
      // Create the menu
      SimplePeripheral_buildMenu();
      // ******************************************************************
      // N0 STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
      // ******************************************************************
      // Register the current thread as an ICall dispatcher application
      // so that the application can send and receive messages.
      ICall_registerApp(&selfEntity, &syncEvent);
    
      /* initialize clock */
      Clock_Params_init(&clockParams);    /* init clock params for clock to be one-shot and halted */
      clockParams.startFlag = true;
      Clock_construct(&clockStruct,
                      ClockEventHandler,
                      10000000/Clock_tickPeriod, /* 10 seconds */
                      &clockParams);
      clockHandle = Clock_handle(&clockStruct);
    
    #ifdef LED_DEBUG
      /* Configure the LED pin */
      GPIO_setConfig(CONFIG_GPIO_GLED, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
      GPIO_setConfig(CONFIG_GPIO_RLED, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
    
      uint_least8_t activeLed;
      uint8_t blinkCnt = 16;
    
      if (blinkCnt < 12)
      {
        activeLed = CONFIG_GPIO_GLED;
      }
      else
      {
        activeLed = CONFIG_GPIO_RLED;
      }
      for(uint8_t numBlinks = 0; numBlinks < blinkCnt; ++numBlinks)
      {
        GPIO_toggle(activeLed);
        // Sleep for 100ms, sys-tick for BLE-Stack is 10us,
        // Task sleep is in # of ticks
        Task_sleep(10000);
      }
      GPIO_write(activeLed, 0);
    #endif //LED_DEBUG
    
    #ifdef USE_RCOSC
      // Set device's Sleep Clock Accuracy
    #if ( HOST_CONFIG & ( CENTRAL_CFG | PERIPHERAL_CFG ) )
      HCI_EXT_SetSCACmd(500);
    #endif // (CENTRAL_CFG | PERIPHERAL_CFG)
      RCOSC_enableCalibration();
    #endif // USE_RCOSC
    
      // Create an RTOS queue for message from profile to be sent to app.
      appMsgQueueHandle = Util_constructQueue(&appMsgQueue);
    
      // Create one-shot clock for internal periodic events.
      Util_constructClock(&clkPeriodic, SimplePeripheral_clockHandler,
                          SP_PERIODIC_EVT_PERIOD, 0, false, (UArg)&argPeriodic);
    
       // Read in the OAD Software version
      uint8_t swVer[OAD_SW_VER_LEN];
      OAD_getSWVersion(swVer, OAD_SW_VER_LEN);
    
      // Set the Device Name characteristic in the GAP GATT Service
      // For more information, see the section in the User's Guide:
      // http://software-dl.ti.com/lprf/ble5stack-latest/
      GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
    
      // Configure GAP
      {
        uint16_t paramUpdateDecision = DEFAULT_PARAM_UPDATE_REQ_DECISION;
    
        // Pass all parameter update requests to the app for it to decide
        GAP_SetParamValue(GAP_PARAM_LINK_UPDATE_DECISION, paramUpdateDecision);
      }
    
      // Setup the GAP Bond Manager. For more information see the GAP Bond Manager
      // section in the User's Guide
      setBondManagerParameters();
    
      // Initialize GATT attributes
      GGS_AddService(GAP_SERVICE);                 // GAP GATT Service
      GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT Service
      DevInfo_AddService();                        // Device Information Service
      SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile
    
      // Setup the SimpleProfile Characteristic Values
      // For more information, see the GATT and GATTServApp sections in the User's Guide:
      // http://software-dl.ti.com/lprf/ble5stack-latest/
      {
        uint8_t charValue1 = 1;
        uint8_t charValue2 = 2;
        uint8_t charValue3 = 3;
        uint8_t charValue4 = 4;
        uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 };
    
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, sizeof(uint8_t),
                                   &charValue1);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, sizeof(uint8_t),
                                   &charValue2);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, sizeof(uint8_t),
                                   &charValue3);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t),
                                   &charValue4);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN,
                                   charValue5);
      }
    
      // Register callback with SimpleGATTprofile
      SimpleProfile_RegisterAppCBs(&SimplePeripheral_simpleProfileCBs);
    
      // Start Bond Manager and register callback
      VOID GAPBondMgr_Register(&SimplePeripheral_BondMgrCBs);
    
      // Register with GAP for HCI/Host messages. This is needed to receive HCI
      // events. For more information, see the HCI section in the User's Guide:
      // http://software-dl.ti.com/lprf/ble5stack-latest/
      GAP_RegisterForMsgs(selfEntity);
    
      // Register for GATT local events and ATT Responses pending for transmission
      GATT_RegisterForMsgs(selfEntity);
    
      // Set default values for Data Length Extension
      // Extended Data Length Feature is already enabled by default
      {
        // Set initial values to maximum, RX is set to max. by default(251 octets, 2120us)
        // Some brand smartphone is essentially needing 251/2120, so we set them here.
        #define APP_SUGGESTED_PDU_SIZE 251 //default is 27 octets(TX)
        #define APP_SUGGESTED_TX_TIME 2120 //default is 328us(TX)
    
        // This API is documented in hci.h
        // See the LE Data Length Extension section in the BLE5-Stack User's Guide for information on using this command:
        // http://software-dl.ti.com/lprf/ble5stack-latest/
        HCI_LE_WriteSuggestedDefaultDataLenCmd(APP_SUGGESTED_PDU_SIZE, APP_SUGGESTED_TX_TIME);
      }
    
      // Initialize GATT Client
      GATT_InitClient();
    
      // Init key debouncer
      Board_initKeys(SimplePeripheral_keyChangeHandler);
    
      // Initialize Connection List
      SimplePeripheral_clearConnListEntry(LINKDB_CONNHANDLE_ALL);
    
      //Initialize GAP layer for Peripheral role and register to receive GAP events
      GAP_DeviceInit(GAP_PROFILE_PERIPHERAL, selfEntity, addrMode, &pRandomAddress);
    
      // Initialize array to store connection handle and RSSI values
      SimplePeripheral_initPHYRSSIArray();
    
      // The type of display is configured based on the BOARD_DISPLAY_USE...
      // preprocessor definitions
      dispHandle = Display_open(Display_Type_ANY, NULL);
    
      // Initialize Two-Button Menu module
      TBM_SET_TITLE(&spMenuMain, "Simple Peripheral");
      tbm_setItemStatus(&spMenuMain, SP_ITEM_SELECT_OAD_DBG, SP_ITEM_SELECT_CONN);
    
      tbm_initTwoBtnMenu(dispHandle, &spMenuMain, 4, SimplePeripheral_menuSwitchCb);
      Display_printf(dispHandle, SP_ROW_SEPARATOR_1, 0, "====================");
    
    
      uint8_t versionStr[OAD_SW_VER_LEN + 1];
    
      memcpy(versionStr, swVer, OAD_SW_VER_LEN);
    
      // Add in Null terminator
      versionStr[OAD_SW_VER_LEN] = 0;
    
      // Display Image version
      Display_print1(dispHandle, 0, 0, "SBP Off-chip OAD v%s",
                     versionStr);
    
    
      // Open the OAD module and add the OAD service to the application
      if(OAD_SUCCESS != OAD_open(OAD_DEFAULT_INACTIVITY_TIME))
      {
        Display_print0(dispHandle, 0, 0, "OAD failed to open");
      }
      else
      {
        // Resiter the OAD callback with the application
        OAD_register(&SimplePeripheral_oadCBs);
      }
    
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_taskFxn
     *
     * @brief   Application task entry point for the Simple Peripheral.
     *
     * @param   a0, a1 - not used.
     */
    static void SimplePeripheral_taskFxn(UArg a0, UArg a1)
    {
      // Initialize application
      SimplePeripheral_init();
    
      // Application main loop
      for (;;)
      {
        uint32_t events;
    
        // Waits for an event to be posted associated with the calling thread.
        // Note that an event associated with a thread is posted when a
        // message is queued to the message receive queue of the thread
        events = Event_pend(syncEvent, Event_Id_NONE, SP_ALL_EVENTS,
                            ICALL_TIMEOUT_FOREVER);
    
        if (events)
        {
          ICall_EntityID dest;
          ICall_ServiceEnum src;
          ICall_HciExtEvt *pMsg = NULL;
    
          // Fetch any available messages that might have been sent from the stack
          if (ICall_fetchServiceMsg(&src, &dest,
                                    (void **)&pMsg) == ICALL_ERRNO_SUCCESS)
          {
            uint8 safeToDealloc = TRUE;
    
            if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
            {
              ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg;
    
              // Check for BLE stack events first
              if (pEvt->signature != 0xffff)
              {
                // Process inter-task message
                safeToDealloc = SimplePeripheral_processStackMsg((ICall_Hdr *)pMsg);
              }
            }
    
            if (pMsg && safeToDealloc)
            {
              ICall_freeMsg(pMsg);
            }
          }
    
          // If RTOS queue is not empty, process app message.
          if (events & SP_QUEUE_EVT)
          {
            while (!Queue_empty(appMsgQueueHandle))
            {
              spEvt_t *pMsg = (spEvt_t *)Util_dequeueMsg(appMsgQueueHandle);
              if (pMsg)
              {
                // Process message.
                SimplePeripheral_processAppMsg(pMsg);
    
                // Free the space from the message.
                ICall_free(pMsg);
              }
            }
          }
           // OAD events
          if(events & SP_OAD_NO_MEM_EVT)
          {
            // The OAD module is unable to allocate memory, print failure, cancel OAD
            Display_print0(dispHandle, SP_ROW_STATUS_1, 0,
                            "OAD malloc fail, cancelling OAD");
            OAD_cancel();
    #ifdef LED_DEBUG
            // Diplay is not enabled in persist app so use LED
            GPIO_write(CONFIG_GPIO_RLED, 1);
    #endif //LED_DEBUG
          }
          // OAD queue processing
          if(events & SP_OAD_QUEUE_EVT)
          {
            // Process the OAD Message Queue
            uint8_t status = OAD_processQueue();
    
            // If the OAD state machine encountered an error, print it
            // Return codes can be found in oad_constants.h
            if(status == OAD_DL_COMPLETE)
            {
              Display_print0(dispHandle, SP_ROW_STATUS_1, 0, "OAD DL Complete, wait for Enable");
            }
            else if(status == OAD_IMG_ID_TIMEOUT)
            {
              Display_print0(dispHandle, SP_ROW_STATUS_1, 0, "ImgID Timeout, disconnecting");
    
              // This may be an attack, terminate the link,
              // Note HCI_DISCONNECT_REMOTE_USER_TERM seems to most closet reason for
              // termination at this state
              MAP_GAP_TerminateLinkReq(OAD_getactiveCxnHandle(), HCI_DISCONNECT_REMOTE_USER_TERM);
            }
            else if(status != OAD_SUCCESS)
            {
              Display_print1(dispHandle, SP_ROW_STATUS_1, 0, "OAD Error: %d", status);
            }
    
          }
    
          if(events & SP_OAD_COMPLETE_EVT)
          {
            // Register for L2CAP Flow Control Events
            L2CAP_RegisterFlowCtrlTask(selfEntity);
          }
    
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processStackMsg
     *
     * @brief   Process an incoming stack message.
     *
     * @param   pMsg - message to process
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t SimplePeripheral_processStackMsg(ICall_Hdr *pMsg)
    {
      // Always dealloc pMsg unless set otherwise
      uint8_t safeToDealloc = TRUE;
    
      switch (pMsg->event)
      {
        case GAP_MSG_EVENT:
          SimplePeripheral_processGapMessage((gapEventHdr_t*) pMsg);
          break;
    
        case GATT_MSG_EVENT:
          // Process GATT message
          safeToDealloc = SimplePeripheral_processGATTMsg((gattMsgEvent_t *)pMsg);
          break;
    
        case HCI_GAP_EVENT_EVENT:
        {
          // Process HCI message
          switch(pMsg->status)
          {
            case HCI_COMMAND_COMPLETE_EVENT_CODE:
            // Process HCI Command Complete Events here
            {
              SimplePeripheral_processCmdCompleteEvt((hciEvt_CmdComplete_t *) pMsg);
              break;
            }
    
            case HCI_BLE_HARDWARE_ERROR_EVENT_CODE:
              AssertHandler(HAL_ASSERT_CAUSE_HARDWARE_ERROR,0);
              break;
    
            // HCI Commands Events
            case HCI_COMMAND_STATUS_EVENT_CODE:
            {
              hciEvt_CommandStatus_t *pMyMsg = (hciEvt_CommandStatus_t *)pMsg;
              switch ( pMyMsg->cmdOpcode )
              {
                case HCI_LE_SET_PHY:
                {
                  if (pMyMsg->cmdStatus == HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE)
                  {
                    Display_printf(dispHandle, SP_ROW_STATUS_1, 0,
                            "PHY Change failure, peer does not support this");
                  }
                  else
                  {
                    Display_printf(dispHandle, SP_ROW_STATUS_1, 0,
                                   "PHY Update Status Event: 0x%x",
                                   pMyMsg->cmdStatus);
                  }
    
                  SimplePeripheral_updatePHYStat(HCI_LE_SET_PHY, (uint8_t *)pMsg);
                  break;
                }
    
                default:
                  break;
              }
              break;
            }
    
            // LE Events
            case HCI_LE_EVENT_CODE:
            {
              hciEvt_BLEPhyUpdateComplete_t *pPUC =
                (hciEvt_BLEPhyUpdateComplete_t*) pMsg;
    
              // A Phy Update Has Completed or Failed
              if (pPUC->BLEEventCode == HCI_BLE_PHY_UPDATE_COMPLETE_EVENT)
              {
                if (pPUC->status != SUCCESS)
                {
                  Display_printf(dispHandle, SP_ROW_STATUS_1, 0,
                                 "PHY Change failure");
                }
                else
                {
                  // Only symmetrical PHY is supported.
                  // rxPhy should be equal to txPhy.
                  Display_printf(dispHandle, SP_ROW_STATUS_2, 0,
                                 "PHY Updated to %s",
                                 (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_1M) ? "1M" :
                                 (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_2M) ? "2M" :
                                 (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_CODED) ? "CODED" : "Unexpected PHY Value");
                }
    
                SimplePeripheral_updatePHYStat(HCI_BLE_PHY_UPDATE_COMPLETE_EVENT, (uint8_t *)pMsg);
              }
              break;
            }
    
            default:
              break;
          }
    
          break;
        }
    
        case L2CAP_SIGNAL_EVENT:
          // Process L2CAP signal
          safeToDealloc = SimplePeripheral_processL2CAPMsg((l2capSignalEvent_t *)pMsg);
          break;
    
        default:
          // do nothing
          break;
      }
    
      return (safeToDealloc);
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processL2CAPMsg
     *
     * @brief   Process L2CAP messages and events.
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t SimplePeripheral_processL2CAPMsg(l2capSignalEvent_t *pMsg)
    {
      uint8_t safeToDealloc = TRUE;
      static bool firstRun = TRUE;
    
      switch (pMsg->opcode)
      {
        case L2CAP_NUM_CTRL_DATA_PKT_EVT:
        {
          /*
          * We cannot reboot the device immediately after receiving
          * the enable command, we must allow the stack enough time
          * to process and respond to the OAD_EXT_CTRL_ENABLE_IMG
          * command. This command will determine the number of
          * packets currently queued up by the LE controller.
          * BIM var is already set via OadPersistApp_processOadWriteCB
          */
          if(firstRun)
          {
            firstRun = false;
    
            // We only want to set the numPendingMsgs once
            numPendingMsgs = MAX_NUM_PDU - pMsg->cmd.numCtrlDataPktEvt.numDataPkt;
    
            // Wait until all PDU have been sent on cxn events
            Gap_RegisterConnEventCb(SimplePeripheral_connEvtCB,
                                      GAP_CB_REGISTER,
                                      GAP_CB_CONN_EVENT_ALL,
                                      OAD_getactiveCxnHandle());
    
            /* Set the flag so that the connection event callback will
             * be processed in the context of a pending OAD reboot
             */
            oadWaitReboot = true;
          }
    
          break;
        }
    
        default:
          // do nothing
          break;
      }
    
      return (safeToDealloc);
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processGATTMsg
     *
     * @brief   Process GATT messages and events.
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t SimplePeripheral_processGATTMsg(gattMsgEvent_t *pMsg)
    {
      if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
      {
        // ATT request-response or indication-confirmation flow control is
        // violated. All subsequent ATT requests or indications will be dropped.
        // The app is informed in case it wants to drop the connection.
    
        // Display the opcode of the message that caused the violation.
        Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode);
      }
      else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
      {
        // MTU size updated
        OAD_setBlockSize(pMsg->msg.mtuEvt.MTU);
        Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "MTU Size: %d", pMsg->msg.mtuEvt.MTU);
      }
    
      // Free message payload. Needed only for ATT Protocol messages
      GATT_bm_free(&pMsg->msg, pMsg->method);
    
      // It's safe to free the incoming message
      return (TRUE);
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processAppMsg
     *
     * @brief   Process an incoming callback from a profile.
     *
     * @param   pMsg - message to process
     *
     * @return  None.
     */
    static void SimplePeripheral_processAppMsg(spEvt_t *pMsg)
    {
      bool dealloc = TRUE;
    
      switch (pMsg->event)
      {
        case SP_CHAR_CHANGE_EVT:
          SimplePeripheral_processCharValueChangeEvt(*(uint8_t*)(pMsg->pData));
          break;
    
        case SP_KEY_CHANGE_EVT:
          SimplePeripheral_handleKeys(*(uint8_t*)(pMsg->pData));
          break;
    
        case SP_ADV_EVT:
          SimplePeripheral_processAdvEvent((spGapAdvEventData_t*)(pMsg->pData));
          break;
    
        case SP_PAIR_STATE_EVT:
          SimplePeripheral_processPairState((spPairStateData_t*)(pMsg->pData));
          break;
    
        case SP_PASSCODE_EVT:
          SimplePeripheral_processPasscode((spPasscodeData_t*)(pMsg->pData));
          break;
    
        case SP_PERIODIC_EVT:
          SimplePeripheral_performPeriodicTask();
          break;
    
        case SP_READ_RPA_EVT:
          SimplePeripheral_updateRPA();
          break;
    
        case SP_SEND_PARAM_UPDATE_EVT:
        {
          // Extract connection handle from data
          uint16_t connHandle = *(uint16_t *)(((spClockEventData_t *)pMsg->pData)->data);
    
          SimplePeripheral_processParamUpdate(connHandle);
    
          // This data is not dynamically allocated
          dealloc = FALSE;
          break;
        }
    
        case SP_CONN_EVT:
          SimplePeripheral_processConnEvt((Gap_ConnEventRpt_t *)(pMsg->pData));
          break;
    
        case SP_TEST_EVT:
        {
            Status_t status = SUCCESS;
            status = HCI_ResetCmd();
    
            if (status != SUCCESS)
            {
                /* ohoh... very bad... consider a sysReset */
            }
        }
        break;
    
        default:
          // Do nothing.
          break;
      }
    
      // Free message data if it exists and we are to dealloc
      if ((dealloc == TRUE) && (pMsg->pData != NULL))
      {
        ICall_free(pMsg->pData);
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processGapMessage
     *
     * @brief   Process an incoming GAP event.
     *
     * @param   pMsg - message to process
     */
    static void SimplePeripheral_processGapMessage(gapEventHdr_t *pMsg)
    {
      switch(pMsg->opcode)
      {
        case GAP_DEVICE_INIT_DONE_EVENT:
        {
          bStatus_t status = FAILURE;
    
          gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;
    
          if(pPkt->hdr.status == SUCCESS)
          {
            // Store the system ID
            uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];
    
            // use 6 bytes of device address for 8 bytes of system ID value
            systemId[0] = pPkt->devAddr[0];
            systemId[1] = pPkt->devAddr[1];
            systemId[2] = pPkt->devAddr[2];
    
            // set middle bytes to zero
            systemId[4] = 0x00;
            systemId[3] = 0x00;
    
            // shift three bytes up
            systemId[7] = pPkt->devAddr[5];
            systemId[6] = pPkt->devAddr[4];
            systemId[5] = pPkt->devAddr[3];
    
            // Set Device Info Service Parameter
            DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);
    
            Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Initialized");
    
            // Setup and start Advertising
            // For more information, see the GAP section in the User's Guide:
            // http://software-dl.ti.com/lprf/ble5stack-latest/
    
            // Create Advertisement set #1 and assign handle
            status = GapAdv_create(&SimplePeripheral_advCallback, &advParams1,
                                   &advHandleLegacy);
            SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
    
            // Load advertising data for set #1 that is statically allocated by the app
            status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_ADV,
                                         sizeof(advData1), advData1);
            SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
    
            // Load scan response data for set #1 that is statically allocated by the app
            status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_SCAN_RSP,
                                         sizeof(scanResData1), scanResData1);
            SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
    
            // Set event mask for set #1
            status = GapAdv_setEventMask(advHandleLegacy,
                                         GAP_ADV_EVT_MASK_START_AFTER_ENABLE |
                                         GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
                                         GAP_ADV_EVT_MASK_SET_TERMINATED);
    
            // Enable legacy advertising for set #1
            status = GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
    
            // Create Advertisement set #2 and assign handle
            status = GapAdv_create(&SimplePeripheral_advCallback, &advParams2,
                                   &advHandleLongRange);
            SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
    
            // Load advertising data for set #2 that is statically allocated by the app
            status = GapAdv_loadByHandle(advHandleLongRange, GAP_ADV_DATA_TYPE_ADV,
                                         sizeof(advData2), advData2);
            SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
    
            // Set event mask for set #2
            status = GapAdv_setEventMask(advHandleLongRange,
                                         GAP_ADV_EVT_MASK_START_AFTER_ENABLE |
                                         GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
                                         GAP_ADV_EVT_MASK_SET_TERMINATED);
    
            // Enable long range advertising for set #2
            status = GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
    
            // Display device address
            Display_printf(dispHandle, SP_ROW_IDA, 0, "%s Addr: %s",
                           (addrMode <= ADDRMODE_RANDOM) ? "Dev" : "ID",
                           Util_convertBdAddr2Str(pPkt->devAddr));
    
            if (addrMode > ADDRMODE_RANDOM)
            {
              SimplePeripheral_updateRPA();
    
              // Create one-shot clock for RPA check event.
              Util_constructClock(&clkRpaRead, SimplePeripheral_clockHandler,
                                  READ_RPA_PERIOD, 0, true,
                                  (UArg) &argRpaRead);
            }
          }
    
          break;
        }
    
        case GAP_LINK_ESTABLISHED_EVENT:
        {
          gapEstLinkReqEvent_t *pPkt = (gapEstLinkReqEvent_t *)pMsg;
    
          // Display the amount of current connections
          uint8_t numActive = linkDB_NumActive();
          Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Num Conns: %d",
                         (uint16_t)numActive);
    
          if (pPkt->hdr.status == SUCCESS)
          {
            // Add connection to list and start RSSI
            SimplePeripheral_addConn(pPkt->connectionHandle);
    
            // Display the address of this connection
            Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connected to %s",
                           Util_convertBdAddr2Str(pPkt->devAddr));
    
            // Enable connection selection option
            tbm_setItemStatus(&spMenuMain, SP_ITEM_SELECT_CONN, TBM_ITEM_NONE);
    
            // Start Periodic Clock.
            Util_startClock(&clkPeriodic);
          }
    
          if (numActive < MAX_NUM_BLE_CONNS)
          {
            // Start advertising since there is room for more connections
            GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
          }
          else
          {
            // Stop advertising since there is no room for more connections
            GapAdv_disable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            GapAdv_disable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
          }
    
          break;
        }
    
        case GAP_LINK_TERMINATED_EVENT:
        {
          gapTerminateLinkEvent_t *pPkt = (gapTerminateLinkEvent_t *)pMsg;
    
          // Display the amount of current connections
          uint8_t numActive = linkDB_NumActive();
          Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Device Disconnected!");
          Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Num Conns: %d",
                         (uint16_t)numActive);
    
          // Remove the connection from the list and disable RSSI if needed
          SimplePeripheral_removeConn(pPkt->connectionHandle);
    
          // If no active connections
          if (numActive == 0)
          {
            // Stop periodic clock
            Util_stopClock(&clkPeriodic);
    
            // Disable Connection Selection option
            tbm_setItemStatus(&spMenuMain, TBM_ITEM_NONE, SP_ITEM_SELECT_CONN);
          }
    
          // Start advertising since there is room for more connections
          GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
          GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
    
          // Clear remaining lines
          Display_clearLine(dispHandle, SP_ROW_CONNECTION);
    
          // Cancel the OAD if one is going on
          // A disconnect forces the peer to re-identify
          OAD_cancel();
    
          break;
        }
    
        case GAP_UPDATE_LINK_PARAM_REQ_EVENT:
        {
          gapUpdateLinkParamReqReply_t rsp;
          gapUpdateLinkParamReqEvent_t *pReq = (gapUpdateLinkParamReqEvent_t *)pMsg;
    
          rsp.connectionHandle = pReq->req.connectionHandle;
          rsp.signalIdentifier = pReq->req.signalIdentifier;
    
          // Only accept connection intervals with slave latency of 0
          // This is just an example of how the application can send a response
          if(pReq->req.connLatency == 0)
          {
            rsp.intervalMin = pReq->req.intervalMin;
            rsp.intervalMax = pReq->req.intervalMax;
            rsp.connLatency = pReq->req.connLatency;
            rsp.connTimeout = pReq->req.connTimeout;
            rsp.accepted = TRUE;
          }
          else
          {
            rsp.accepted = FALSE;
          }
    
          // Send Reply
          VOID GAP_UpdateLinkParamReqReply(&rsp);
    
          break;
        }
    
        case GAP_LINK_PARAM_UPDATE_EVENT:
        {
          gapLinkUpdateEvent_t *pPkt = (gapLinkUpdateEvent_t *)pMsg;
    
          // Get the address from the connection handle
          linkDBInfo_t linkInfo;
          linkDB_GetInfo(pPkt->connectionHandle, &linkInfo);
    
          if(pPkt->status == SUCCESS)
          {
            // Display the address of the connection update
            Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Link Param Updated: %s",
                           Util_convertBdAddr2Str(linkInfo.addr));
          }
          else
          {
            // Display the address of the connection update failure
            Display_printf(dispHandle, SP_ROW_STATUS_2, 0,
                           "Link Param Update Failed 0x%x: %s", pPkt->opcode,
                           Util_convertBdAddr2Str(linkInfo.addr));
          }
    
          // Check if there are any queued parameter updates
          spConnHandleEntry_t *connHandleEntry = (spConnHandleEntry_t *)List_get(&paramUpdateList);
          if (connHandleEntry != NULL)
          {
            // Attempt to send queued update now
            SimplePeripheral_processParamUpdate(connHandleEntry->connHandle);
    
            // Free list element
            ICall_free(connHandleEntry);
          }
    
          break;
        }
    
    #if defined ( NOTIFY_PARAM_UPDATE_RJCT )
        case GAP_LINK_PARAM_UPDATE_REJECT_EVENT:
        {
          linkDBInfo_t linkInfo;
          gapLinkUpdateEvent_t *pPkt = (gapLinkUpdateEvent_t *)pMsg;
    
          // Get the address from the connection handle
          linkDB_GetInfo(pPkt->connectionHandle, &linkInfo);
    
          // Display the address of the connection update failure
          Display_printf(dispHandle, SP_ROW_STATUS_2, 0,
                         "Peer Device's Update Request Rejected 0x%x: %s", pPkt->opcode,
                         Util_convertBdAddr2Str(linkInfo.addr));
    
          break;
        }
    #endif
        default:
          Display_clearLines(dispHandle, SP_ROW_STATUS_1, SP_ROW_STATUS_2);
          break;
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_charValueChangeCB
     *
     * @brief   Callback from Simple Profile indicating a characteristic
     *          value change.
     *
     * @param   paramId - parameter Id of the value that was changed.
     *
     * @return  None.
     */
    static void SimplePeripheral_charValueChangeCB(uint8_t paramId)
    {
      uint8_t *pValue = ICall_malloc(sizeof(uint8_t));
    
      if (pValue)
      {
        *pValue = paramId;
    
        if (SimplePeripheral_enqueueMsg(SP_CHAR_CHANGE_EVT, pValue) != SUCCESS)
        {
          ICall_free(pValue);
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processOadWriteCB
     *
     * @brief   Process a write request to the OAD reset service
     *
     * @param   connHandle - the connection Handle this request is from.
     * @param   bim_var    - bim_var to set before resetting.
     *
     * @return  None.
     */
    void SimplePeripheral_processOadWriteCB(uint8_t event, uint16_t arg)
    {
      Event_post(syncEvent, event);
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processCharValueChangeEvt
     *
     * @brief   Process a pending Simple Profile characteristic value change
     *          event.
     *
     * @param   paramID - parameter ID of the value that was changed.
     */
    static void SimplePeripheral_processCharValueChangeEvt(uint8_t paramId)
    {
      uint8_t newValue;
    
      switch(paramId)
      {
        case SIMPLEPROFILE_CHAR1:
          SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue);
    
          Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Char 1: %d", (uint16_t)newValue);
          break;
    
        case SIMPLEPROFILE_CHAR3:
          SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &newValue);
    
          Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Char 3: %d", (uint16_t)newValue);
          break;
    
        default:
          // should not reach here!
          break;
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_performPeriodicTask
     *
     * @brief   Perform a periodic application task. This function gets called
     *          every five seconds (SP_PERIODIC_EVT_PERIOD). In this example,
     *          the value of the third characteristic in the SimpleGATTProfile
     *          service is retrieved from the profile, and then copied into the
     *          value of the the fourth characteristic.
     *
     * @param   None.
     *
     * @return  None.
     */
    static void SimplePeripheral_performPeriodicTask(void)
    {
      uint8_t valueToCopy;
    
      // Call to retrieve the value of the third characteristic in the profile
      if (SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &valueToCopy) == SUCCESS)
      {
        // Call to set that value of the fourth characteristic in the profile.
        // Note that if notifications of the fourth characteristic have been
        // enabled by a GATT client device, then a notification will be sent
        // every time this function is called.
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t),
                                   &valueToCopy);
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_updateRPA
     *
     * @brief   Read the current RPA from the stack and update display
     *          if the RPA has changed.
     *
     * @param   None.
     *
     * @return  None.
     */
    static void SimplePeripheral_updateRPA(void)
    {
      // Read the current RPA.
      // The parameters for the call to HCI_LE_ReadLocalResolvableAddressCmd
      // are not needed to be accurate to retrieve the local resolvable address.
      // The 1st parameter can be any of ADDRMODE_PUBLIC and ADDRMODE_RANDOM.
      // The 2nd parameter only has to be not NULL.
      // The result will come with HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS
      // complete event.
      HCI_LE_ReadLocalResolvableAddressCmd(0, rpa);
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_clockHandler
     *
     * @brief   Handler function for clock timeouts.
     *
     * @param   arg - event type
     *
     * @return  None.
     */
    static void SimplePeripheral_clockHandler(UArg arg)
    {
      spClockEventData_t *pData = (spClockEventData_t *)arg;
    
      if (pData->event == SP_PERIODIC_EVT)
      {
        // Start the next period
        Util_startClock(&clkPeriodic);
    
        // Post event to wake up the application
        SimplePeripheral_enqueueMsg(SP_PERIODIC_EVT, NULL);
      }
      else if (pData->event == SP_READ_RPA_EVT)
      {
        // Start the next period
        Util_startClock(&clkRpaRead);
    
        // Post event to read the current RPA
        SimplePeripheral_enqueueMsg(SP_READ_RPA_EVT, NULL);
      }
      else if (pData->event == SP_SEND_PARAM_UPDATE_EVT)
      {
        // Send message to app
        SimplePeripheral_enqueueMsg(SP_SEND_PARAM_UPDATE_EVT, pData);
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_keyChangeHandler
     *
     * @brief   Key event handler function
     *
     * @param   keys - bitmap of pressed keys
     *
     * @return  none
     */
    static void SimplePeripheral_keyChangeHandler(uint8_t keys)
    {
      uint8_t *pValue = ICall_malloc(sizeof(uint8_t));
    
      if (pValue)
      {
        *pValue = keys;
    
        if(SimplePeripheral_enqueueMsg(SP_KEY_CHANGE_EVT, pValue) != SUCCESS)
        {
          ICall_free(pValue);
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_handleKeys
     *
     * @brief   Handles all key events for this device.
     *
     * @param   keys - bit field for key events. Valid entries:
     *                 KEY_LEFT
     *                 KEY_RIGHT
     */
    static void SimplePeripheral_handleKeys(uint8_t keys)
    {
      if (keys & KEY_LEFT)
      {
        // Check if the key is still pressed. Workaround for possible bouncing.
        if (GPIO_read(CONFIG_GPIO_BTN1) == 0)
        {
          tbm_buttonLeft();
        }
      }
      else if (keys & KEY_RIGHT)
      {
        // Check if the key is still pressed. Workaround for possible bouncing.
        if (GPIO_read(CONFIG_GPIO_BTN2) == 0)
        {
          tbm_buttonRight();
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_doSetConnPhy
     *
     * @brief   Set PHY preference.
     *
     * @param   index - 0: 1M PHY
     *                  1: 2M PHY
     *                  2: 1M + 2M PHY
     *                  3: CODED PHY (Long range)
     *                  4: 1M + 2M + CODED PHY
     *
     * @return  always true
     */
    bool SimplePeripheral_doSetConnPhy(uint8 index)
    {
      bool status = TRUE;
    
      static uint8_t phy[] = {
        HCI_PHY_1_MBPS, HCI_PHY_2_MBPS, HCI_PHY_1_MBPS | HCI_PHY_2_MBPS,
        HCI_PHY_CODED, HCI_PHY_1_MBPS | HCI_PHY_2_MBPS | HCI_PHY_CODED,
        AUTO_PHY_UPDATE
      };
    
      uint8_t connIndex = SimplePeripheral_getConnIndex(menuConnHandle);
      SIMPLEPERIPHERAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
    
      // Set Phy Preference on the current connection. Apply the same value
      // for RX and TX.
      // If auto PHY update is not selected and if auto PHY update is enabled, then
      // stop auto PHY update
      // Note PHYs are already enabled by default in build_config.opt in stack project.
      if(phy[index] != AUTO_PHY_UPDATE)
      {
        // Cancel RSSI reading  and auto phy changing
        SimplePeripheral_stopAutoPhyChange(connList[connIndex].connHandle);
    
        SimplePeripheral_setPhy(menuConnHandle, 0, phy[index], phy[index], 0);
    
        Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "PHY preference: %s",
                       TBM_GET_ACTION_DESC(&spMenuConnPhy, index));
      }
      else
      {
        // Start RSSI read for auto PHY update (if it is disabled)
        SimplePeripheral_startAutoPhyChange(menuConnHandle);
      }
    
      return status;
    }
    /*********************************************************************
     * @fn      SimplePeripheral_advCallback
     *
     * @brief   GapAdv module callback
     *
     * @param   pMsg - message to process
     */
    static void SimplePeripheral_advCallback(uint32_t event, void *pBuf, uintptr_t arg)
    {
      spGapAdvEventData_t *pData = ICall_malloc(sizeof(spGapAdvEventData_t));
    
      if (pData)
      {
        pData->event = event;
        pData->pBuf = pBuf;
    
        if(SimplePeripheral_enqueueMsg(SP_ADV_EVT, pData) != SUCCESS)
        {
          ICall_free(pData);
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processAdvEvent
     *
     * @brief   Process advertising event in app context
     *
     * @param   pEventData
     */
    static void SimplePeripheral_processAdvEvent(spGapAdvEventData_t *pEventData)
    {
      switch (pEventData->event)
      {
        case GAP_EVT_ADV_START_AFTER_ENABLE:
          Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d Enabled",
                         *(uint8_t *)(pEventData->pBuf));
          break;
    
        case GAP_EVT_ADV_END_AFTER_DISABLE:
          Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d Disabled",
                         *(uint8_t *)(pEventData->pBuf));
          break;
    
        case GAP_EVT_ADV_START:
          break;
    
        case GAP_EVT_ADV_END:
          break;
    
        case GAP_EVT_ADV_SET_TERMINATED:
        {
    #ifndef Display_DISABLE_ALL
          GapAdv_setTerm_t *advSetTerm = (GapAdv_setTerm_t *)(pEventData->pBuf);
    
          Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d disabled after conn %d",
                         advSetTerm->handle, advSetTerm->connHandle );
    #endif
        }
        break;
    
        case GAP_EVT_SCAN_REQ_RECEIVED:
          break;
    
        case GAP_EVT_INSUFFICIENT_MEMORY:
          break;
    
        default:
          break;
      }
    
      // All events have associated memory to free except the insufficient memory
      // event
      if (pEventData->event != GAP_EVT_INSUFFICIENT_MEMORY)
      {
        ICall_free(pEventData->pBuf);
      }
    }
    
    
    /*********************************************************************
     * @fn      SimplePeripheral_pairStateCb
     *
     * @brief   Pairing state callback.
     *
     * @return  none
     */
    static void SimplePeripheral_pairStateCb(uint16_t connHandle, uint8_t state,
                                             uint8_t status)
    {
      spPairStateData_t *pData = ICall_malloc(sizeof(spPairStateData_t));
    
      // Allocate space for the event data.
      if (pData)
      {
        pData->state = state;
        pData->connHandle = connHandle;
        pData->status = status;
    
        // Queue the event.
        if(SimplePeripheral_enqueueMsg(SP_PAIR_STATE_EVT, pData) != SUCCESS)
        {
          ICall_free(pData);
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_passcodeCb
     *
     * @brief   Passcode callback.
     *
     * @return  none
     */
    static void SimplePeripheral_passcodeCb(uint8_t *pDeviceAddr,
                                            uint16_t connHandle,
                                            uint8_t uiInputs,
                                            uint8_t uiOutputs,
                                            uint32_t numComparison)
    {
      spPasscodeData_t *pData = ICall_malloc(sizeof(spPasscodeData_t));
    
      // Allocate space for the passcode event.
      if (pData )
      {
        pData->connHandle = connHandle;
        memcpy(pData->deviceAddr, pDeviceAddr, B_ADDR_LEN);
        pData->uiInputs = uiInputs;
        pData->uiOutputs = uiOutputs;
        pData->numComparison = numComparison;
    
        // Enqueue the event.
        if(SimplePeripheral_enqueueMsg(SP_PASSCODE_EVT, pData) != SUCCESS)
        {
          ICall_free(pData);
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processPairState
     *
     * @brief   Process the new paring state.
     *
     * @return  none
     */
    static void SimplePeripheral_processPairState(spPairStateData_t *pPairData)
    {
      uint8_t state = pPairData->state;
      uint8_t status = pPairData->status;
    
      switch (state)
      {
        case GAPBOND_PAIRING_STATE_STARTED:
          Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Pairing started");
          break;
    
        case GAPBOND_PAIRING_STATE_COMPLETE:
          if (status == SUCCESS)
          {
            Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Pairing success");
          }
          else
          {
            Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Pairing fail: %d", status);
          }
          break;
    
        case GAPBOND_PAIRING_STATE_ENCRYPTED:
          if (status == SUCCESS)
          {
            Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Encryption success");
          }
          else
          {
            Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Encryption failed: %d", status);
          }
          break;
    
        case GAPBOND_PAIRING_STATE_BOND_SAVED:
          if (status == SUCCESS)
          {
            Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Bond save success");
          }
          else
          {
            Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Bond save failed: %d", status);
          }
          break;
    
        default:
          break;
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processPasscode
     *
     * @brief   Process the Passcode request.
     *
     * @return  none
     */
    static void SimplePeripheral_processPasscode(spPasscodeData_t *pPasscodeData)
    {
      // Display passcode to user
      if (pPasscodeData->uiOutputs != 0)
      {
        Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Passcode: %d",
                       B_APP_DEFAULT_PASSCODE);
      }
    
      // Send passcode response
      GAPBondMgr_PasscodeRsp(pPasscodeData->connHandle , SUCCESS,
                             B_APP_DEFAULT_PASSCODE);
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_connEvtCB
     *
     * @brief   Connection event callback.
     *
     * @param pReport pointer to connection event report
     */
    static void SimplePeripheral_connEvtCB(Gap_ConnEventRpt_t *pReport)
    {
      // Enqueue the event for processing in the app context.
      if(SimplePeripheral_enqueueMsg(SP_CONN_EVT, pReport) != SUCCESS)
      {
        ICall_free(pReport);
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processConnEvt
     *
     * @brief   Process connection event.
     *
     * @param pReport pointer to connection event report
     */
    static void SimplePeripheral_processConnEvt(Gap_ConnEventRpt_t *pReport)
    {
      /* If we are waiting for an OAD Reboot, process connection events to ensure
       * that we are not waiting to send data before restarting
       */
      if(oadWaitReboot)
      {
        // Wait until all pending messages are sent
        if(numPendingMsgs == 0)
        {
          // Store the flag to indicate that a service changed IND will
          // be sent at the next boot
          sendSvcChngdOnNextBoot = TRUE;
    
          uint8_t status = osal_snv_write(BLE_NVID_CUST_START,
                                          sizeof(sendSvcChngdOnNextBoot),
                                          (uint8 *)&sendSvcChngdOnNextBoot);
          if(status != SUCCESS)
          {
            Display_print1(dispHandle, 5, 0, "SNV WRITE FAIL: %d", status);
          }
    
          // Reset the system
          SystemReset();
        }
        else
        {
          numPendingMsgs--;
        }
      }
      else
      {
        // Get index from handle
        uint8_t connIndex = SimplePeripheral_getConnIndex(pReport->handle);
    
        // If auto phy change is enabled
        if (connList[connIndex].isAutoPHYEnable == TRUE)
        {
          // Read the RSSI
          HCI_ReadRssiCmd(pReport->handle);
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_enqueueMsg
     *
     * @brief   Creates a message and puts the message in RTOS queue.
     *
     * @param   event - message event.
     * @param   state - message state.
     */
    static status_t SimplePeripheral_enqueueMsg(uint8_t event, void *pData)
    {
      uint8_t success;
      spEvt_t *pMsg = ICall_malloc(sizeof(spEvt_t));
    
      // Create dynamic pointer to message.
      if(pMsg)
      {
        pMsg->event = event;
        pMsg->pData = pData;
    
        // Enqueue the message.
        success = Util_enqueueMsg(appMsgQueueHandle, syncEvent, (uint8_t *)pMsg);
        return (success) ? SUCCESS : FAILURE;
      }
    
      return(bleMemAllocError);
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_doSelectConn
     *
     * @brief   Select a connection to communicate with
     *
     * @param   index - item index from the menu
     *
     * @return  always true
     */
    bool SimplePeripheral_doSelectConn(uint8_t index)
    {
      menuConnHandle = connList[index].connHandle;
    
      // Set the menu title and go to this connection's context
      TBM_SET_TITLE(&spMenuPerConn, TBM_GET_ACTION_DESC(&spMenuSelectConn, index));
    
      // Clear non-connection-related message
      Display_clearLine(dispHandle, SP_ROW_CONNECTION);
    
      tbm_goTo(&spMenuPerConn);
    
      return (true);
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_addConn
     *
     * @brief   Add a device to the connected device list
     *
     * @return  index of the connected device list entry where the new connection
     *          info is put in.
     *          if there is no room, MAX_NUM_BLE_CONNS will be returned.
     */
    static uint8_t SimplePeripheral_addConn(uint16_t connHandle)
    {
      uint8_t i;
      uint8_t status = bleNoResources;
    
      // Try to find an available entry
      for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
      {
        if (connList[i].connHandle == LINKDB_CONNHANDLE_INVALID)
        {
    #ifdef DEFAULT_SEND_PARAM_UPDATE_REQ
          spClockEventData_t *paramUpdateEventData;
    
          // Allocate data to send through clock handler
          paramUpdateEventData = ICall_malloc(sizeof(spClockEventData_t) +
                                              sizeof (uint16_t));
          if(paramUpdateEventData)
          {
            paramUpdateEventData->event = SP_SEND_PARAM_UPDATE_EVT;
            *((uint16_t *)paramUpdateEventData->data) = connHandle;
    
            // Create a clock object and start
            connList[i].pUpdateClock
              = (Clock_Struct*) ICall_malloc(sizeof(Clock_Struct));
    
            if (connList[i].pUpdateClock)
            {
              Util_constructClock(connList[i].pUpdateClock,
                                  SimplePeripheral_clockHandler,
                                  SEND_PARAM_UPDATE_DELAY, 0, true,
                                  (UArg) paramUpdateEventData);
            }
          }
          else
          {
            status = bleMemAllocError;
          }
    #endif
          // Found available entry to put a new connection info in
          connList[i].connHandle = connHandle;
    
          // Set default PHY to 1M
          connList[i].currPhy = HCI_PHY_1_MBPS;
    
          break;
        }
      }
    
      return status;
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_getConnIndex
     *
     * @brief   Find index in the connected device list by connHandle
     *
     * @return  the index of the entry that has the given connection handle.
     *          if there is no match, MAX_NUM_BLE_CONNS will be returned.
     */
    static uint8_t SimplePeripheral_getConnIndex(uint16_t connHandle)
    {
      uint8_t i;
    
      for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
      {
        if (connList[i].connHandle == connHandle)
        {
          return i;
        }
      }
    
      return(MAX_NUM_BLE_CONNS);
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_getConnIndex
     *
     * @brief   Find index in the connected device list by connHandle
     *
     * @return  SUCCESS if connHandle found valid index or bleInvalidRange
     *          if index wasn't found. LINKDB_CONNHANDLE_ALL will always succeed.
     */
    static uint8_t SimplePeripheral_clearConnListEntry(uint16_t connHandle)
    {
      uint8_t i;
      // Set to invalid connection index initially
      uint8_t connIndex = MAX_NUM_BLE_CONNS;
    
      if(connHandle != LINKDB_CONNHANDLE_ALL)
      {
        // Get connection index from handle
        connIndex = SimplePeripheral_getConnIndex(connHandle);
        if(connIndex >= MAX_NUM_BLE_CONNS)
        {
          return(bleInvalidRange);
        }
      }
    
      // Clear specific handle or all handles
      for(i = 0; i < MAX_NUM_BLE_CONNS; i++)
      {
        if((connIndex == i) || (connHandle == LINKDB_CONNHANDLE_ALL))
        {
          connList[i].connHandle = LINKDB_CONNHANDLE_INVALID;
          connList[i].currPhy = 0;
          connList[i].phyCngRq = 0;
          connList[i].phyRqFailCnt = 0;
          connList[i].rqPhy = 0;
          memset(connList[i].rssiArr, 0, SP_MAX_RSSI_STORE_DEPTH);
          connList[i].rssiAvg = 0;
          connList[i].rssiCntr = 0;
          connList[i].isAutoPHYEnable = FALSE;
        }
      }
    
      return(SUCCESS);
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_removeConn
     *
     * @brief   Remove a device from the connected device list
     *
     * @return  index of the connected device list entry where the new connection
     *          info is removed from.
     *          if connHandle is not found, MAX_NUM_BLE_CONNS will be returned.
     */
    static uint8_t SimplePeripheral_removeConn(uint16_t connHandle)
    {
      uint8_t connIndex = SimplePeripheral_getConnIndex(connHandle);
    
      if(connIndex != MAX_NUM_BLE_CONNS)
      {
        Clock_Struct* pUpdateClock = connList[connIndex].pUpdateClock;
    
        if (pUpdateClock != NULL)
        {
          // Stop and destruct the RTOS clock if it's still alive
          if (Util_isActive(pUpdateClock))
          {
            Util_stopClock(pUpdateClock);
          }
    
          // Destruct the clock object
          Clock_destruct(pUpdateClock);
          // Free clock struct
          ICall_free(pUpdateClock);
        }
        // Stop Auto PHY Change
        SimplePeripheral_stopAutoPhyChange(connHandle);
        // Clear Connection List Entry
        SimplePeripheral_clearConnListEntry(connHandle);
      }
    
      return connIndex;
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processParamUpdate
     *
     * @brief   Remove a device from the connected device list
     *
     * @return  index of the connected device list entry where the new connection
     *          info is removed from.
     *          if connHandle is not found, MAX_NUM_BLE_CONNS will be returned.
     */
    static void SimplePeripheral_processParamUpdate(uint16_t connHandle)
    {
      gapUpdateLinkParamReq_t req;
      uint8_t connIndex;
    
      req.connectionHandle = connHandle;
    #ifdef DEFAULT_SEND_PARAM_UPDATE_REQ
      req.connLatency = DEFAULT_DESIRED_SLAVE_LATENCY;
      req.connTimeout = DEFAULT_DESIRED_CONN_TIMEOUT;
      req.intervalMin = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
      req.intervalMax = DEFAULT_DESIRED_MAX_CONN_INTERVAL;
    #endif
    
      connIndex = SimplePeripheral_getConnIndex(connHandle);
      SIMPLEPERIPHERAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
    
      // Deconstruct the clock object
      Clock_destruct(connList[connIndex].pUpdateClock);
      // Free clock struct
      ICall_free(connList[connIndex].pUpdateClock);
      connList[connIndex].pUpdateClock = NULL;
    
      // Send parameter update
      bStatus_t status = GAP_UpdateLinkParamReq(&req);
    
      // If there is an ongoing update, queue this for when the udpate completes
      if (status == bleAlreadyInRequestedMode)
      {
        spConnHandleEntry_t *connHandleEntry = ICall_malloc(sizeof(spConnHandleEntry_t));
        if (connHandleEntry)
        {
          connHandleEntry->connHandle = connHandle;
    
          List_put(&paramUpdateList, (List_Elem *)&connHandleEntry);
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimpleCentral_processCmdCompleteEvt
     *
     * @brief   Process an incoming OSAL HCI Command Complete Event.
     *
     * @param   pMsg - message to process
     *
     * @return  none
     */
    static void SimplePeripheral_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg)
    {
      uint8_t status = pMsg->pReturnParam[0];
    
      //Find which command this command complete is for
      switch (pMsg->cmdOpcode)
      {
        case HCI_READ_RSSI:
        {
          // Display RSSI value, if RSSI is higher than threshold, change to faster PHY
          if (status == SUCCESS)
          {
            uint16_t handle = BUILD_UINT16(pMsg->pReturnParam[1], pMsg->pReturnParam[2]);
    
            uint8_t index = SimplePeripheral_getConnIndex(handle);
            SIMPLEPERIPHERAL_ASSERT(index < MAX_NUM_BLE_CONNS);
    
            connList[index].rssiArr[connList[index].rssiCntr++] =
                                                      (int8_t)pMsg->pReturnParam[3];
            connList[index].rssiCntr %= SP_MAX_RSSI_STORE_DEPTH;
    
            int16_t sum_rssi = 0;
            for(uint8_t cnt=0; cnt<SP_MAX_RSSI_STORE_DEPTH; cnt++)
            {
              sum_rssi += connList[index].rssiArr[cnt];
            }
            connList[index].rssiAvg = (uint32_t)(sum_rssi/SP_MAX_RSSI_STORE_DEPTH);
    
            Display_printf(dispHandle, SP_ROW_RSSI, 0,
                           "RSSI:-%d, AVG RSSI:-%d",
                           (uint32_t)(-(int8_t)pMsg->pReturnParam[3]),
                           (uint32_t)(-sum_rssi/SP_MAX_RSSI_STORE_DEPTH));
    
            uint8_t phyRq = SP_PHY_NONE;
            uint8_t phyRqS = SP_PHY_NONE;
            uint8_t phyOpt = LL_PHY_OPT_NONE;
    
            if(connList[index].phyCngRq == FALSE)
            {
              if((connList[index].rssiAvg >= RSSI_2M_THRSHLD) &&
                 (connList[index].currPhy != HCI_PHY_2_MBPS) &&
                 (connList[index].currPhy != SP_PHY_NONE))
              {
                // try to go to higher data rate
                phyRqS = phyRq = HCI_PHY_2_MBPS;
              }
              else if((connList[index].rssiAvg < RSSI_2M_THRSHLD) &&
                      (connList[index].rssiAvg >= RSSI_1M_THRSHLD) &&
                      (connList[index].currPhy != HCI_PHY_1_MBPS) &&
                      (connList[index].currPhy != SP_PHY_NONE))
              {
                // try to go to legacy regular data rate
                phyRqS = phyRq = HCI_PHY_1_MBPS;
              }
              else if((connList[index].rssiAvg >= RSSI_S2_THRSHLD) &&
                      (connList[index].rssiAvg < RSSI_1M_THRSHLD) &&
                      (connList[index].currPhy != SP_PHY_NONE))
              {
                // try to go to lower data rate S=2(500kb/s)
                phyRqS = HCI_PHY_CODED;
                phyOpt = LL_PHY_OPT_S2;
                phyRq = BLE5_CODED_S2_PHY;
              }
              else if(connList[index].rssiAvg < RSSI_S2_THRSHLD )
              {
                // try to go to lowest data rate S=8(125kb/s)
                phyRqS = HCI_PHY_CODED;
                phyOpt = LL_PHY_OPT_S8;
                phyRq = BLE5_CODED_S8_PHY;
              }
              if((phyRq != SP_PHY_NONE) &&
                 // First check if the request for this phy change is already not honored then don't request for change
                 (((connList[index].rqPhy == phyRq) &&
                   (connList[index].phyRqFailCnt < 2)) ||
                  (connList[index].rqPhy != phyRq)))
              {
                //Initiate PHY change based on RSSI
                SimplePeripheral_setPhy(connList[index].connHandle, 0,
                                        phyRqS, phyRqS, phyOpt);
                connList[index].phyCngRq = TRUE;
    
                // If it a request for different phy than failed request, reset the count
                if(connList[index].rqPhy != phyRq)
                {
                  // then reset the request phy counter and requested phy
                  connList[index].phyRqFailCnt = 0;
                }
    
                if(phyOpt == LL_PHY_OPT_NONE)
                {
                  connList[index].rqPhy = phyRq;
                }
                else if(phyOpt == LL_PHY_OPT_S2)
                {
                  connList[index].rqPhy = BLE5_CODED_S2_PHY;
                }
                else
                {
                  connList[index].rqPhy = BLE5_CODED_S8_PHY;
                }
    
              }
            } // end of if(connList[index].phyCngRq == FALSE)
          } // end of if (status == SUCCESS)
          break;
        }
    
        case HCI_LE_READ_PHY:
        {
          if (status == SUCCESS)
          {
            Display_printf(dispHandle, SP_ROW_RSSI + 2, 0, "RXPh: %d, TXPh: %d",
                           pMsg->pReturnParam[3], pMsg->pReturnParam[4]);
          }
          break;
        }
    
        case HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS:
        {
          uint8_t* pRpaNew = &(pMsg->pReturnParam[1]);
    
          if (memcmp(pRpaNew, rpa, B_ADDR_LEN))
          {
            // If the RPA has changed, update the display
            Display_printf(dispHandle, SP_ROW_RPA, 0, "RP Addr: %s",
                           Util_convertBdAddr2Str(pRpaNew));
            memcpy(rpa, pRpaNew, B_ADDR_LEN);
          }
          break;
        }
    
        case HCI_RESET:
        {
            Status_t status = SUCCESS;
    
            /* sleep 100 ms, as suggested in https://e2e.ti.com/support/wireless-connectivity/bluetooth-group/bluetooth/f/bluetooth-forum/1137466/cc1352r-reset-ble-stack-does-not-work/ */
            Task_sleep(1000000u / Clock_tickPeriod);
    
            //Initialize GAP layer for Peripheral role and register to receive GAP events
            status = GAP_DeviceInit(GAP_PROFILE_PERIPHERAL, selfEntity, addrMode, &pRandomAddress);
    
            if (status != SUCCESS)
            {
                /* ohoh, very bad :( */
            }
        }
        break;
    
        default:
          break;
      } // end of switch (pMsg->cmdOpcode)
    }
    
    /*********************************************************************
    * @fn      SimplePeripheral_initPHYRSSIArray
    *
    * @brief   Initializes the array of structure/s to store data related
    *          RSSI based auto PHy change
    *
    * @param   connHandle - the connection handle
    *
    * @param   addr - pointer to device address
    *
    * @return  index of connection handle
    */
    static void SimplePeripheral_initPHYRSSIArray(void)
    {
      //Initialize array to store connection handle and RSSI values
      memset(connList, 0, sizeof(connList));
      for (uint8_t index = 0; index < MAX_NUM_BLE_CONNS; index++)
      {
        connList[index].connHandle = SP_INVALID_HANDLE;
      }
    }
    /*********************************************************************
          // Set default PHY to 1M
     * @fn      SimplePeripheral_startAutoPhyChange
     *
     * @brief   Start periodic RSSI reads on a link.
     *
     * @param   connHandle - connection handle of link
     * @param   devAddr - device address
     *
     * @return  SUCCESS: Terminate started
     *          bleIncorrectMode: No link
     *          bleNoResources: No resources
     */
    static status_t SimplePeripheral_startAutoPhyChange(uint16_t connHandle)
    {
      status_t status = FAILURE;
    
      // Get connection index from handle
      uint8_t connIndex = SimplePeripheral_getConnIndex(connHandle);
      SIMPLEPERIPHERAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
    
      // Start Connection Event notice for RSSI calculation
      Gap_RegisterConnEventCb(SimplePeripheral_connEvtCB, GAP_CB_REGISTER, GAP_CB_CONN_EVENT_ALL, connHandle);
    
      // Flag in connection info if successful
      if (status == SUCCESS)
      {
        connList[connIndex].isAutoPHYEnable = TRUE;
      }
    
      return status;
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_stopAutoPhyChange
     *
     * @brief   Cancel periodic RSSI reads on a link.
     *
     * @param   connHandle - connection handle of link
     *
     * @return  SUCCESS: Operation successful
     *          bleIncorrectMode: No link
     */
    static status_t SimplePeripheral_stopAutoPhyChange(uint16_t connHandle)
    {
      // Get connection index from handle
      uint8_t connIndex = SimplePeripheral_getConnIndex(connHandle);
      SIMPLEPERIPHERAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
    
      // Stop connection event notice
      Gap_RegisterConnEventCb(NULL, GAP_CB_UNREGISTER, GAP_CB_CONN_EVENT_ALL, connHandle);
    
      // Also update the phychange request status for active RSSI tracking connection
      connList[connIndex].phyCngRq = FALSE;
      connList[connIndex].isAutoPHYEnable = FALSE;
    
      return SUCCESS;
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_setPhy
     *
     * @brief   Call the HCI set phy API and and add the handle to a
     *          list to match it to an incoming command status event
     */
    static status_t SimplePeripheral_setPhy(uint16_t connHandle, uint8_t allPhys,
                                            uint8_t txPhy, uint8_t rxPhy,
                                            uint16_t phyOpts)
    {
      // Allocate list entry to store handle for command status
      spConnHandleEntry_t *connHandleEntry = ICall_malloc(sizeof(spConnHandleEntry_t));
    
      if (connHandleEntry)
      {
        connHandleEntry->connHandle = connHandle;
    
        // Add entry to the phy command status list
        List_put(&setPhyCommStatList, (List_Elem *)connHandleEntry);
    
        // Send PHY Update
        HCI_LE_SetPhyCmd(connHandle, allPhys, txPhy, rxPhy, phyOpts);
      }
    
      return SUCCESS;
    }
    
    /*********************************************************************
    * @fn      SimplePeripheral_updatePHYStat
    *
    * @brief   Update the auto phy update state machine
    *
    * @param   connHandle - the connection handle
    *
    * @return  None
    */
    static void SimplePeripheral_updatePHYStat(uint16_t eventCode, uint8_t *pMsg)
    {
      uint8_t connIndex;
    
      switch (eventCode)
      {
        case HCI_LE_SET_PHY:
        {
          // Get connection handle from list
          spConnHandleEntry_t *connHandleEntry =
                               (spConnHandleEntry_t *)List_get(&setPhyCommStatList);
    
          if (connHandleEntry)
          {
            // Get index from connection handle
            connIndex = SimplePeripheral_getConnIndex(connHandleEntry->connHandle);
    
            ICall_free(connHandleEntry);
    
            // Is this connection still valid?
            if (connIndex < MAX_NUM_BLE_CONNS)
            {
              hciEvt_CommandStatus_t *pMyMsg = (hciEvt_CommandStatus_t *)pMsg;
    
              if (pMyMsg->cmdStatus == HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE)
              {
                // Update the phychange request status for active RSSI tracking connection
                connList[connIndex].phyCngRq = FALSE;
                connList[connIndex].phyRqFailCnt++;
              }
            }
          }
          break;
        }
    
        // LE Event - a Phy update has completed or failed
        case HCI_BLE_PHY_UPDATE_COMPLETE_EVENT:
        {
          hciEvt_BLEPhyUpdateComplete_t *pPUC =
                                         (hciEvt_BLEPhyUpdateComplete_t*) pMsg;
    
          if(pPUC)
          {
            // Get index from connection handle
            connIndex = SimplePeripheral_getConnIndex(pPUC->connHandle);
    
            // Is this connection still valid?
            if (connIndex < MAX_NUM_BLE_CONNS)
            {
              // Update the phychange request status for active RSSI tracking connection
              connList[connIndex].phyCngRq = FALSE;
    
              if (pPUC->status == SUCCESS)
              {
                connList[connIndex].currPhy = pPUC->rxPhy;
              }
              if(pPUC->rxPhy != connList[connIndex].rqPhy)
              {
                connList[connIndex].phyRqFailCnt++;
              }
              else
              {
                // Reset the request phy counter and requested phy
                connList[connIndex].phyRqFailCnt = 0;
                connList[connIndex].rqPhy = 0;
              }
            }
          }
    
          break;
        }
    
        default:
          break;
      } // end of switch (eventCode)
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_menuSwitchCb
     *
     * @brief   Detect menu context switching
     *
     * @param   pMenuObjCurr - the current menu object
     * @param   pMenuObjNext - the menu object the context is about to switch to
     *
     * @return  none
     */
    static void SimplePeripheral_menuSwitchCb(tbmMenuObj_t* pMenuObjCurr,
                                           tbmMenuObj_t* pMenuObjNext)
    {
      uint8_t NUMB_ACTIVE_CONNS = linkDB_NumActive();
    
      // interested in only the events of
      // entering scMenuConnect, spMenuSelectConn, and scMenuMain for now
      if (pMenuObjNext == &spMenuSelectConn)
      {
        static uint8_t* pAddrs;
        uint8_t* pAddrTemp;
    
        if (pAddrs != NULL)
        {
          ICall_free(pAddrs);
        }
    
        // Allocate buffer to display addresses
        pAddrs = ICall_malloc(NUMB_ACTIVE_CONNS * SP_ADDR_STR_SIZE);
    
        if (pAddrs == NULL)
        {
          TBM_SET_NUM_ITEM(&spMenuSelectConn, 0);
        }
        else
        {
          uint8_t i;
    
          TBM_SET_NUM_ITEM(&spMenuSelectConn, MAX_NUM_BLE_CONNS);
    
          pAddrTemp = pAddrs;
    
          // Add active connection info to the menu object
          for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
          {
            if (connList[i].connHandle != LINKDB_CONNHANDLE_INVALID)
            {
              // Get the address from the connection handle
              linkDBInfo_t linkInfo;
              linkDB_GetInfo(connList[i].connHandle, &linkInfo);
              // This connection is active. Set the corresponding menu item with
              // the address of this connection and enable the item.
              memcpy(pAddrTemp, Util_convertBdAddr2Str(linkInfo.addr),
                     SP_ADDR_STR_SIZE);
              TBM_SET_ACTION_DESC(&spMenuSelectConn, i, pAddrTemp);
              tbm_setItemStatus(&spMenuSelectConn, (1 << i), SP_ITEM_NONE);
              pAddrTemp += SP_ADDR_STR_SIZE;
            }
            else
            {
              // This connection is not active. Disable the corresponding menu item.
              tbm_setItemStatus(&spMenuSelectConn, SP_ITEM_NONE, (1 << i));
            }
          }
        }
      }
      else if (pMenuObjNext == &spMenuMain)
      {
        // Now we are not in a specific connection's context
    
        // Clear connection-related message
        Display_clearLine(dispHandle, SP_ROW_CONNECTION);
      }
    }
    
    /*******************************************************************************
     * @fn          ClockEventHandler
     *
     * @brief       Gets calles by clock after timeout - icall reinit test helper
     *
     * input parameters
     *
     * @param       arg - create time user argument
     *
     * output parameters
     *
     * @param       None.
     *
     * @return      None.
     */
    static void ClockEventHandler(UArg arg)
    {
        SimplePeripheral_enqueueMsg(SP_TEST_EVT, NULL);
    }
    
    /*********************************************************************
    *********************************************************************/
    

  • Hi Gabiel,

    Thank you for the source code.

    I have traced back the issue to a BLE stack state variable supposedly not being re-initialized.
    What we are trying to achieve (Resetting the BLE stack only) seems to be non-standard behavior, but we might have a fix for that.

    I will come back to you with more information.

    Regards,

    Arthur

  • Hi again Gabiel,

    It seems like we do not have any mechanism to only reset the BLE5 stack in the 4.40 stack, as well as 6.20 (where some GAP_DeInit and GAP_ReInit methods were added, that only reset the RF core), unfortunately.

    Regards,
    Arthur

  • Hi Arthur,

    thanks for your conclusive answer and your tries.

    Ok, so we do not go on with this. But at least it is interesting that 6.20. has a ReInit method for the RF Core (although I am sad to not have it in 4.10).

    Kind regards

    Gabriel