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.

RE: CC2642R: Issue with sha drivers and simple peripheral

Other Parts Discussed in Thread: SHA-256, LAUNCHXL-CC26X2R1, SYSCONFIG, SIMPLELINK-CC13X2-26X2-SDK

Hi,

Previous E2E thread: https://e2e.ti.com/f/1/t/1134916 

I am giving details on using the Simple Peripheral application example project from the SDK 5.10 where the SHA operation is stuck. Essentially, I have below threads as per priority highest to lowest in the firmware:

- ICall (BLE thread, already present)

- Simple Peripheral (changed priority from 1 to 2)

- Hash operation (priority 1) --> Added newly

- IDLE (priority 0)

The whole goal of this exercise is to reproduce the issue where the SHA operation is preempted by high priority thread (Simple Peripheral) and then the SHA operation is stuck forever. So, please take a note of this.

The example also uses the LED on the LP to let the user know when the firmware is stuck (when the Green LED is ON forever). Other than that, the firmware also uses the Display printf statements where the PUTTY shows freezed content when the issue happens. In normal case, the firmware calls the system reset after the GAP_DEVICE_INIT_DONE_EVENT is processed completely as we are seeing the issue only before that.

When I have the SHA operation performed under the critical section, the issue is never observed. However, if it is not under the critical section (see Hash_taskFxn()), the issue is observed.

//ICall_CSState key = ICall_enterCriticalSection();

// Verify correct input generates expected output
result = SHA2_addData(sha2Handle, &dataToHash[0], HASH_VERIFICATION_PARTIAL_BLOCK_SIZE); // --> ISSUE: Never returns

//ICall_leaveCriticalSection(key);

Task_sleep((xdc_UInt32)randomNumber+1);

Display_printf(dispHandle, SP_ROW_SHA+1, 0, "SHA Done: %d, RP Addr: %s", randomNumber, Util_convertBdAddr2Str(pRpaNew));
GPIO_write(CONFIG_LED_1_GPIO, CONFIG_GPIO_LED_OFF);

See the two files attached which has been modified. Also, we have this issue as the highest priority to analyze and fix so your help is very much appreciated. 

4186.simple_peripheral.c

simple_peripheral.syscfg:

/**
 * These arguments were used when this file was generated. They will be automatically applied on subsequent loads
 * via the GUI or CLI. Run CLI with '--help' for additional information on how to override these arguments.
 * @cliArgs --board "/ti/boards/CC26X2R1_LAUNCHXL" --product "simplelink_cc13x2_26x2_sdk@5.10.00.48"
 * @versions {"data":"2021031521","timestamp":"2021031521","tool":"1.8.0+1863","templates":null}
 */

/**
 * Import the modules used in this configuration.
 */
const ble         = scripting.addModule("/ti/ble5stack/ble");
const CCFG        = scripting.addModule("/ti/devices/CCFG");
const rfdesign    = scripting.addModule("/ti/devices/radioconfig/rfdesign");
const Display     = scripting.addModule("/ti/display/Display");
const Display1    = Display.addInstance();
const AESCCM      = scripting.addModule("/ti/drivers/AESCCM");
const AESCCM1     = AESCCM.addInstance();
const AESCTRDRBG  = scripting.addModule("/ti/drivers/AESCTRDRBG");
const AESCTRDRBG1 = AESCTRDRBG.addInstance();
const AESECB      = scripting.addModule("/ti/drivers/AESECB");
const AESECB1     = AESECB.addInstance();
const ECDH        = scripting.addModule("/ti/drivers/ECDH");
const ECDH1       = ECDH.addInstance();
const GPIO        = scripting.addModule("/ti/drivers/GPIO");
const GPIO1       = GPIO.addInstance();
const GPIO2       = GPIO.addInstance();
const NVS         = scripting.addModule("/ti/drivers/NVS");
const NVS1        = NVS.addInstance();
const Power       = scripting.addModule("/ti/drivers/Power");
const RF          = scripting.addModule("/ti/drivers/RF");
const RTOS        = scripting.addModule("/ti/drivers/RTOS");
const SHA2        = scripting.addModule("/ti/drivers/SHA2", {}, false);
const SHA21       = SHA2.addInstance();
const TRNG        = scripting.addModule("/ti/drivers/TRNG");
const TRNG1       = TRNG.addInstance();
const LED         = scripting.addModule("/ti/drivers/apps/LED", {}, false);
const LED1        = LED.addInstance();
const LED2        = LED.addInstance();

/**
 * Write custom configuration values to the imported modules.
 */
ble.lockProject                                           = true;
ble.radioConfig.codeExportConfig.$name                    = "ti_devices_radioconfig_code_export_param0";
ble.connUpdateParamsPeripheral.$name                      = "ti_ble5stack_general_ble_conn_update_params0";
ble.advSet1.$name                                         = "ti_ble5stack_broadcaster_advertisement_set0";
ble.advSet1.advParam1.$name                               = "ti_ble5stack_broadcaster_advertisement_params0";
ble.advSet1.advData1.$name                                = "ti_ble5stack_broadcaster_advertisement_data0";
ble.advSet1.advData1.GAP_ADTYPE_FLAGS                     = true;
ble.advSet1.advData1.advertisingFlags                     = ["GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED","GAP_ADTYPE_FLAGS_GENERAL"];
ble.advSet1.advData1.GAP_ADTYPE_LOCAL_NAME_SHORT          = true;
ble.advSet1.advData1.shortenedLocalName                   = "SP";
ble.advSet1.advData1.GAP_ADTYPE_16BIT_MORE                = true;
ble.advSet1.advData1.numOfUUIDs16More                     = 1;
ble.advSet1.advData1.UUID016More                          = 0xFFF0;
ble.advSet1.scanRes1.$name                                = "ti_ble5stack_broadcaster_advertisement_data1";
ble.advSet1.scanRes1.GAP_ADTYPE_LOCAL_NAME_COMPLETE       = true;
ble.advSet1.scanRes1.completeLocalName                    = "Simple Peripheral";
ble.advSet1.scanRes1.GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE = true;
ble.advSet1.scanRes1.maxConnInterval                      = 130;
ble.advSet1.scanRes1.GAP_ADTYPE_POWER_LEVEL               = true;
ble.advSet2.$name                                         = "ti_ble5stack_broadcaster_advertisement_set1";
ble.advSet2.advParam2.$name                               = "ti_ble5stack_broadcaster_advertisement_params1";
ble.advSet2.advParam2.advType                             = "extended";
ble.advSet2.advData2.$name                                = "ti_ble5stack_broadcaster_advertisement_data2";
ble.advSet2.advData2.GAP_ADTYPE_FLAGS                     = true;
ble.advSet2.advData2.advertisingFlags                     = ["GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED","GAP_ADTYPE_FLAGS_GENERAL"];
ble.advSet2.advData2.GAP_ADTYPE_LOCAL_NAME_SHORT          = true;
ble.advSet2.advData2.shortenedLocalName                   = "SP";
ble.advSet2.advData2.GAP_ADTYPE_16BIT_MORE                = true;
ble.advSet2.advData2.numOfUUIDs16More                     = 1;
ble.advSet2.advData2.UUID016More                          = 0xFFF0;
ble.advSet2.scanRes2.$name                                = "ti_ble5stack_broadcaster_advertisement_data3";
ble.advSet2.scanRes2.GAP_ADTYPE_LOCAL_NAME_COMPLETE       = true;
ble.advSet2.scanRes2.completeLocalName                    = "Simple Peripheral";
ble.advSet2.scanRes2.GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE = true;
ble.advSet2.scanRes2.maxConnInterval                      = 130;
ble.advSet2.scanRes2.GAP_ADTYPE_POWER_LEVEL               = true;

CCFG.enableBootloader         = true;
CCFG.enableBootloaderBackdoor = true;
CCFG.dioBootloaderBackdoor    = 13;
CCFG.levelBootloaderBackdoor  = "Active low";
CCFG.ccfgTemplate.$name       = "ti_devices_CCFGTemplate0";

Display1.$name                          = "CONFIG_Display_0";
Display1.$hardware                      = system.deviceData.board.components.XDS110UART;
Display1.enableANSI                     = true;
Display1.uartBufferSize                 = 128;
Display1.uart.$name                     = "CONFIG_DISPLAY_UART";
Display1.uart.txPinInstance.outputState = "High";
Display1.uart.txPinInstance.$name       = "CONFIG_PIN_UART_TX";
Display1.uart.rxPinInstance.$name       = "CONFIG_PIN_UART_RX";

AESCCM1.$name = "CONFIG_AESCCM0";

AESCTRDRBG1.$name              = "CONFIG_AESCTRDRBG_0";
AESCTRDRBG1.aesctrObject.$name = "CONFIG_AESCTR_0";

AESECB1.$name = "CONFIG_AESECB0";

ECDH1.$name = "CONFIG_ECDH0";

GPIO1.$hardware         = system.deviceData.board.components["BTN-1"];
GPIO1.mode              = "Dynamic";
GPIO1.$name             = "CONFIG_GPIO_BTN1";
GPIO1.pinInstance.$name = "CONFIG_PIN_BTN1";

GPIO2.$hardware         = system.deviceData.board.components["BTN-2"];
GPIO2.mode              = "Dynamic";
GPIO2.$name             = "CONFIG_GPIO_BTN2";
GPIO2.pinInstance.$name = "CONFIG_PIN_BTN2";

NVS1.$name                    = "CONFIG_NVSINTERNAL";
NVS1.internalFlash.$name      = "ti_drivers_nvs_NVSCC26XX0";
NVS1.internalFlash.regionBase = 0x48000;
NVS1.internalFlash.regionSize = 0x4000;

SHA21.$name = "CONFIG_SHA2_0";

TRNG1.$name = "CONFIG_TRNG_0";

LED1.$name                     = "CONFIG_LED_0";
LED1.$hardware                 = system.deviceData.board.components.LED_RED;
LED1.gpioPin.pinInstance.$name = "CONFIG_PIN_0";

LED2.$name                     = "CONFIG_LED_1";
LED2.$hardware                 = system.deviceData.board.components.LED_GREEN;
LED2.gpioPin.pinInstance.$name = "CONFIG_PIN_1";

/**
 * Pinmux solution for unlocked pins/peripherals. This ensures that minor changes to the automatic solver in a future
 * version of the tool will not impact the pinmux you originally saw.  These lines can be completely deleted in order to
 * re-solve from scratch.
 */
Display1.uart.uart.$suggestSolution       = "UART1";
Display1.uart.uart.txPin.$suggestSolution = "boosterpack.4";
Display1.uart.uart.rxPin.$suggestSolution = "boosterpack.3";
GPIO1.gpioPin.$suggestSolution            = "boosterpack.13";
GPIO2.gpioPin.$suggestSolution            = "boosterpack.12";
LED1.gpioPin.gpioPin.$suggestSolution     = "boosterpack.39";
LED2.gpioPin.gpioPin.$suggestSolution     = "boosterpack.40";

  • Hey Mehul,

    Thanks for posting on the forums. We've received your inquiry and will look into this and follow up after reviewing the details provided.

  • Hi Mehul,

    The issue experienced here is likely to be caused by having two tasks accessing the Crypto-Engine (i.e. your task for the SHA driver and the BLE stack with the AES driver). 

    Having the access to the SHA driver in a critical section should prevent other tasks to access it, hence to avoid the issues you have mentioned. 

    May I kindly ask if adding your code in a critical section (as done in the code snippet shared) is acceptable for you?

    Best regards,

  • Hi Clement,

    Before we go into work around, are you sure that the issue is due to Crypto being used by AES and SHA together? Have you verified  that is the case that the BLE stack has AES in use by preempting the SHA task?

    The reason I am asking for this as few TI documentations suggests that above scenario should not be an issue:

    C:\ti\simplelink_cc13x2_26x2_sdk_5_10_00_48\source\ti\drivers\aesccm\AESCCMCC26XX.h

    * # Hardware Accelerator #
    * The CC26XX family has a dedicated hardware crypto accelerator. It is capable
    * of multiple AES block cipher modes of operation including CCM. Only one operation
    * can be carried out on the accerator at a time. Mutual exclusion is
    * implemented at the driver level and coordinated between all drivers relying on
    * the accelerator. It is transparent to the application and only noted to ensure that
    * sensible access timeouts are set.

    Ryan from TI also responded in other thread that the access should not be an issue. Below thread is a parent thread out of which Ryan has created this thread:

    e2e.ti.com/.../cc2642r-issue-with-aes-and-sha-drivers

    TI__Guru**** 167895 points

    The documentation states that "only one of SHA2 and AES may be used at the same time", meaning both can be used simultaneously but two instances of either cannot be used at the same time.

    Also, we have several places in the code where SHA and other AES operations are being used. Putting them under critical section have a risk or getting the system latency increased with an additional risk on the driver function never returns and keeps the interrupts disabled forever. This is a bigger risk.

  • Also, it would be great to understand the root cause of the issue here as well. We are not preferring to go with work around which might be working fine as of now but without understanding the root cause, we live in a fear of work around without understanding the root cause involves even a greater uncomfortable situation.

  • For clarification, I think my usage of the word "simultaneous" is poor depending on the context.  The related thread discusses two RTOS threads which coordinate accelerator usage between the AES and SHA2.  Hence they can both operate in the same application by sharing the crypto accelerator but not perform operations at the exact same time given the hardware accelerator limitation.  It was concluded that the AES thread was not causing SHA2 operation to fail in that regard.

    This thread has a different context in that the BLE5 Stack is also involved, adding complexity through the BLE radio operation and ICall dispatcher.  As has been discovered, the BLE5 Stack preempts and interferes with SHA2 operation.  Thus it is necessary to create a critical section so that they SHA2 can safely complete its task.  This way, both SHA2 and BLE5 can be used "simultaneously" in the application while respecting that the hardware crypto accelerator and only carry out one operation at a time.

    Regards,
    Ryan

  • Ryan, Ammar, Clement,

    I appreciate all of your comments. 

    However, have you identified the exact root cause of the issue at least what is happening in the SHA driver when the BLE stack preempts the SHA thread? It is much easier for us to accept the workaround if we know the root cause.

    I am no expert in the BLE but it feels strange to me that we may not be the first user of using the crypto engine (SHA or AES) in the application thread while the BLE stack is running that the highest priority in the system. During initial days of the project, we have been also told that not to preempt the BLE thread or have anything in high priority then the BLE thread.

    I strongly request that we should know the root cause. Adding the region into a critical section is a workaround. There are many modules interacting (radio on a different core, dispatcher from Icall, the link layer scheduler itself, application having AES-CCM, AES-CTR, SHA-256, SHA-512, TRNG, PRNG, HMAC, etc.). Below are the pertinent question I have:

    - How many different calls to the drivers (AES-CCM, AES-CTR, SHA-256, SHA-512, TRNG, PRNG, HMAC) we need protect against with the critical section in the application? 

    - What could be the impact of having a driver call under the critical section if the critical section is for long duration? As an example, AES-CTR for 2048 bytes 300 us, SHA can take 80-100 us? (reference: TI SWRA667).  What if the driver call takes more time as these numbers might be the actual time taken by the accelerator but may not have included the overhead of the driver call itself (mutex, polling, etc.). What if the driver function takes more time to return? Whether the BLE Stack can go out of sync? Whether the radio scheduler (Icall dispatcher) can go out of sync?

    In essence, we should not be calling an API under the critical section if it is taking more time. We generally use it for a very small period (e.g. multiple timer register, updating multiple variables with taking a snapshot). Calling a driver function which takes more time is something we cannot have under the critical section.

    - In our application as well, we are relying on timely interrupt being served for few GPIOs and delaying them due to the critical section (for crypto APIs being called) will have a design impact as well.

    - Any other area we should have under the critical section?

    Regards,

    Mehul

  • Hi Mehul,

    In order to best address each of these questions, it would be optimal for TI to first be capable of replicating the behavior you are observing.  To that effect I have imported the SIMPLELINK-CC13X2-26X2-SDK v5.10 LAUNCHXL-CC26X2R1 simple_peripheral examples (both CCS and TI-CLANG compiler versions) into CCS v11 using XDCtools v3.62.1.15 and SysConfig v1.8.0 dependencies.  I have added one SHA2 instance inside SysConfig and modified simple_peripheral.c such that the second hash task is initialized and continuously calls SHA2_addData.

    /******************************************************************************
    
     @file  simple_peripheral.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-2021, 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 <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 "simple_peripheral_menu.h"
    #include "simple_peripheral.h"
    #include "ti_ble_config.h"
    
    #ifdef PTM_MODE
    #include "npi_task.h"               // To allow RX event registration
    #include "npi_ble.h"                // To enable transmission of messages to UART
    #include "icall_hci_tl.h"   // To allow ICall HCI Transport Layer
    #endif // PTM_MODE
    
    #include <ti/drivers/SHA2.h>
    
    
    /*********************************************************************
     * MACROS
     */
    
    /*********************************************************************
     * CONSTANTS
     */
    // How often to perform periodic event (in ms)
    #define SP_PERIODIC_EVT_PERIOD               5000
    
    // Task configuration
    #define SP_TASK_PRIORITY                     1
    #define HASH_TASK_PRIORITY                     1
    #define HASH_TASK_STACK_SIZE                   2048
    
    #ifndef SP_TASK_STACK_SIZE
    #define SP_TASK_STACK_SIZE                   1024
    #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
    
    // 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)
    
    // 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)
    #define SP_ROW_AC            (TBM_ROW_APP + 9)
    
    // 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           
    #define RSSI_1M_THRSHLD           -40           
    #define RSSI_S2_THRSHLD           -50           
    #define RSSI_S8_THRSHLD           -60           
    #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
     */
    
    // Auto connect availble groups
    enum
    {
      AUTOCONNECT_DISABLE = 0,              // Disable
      AUTOCONNECT_GROUP_A = 1,              // Group A
      AUTOCONNECT_GROUP_B = 2               // Group B
    };
    
    
    // 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
      spClockEventData_t*   pParamUpdateEventData;
      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;
    
    // Task configuration
    Task_Struct spTask;
    Task_Struct hashTask;
    #if defined __TI_COMPILER_VERSION__
    #pragma DATA_ALIGN(spTaskStack, 8)
    #else
    #pragma data_alignment=8
    #endif
    uint8_t spTaskStack[SP_TASK_STACK_SIZE];
    uint8_t hashTaskStack[HASH_TASK_STACK_SIZE];
    
    #define APP_EVT_EVENT_MAX 0x9
    char *appEventStrings[] = {
      "APP_STATE_CHANGE_EVT     ",
      "APP_CHAR_CHANGE_EVT      ",
      "APP_KEY_CHANGE_EVT       ",
      "APP_ADV_EVT              ",
      "APP_PAIR_STATE_EVT       ",
      "APP_PASSCODE_EVT         ",
      "APP_READ_RPA_EVT         ",
      "APP_PERIODIC_EVT         ",
      "APP_SEND_PARAM_UPDATE_EVT",
      "APP_CONN_EVT             ",
    };
    
    /*********************************************************************
     * 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;
    
    // Auto connect Disabled/Enabled {0 - Disabled, 1- Group A , 2-Group B, ...}
    uint8_t autoConnect = AUTOCONNECT_DISABLE;
    
    // 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};
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    
    static void SimplePeripheral_init( void );
    static void SimplePeripheral_taskFxn(UArg a0, UArg a1);
    static void Hash_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_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 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);
    #ifdef PTM_MODE
    void simple_peripheral_handleNPIRxInterceptEvent(uint8_t *pMsg);  // Declaration
    static void simple_peripheral_sendToNPI(uint8_t *buf, uint16_t len);  // Declaration
    #endif // PTM_MODE
    
    /*********************************************************************
     * 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
    };
    
    /*********************************************************************
     * 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++;
      }
    }
    
    #ifdef PTM_MODE
    /*********************************************************************
    * @fn      simple_peripheral_handleNPIRxInterceptEvent
    *
    * @brief   Intercept an NPI RX serial message and queue for this application.
    *
    * @param   pMsg - a NPIMSG_msg_t containing the intercepted message.
    *
    * @return  none.
    */
    void simple_peripheral_handleNPIRxInterceptEvent(uint8_t *pMsg)
    {
     // Send Command via HCI TL
     HCI_TL_SendToStack(((NPIMSG_msg_t *)pMsg)->pBuf);
    
     // The data is stored as a message, free this first.
     ICall_freeMsg(((NPIMSG_msg_t *)pMsg)->pBuf);
    
     // Free container.
     ICall_free(pMsg);
    }
    
    /*********************************************************************
    * @fn      simple_peripheral_sendToNPI
    *
    * @brief   Create an NPI packet and send to NPI to transmit.
    *
    * @param   buf - pointer HCI event or data.
    *
    * @param   len - length of buf in bytes.
    *
    * @return  none
    */
    static void simple_peripheral_sendToNPI(uint8_t *buf, uint16_t len)
    {
     npiPkt_t *pNpiPkt = (npiPkt_t *)ICall_allocMsg(sizeof(npiPkt_t) + len);
    
     if (pNpiPkt)
     {
       pNpiPkt->hdr.event = buf[0]; //Has the event status code in first byte of payload
       pNpiPkt->hdr.status = 0xFF;
       pNpiPkt->pktLen = len;
       pNpiPkt->pData  = (uint8 *)(pNpiPkt + 1);
    
       memcpy(pNpiPkt->pData, buf, len);
    
       // Send to NPI
       // Note: there is no need to free this packet.  NPI will do that itself.
       NPITask_sendToHost((uint8_t *)pNpiPkt);
     }
    }
    #endif // PTM_MODE
    
    /*********************************************************************
     * @fn      SimplePeripheral_createTask
     *
     * @brief   Task creation function for the Simple Peripheral.
     */
    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);
    
      // Configure task
      Task_Params hashtaskParams;
      Task_Params_init(&hashtaskParams);
      hashtaskParams.stack = hashTaskStack;
      hashtaskParams.stackSize = HASH_TASK_STACK_SIZE;
      hashtaskParams.priority = HASH_TASK_PRIORITY;
    
      Task_construct(&hashTask, Hash_taskFxn, &hashtaskParams, 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)
    {
      BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- init ", SP_TASK_PRIORITY);
      // 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);
    
    #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);
    
      // 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);
    
      BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- call GAP_DeviceInit", GAP_PROFILE_PERIPHERAL);
      //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, TBM_ITEM_NONE, TBM_ITEM_ALL);
    
      tbm_initTwoBtnMenu(dispHandle, &spMenuMain, 5, SimplePeripheral_menuSwitchCb);
      Display_printf(dispHandle, SP_ROW_SEPARATOR_1, 0, "====================");
    
    #ifdef PTM_MODE
      // Intercept NPI RX events.
      NPITask_registerIncomingRXEventAppCB(simple_peripheral_handleNPIRxInterceptEvent, INTERCEPT);
    
      // Register for Command Status information
      HCI_TL_Init(NULL, (HCI_TL_CommandStatusCB_t) simple_peripheral_sendToNPI, NULL, selfEntity);
    
      // Register for Events
      HCI_TL_getCmdResponderID(ICall_getLocalMsgEntityId(ICALL_SERVICE_CLASS_BLE_MSG, selfEntity));
    
      // Inform Stack to Initialize PTM
      HCI_EXT_EnablePTMCmd();
    #endif // PTM_MODE
    }
    
    /*********************************************************************
     * @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);
              }
            }
          }
        }
      }
    }
    
    #define HASH_VERIFICATION_PARTIAL_BLOCK_SIZE  (size_t)64
    static uint8_t dataToHash[HASH_VERIFICATION_PARTIAL_BLOCK_SIZE];
    static void Hash_taskFxn(UArg a0, UArg a1)
    {
        int_fast16_t result;
    
        /* Driver handles */
        SHA2_Handle                 sha2Handle;
    
        /* Call driver initialization functions */
        SHA2_init();
    
        SHA2_Params params;
        SHA2_Params_init(&params);
        params.hashType = SHA2_HASH_TYPE_512;
        params.returnBehavior = SHA2_RETURN_BEHAVIOR_POLLING;
        sha2Handle = SHA2_open(0, &params);
    
        if (!sha2Handle) {
            /* SHA2_open() failed */
            while(1);
        }
    
        (void)memset((void*)&dataToHash[0], 0x00, HASH_VERIFICATION_PARTIAL_BLOCK_SIZE);
    
        for(;;)
        {
            Task_sleep((xdc_UInt32)10);
            GPIO_write(CONFIG_LED_1_GPIO, CONFIG_GPIO_LED_OFF);
            //ICall_CSState key = ICall_enterCriticalSection();
    
            // Verify correct input generates expected output
            result = SHA2_addData(sha2Handle, &dataToHash[0], HASH_VERIFICATION_PARTIAL_BLOCK_SIZE); // --> ISSUE: Never returns
    
            //ICall_leaveCriticalSection(key);
    
            Task_sleep((xdc_UInt32)10);
            GPIO_write(CONFIG_LED_1_GPIO, CONFIG_GPIO_LED_ON);
        }
    }
    
    /*********************************************************************
     * @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;
    
      BLE_LOG_INT_INT(0, BLE_LOG_MODULE_APP, "APP : Stack msg status=%d, event=0x%x\n", pMsg->status, pMsg->event);
    
      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;
        }
    
        default:
          // do nothing
          break;
      }
    
    #ifdef PTM_MODE
      // Check for NPI Messages
      hciPacket_t *pBuf = (hciPacket_t *)pMsg;
    
      // Serialized HCI Event
      if (pBuf->hdr.event == HCI_CTRL_TO_HOST_EVENT)
      {
        uint16_t len = 0;
    
        // Determine the packet length
        switch(pBuf->pData[0])
        {
          case HCI_EVENT_PACKET:
            len = HCI_EVENT_MIN_LENGTH + pBuf->pData[2];
            break;
    
          case HCI_ACL_DATA_PACKET:
            len = HCI_DATA_MIN_LENGTH + BUILD_UINT16(pBuf->pData[3], pBuf->pData[4]);
            break;
    
          default:
            break;
        }
    
        // Send to Remote Host.
        simple_peripheral_sendToNPI(pBuf->pData, len);
    
        // Free buffers if needed.
        switch (pBuf->pData[0])
        {
          case HCI_ACL_DATA_PACKET:
          case HCI_SCO_DATA_PACKET:
            BM_free(pBuf->pData);
          default:
            break;
        }
      }
    #endif // PTM_MODE
    
      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
        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;
    
      if (pMsg->event <= APP_EVT_EVENT_MAX)
      {
        BLE_LOG_INT_STR(0, BLE_LOG_MODULE_APP, "APP : App msg status=%d, event=%s\n", 0, appEventStrings[pMsg->event]);
      }
      else
      {
        BLE_LOG_INT_INT(0, BLE_LOG_MODULE_APP, "APP : App msg status=%d, event=0x%x\n", 0, pMsg->event);
      }
    
      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;
    
        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");
    
            BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- got GAP_DEVICE_INIT_DONE_EVENT", 0);
            // Setup and start Advertising
            // For more information, see the GAP section in the User's Guide:
            // http://software-dl.ti.com/lprf/ble5stack-latest/
    
            BLE_LOG_INT_INT(0, BLE_LOG_MODULE_APP, "APP : ---- call GapAdv_create set=%d,%d\n", 0, 0);
            // 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);
    
            BLE_LOG_INT_INT(0, BLE_LOG_MODULE_APP, "APP : ---- call GapAdv_create set=%d,%d\n", 1, 0);
            // 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);
    
            BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- GapAdv_enable", 0);
            // 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);
            }
            tbm_setItemStatus(&spMenuMain, SP_ITEM_AUTOCONNECT, TBM_ITEM_NONE);
          }
    
          break;
        }
    
        case GAP_LINK_ESTABLISHED_EVENT:
        {
          gapEstLinkReqEvent_t *pPkt = (gapEstLinkReqEvent_t *)pMsg;
    
          BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- got GAP_LINK_ESTABLISHED_EVENT", 0);
          // 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,SP_ITEM_AUTOCONNECT);
    
            // Start Periodic Clock.
            Util_startClock(&clkPeriodic);
          }
          if ((numActive < MAX_NUM_BLE_CONNS) && (autoConnect == AUTOCONNECT_DISABLE))
          {
            // 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);
            GapAdv_disable(advHandleLegacy);
          }
          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, SP_ITEM_AUTOCONNECT, SP_ITEM_SELECT_CONN);
          }
    
          BLE_LOG_INT_STR(0, BLE_LOG_MODULE_APP, "APP : GAP msg: status=%d, opcode=%s\n", 0, "GAP_LINK_TERMINATED_EVENT");
          // 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);
    
          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_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)
    {
      uint8_t* pRpaNew;
    
      // Read the current RPA.
      pRpaNew = GAP_GetDevAddress(FALSE);
    
      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);
      }
    }
    
    /*********************************************************************
     * @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);
      if (connIndex >= MAX_NUM_BLE_CONNS)
      {
        Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!");
        return FALSE;
      }
    
      // 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:
          BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- GAP_EVT_ADV_START_AFTER_ENABLE", 0);
          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)
    {
      // Get index from handle
      uint8_t connIndex = SimplePeripheral_getConnIndex(pReport->handle);
    
      if (connIndex >= MAX_NUM_BLE_CONNS)
      {
        Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!");
        return;
      }
    
      // 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_doAutoConnect
     *
     * @brief   Enable/Disable peripheral as AutoConnect node.
     *
     * @param   index - 0 : Disable AutoConnect
     *                  1 : Enable Group A
     *                  2 : Enable Group B
     *
     * @return  always true
     */
    bool SimplePeripheral_doAutoConnect(uint8_t index)
    {
        if (index == 1)
        {
          if (autoConnect != AUTOCONNECT_GROUP_A)
          {
            GapAdv_disable(advHandleLongRange);
            GapAdv_disable(advHandleLegacy);
            advData1[2] = 'G';
            advData1[3] = 'A';
            advData2[2] = 'G';
            advData2[3] = 'A';
            GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            autoConnect = AUTOCONNECT_GROUP_A;
          }	
    	  Display_printf(dispHandle, SP_ROW_AC, 0, "AutoConnect enabled: Group A");
        }
        else if (index == 2)
        {
          if (autoConnect != AUTOCONNECT_GROUP_B)
          {
            GapAdv_disable(advHandleLongRange);
            GapAdv_disable(advHandleLegacy);
            advData1[2] = 'G';
            advData1[3] = 'B';
            advData2[2] = 'G';
            advData2[3] = 'B';
            GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            autoConnect = AUTOCONNECT_GROUP_B;
          } 
          Display_printf(dispHandle, SP_ROW_AC, 0, "AutoConnect enabled: Group B");
        }
        else
        {
          if (autoConnect)
          {
            GapAdv_disable(advHandleLongRange);
            GapAdv_disable(advHandleLegacy);
            advData1[2] = 'S';
            advData1[3] = 'P';
            advData2[2] = 'S';
            advData2[3] = 'P';
            GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            autoConnect = AUTOCONNECT_DISABLE;
          } 
          Display_printf(dispHandle, SP_ROW_AC, 0, "AutoConnect disabled");
        }
        tbm_goTo(&spMenuMain);
        
        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)
        {
          // Found available entry to put a new connection info in
          connList[i].connHandle = connHandle;
    
    #ifdef DEFAULT_SEND_PARAM_UPDATE_REQ
          // Allocate data to send through clock handler
          connList[i].pParamUpdateEventData = ICall_malloc(sizeof(spClockEventData_t) +
                                                           sizeof (uint16_t));
          if(connList[i].pParamUpdateEventData)
          {
            connList[i].pParamUpdateEventData->event = SP_SEND_PARAM_UPDATE_EVT;
            *((uint16_t *)connList[i].pParamUpdateEventData->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) (connList[i].pParamUpdateEventData));
            }
            else
            {
                ICall_free(connList[i].pParamUpdateEventData);
            }
          }
          else
          {
            status = bleMemAllocError;
          }
    #endif
    
          // 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_clearPendingParamUpdate
     *
     * @brief   clean pending param update request in the paramUpdateList list
     *
     * @param   connHandle - connection handle to clean
     *
     * @return  none
     */
    void SimplePeripheral_clearPendingParamUpdate(uint16_t connHandle)
    {
      List_Elem *curr;
    
      for (curr = List_head(&paramUpdateList); curr != NULL; curr = List_next(curr)) 
      {
        if (((spConnHandleEntry_t *)curr)->connHandle == connHandle)
        {
          List_remove(&paramUpdateList, curr);
        }
      }
    }
    
    /*********************************************************************
     * @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);
          // Free ParamUpdateEventData
          ICall_free(connList[connIndex].pParamUpdateEventData);
        }
        // Clear pending update requests from paramUpdateList
        SimplePeripheral_clearPendingParamUpdate(connHandle);
        // Stop Auto PHY Change
        SimplePeripheral_stopAutoPhyChange(connHandle);
        // Clear Connection List Entry
        SimplePeripheral_clearConnListEntry(connHandle);
      }
    
      return connIndex;
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processParamUpdate
     *
     * @brief   Process a parameters update request
     *
     * @return  None
     */
    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);
      if (connIndex >= MAX_NUM_BLE_CONNS)
      {
        Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!");
        return;
      }
    
      // Deconstruct the clock object
      Clock_destruct(connList[connIndex].pUpdateClock);
      // Free clock struct, only in case it is not NULL
      if (connList[connIndex].pUpdateClock != NULL)
      {
          ICall_free(connList[connIndex].pUpdateClock);
          connList[connIndex].pUpdateClock = NULL;
      }
      // Free ParamUpdateEventData, only in case it is not NULL
      if (connList[connIndex].pParamUpdateEventData != NULL)
          ICall_free(connList[connIndex].pParamUpdateEventData);
    
      // 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:
        {
          int8 rssi = (int8)pMsg->pReturnParam[3];  
    
          // 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);
            if (index >= MAX_NUM_BLE_CONNS)
            {
              Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!");
              return;
            }
    
            if (rssi != LL_RSSI_NOT_AVAILABLE)
            {
              connList[index].rssiArr[connList[index].rssiCntr++] = rssi;
              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);
    
              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 ((phyRq != SP_PHY_NONE) && ...
              } // end of if (connList[index].phyCngRq == FALSE)
            } // end of if (rssi != LL_RSSI_NOT_AVAILABLE)
    
            Display_printf(dispHandle, SP_ROW_RSSI, 0,
                           "RSSI:%d dBm, AVG RSSI:%d dBm",
                           (uint32_t)(rssi),
                           connList[index].rssiAvg);
    
    	  } // 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;
        }
    
        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
      status = 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);
      }
    }
    /*********************************************************************
    *********************************************************************/
    
     

    I was not able to observe the application crashing using this firmware without critical sections.  The BLE connection to the LightBlue application on my phone is stable, the UART COM port terminal updates when push buttons are selected, the LED toggles, and I can pause at the SHA2_addData line at any time.  The only differences between the two implementations appear trivial, such as the random number generator.  Can you please evaluate the version of simple_peripheral which I have provided and give further instructions on what needs to be added to cause the application to fail?

    Regards,
    Ryan

  • Hi Ryan,

    Thanks for reply. I am not sure why you have changed the source file I had provided or started from your end afresh. But the source file simple_peripheral.c I had provided reproduced the issue of SHA getting stuck.

    I went ahead anyhow and pick up the simple_peripheral.c you have attached above. There are following changes I have made in order to reproduce the issue and being the scenario closest to our application:

    - Changed the SP task priority to 2 --> we want to preempt the Hash task so that we can have SHA operation interrupted by high priority task 

    - Added a random number task delay Hash task so that in multiple iterations, we can get the issues reproduced. Note that the random sleep is essential as the issue doe snot reproduce on every single start up. The issue happens rarely but catastrophic for our application where the SHA operation is stuck.

    - Added Red LED to be used when handling GAP_DEVICE_INIT_DONE_EVENT event in SimplePeripheral_processGapMessage(). The LED is turned OFF when this event is handled all the way. After that, called SysCtrlSystemReset() to again perform the test cycle as the issue is not reproduced in a first attempt. Also, we have observed that the issue is happened when GapAdv_loadByHandle() is called in  GAP_DEVICE_INIT_DONE_EVENT  event handling which is at higher priority task than Hash. But that is not always the case is but the issue is not observed after GapAdv_loadByHandle() is completed. 

    All we are trying to do is perform the test again and again until the observation in the next step is seen.

    - Used Green LED on LP under the Hash task start and end. At start, the LED is ON and once the SHA operation is completed, the LEDS is OFF. When the issue is present, the Green LED remains ON which signifies that the SHA operation is not returned.

    - Used Debug configuration build. But the debug session is not present as the code tries to reboot itself by calling SysCtrlSystemReset() as mentioned above.

    - Also, we have freezed CCS 10.3 for our environment so below code is built out of v10.3. I expect that the issue should be reproduced on your CCS as well but we must be using v10.3 at the end.

    Here is the source file. hope this helps.

    0066.simple_peripheral.c

  • Also, the SDK is 5.10

  • Thanks for providing your code example, I was able to use it to replace my version without any difficulty.

    You cannot put SysCtrlSystemReset at the end of the GAP_DEVICE_INIT_DONE_EVENT case as this will continuously reset the device after every initialization.  Thus the LEDs alternate constantly but the BLE connection and terminal control are broken.  You can simply perform a hardware reset if the issue is not reproduced after the first run.

    As the hash task is continuously cycling through SHA2_addData, the green LED will be on constantly as a high duty cycle PWM.  However the period is so fast that the toggling cannot be detected visually, although it should be dimmer than 100% upon further inspection.  You could prove this by switching the order of CONFIG_GPIO_LED_OFF/CONFIG_GPIO_LED_ON and observing that the green LED is still on, albeit much more dim.

    The code example you provided still operates as expected as far as I can ascertain.  I am using all of the necessary dependencies except CCS v11.2 which should not make a difference.

    Regards,
    Ryan

  • Hi Ryan,

    I'm working on this issue with Mehul, so I'll provide some more explanation why the code we provided is setup the way it is.

    Perhaps we haven't made this clear. We've traced the firmware lockup issue we're discussing here to when the SHA2_addData function is interrupted by a higher priority task to process the GAP_DEVICE_INIT_DONE_EVENT, specifically around when the GapAdv_loadByHandle function is called.

    Because the GAP_DEVICE_INIT_DONE_EVENT is triggered only once when the simple peripheral project goes through it's startup routine, we've intentionally put the SysCtrlSystemReset at the end of the GAP_DEVICE_INIT_DONE_EVENT to restart the device continuously to be able to reproduce the issue we're seeing quickly and consistently and thus be able to share with you to be able to reproduce on your setup. When the issue is reproduced, the firmware will lock up and thus stop the BLE MCU from continuously rebooting. Our intention of adding the LED toggles are to visually show the state of the BLE firmware when the firmware is locked up, specifically that the SHA2_addData operation is the active operation that is stuck. 

    So, using the simple peripheral source code that Mehul attached, you should be able to follow the steps below and see the firmware lockup behavior we're seeing.

    1. Build the simple peripheral project using the attached simple peripheral code that Mehul provided. Program the built firmware onto a launchpad. 
    2. Reset the launchpad by pressing the reset button next to the USB port
    3. Observe the LED behavior. You should see the Red and Green LED flash on and off a few times followed by the Green LED remaining on and the Red LED remaining off. 
    4. This indicates that the firmware is now stuck. The Green LED is on signifying the SHA2_addData operation was active but interrupted. The red LED is off showing the GapAdv_loadByHandle function calls have completed, but the SysCtrlSystemReset did not complete as the BLE MCU did not reset.
    5. Performing step 2 above again will recreate steps 3 and 4. The number of times the Red and Green LEDs will blink will vary due to the random number generator controlling the task sleep for the hash task function to make it so the SHA2_addData function is interrupted by the simple peripheral task.

    I just deleted and reimported the simple peripheral example project, added the red/green LEDs and SHA2 module using sysconfig, and copied in the simple peripheral source code Mehul posted and can follow the steps above and repeatedly produce the behavior described above in the steps. 

    Let us know if this helps at all or if anything is unclear.

    Thanks for your continued efforts and support.

    -Matt

  • Thanks for clarifying, Matt.  I used Mehul's code as provided and replicated your instructions but was unable to observe the issue.  The LEDs consistently blink without stopping.  It would be best to have a setup which does not rely on continuous system resets.

    Regards,
    Ryan

  • Interesting and unfortunate that the behavior is not the same for you. To remove any remaining differences between your setup and ours, I downloaded and installed CCS v11.2, which installed xdctools v3.62.01.16 (you mentioned you were using v3.62.01.15), and performed the same steps I outlined above in my previous post and saw the same results, the LEDs stopped flashing with the green LED on and the red LED off. So I'm not sure what else could be different between our setups at this point.

    We can try to find another way to recreate the issue we're seeing without the continuous system resets. That was our intention with the other thread we have going with you as well (CC2642R: Issue with aes and sha drivers - Bluetooth forum - Bluetooth®︎ - TI E2E support forums). Any reason the continuous system resets are problematic? Just curious.

    Thanks,

    -Matt

  • How quickly/often does the operation freeze occur?  Have you tested different LaunchPads and are they modified in any way?  Can other users recreate the behavior you are describing using their own system?

    The other E2E thread was resolved as being unrelated to the behavior reported here.  Continuous resets are not practical and difficult to debug since the JTAG interface cannot be connected.

    Regards,
    Ryan

  • Very rarely. We have custom hardware/firmware and are able to reproduce the behavior we're seeing on multiple boards and have narrowed down the firmware behavior to be performing the SHA2_addData operation and it gets interrupted to perform the functionality similar to GAP_DEVICE_INIT_DONE_EVENT where the advertising data is loaded for the first time at startup with the GapAdv_loadByHandle function. At this point the firmware locks up. We have not seen this issue occur during normal runtime of the firmware after this event completes successfully.

    As such, we've been trying to reproduce this behavior in the simple peripheral project to be able to share with you. The code that Mehul has been sharing in this thread, we're able to reproduce the behavior we're describing to you on our own respective stock/unmodified launchpads. 

    Understood and agreed with the continuous reboots being difficult to debug. Our design is very low power and is unable to run a debug session with JTAG while reproducing this issue we're seeing unfortunately, so we were utilizing other means (pin toggling, system resets) to get to the bottom of the firmware behavior and recreate what we're seeing in simple peripheral example project.

  • Ryan,

    I have updated the code to use the UART for understanding when the SHA operation is freezed since we cannot reliably determine it from the LED. The LED code is present as well.

    Essentially, the SHA task is writing to the UART continuously (PuTTY terminal shows frequent updates) and when the issue happens, the PuTTY window does no longer updates from the SHA task as the SHA task is stuck in waiting.

    I have commented out SysCtrlSystemReset() call. We need to reset the board manually now using the reset key available on LP to reproduce the issue. It did take only 10-15 manual reset to reproduce the issue. Sometimes, it is even before that. Also, when the SHA task is having critical section enabled, the issue is not seen even after ~50 manual reset cycle. See attached code:

    1817.simple_peripheral.c

  • Mehul,

    Thank you for providing your code and reproduction steps.  I have been able to observe the issue from my side as well.  The hash task appears to freeze upon startup about one in five times, although the occurrence is quite random.  It seems that the issue always occurs during application initialization, otherwise it will be fine for the entire run time.

    I will align with the BLE Development Team so that we may further determine the root cause of this behavior.  Meanwhile, it appears that starting the hash task after BLE initialization resolves the issue.  I moved Task_construct to the bottom of SimplePeripheral_init and no longer observed the problem.

    /******************************************************************************
    
     @file  simple_peripheral.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-2021, 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>
    #include <ti/drivers/GPIO.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 <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/devices/cc13x2_cc26x2/driverlib/sys_ctrl.h>                  // for SysCtrlSystemReset()
    
    #include "simple_peripheral_menu.h"
    #include "simple_peripheral.h"
    #include "ti_ble_config.h"
    
    #ifdef PTM_MODE
    #include "npi_task.h"               // To allow RX event registration
    #include "npi_ble.h"                // To enable transmission of messages to UART
    #include "icall_hci_tl.h"   // To allow ICall HCI Transport Layer
    #endif // PTM_MODE
    
    
    #include <ti/sysbios/knl/Task.h>        // For Task_construct and task related configuration
    
    #include <ti/drivers/utils/Random.h>                                // for Random_getNumber()
    #include <ti/sysbios/hal/Hwi.h>               // For HWI disable and restore
    #include <ti/sysbios/knl/Swi.h>               // For SWI disable and restore
    
    
    /* Driver Header files */
    #include <ti/drivers/SHA2.h>
    
    /* Driver configuration */
    #include "ti_drivers_config.h"
    
    
    /*********************************************************************
     * MACROS
     */
    
    /*********************************************************************
     * CONSTANTS
     */
    // How often to perform periodic event (in ms)
    #define SP_PERIODIC_EVT_PERIOD               5000
    
    // Task configuration
    #define SP_TASK_PRIORITY                     2
    #define HASH_TASK_PRIORITY                     1
    #define HASH_TASK_STACK_SIZE                   2048
    
    #ifndef SP_TASK_STACK_SIZE
    #define SP_TASK_STACK_SIZE                   1024
    #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
    
    // 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)
    
    // 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)
    #define SP_ROW_AC            (TBM_ROW_APP + 9)
    #define SP_ROW_SHA            (TBM_ROW_APP + 10)
    #define SP_ROW_GAP            (TBM_ROW_APP + 12)
    
    // 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           
    #define RSSI_1M_THRSHLD           -40           
    #define RSSI_S2_THRSHLD           -50           
    #define RSSI_S8_THRSHLD           -60           
    #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
     */
    
    // Auto connect availble groups
    enum
    {
      AUTOCONNECT_DISABLE = 0,              // Disable
      AUTOCONNECT_GROUP_A = 1,              // Group A
      AUTOCONNECT_GROUP_B = 2               // Group B
    };
    
    
    // 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
      spClockEventData_t*   pParamUpdateEventData;
      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
     */
    static uint8_t* pRpaNew;
    
    // Display Interface
    Display_Handle dispHandle = NULL;
    
    // Task configuration
    Task_Struct spTask;
    Task_Struct hashTask;
    #if defined __TI_COMPILER_VERSION__
    #pragma DATA_ALIGN(spTaskStack, 8)
    #else
    #pragma data_alignment=8
    #endif
    uint8_t spTaskStack[SP_TASK_STACK_SIZE];
    uint8_t hashTaskStack[HASH_TASK_STACK_SIZE];
    
    #define APP_EVT_EVENT_MAX 0x9
    char *appEventStrings[] = {
      "APP_STATE_CHANGE_EVT     ",
      "APP_CHAR_CHANGE_EVT      ",
      "APP_KEY_CHANGE_EVT       ",
      "APP_ADV_EVT              ",
      "APP_PAIR_STATE_EVT       ",
      "APP_PASSCODE_EVT         ",
      "APP_READ_RPA_EVT         ",
      "APP_PERIODIC_EVT         ",
      "APP_SEND_PARAM_UPDATE_EVT",
      "APP_CONN_EVT             ",
    };
    
    /*********************************************************************
     * 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;
    
    // Auto connect Disabled/Enabled {0 - Disabled, 1- Group A , 2-Group B, ...}
    uint8_t autoConnect = AUTOCONNECT_DISABLE;
    
    // 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};
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    
    static void SimplePeripheral_init( void );
    static void SimplePeripheral_taskFxn(UArg a0, UArg a1);
    static void Hash_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_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 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);
    #ifdef PTM_MODE
    void simple_peripheral_handleNPIRxInterceptEvent(uint8_t *pMsg);  // Declaration
    static void simple_peripheral_sendToNPI(uint8_t *buf, uint16_t len);  // Declaration
    #endif // PTM_MODE
    
    /*********************************************************************
     * 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
    };
    
    /*********************************************************************
     * 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++;
      }
    }
    
    #ifdef PTM_MODE
    /*********************************************************************
    * @fn      simple_peripheral_handleNPIRxInterceptEvent
    *
    * @brief   Intercept an NPI RX serial message and queue for this application.
    *
    * @param   pMsg - a NPIMSG_msg_t containing the intercepted message.
    *
    * @return  none.
    */
    void simple_peripheral_handleNPIRxInterceptEvent(uint8_t *pMsg)
    {
     // Send Command via HCI TL
     HCI_TL_SendToStack(((NPIMSG_msg_t *)pMsg)->pBuf);
    
     // The data is stored as a message, free this first.
     ICall_freeMsg(((NPIMSG_msg_t *)pMsg)->pBuf);
    
     // Free container.
     ICall_free(pMsg);
    }
    
    /*********************************************************************
    * @fn      simple_peripheral_sendToNPI
    *
    * @brief   Create an NPI packet and send to NPI to transmit.
    *
    * @param   buf - pointer HCI event or data.
    *
    * @param   len - length of buf in bytes.
    *
    * @return  none
    */
    static void simple_peripheral_sendToNPI(uint8_t *buf, uint16_t len)
    {
     npiPkt_t *pNpiPkt = (npiPkt_t *)ICall_allocMsg(sizeof(npiPkt_t) + len);
    
     if (pNpiPkt)
     {
       pNpiPkt->hdr.event = buf[0]; //Has the event status code in first byte of payload
       pNpiPkt->hdr.status = 0xFF;
       pNpiPkt->pktLen = len;
       pNpiPkt->pData  = (uint8 *)(pNpiPkt + 1);
    
       memcpy(pNpiPkt->pData, buf, len);
    
       // Send to NPI
       // Note: there is no need to free this packet.  NPI will do that itself.
       NPITask_sendToHost((uint8_t *)pNpiPkt);
     }
    }
    #endif // PTM_MODE
    
    /*********************************************************************
     * @fn      SimplePeripheral_createTask
     *
     * @brief   Task creation function for the Simple Peripheral.
     */
    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)
    {
      BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- init ", SP_TASK_PRIORITY);
      // 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);
    
    #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);
    
      // 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);
    
      BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- call GAP_DeviceInit", GAP_PROFILE_PERIPHERAL);
      //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, TBM_ITEM_NONE, TBM_ITEM_ALL);
    
      tbm_initTwoBtnMenu(dispHandle, &spMenuMain, 5, SimplePeripheral_menuSwitchCb);
      Display_printf(dispHandle, SP_ROW_SEPARATOR_1, 0, "====================");
    
    #ifdef PTM_MODE
      // Intercept NPI RX events.
      NPITask_registerIncomingRXEventAppCB(simple_peripheral_handleNPIRxInterceptEvent, INTERCEPT);
    
      // Register for Command Status information
      HCI_TL_Init(NULL, (HCI_TL_CommandStatusCB_t) simple_peripheral_sendToNPI, NULL, selfEntity);
    
      // Register for Events
      HCI_TL_getCmdResponderID(ICall_getLocalMsgEntityId(ICALL_SERVICE_CLASS_BLE_MSG, selfEntity));
    
      // Inform Stack to Initialize PTM
      HCI_EXT_EnablePTMCmd();
    #endif // PTM_MODE
    
      // Configure task
      Task_Params hashtaskParams;
      Task_Params_init(&hashtaskParams);
      hashtaskParams.stack = hashTaskStack;
      hashtaskParams.stackSize = HASH_TASK_STACK_SIZE;
      hashtaskParams.priority = HASH_TASK_PRIORITY;
    
      Task_construct(&hashTask, Hash_taskFxn, &hashtaskParams, NULL);
    
    }
    
    /*********************************************************************
     * @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);
              }
            }
          }
        }
      }
    }
    
    #define HASH_VERIFICATION_PARTIAL_BLOCK_SIZE  (size_t)64
    static uint8_t dataToHash[HASH_VERIFICATION_PARTIAL_BLOCK_SIZE];
    static void Hash_taskFxn(UArg a0, UArg a1)
    {
        int_fast16_t result;
    
        /* Driver handles */
        SHA2_Handle                 sha2Handle;
    
        /* Call driver initialization functions */
        SHA2_init();
    
        SHA2_Params params;
        SHA2_Params_init(&params);
        params.hashType = SHA2_HASH_TYPE_512;
        params.returnBehavior = SHA2_RETURN_BEHAVIOR_POLLING;
        sha2Handle = SHA2_open(0, &params);
    
        if (!sha2Handle) {
            /* SHA2_open() failed */
            while(1);
        }
    
        uint32_t randomNumber = Random_getNumber();
        unsigned char* input_correct_2 = (unsigned char *)(randomNumber % 1000);
    
        (void)memset((void*)&dataToHash[0], 0x00, HASH_VERIFICATION_PARTIAL_BLOCK_SIZE);
    //    (void)memcpy((void*)&dataToHash[0], (void*)input_correct_2, HASH_VERIFICATION_PARTIAL_BLOCK_SIZE);
    
        for(;;)
        {
            randomNumber = Random_getNumber();
    
            randomNumber %= 17;
            GPIO_write(CONFIG_LED_1_GPIO, CONFIG_GPIO_LED_ON);
    
    
            //ICall_CSState key = ICall_enterCriticalSection();
    
            // Verify correct input generates expected output
            result = SHA2_addData(sha2Handle, &dataToHash[0], HASH_VERIFICATION_PARTIAL_BLOCK_SIZE); // --> ISSUE: Never returns
    
            //ICall_leaveCriticalSection(key);
    
            Task_sleep((xdc_UInt32)randomNumber+1);
    
            Display_printf(dispHandle, SP_ROW_SHA+1, 0, "SHA Done: %d, RP Addr: %s", randomNumber, Util_convertBdAddr2Str(pRpaNew));
            GPIO_write(CONFIG_LED_1_GPIO, CONFIG_GPIO_LED_OFF);
    
            Task_sleep((xdc_UInt32)randomNumber+100);
    
         }
    }
    
    /*********************************************************************
     * @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;
    
      BLE_LOG_INT_INT(0, BLE_LOG_MODULE_APP, "APP : Stack msg status=%d, event=0x%x\n", pMsg->status, pMsg->event);
    
      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;
        }
    
        default:
          // do nothing
          break;
      }
    
    #ifdef PTM_MODE
      // Check for NPI Messages
      hciPacket_t *pBuf = (hciPacket_t *)pMsg;
    
      // Serialized HCI Event
      if (pBuf->hdr.event == HCI_CTRL_TO_HOST_EVENT)
      {
        uint16_t len = 0;
    
        // Determine the packet length
        switch(pBuf->pData[0])
        {
          case HCI_EVENT_PACKET:
            len = HCI_EVENT_MIN_LENGTH + pBuf->pData[2];
            break;
    
          case HCI_ACL_DATA_PACKET:
            len = HCI_DATA_MIN_LENGTH + BUILD_UINT16(pBuf->pData[3], pBuf->pData[4]);
            break;
    
          default:
            break;
        }
    
        // Send to Remote Host.
        simple_peripheral_sendToNPI(pBuf->pData, len);
    
        // Free buffers if needed.
        switch (pBuf->pData[0])
        {
          case HCI_ACL_DATA_PACKET:
          case HCI_SCO_DATA_PACKET:
            BM_free(pBuf->pData);
          default:
            break;
        }
      }
    #endif // PTM_MODE
    
      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
        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;
    
      if (pMsg->event <= APP_EVT_EVENT_MAX)
      {
        BLE_LOG_INT_STR(0, BLE_LOG_MODULE_APP, "APP : App msg status=%d, event=%s\n", 0, appEventStrings[pMsg->event]);
      }
      else
      {
        BLE_LOG_INT_INT(0, BLE_LOG_MODULE_APP, "APP : App msg status=%d, event=0x%x\n", 0, pMsg->event);
      }
    
      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;
    
        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)
          {
              GPIO_write(CONFIG_LED_0_GPIO, CONFIG_GPIO_LED_ON);
            // 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");
    
            BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- got GAP_DEVICE_INIT_DONE_EVENT", 0);
            // Setup and start Advertising
            // For more information, see the GAP section in the User's Guide:
            // http://software-dl.ti.com/lprf/ble5stack-latest/
    
            BLE_LOG_INT_INT(0, BLE_LOG_MODULE_APP, "APP : ---- call GapAdv_create set=%d,%d\n", 0, 0);
            // 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);
    
            BLE_LOG_INT_INT(0, BLE_LOG_MODULE_APP, "APP : ---- call GapAdv_create set=%d,%d\n", 1, 0);
            // Create Advertisement set #2 and assign handle
            status = GapAdv_create(&SimplePeripheral_advCallback, &advParams2,
                                   &advHandleLongRange);
            SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
    
            Display_printf(dispHandle, SP_ROW_GAP, 0, "GAP Load: Before, RP Addr: %s", Util_convertBdAddr2Str(pRpaNew));
    
            // 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);
    
            BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- GapAdv_enable", 0);
            // 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);
            }
            tbm_setItemStatus(&spMenuMain, SP_ITEM_AUTOCONNECT, TBM_ITEM_NONE);
    
            Display_printf(dispHandle, SP_ROW_GAP+1, 0, "GAP Load: After, RP Addr: %s", Util_convertBdAddr2Str(pRpaNew));
    
            GPIO_write(CONFIG_LED_0_GPIO, CONFIG_GPIO_LED_OFF);
            // Reboot BLE MCU
            //SysCtrlSystemReset();
          }
    
          break;
        }
    
        case GAP_LINK_ESTABLISHED_EVENT:
        {
          gapEstLinkReqEvent_t *pPkt = (gapEstLinkReqEvent_t *)pMsg;
    
          BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- got GAP_LINK_ESTABLISHED_EVENT", 0);
          // 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,SP_ITEM_AUTOCONNECT);
    
            // Start Periodic Clock.
            Util_startClock(&clkPeriodic);
          }
          if ((numActive < MAX_NUM_BLE_CONNS) && (autoConnect == AUTOCONNECT_DISABLE))
          {
            // 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);
            GapAdv_disable(advHandleLegacy);
          }
          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, SP_ITEM_AUTOCONNECT, SP_ITEM_SELECT_CONN);
          }
    
          BLE_LOG_INT_STR(0, BLE_LOG_MODULE_APP, "APP : GAP msg: status=%d, opcode=%s\n", 0, "GAP_LINK_TERMINATED_EVENT");
          // 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);
    
          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_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)
    {
      //uint8_t* pRpaNew;
    
      // Read the current RPA.
      pRpaNew = GAP_GetDevAddress(FALSE);
    
      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);
      }
    }
    
    /*********************************************************************
     * @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);
      if (connIndex >= MAX_NUM_BLE_CONNS)
      {
        Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!");
        return FALSE;
      }
    
      // 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:
          BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- GAP_EVT_ADV_START_AFTER_ENABLE", 0);
          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)
    {
      // Get index from handle
      uint8_t connIndex = SimplePeripheral_getConnIndex(pReport->handle);
    
      if (connIndex >= MAX_NUM_BLE_CONNS)
      {
        Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!");
        return;
      }
    
      // 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_doAutoConnect
     *
     * @brief   Enable/Disable peripheral as AutoConnect node.
     *
     * @param   index - 0 : Disable AutoConnect
     *                  1 : Enable Group A
     *                  2 : Enable Group B
     *
     * @return  always true
     */
    bool SimplePeripheral_doAutoConnect(uint8_t index)
    {
        if (index == 1)
        {
          if (autoConnect != AUTOCONNECT_GROUP_A)
          {
            GapAdv_disable(advHandleLongRange);
            GapAdv_disable(advHandleLegacy);
            advData1[2] = 'G';
            advData1[3] = 'A';
            advData2[2] = 'G';
            advData2[3] = 'A';
            GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            autoConnect = AUTOCONNECT_GROUP_A;
          }
          Display_printf(dispHandle, SP_ROW_AC, 0, "AutoConnect enabled: Group A");
        }
        else if (index == 2)
        {
          if (autoConnect != AUTOCONNECT_GROUP_B)
          {
            GapAdv_disable(advHandleLongRange);
            GapAdv_disable(advHandleLegacy);
            advData1[2] = 'G';
            advData1[3] = 'B';
            advData2[2] = 'G';
            advData2[3] = 'B';
            GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            autoConnect = AUTOCONNECT_GROUP_B;
          } 
          Display_printf(dispHandle, SP_ROW_AC, 0, "AutoConnect enabled: Group B");
        }
        else
        {
          if (autoConnect)
          {
            GapAdv_disable(advHandleLongRange);
            GapAdv_disable(advHandleLegacy);
            advData1[2] = 'S';
            advData1[3] = 'P';
            advData2[2] = 'S';
            advData2[3] = 'P';
            GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
            autoConnect = AUTOCONNECT_DISABLE;
          } 
          Display_printf(dispHandle, SP_ROW_AC, 0, "AutoConnect disabled");
        }
        tbm_goTo(&spMenuMain);
        
        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)
        {
          // Found available entry to put a new connection info in
          connList[i].connHandle = connHandle;
    
    #ifdef DEFAULT_SEND_PARAM_UPDATE_REQ
          // Allocate data to send through clock handler
          connList[i].pParamUpdateEventData = ICall_malloc(sizeof(spClockEventData_t) +
                                                           sizeof (uint16_t));
          if(connList[i].pParamUpdateEventData)
          {
            connList[i].pParamUpdateEventData->event = SP_SEND_PARAM_UPDATE_EVT;
            *((uint16_t *)connList[i].pParamUpdateEventData->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) (connList[i].pParamUpdateEventData));
            }
            else
            {
                ICall_free(connList[i].pParamUpdateEventData);
            }
          }
          else
          {
            status = bleMemAllocError;
          }
    #endif
    
          // 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_clearPendingParamUpdate
     *
     * @brief   clean pending param update request in the paramUpdateList list
     *
     * @param   connHandle - connection handle to clean
     *
     * @return  none
     */
    void SimplePeripheral_clearPendingParamUpdate(uint16_t connHandle)
    {
      List_Elem *curr;
    
      for (curr = List_head(&paramUpdateList); curr != NULL; curr = List_next(curr)) 
      {
        if (((spConnHandleEntry_t *)curr)->connHandle == connHandle)
        {
          List_remove(&paramUpdateList, curr);
        }
      }
    }
    
    /*********************************************************************
     * @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);
          // Free ParamUpdateEventData
          ICall_free(connList[connIndex].pParamUpdateEventData);
        }
        // Clear pending update requests from paramUpdateList
        SimplePeripheral_clearPendingParamUpdate(connHandle);
        // Stop Auto PHY Change
        SimplePeripheral_stopAutoPhyChange(connHandle);
        // Clear Connection List Entry
        SimplePeripheral_clearConnListEntry(connHandle);
      }
    
      return connIndex;
    }
    
    /*********************************************************************
     * @fn      SimplePeripheral_processParamUpdate
     *
     * @brief   Process a parameters update request
     *
     * @return  None
     */
    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);
      if (connIndex >= MAX_NUM_BLE_CONNS)
      {
        Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!");
        return;
      }
    
      // Deconstruct the clock object
      Clock_destruct(connList[connIndex].pUpdateClock);
      // Free clock struct, only in case it is not NULL
      if (connList[connIndex].pUpdateClock != NULL)
      {
          ICall_free(connList[connIndex].pUpdateClock);
          connList[connIndex].pUpdateClock = NULL;
      }
      // Free ParamUpdateEventData, only in case it is not NULL
      if (connList[connIndex].pParamUpdateEventData != NULL)
          ICall_free(connList[connIndex].pParamUpdateEventData);
    
      // 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:
        {
          int8 rssi = (int8)pMsg->pReturnParam[3];  
    
          // 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);
            if (index >= MAX_NUM_BLE_CONNS)
            {
              Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!");
              return;
            }
    
            if (rssi != LL_RSSI_NOT_AVAILABLE)
            {
              connList[index].rssiArr[connList[index].rssiCntr++] = rssi;
              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);
    
              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 ((phyRq != SP_PHY_NONE) && ...
              } // end of if (connList[index].phyCngRq == FALSE)
            } // end of if (rssi != LL_RSSI_NOT_AVAILABLE)
    
            Display_printf(dispHandle, SP_ROW_RSSI, 0,
                           "RSSI:%d dBm, AVG RSSI:%d dBm",
                           (uint32_t)(rssi),
                           connList[index].rssiAvg);
    
          } // 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;
        }
    
        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
      status = 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);
      }
    }
    /*********************************************************************
    *********************************************************************/
    

    Please try evaluating this solution while TI investigates the effect BLE initialization may have on SHA2 operations.

    Regards,
    Ryan

  • Hi Ryan,

    Thanks for confirming that you are able to reproduce the issue. We are working on alternatives as well. However, kindly let us know your findings while working with the TI BLE team. That will give us reasonable confidence on the fix.

  • Hi Ryan, 

    Any updates?

    Mehul

  • Hi Mehul,

    There are no updates to report at the moment.

    Regards,
    Ryan

  • Hi Ryan,

    Any plan to work on that issue by TI for root cause and analysis?

    Mehul

  • Plans to investigate the root cause and further analyze are still underway.

    Regards,
    Ryan