/******************************************************************************* * File Name: host_main.c * * Version: 1.0 * * Description: * BLE HID keyboard example project that supports both input and output reports * in boot and protocol mode. The example also demonstrates handling suspend * event from the central device and enters low power mode when suspended. * * References: * BLUETOOTH SPECIFICATION Version 5.0 * HID Usage Tables spec ver 1.12 * * Hardware Dependency: * CY8CKIT-062 PSoC6 BLE Pioneer Kit * ****************************************************************************** * 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 "dfu/cy_dfu.h" #include "common.h" #include "user_interface.h" #include "ias.h" #include "scps.h" /* Global Variables */ cy_stc_ble_conn_handle_t appConnHandle; static volatile uint32_t mainTimer = 1u; static cy_stc_ble_timer_info_t timerParam = { .timeout = ADV_TIMER_TIMEOUT }; /* Private Function Prototypes */ static void LowPowerImplementation(void); /******************************************************************************* * Function Name: AppCallBack() ******************************************************************************** * * Summary: * This is an event callback function to receive events from the BLE Component. * * event - the event code * *eventParam - the event parameters * *******************************************************************************/ void AppCallBack(uint32_t event, void* eventParam) { cy_en_ble_api_result_t apiResult; uint8_t i; 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 ***********************************************************/ case CY_BLE_EVT_STACK_ON: /* This event is received when the component is Started */ DBG_PRINTF("CY_BLE_EVT_STACK_ON, StartAdvertisement \r\n"); /* Enter into discoverable mode so that remote can find it. */ apiResult = Cy_BLE_GAPP_StartAdvertisement(CY_BLE_ADVERTISING_FAST, CY_BLE_PERIPHERAL_CONFIGURATION_0_INDEX); if(apiResult != CY_BLE_SUCCESS) { DBG_PRINTF("Cy_BLE_GAPP_StartAdvertisement API Error: 0x%x \r\n", apiResult); } /* Generates the security keys */ apiResult = Cy_BLE_GAP_GenerateKeys(&keyInfo); if(apiResult != CY_BLE_SUCCESS) { DBG_PRINTF("Cy_BLE_GAP_GenerateKeys API Error: 0x%x \r\n", apiResult); } /* Display Bond list */ App_DisplayBondList(); break; case CY_BLE_EVT_TIMEOUT: if((((cy_stc_ble_timeout_param_t *)eventParam)->reasonCode == CY_BLE_GENERIC_APP_TO) && (((cy_stc_ble_timeout_param_t *)eventParam)->timerHandle == timerParam.timerHandle)) { /* Update LED State */ UpdateLedState(); /* Indicate that timer is raised to the main loop */ mainTimer++; /* Press and hold the mechanical button (SW2) during 4 seconds to clear the bond list. */ App_RemoveDevicesFromBondListBySW2Press(SW2_PRESS_TIME_DEL_BOND_LIST); } break; case CY_BLE_EVT_HARDWARE_ERROR: /* This event indicates that some internal HW error has occurred. */ DBG_PRINTF("CY_BLE_EVT_HARDWARE_ERROR \r\n"); ShowError(); break; /* This event will be triggered by host stack if BLE stack is busy or not busy. * Parameter corresponding to this event will be the state of BLE stack. * BLE stack busy = CY_BLE_STACK_STATE_BUSY, * BLE stack not busy = CY_BLE_STACK_STATE_FREE */ case CY_BLE_EVT_STACK_BUSY_STATUS: DBG_PRINTF("CY_BLE_EVT_STACK_BUSY_STATUS: %x\r\n", *(uint8_t *)eventParam); break; case CY_BLE_EVT_SET_TX_PWR_COMPLETE: DBG_PRINTF("CY_BLE_EVT_SET_TX_PWR_COMPLETE \r\n"); break; case CY_BLE_EVT_LE_SET_EVENT_MASK_COMPLETE: DBG_PRINTF("CY_BLE_EVT_LE_SET_EVENT_MASK_COMPLETE \r\n"); break; case CY_BLE_EVT_SET_DEVICE_ADDR_COMPLETE: DBG_PRINTF("CY_BLE_EVT_SET_DEVICE_ADDR_COMPLETE \r\n"); /* Reads the BD device address from BLE Controller's memory */ apiResult = Cy_BLE_GAP_GetBdAddress(); if(apiResult != CY_BLE_SUCCESS) { DBG_PRINTF("Cy_BLE_GAP_GetBdAddress API Error: 0x%x \r\n", apiResult); } break; case CY_BLE_EVT_GET_DEVICE_ADDR_COMPLETE: DBG_PRINTF("CY_BLE_EVT_GET_DEVICE_ADDR_COMPLETE: "); for(i = CY_BLE_GAP_BD_ADDR_SIZE; i > 0u; i--) { DBG_PRINTF("%2.2x", ((cy_stc_ble_bd_addrs_t *) ((cy_stc_ble_events_param_generic_t *)eventParam)->eventParams)->publicBdAddr[i-1]); } DBG_PRINTF("\r\n"); break; case CY_BLE_EVT_STACK_SHUTDOWN_COMPLETE: DBG_PRINTF("CY_BLE_EVT_STACK_SHUTDOWN_COMPLETE \r\n"); DBG_PRINTF("Hibernate \r\n"); UART_DEB_WAIT_TX_COMPLETE(); /* Hibernate */ Cy_SysPm_Hibernate(); break; /********************************************************** * GAP Events ***********************************************************/ case CY_BLE_EVT_GAP_AUTH_REQ: /* This event is received by Peripheral and Central devices. When it is received by a peripheral, * that peripheral must Call Cy_BLE_GAPP_AuthReqReply() to reply to the authentication request * from Central. */ DBG_PRINTF("CY_BLE_EVT_GAP_AUTH_REQ: bdHandle=%x, security=%x, bonding=%x, ekeySize=%x, err=%x \r\n", (*(cy_stc_ble_gap_auth_info_t *)eventParam).bdHandle, (*(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; /* Pass security information for authentication in reply to an authentication request * from the master device */ 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("Cy_BLE_GAPP_AuthReqReply API Error: 0x%x \r\n", apiResult); } } break; case CY_BLE_EVT_GAP_PASSKEY_ENTRY_REQUEST: DBG_PRINTF("CY_BLE_EVT_GAP_PASSKEY_ENTRY_REQUEST\r\n"); DBG_PRINTF("Please enter the passkey displayed on the peer device:\r\n"); break; case CY_BLE_EVT_GAP_PASSKEY_DISPLAY_REQUEST: DBG_PRINTF("CY_BLE_EVT_GAP_PASSKEY_DISPLAY_REQUEST: %6.6ld\r\n", *(uint32_t *)eventParam); break; case CY_BLE_EVT_GAP_NUMERIC_COMPARISON_REQUEST: DBG_PRINTF("Compare this passkey with the one displayed in your peer device and press 'y' or 'n':" " %6.6lu \r\n", *(uint32_t *)eventParam); break; case CY_BLE_EVT_GAP_KEYINFO_EXCHNGE_CMPLT: DBG_PRINTF("CY_BLE_EVT_GAP_KEYINFO_EXCHNGE_CMPLT \r\n"); break; case CY_BLE_EVT_GAP_SMP_NEGOTIATED_AUTH_INFO: DBG_PRINTF("CY_BLE_EVT_GAP_SMP_NEGOTIATED_AUTH_INFO:" " bdHandle=%x, security=%x, bonding=%x, ekeySize=%x, err=%x \r\n", (*(cy_stc_ble_gap_auth_info_t *)eventParam).bdHandle, (*(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); break; case CY_BLE_EVT_GAP_AUTH_COMPLETE: DBG_PRINTF("CY_BLE_EVT_GAP_AUTH_COMPLETE: bdHandle=%x, security=%x, bonding=%x, ekeySize=%x, err=%x \r\n", (*(cy_stc_ble_gap_auth_info_t *)eventParam).bdHandle, (*(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); break; case CY_BLE_EVT_GAP_AUTH_FAILED: DBG_PRINTF("CY_BLE_EVT_GAP_AUTH_FAILED: bdHandle=%x, authErr=%x\r\n", (*(cy_stc_ble_gap_auth_info_t *)eventParam).bdHandle, (*(cy_stc_ble_gap_auth_info_t *)eventParam).authErr); break; case CY_BLE_EVT_GAPP_ADVERTISEMENT_START_STOP: DBG_PRINTF("CY_BLE_EVT_GAPP_ADVERTISEMENT_START_STOP, state: %d \r\n", Cy_BLE_GetAdvertisementState()); if((Cy_BLE_GetAdvertisementState() == CY_BLE_ADV_STATE_STOPPED) && (Cy_BLE_GetNumOfActiveConn() == 0u)) { /* Fast and slow advertising period complete, go to low power * mode (Hibernate) and wait for an external * user event to wake up the device again */ UpdateLedState(); Cy_BLE_Stop(); } break; case CY_BLE_EVT_GAP_DEVICE_CONNECTED: DBG_PRINTF("CY_BLE_EVT_GAP_DEVICE_CONNECTED: connIntv = %d ms \r\n", ((cy_stc_ble_gap_connected_param_t *)eventParam)->connIntv * 5u /4u); /* in milliseconds / 1.25ms */ /* Set security keys for new device which is not already bonded */ if(App_IsDeviceInBondList((*(cy_stc_ble_gap_connected_param_t *)eventParam).bdHandle) == 0u) { 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("Cy_BLE_GAP_SetSecurityKeys API Error: 0x%x \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("CY_BLE_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_CONNECTION_UPDATE_COMPLETE: DBG_PRINTF("CY_BLE_EVT_GAP_CONNECTION_UPDATE_COMPLETE: connIntv = %d ms \r\n", /* in milliseconds / 1.25ms */ ((cy_stc_ble_gap_conn_param_updated_in_controller_t *)eventParam)->connIntv * 5u /4u); break; case CY_BLE_EVT_GAP_DEVICE_DISCONNECTED: DBG_PRINTF("CY_BLE_EVT_GAP_DEVICE_DISCONNECTED: bdHandle=%x, reason=%x, status=%x\r\n", (*(cy_stc_ble_gap_disconnect_param_t *)eventParam).bdHandle, (*(cy_stc_ble_gap_disconnect_param_t *)eventParam).reason, (*(cy_stc_ble_gap_disconnect_param_t *)eventParam).status); /* 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: 0x%x \r\n", apiResult); } break; case CY_BLE_EVT_GAP_ENCRYPT_CHANGE: DBG_PRINTF("CY_BLE_EVT_GAP_ENCRYPT_CHANGE: %x \r\n", *(uint8_t *)eventParam); break; /********************************************************** * GATT Events ***********************************************************/ case CY_BLE_EVT_GATT_CONNECT_IND: appConnHandle = *(cy_stc_ble_conn_handle_t *)eventParam; DBG_PRINTF("CY_BLE_EVT_GATT_CONNECT_IND: %x, %x \r\n", (*(cy_stc_ble_conn_handle_t *)eventParam).attId, (*(cy_stc_ble_conn_handle_t *)eventParam).bdHandle); break; case CY_BLE_EVT_GATT_DISCONNECT_IND: DBG_PRINTF("CY_BLE_EVT_GATT_DISCONNECT_IND: %x, %x \r\n", (*(cy_stc_ble_conn_handle_t *)eventParam).attId, (*(cy_stc_ble_conn_handle_t *)eventParam).bdHandle); break; case CY_BLE_EVT_GATTS_XCNHG_MTU_REQ: { cy_stc_ble_gatt_xchg_mtu_param_t mtu = { .connHandle = ((cy_stc_ble_gatt_xchg_mtu_param_t *)eventParam)->connHandle }; Cy_BLE_GATT_GetMtuSize(&mtu); DBG_PRINTF("CY_BLE_EVT_GATTS_XCNHG_MTU_REQ %x, %x, final mtu= %d \r\n", mtu.connHandle.attId, mtu.connHandle.bdHandle, mtu.mtu); } break; case CY_BLE_EVT_GATTS_READ_CHAR_VAL_ACCESS_REQ: DBG_PRINTF("CY_BLE_EVT_GATTS_READ_CHAR_VAL_ACCESS_REQ %x %x: handle: %x \r\n", ((cy_stc_ble_gatts_char_val_read_req_t *)eventParam)->connHandle.attId, ((cy_stc_ble_gatts_char_val_read_req_t *)eventParam)->connHandle.bdHandle, ((cy_stc_ble_gatts_char_val_read_req_t *)eventParam)->attrHandle); break; case CY_BLE_EVT_GATTS_WRITE_REQ: DBG_PRINTF("CY_BLE_EVT_GATTS_WRITE_REQ attr handle: %4.4x , value: ", ((cy_stc_ble_gatts_write_cmd_req_param_t *)eventParam)->handleValPair.attrHandle); for(i = 0; i < ((cy_stc_ble_gatts_write_cmd_req_param_t *)eventParam)->handleValPair.value.len; i++) { DBG_PRINTF("%2.2x ", ((cy_stc_ble_gatts_write_cmd_req_param_t *)eventParam)->handleValPair.value.val[i]); } DBG_PRINTF("\r\n"); break; case CY_BLE_EVT_GATTS_INDICATION_DISABLED: DBG_PRINTF("CY_BLE_EVT_GATTS_INDICATION_DISABLED \r\n"); break; case CY_BLE_EVT_GATTS_INDICATION_ENABLED: DBG_PRINTF("CY_BLE_EVT_GATTS_INDICATION_ENABLED \r\n"); break; /********************************************************** * Other Events ***********************************************************/ case CY_BLE_EVT_PENDING_FLASH_WRITE: /* Inform application that flash write is pending. Stack internal data * structures are modified and require to be stored in Flash using * Cy_BLE_StoreBondingData() */ DBG_PRINTF("CY_BLE_EVT_PENDING_FLASH_WRITE\r\n"); break; default: DBG_PRINTF("Other event: 0x%lx \r\n", event); break; } } /******************************************************************************* * Function Name: LowPowerImplementation() ******************************************************************************** * Summary: * Implements low power in the project. * * Theory: * The function tries to enter deep sleep as much as possible - whenever the * BLE is idle and the UART transmission/reception is not happening. * *******************************************************************************/ static void LowPowerImplementation(void) { if(UART_DEB_IS_TX_COMPLETE() != 0u) { /* Entering into the Deep Sleep */ Cy_SysPm_DeepSleep(CY_SYSPM_WAIT_FOR_INTERRUPT); } } /******************************************************************************* * Function Name: HostMain() ******************************************************************************** * Summary: * Main function for the project. * *******************************************************************************/ int HostMain(void) { cy_en_ble_api_result_t apiResult; /* Initialization the user interface: LEDs, SW2, ect. */ InitUserInterface(); /* Initialize Debug UART */ UART_START(); DBG_PRINTF("BLE HID Keyboard Example\r\n"); /* Start BLE component and register generic event handler */ apiResult = Cy_BLE_Start(AppCallBack); if(apiResult != CY_BLE_SUCCESS) { DBG_PRINTF("Cy_BLE_Start API Error: 0x%x \r\n", apiResult); } /* Initialize BLE Services */ ScpsInit(); IasInit(); /*************************************************************************** * Main polling loop ***************************************************************************/ while(1) { /* Cy_BLE_ProcessEvents() allows BLE stack to process pending events */ Cy_BLE_ProcessEvents(); /* To achieve low power in the device */ LowPowerImplementation(); /* Restart 1s timer */ if(mainTimer != 0u) { mainTimer = 0u; Cy_BLE_StartTimer(&timerParam); } /* Remove devices from the bond list. Should be done when no active connections */ if((Cy_BLE_GetNumOfActiveConn() == 0u) && (App_IsRemoveBondListFlag() == true)) { App_RemoveDevicesFromBondList(); } #if(CY_BLE_BONDING_REQUIREMENT == CY_BLE_BONDING_YES) /* Store bonding data to flash only when all debug information has been sent */ if(cy_ble_pendingFlashWrite != 0u) { apiResult = Cy_BLE_StoreBondingData(); DBG_PRINTF("Store bonding data, status: %x, pending: %x \r\n", apiResult, cy_ble_pendingFlashWrite); } #endif /* CY_BLE_BONDING_REQUIREMENT == CY_BLE_BONDING_YES */ if (alertLevel != 0u) { cy_stc_ble_gap_disconnect_info_t disconnectInfoParam = { .bdHandle = appConnHandle.bdHandle, .reason = CY_BLE_HCI_ERROR_OTHER_END_TERMINATED_USER }; /* Initiate disconnection from the peer device*/ if(Cy_BLE_GAP_Disconnect(&disconnectInfoParam) == CY_BLE_SUCCESS) { /* Wait for disconnection event */ while(Cy_BLE_GetConnectionState(appConnHandle) == CY_BLE_CONN_STATE_CONNECTED) { /* Process BLE events */ Cy_BLE_ProcessEvents(); } } /* Stop BLE component. */ Cy_BLE_Disable(); Cy_DFU_ExecuteApp(0u); } } } /* [] END OF FILE */