/*******************************************************************************
* File Name: main_cm4.c
*
* Version: 1.30
*
* Description: This file provides the source code for the DFU (App0)
*              running on the core CM4 (core1).
*              App0 core1 firmware does the following:
*              - Downloads App1 firmware image
*              - Switches to App1 if App1 image has successfully downloaded
*                and is valid
*              - Switches to existing App1 if button is pressed
*              - Turn on an LED depending on status
*              - Hibernates on timeout
*******************************************************************************
* Related Document: CE216767.pdf
*
* Hardware Dependency: CY8CKIT-062-BLE PSoC 6 BLE Pioneer Kit
*                      CY5677 CySmart USB Dongle
*
******************************************************************************
* Copyright (2019), Cypress Semiconductor Corporation.
******************************************************************************
* This software is owned by Cypress Semiconductor Corporation (Cypress) and is
* protected by and subject to worldwide patent protection (United States and
* foreign), United States copyright laws and international treaty provisions.
* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable
* license to copy, use, modify, create derivative works of, and compile the
* Cypress Source Code and derivative works for the sole purpose of creating
* custom software in support of licensee product to be used only in conjunction
* with a Cypress integrated circuit as specified in the applicable agreement.
* Any reproduction, modification, translation, compilation, or representation of
* this software except as specified above is prohibited without the express
* written permission of Cypress.
*
* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH
* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* Cypress reserves the right to make changes without further notice to the
* materials described herein. Cypress does not assume any liability arising out
* of the application or use of any product or circuit described herein. Cypress
* does not authorize its products for use as critical components in life-support
* systems where a malfunction or failure may reasonably be expected to result in
* significant injury to the user. The inclusion of Cypress' product in a life-
* support systems application implies that the manufacturer assumes all risk of
* such use and in doing so indemnifies Cypress against all charges. Use may be
* limited by and subject to the applicable Cypress software license agreement.
*******************************************************************************/

#include <string.h>
#include "project.h"
#include "debug.h"
#include "ias.h"
#include "transport_ble.h"

#if CY_DFU_OPT_CRYPTO_HW != 0
    #define MY_CHAN_CRYPTO         (uint32_t)(3u)    /* IPC data channel for the Crypto */
    #define MY_INTR_CRYPTO_SRV     (uint32_t)(1u)    /* IPC interrupt structure for the Crypto server */
    #define MY_INTR_CRYPTO_CLI     (uint32_t)(2u)    /* IPC interrupt structure for the Crypto client */
    #define MY_INTR_CRYPTO_SRV_MUX (IRQn_Type)(2u)   /* CM0+ IPC interrupt mux number the Crypto server */
    #define MY_INTR_CRYPTO_CLI_MUX (IRQn_Type)(3u)   /* CM0+ IPC interrupt mux number the Crypto client */
    #define MY_INTR_CRYPTO_ERR_MUX (IRQn_Type)(4u)   /* CM0+ ERROR interrupt mux number the Crypto server */
    
    const cy_stc_crypto_config_t cryptoConfig =
    {
        /* .ipcChannel             */ MY_CHAN_CRYPTO,
        /* .acquireNotifierChannel */ MY_INTR_CRYPTO_SRV,
        /* .releaseNotifierChannel */ MY_INTR_CRYPTO_CLI,
        /* .releaseNotifierConfig */ {
        #if (CY_CPU_CORTEX_M0P)
            /* .intrSrc            */ MY_INTR_CRYPTO_CLI_MUX,
            /* .cm0pSrc            */ cpuss_interrupts_ipc_2_IRQn, /* depends on selected releaseNotifierChannel value */
        #else
            /* .intrSrc            */ cpuss_interrupts_ipc_2_IRQn, /* depends on selected releaseNotifierChannel value */
        #endif
            /* .intrPriority       */ 2u,
        },
        /* .userCompleteCallback   */ NULL,
        /* .userGetDataHandler     */ NULL,
        /* .userErrorHandler       */ NULL,
        /* .acquireNotifierConfig */ {
        #if (CY_CPU_CORTEX_M0P)
            /* .intrSrc            */ MY_INTR_CRYPTO_SRV_MUX,      /* to use with DeepSleep mode should be in DeepSleep capable muxer's range */
            /* .cm0pSrc            */ cpuss_interrupts_ipc_1_IRQn, /* depends on selected acquireNotifierChannel value */
        #else
            /* .intrSrc            */ cpuss_interrupts_ipc_1_IRQn, /* depends on selected acquireNotifierChannel value */
        #endif
            /* .intrPriority       */ 2u,
        },
        /* .cryptoErrorIntrConfig */ {
        #if (CY_CPU_CORTEX_M0P)
            /* .intrSrc            */ MY_INTR_CRYPTO_ERR_MUX,
            /* .cm0pSrc            */ cpuss_interrupt_crypto_IRQn,
        #else
            /* .intrSrc            */ cpuss_interrupt_crypto_IRQn,
        #endif
            /* .intrPriority       */ 2u,
        }
    };
    cy_stc_crypto_context_t     cryptoContext;
    cy_en_crypto_status_t       cryptoStatus;
#endif

/* BLE GAPP Connection Settings */
#define CYBLE_GAPP_CONNECTION_INTERVAL_MIN  (0x000Cu) /* 15 ms - (N * 1,25)*/
#define CYBLE_GAPP_CONNECTION_INTERVAL_MAX  (0x000Cu) /* 15 ms */
#define CYBLE_GAPP_CONNECTION_SLAVE_LATENCY (0x0000u)
#define CYBLE_GAPP_CONNECTION_TIME_OUT      (0x00C8u) /* 2000 ms */

/* BLE Callback function, defined in this file */
void AppCallBack(uint32 event, void* eventParam);

/* Local functions */
static cy_en_dfu_status_t CopyRow(uint32_t dest, uint32_t src, uint32_t rowSize, cy_stc_dfu_params_t * params);
static cy_en_dfu_status_t HandleMetadata(cy_stc_dfu_params_t *params);
static bool               IsButtonPressed(uint16_t timeoutInMilis);
static uint32_t           counterTimeoutSeconds(uint32_t seconds, uint32_t timeout);

/* This section is used to verify an application signature 
   For sha256 verification, set the number of elements in the array to 64, and
   in bootload_common.ld set __cy_boot_signature_size = 256.
*/
CY_SECTION(".cy_app_signature") __USED static const uint32_t cy_bootload_appSignature[64];

/*******************************************************************************
* Function Name: main
********************************************************************************
*
* Summary:
*  Main function of the DFU application (App0).
*  1. If application started from non-software reset it validates App1
*  1.1. If App1 is valid it switches to App1, else goto #2.
*  2. Start DFU communication.
*  3. If updated application has been received it validates this app.
*  4. If App1 is valid it switches to it, else wait for new application.
*  5. If 300 seconds have passed and no new application has been received
*     then validate App1, if it is valid then switch to it, else hibernate
*     (Happens in the BLE AppCallBack).
*
*******************************************************************************/
int main(void)
{
    /* timeout for Cy_DFU_Continue(), in milliseconds */
    const uint32_t paramsTimeout = 20u;

    /* used to configure DFU */
    static cy_stc_dfu_params_t dfuParams;

    /* Status codes from DFU SDK API */
    cy_en_dfu_status_t status;

    /* SW2 released after deciding to stay in App0 */
    bool buttonReleased = false;

    /* 
    * DFU state, one of
    * - CY_DFU_STATE_NONE
    * - CY_DFU_STATE_UPDATING
    * - CY_DFU_STATE_FINISHED
    * - CY_DFU_STATE_FAILED
    */
    uint32_t state = CY_DFU_STATE_NONE;
    
    cy_en_ble_api_result_t apiResult;
    cy_stc_ble_stack_lib_version_t stackVersion;
    
    /*
    * Used to count seconds, to convert counts to seconds use
    * counterTimeoutSeconds(SECONDS, paramsTimeout)
    */
    uint32_t count = 0;
    
    uint32_t ledTimer = 0;
    
#if CY_DFU_OPT_CRYPTO_HW != 0
    cy_en_crypto_status_t cryptoStatus;
#endif

    /* Buffer to store DFU commands */
    CY_ALIGN(4) static uint8_t buffer[CY_DFU_SIZEOF_DATA_BUFFER];

    /* Buffer for DFU packets for Transport API */
    CY_ALIGN(4) static uint8_t packet[CY_DFU_SIZEOF_CMD_BUFFER];    
    
    /* Enable global interrupts */
    __enable_irq();
    
    /* Start UART Services */
    UART_START();
    
    /* Initializes LEDs */
    InitLED();
    
#if CY_DFU_OPT_CRYPTO_HW != 0
    /* Initialize the Crypto Client code */
    cryptoStatus = Cy_Crypto_Init(&cryptoConfig, &cryptoContext);
    if (cryptoStatus != CY_CRYPTO_SUCCESS)
    {
        /* Crypto not initialized; debug what is the problem */
        Cy_SysLib_Halt(0x00u);
    }
    cryptoStatus = Cy_Crypto_Enable();
    if (cryptoStatus != CY_CRYPTO_SUCCESS)
    {
        /* Crypto not enabled; debug what is the problem */
        Cy_SysLib_Halt(0x00u);
    }
#endif /* CY_DFU_OPT_CRYPTO_HW != 0 */

    /* Initialize dfuParams structure and DFU SDK state */
    dfuParams.timeout      = paramsTimeout;
    dfuParams.dataBuffer   = &buffer[0];
    dfuParams.packetBuffer = &packet[0];

    status = Cy_DFU_Init(&state, &dfuParams);

    /* Ensure DFU Metadata is valid */
    status = HandleMetadata(&dfuParams);
    if (status != CY_DFU_SUCCESS)
    {
        Cy_SysLib_Halt(0x00u);
    }
    
    /*
    * In the case of non-software reset and user does not
    * want to stay in App0, check if there is a valid app image.
    * If there is - switch to it.
    */
    if ((Cy_SysLib_GetResetReason() != CY_SYSLIB_RESET_SOFT) && (IsButtonPressed(2000u) == false))
    {
        status = Cy_DFU_ValidateApp(1u, &dfuParams);
        if (status == CY_DFU_SUCCESS)
        {
            /*        
            * Clear reset reason because Cy_DFU_ExecuteApp() performs        
            * a software reset.        
            * Without clearing two reset reasons would be present.        
            */
            do
            {
                Cy_SysLib_ClearResetReason();
            }while(Cy_SysLib_GetResetReason() != 0);
            /* Never returns */
            Cy_DFU_ExecuteApp(1u);
        }
    }
    
    /* Initialize DFU communication */
    Cy_DFU_TransportStart();
    /* Initializes the Immediate Alert Service */
    IasInit();
    
    /* Output current stack version to UART */
    apiResult = Cy_BLE_GetStackLibraryVersion(&stackVersion);
    if(apiResult != CY_BLE_SUCCESS)
    {
        DBG_PRINTF("CyBle_GetStackLibraryVersion API Error: 0x%2.2x \r\n", apiResult);
    }
    else
    {
        DBG_PRINTF("Stack Version: %d.%d.%d.%d \r\n", stackVersion.majorVersion, 
            stackVersion.minorVersion, stackVersion.patch, stackVersion.buildNumber);
    }
    
    for(;;)
    {
        /* CyBle_ProcessEvents() allows BLE stack to process pending events */
        Cy_BLE_ProcessEvents();

        /* Process DFU commands */
        status = Cy_DFU_Continue(&state, &dfuParams);
        ++count;

        switch(state)
        {
        case CY_DFU_STATE_FINISHED:
            /* Finished downloading the application image */
            
            /* Validate downloaded application, if it is valid then switch to it */
            status = Cy_DFU_ValidateApp(1u, &dfuParams);
            if (status == CY_DFU_SUCCESS)
            {
                Cy_DFU_TransportStop();
                /*        
                * Clear reset reason because Cy_DFU_ExecuteApp() performs        
                * a software reset.        
                * Without clearing two reset reasons would be present.        
                */        
                do
                {
                    Cy_SysLib_ClearResetReason();
                }while(Cy_SysLib_GetResetReason() != 0);
                /* Never returns */
                Cy_DFU_ExecuteApp(1u);
            }
            else if (status == CY_DFU_ERROR_VERIFY)
            {
                /*
                * Restarts DFU, alternatives are to Halt MCU here
                * or switch to the other app if it is valid.
                * Error code may be handled here, i.e. print to debug UART.
                */
                status = Cy_DFU_Init(&state, &dfuParams);
                /* Reset LED */
                ConnectedLED();
                ledTimer = 0;
                Cy_DFU_TransportReset();
            }
            break;
        case CY_DFU_STATE_FAILED:
            /* Handle error here */
            DBG_PRINTF("Downloading has failed with error code 0x%x, try again\r\n", status);
            
            /* In this Code Example just restart DFU process */
            status = Cy_DFU_Init(&state, &dfuParams);
            /* Reset LED */
            ConnectedLED();
            ledTimer = 0;
            Cy_DFU_TransportReset();
            break;        
        case CY_DFU_STATE_UPDATING:            
            /* Reset timeout counter, if a command was correctly received */
            if (status == CY_DFU_SUCCESS)
            {
                count = 0u;
            }
            else if (status == CY_DFU_ERROR_TIMEOUT)
            {
                /*
                * if no command has been received during 5 seconds when DFU
                * has started then restart DFU.
                */
                if (count >= counterTimeoutSeconds(5u, paramsTimeout))
                {
                    count = 0u;
                    Cy_DFU_Init(&state, &dfuParams);
                    /* Reset LED */
                    ConnectedLED();
                    ledTimer = 0;
                    Cy_DFU_TransportReset();
                }
            }
            else
            {
                count = 0u;
                /* Delay because Transport still may be sending error response to a host */
                Cy_SysLib_Delay(paramsTimeout);
                Cy_DFU_Init(&state, &dfuParams);
                Cy_DFU_TransportReset();
            }
            break;
        }
        
        /* LED logic, constant values are optimized out. */
        /* Reset timer after 2 seconds */
        if(ledTimer == (2000u / paramsTimeout)) ledTimer = 0;
        /* Every 100 miliseconds */
        if(!(ledTimer % (100u / paramsTimeout)))
        {
            /* Generates two 100 miliseconds pulses, every 2 seconds */
            if((state == CY_DFU_STATE_UPDATING) && (ledTimer < (400u / paramsTimeout)))
            {
                BlinkLED();
            }
            /* Generates one 100 miliseconds pulse, every 2 seconds */
            else if ((Cy_BLE_GetAdvertisementState() == CY_BLE_ADV_STATE_ADVERTISING)
                     && (ledTimer < (200u / paramsTimeout)))
            {
                BlinkLED();            
            }
            else
            {
                /* Remain OFF */
                ConnectedLED();
            }
        }
        ++ledTimer;
        
        /* Check if a switch to the other app is requested and perform the switch if it is */
        if((buttonReleased == true) && (state == CY_DFU_STATE_NONE))
        {
            bool switchRequested = false;
            if (alertLevel != 0)
            {
                switchRequested = true;
            }
            else if(IsButtonPressed(500u) == true)
            {
                switchRequested = true;
                buttonReleased = false;
            }
            if (switchRequested)
            {
                /* Validate and switch to App1 */
                cy_en_dfu_status_t status = Cy_DFU_ValidateApp(1u, &dfuParams);
                
                if (status == CY_DFU_SUCCESS)
                {
                    Cy_DFU_TransportStop();
                    /*        
                    * Clear reset reason because Cy_DFU_ExecuteApp() performs        
                    * a software reset.        
                    * Without clearing two reset reasons would be present.        
                    */
                    do
                    {
                        Cy_SysLib_ClearResetReason();
                    }while(Cy_SysLib_GetResetReason() != 0);
                    /* Never returns */
                    Cy_DFU_ExecuteApp(1u);
                }
            }
        }
        else
        {
            buttonReleased = Cy_GPIO_Read(PIN_SW2_PORT, PIN_SW2_NUM);
        }     
    }
}


/*******************************************************************************
* Function Name: IsButtonPressed
********************************************************************************
*  Checks if button is pressed for a 'timeoutInMilis' time.
*
* Params:
*   timeout: Amount of time to check if button was pressed. Broken into
*            20 miliseconds steps.
* Returns:
*  true if button is pressed for specified amount.
*  false otherwise.
*******************************************************************************/
static bool IsButtonPressed(uint16_t timeoutInMilis)
{
    uint16_t buttonTime = 0;
    bool buttonPressed = false;
    timeoutInMilis /= 20;
    while(Cy_GPIO_Read(PIN_SW2_PORT, PIN_SW2_NUM) == 0u)
    {
        Cy_SysLib_Delay(20u);
        if(++buttonTime == timeoutInMilis)
        {
            /* time has passed */
            buttonPressed = true;
            break;
        }
    }
    return buttonPressed;
}

/*******************************************************************************
* Function Name: counterTimeoutSeconds
********************************************************************************
* Returns number of counts that correspond to number of seconds passed as
* a parameter.
* E.g. comparing counter with 300 seconds is like this.
* ---
* uint32_t counter = 0u;
* for (;;)
* {
*     Cy_SysLib_Delay(UART_TIMEOUT);
*     ++count;
*     if (count >= counterTimeoutSeconds(seconds: 300u, timeout: UART_TIMEOUT))
*     {
*         count = 0u;
*         DoSomething();
*     }
* }
* ---
*
* Both parameters are required to be compile time constants,
* so this function gets optimized out to single constant value.
*
* Parameters:
*  seconds    Number of seconds to pass. Must be less that 4_294_967 seconds.
*  timeout    Timeout for Cy_DFU_Continue() function, in milliseconds.
*             Must be greater than zero.
*             It is recommended to be a value that produces no reminder
*             for this function to be precise.
* Return:
*  See description.
*******************************************************************************/
static uint32_t counterTimeoutSeconds(uint32_t seconds, uint32_t timeout)
{
    return (seconds * 1000ul) / timeout;
}

/*******************************************************************************
* Function Name: CopyRow
********************************************************************************
* Copies data from a "src" address to a flash row with the address "dest".
* If "src" data is the same as "dest" data then no copy is needed.
*
* Parameters:
*  dest     Destination address. Has to be an address of the start of flash row.
*  src      Source address. Has to be properly aligned.
*  rowSize  Size of flash row.
*
* Returns:
*  CY_DFU_SUCCESS if operation is successful.
*  Error code in a case of failure.
*******************************************************************************/
static cy_en_dfu_status_t CopyRow(uint32_t dest, uint32_t src, uint32_t rowSize, cy_stc_dfu_params_t * params)
{
    cy_en_dfu_status_t status;
    
    /* Save params->dataBuffer value */
    uint8_t *buffer = params->dataBuffer;

    /* Compare "dest" and "src" content */
    params->dataBuffer = (uint8_t *)src;
    status = Cy_DFU_ReadData(dest, rowSize, CY_DFU_IOCTL_COMPARE, params);
    
    /* Restore params->dataBuffer */
    params->dataBuffer = buffer;

    /* If "dest" differs from "src" then copy "src" to "dest" */
    if (status != CY_DFU_SUCCESS)
    {
        (void) memcpy((void *) params->dataBuffer, (const void*)src, rowSize);
        status = Cy_DFU_WriteData(dest, rowSize, CY_DFU_IOCTL_WRITE, params);
    }
    /* Restore params->dataBuffer */
    params->dataBuffer = buffer;
    
    return (status);
}

/*******************************************************************************
* Function Name: HandleMetadata
********************************************************************************
* The goal of this function is to make DFU SDK metadata (MD) valid.
* The following algorithm is used (in C-like pseudocode):
* ---
* if (isValid(MD) == true)
* {   if (MDC != MD)
*         MDC = MD;
* } else
* {   if(isValid(MDC) )
*         MD = MDC;
*     #if MD Writeable
*     else
*         MD = INITIAL_VALUE;
*     #endif
* }
* ---
* Here MD is metadata flash row, MDC is flash row with metadata copy,
* INITIAL_VALUE is known initial value.
*
* In this code example MDC is placed in the next flash row after the MD, and
* INITIAL_VALUE is MD with only CRC, App0 start and size initialized,
* all the other fields are not touched. This is only done if metadata is
* writeable when downloading.
*
* Parameters:
*  params   A pointer to a DFU SDK parameters structure.
*
* Returns:
* - CY_DFU_SUCCESS when finished normally.
* - Any other status code on error.
*******************************************************************************/
static cy_en_dfu_status_t HandleMetadata(cy_stc_dfu_params_t *params)
{
    const uint32_t MD     = (uint32_t)(&__cy_boot_metadata_addr   ); /* MD address  */
    const uint32_t mdSize = (uint32_t)(&__cy_boot_metadata_length ); /* MD size, assumed to be one flash row */
    const uint32_t MDC    = MD + mdSize;                             /* MDC address */

    cy_en_dfu_status_t status = CY_DFU_SUCCESS;
    
    status = Cy_DFU_ValidateMetadata(MD, params);
    if (status == CY_DFU_SUCCESS)
    {
        /* Checks if MDC equals to DC, if no then copies MD to MDC */
        status = CopyRow(MDC, MD, mdSize, params);
    }
    else
    {
        status = Cy_DFU_ValidateMetadata(MDC, params);
        if (status == CY_DFU_SUCCESS)
        {
            /* Copy MDC to MD */
            status = CopyRow(MD, MDC, mdSize, params);
        }
        #if CY_DFU_METADATA_WRITABLE != 0
        if (status != CY_DFU_SUCCESS)
        {
            const uint32_t elfStartAddress = 0x10000000;
            const uint32_t elfAppSize      = 0x40000;
            /* Set MD to INITIAL_VALUE */
            status = Cy_DFU_SetAppMetadata(0u, elfStartAddress, elfAppSize, params);
        }
        #endif /* CY_DFU_METADATA_WRITABLE != 0 */
    }
    return (status);
}


/*******************************************************************************
* Function Name: AppCallBack()
********************************************************************************
*
* Summary:
*   This is an event callback function to receive events from the BLE Component.
*   Used in Cy_DFU_TransportStart()
*
*  event - the event code
*  *eventParam - the event parameters
*
*******************************************************************************/
void AppCallBack(uint32 event, void* eventParam)
{
    cy_en_ble_api_result_t apiResult;
    
    static cy_stc_ble_gap_sec_key_info_t keyInfo =
    {
        .localKeysFlag = CY_BLE_GAP_SMP_INIT_ENC_KEY_DIST | 
                         CY_BLE_GAP_SMP_INIT_IRK_KEY_DIST | 
                         CY_BLE_GAP_SMP_INIT_CSRK_KEY_DIST,
        .exchangeKeysFlag = CY_BLE_GAP_SMP_INIT_ENC_KEY_DIST | 
                            CY_BLE_GAP_SMP_INIT_IRK_KEY_DIST | 
                            CY_BLE_GAP_SMP_INIT_CSRK_KEY_DIST |
                            CY_BLE_GAP_SMP_RESP_ENC_KEY_DIST |
                            CY_BLE_GAP_SMP_RESP_IRK_KEY_DIST |
                            CY_BLE_GAP_SMP_RESP_CSRK_KEY_DIST,
    };
        
    switch (event)
    {
    /**********************************************************
    *                       General Events
    ***********************************************************/
    
    /* This event received when BLE communication starts */
    case CY_BLE_EVT_STACK_ON:
        /* Enter into discoverable mode so that remote can search it. */
        apiResult = Cy_BLE_GAPP_StartAdvertisement(CY_BLE_ADVERTISING_FAST, 0u);
        if(apiResult != CY_BLE_SUCCESS)
        {
        }
        
        apiResult = Cy_BLE_GAP_GenerateKeys(&keyInfo);
        if(apiResult != CY_BLE_SUCCESS)
        {
            DBG_PRINTF("CyBle_GapGenerateKeys API Error: %d \r\n", apiResult);
        }
        break;

    /* This event indicates that some internal HW error has occurred. */
    case CY_BLE_EVT_HARDWARE_ERROR:
        DBG_PRINTF("CYBLE_EVT_HARDWARE_ERROR\r\n");
        break;
    /**********************************************************
    *                       GAP Events
    ***********************************************************/
    case CY_BLE_EVT_GAP_AUTH_REQ:
        DBG_PRINTF("CYBLE_EVT_AUTH_REQ: security=%x, bonding=%x, ekeySize=%x, err=%x \r\n",
            (*(cy_stc_ble_gap_auth_info_t *)eventParam).security,
            (*(cy_stc_ble_gap_auth_info_t *)eventParam).bonding,
            (*(cy_stc_ble_gap_auth_info_t*)eventParam).ekeySize,
            (*(cy_stc_ble_gap_auth_info_t *)eventParam).authErr    );
        if ( cy_ble_configPtr->authInfo[CY_BLE_SECURITY_CONFIGURATION_0_INDEX].security 
             == (CY_BLE_GAP_SEC_MODE_1 | CY_BLE_GAP_SEC_LEVEL_1)  )
        {
            cy_ble_configPtr->authInfo[CY_BLE_SECURITY_CONFIGURATION_0_INDEX].authErr =
                CY_BLE_GAP_AUTH_ERROR_PAIRING_NOT_SUPPORTED;
        }    
        
        cy_ble_configPtr->authInfo[CY_BLE_SECURITY_CONFIGURATION_0_INDEX].bdHandle =
            ((cy_stc_ble_gap_auth_info_t *)eventParam)->bdHandle;

        apiResult = Cy_BLE_GAPP_AuthReqReply(&cy_ble_configPtr->authInfo[CY_BLE_SECURITY_CONFIGURATION_0_INDEX]);
        if(apiResult != CY_BLE_SUCCESS)
        {
            Cy_BLE_GAP_RemoveOldestDeviceFromBondedList();
            apiResult = Cy_BLE_GAPP_AuthReqReply(&cy_ble_configPtr->authInfo[CY_BLE_SECURITY_CONFIGURATION_0_INDEX]);
            if(apiResult != CY_BLE_SUCCESS)
            {
               DBG_PRINTF("CyBle_GappAuthReqReply API Error: %d \r\n", apiResult);
            }
        }
        break;

    case CY_BLE_EVT_GAP_PASSKEY_ENTRY_REQUEST:
        DBG_PRINTF("CYBLE_EVT_PASSKEY_ENTRY_REQUEST press 'p' to enter passkey \r\n");
        break;

    case CY_BLE_EVT_GAP_PASSKEY_DISPLAY_REQUEST:
        DBG_PRINTF("CYBLE_EVT_PASSKEY_DISPLAY_REQUEST %6.6d \r\n", *(int *)eventParam);
        break;
    
    case CY_BLE_EVT_GAP_KEYINFO_EXCHNGE_CMPLT:
        DBG_PRINTF("CYBLE_EVT_GAP_KEYINFO_EXCHNGE_CMPLT \r\n");
        break;
    
    case CY_BLE_EVT_GAP_AUTH_COMPLETE:
        DBG_PRINTF("AUTH_COMPLETE \r\n");
        break;
    
    case CY_BLE_EVT_GAP_AUTH_FAILED:
        DBG_PRINTF("CYBLE_EVT_AUTH_FAILED: %x \r\n", *(uint8 *)eventParam);
        break;

    case CY_BLE_EVT_GAP_DEVICE_CONNECTED:
        DBG_PRINTF("CYBLE_EVT_GAP_DEVICE_CONNECTED: %d \r\n", appConnHandle.bdHandle);
        
        if ( ((*(cy_stc_ble_gap_connected_param_t *)eventParam).connIntv 
             < CYBLE_GAPP_CONNECTION_INTERVAL_MIN ) || (
             (*(cy_stc_ble_gap_connected_param_t *)eventParam).connIntv 
             > CYBLE_GAPP_CONNECTION_INTERVAL_MAX ) )
        {
            cy_stc_ble_gap_conn_update_param_info_t connUpdateParam;
            /* If connection settings do not match expected ones - request parameter update */
            connUpdateParam.connIntvMin   = CYBLE_GAPP_CONNECTION_INTERVAL_MIN;
            connUpdateParam.connIntvMax   = CYBLE_GAPP_CONNECTION_INTERVAL_MAX;
            connUpdateParam.connLatency   = CYBLE_GAPP_CONNECTION_SLAVE_LATENCY;
            connUpdateParam.supervisionTO = CYBLE_GAPP_CONNECTION_TIME_OUT;
            connUpdateParam.bdHandle = appConnHandle.bdHandle;
            apiResult = Cy_BLE_L2CAP_LeConnectionParamUpdateRequest(&connUpdateParam);
            DBG_PRINTF("Cy_BLE_L2CAP_LeConnectionParamUpdateRequest API: 0x%2.2x \r\n", apiResult);
        }        
        keyInfo.SecKeyParam.bdHandle = (*(cy_stc_ble_gap_connected_param_t *)eventParam).bdHandle;
        apiResult = Cy_BLE_GAP_SetSecurityKeys(&keyInfo);
        if(apiResult != CY_BLE_SUCCESS)
        {
            DBG_PRINTF("CyBle_GapSetSecurityKeys API Error: %d \r\n", apiResult);
        }
        break;
        
    case CY_BLE_EVT_L2CAP_CONN_PARAM_UPDATE_RSP:
        DBG_PRINTF("CY_BLE_EVT_L2CAP_CONN_PARAM_UPDATE_RSP, result = %d\r\n", 
        (*(cy_stc_ble_l2cap_conn_update_rsp_param_t *)eventParam).result);
        break; 

    case CY_BLE_EVT_GAP_KEYS_GEN_COMPLETE:
        DBG_PRINTF("CYBLE_EVT_GAP_KEYS_GEN_COMPLETE \r\n");
        keyInfo.SecKeyParam = (*(cy_stc_ble_gap_sec_key_param_t *)eventParam);
        Cy_BLE_GAP_SetIdAddress(&cy_ble_deviceAddress);
        break;
    
    case CY_BLE_EVT_GAP_DEVICE_DISCONNECTED:
        DBG_PRINTF("CYBLE_EVT_GAP_DEVICE_DISCONNECTED\r\n");
        /* Put the device into discoverable mode so that a remote can search it. */
        apiResult = Cy_BLE_GAPP_StartAdvertisement(CY_BLE_ADVERTISING_FAST, CY_BLE_PERIPHERAL_CONFIGURATION_0_INDEX);
        if(apiResult != CY_BLE_SUCCESS)
        {
            DBG_PRINTF("StartAdvertisement API Error: %d \r\n", apiResult);
        }
        break;
    
    case CY_BLE_EVT_GAP_ENCRYPT_CHANGE:
        DBG_PRINTF("CYBLE_EVT_GAP_ENCRYPT_CHANGE: %x \r\n", *(uint8 *)eventParam);
        break;
    
    case CY_BLE_EVT_GAP_CONNECTION_UPDATE_COMPLETE:
        DBG_PRINTF("CYBLE_EVT_CONNECTION_UPDATE_COMPLETE: %x \r\n", *(uint8 *)eventParam);
        break;
    
    case CY_BLE_EVT_GAPP_ADVERTISEMENT_START_STOP:
        if(Cy_BLE_GetAdvertisementState() == CY_BLE_ADV_STATE_STOPPED)
        {   
            /* Fast and slow advertising period complete, go to low power  
             * mode (Hibernate mode) and wait for an external
             * user event to wake up the device again */
            
            /* Stop DFU communication */
            Cy_DFU_TransportStop();
            /* Check if app is valid, if it is then switch to it */
            uint32_t status = Cy_DFU_ValidateApp(1u, NULL);
            if (status == CY_DFU_SUCCESS)
            {
                /*        
                * Clear reset reason because Cy_DFU_ExecuteApp() performs        
                * a software reset.        
                * Without clearing two reset reasons would be present.        
                */
                do
                {
                    Cy_SysLib_ClearResetReason();
                }while(Cy_SysLib_GetResetReason() != 0);
                /* Never returns */
                Cy_DFU_ExecuteApp(1u);
            }
            /* 300 seconds has passed and App is invalid. Hibernate */
            HibernateLED();
            Cy_SysPm_Hibernate();
        }
        break;

    /**********************************************************
    *                       GATT Events
    ***********************************************************/
    case CY_BLE_EVT_GATT_CONNECT_IND:
        appConnHandle = *(cy_stc_ble_conn_handle_t *)eventParam;
        DBG_PRINTF("CYBLE_EVT_GATT_CONNECT_IND: %d \r\n", appConnHandle.bdHandle);
        break;
    
    case CY_BLE_EVT_GATT_DISCONNECT_IND:
        DBG_PRINTF("CYBLE_EVT_GATT_DISCONNECT_IND: %d \r\n", ((cy_stc_ble_conn_handle_t *)eventParam)->bdHandle);
        break;
    
    case CY_BLE_EVT_GATTS_WRITE_CMD_REQ:
        DBG_PRINTF("CYBLE_EVT_GATTS_WRITE_CMD_REQ\r\n");
        break;

    default:
        break;
    }
}


/* [] END OF FILE */
