/************************************************************************************************** Phyplus Microelectronics Limited confidential and proprietary. All rights reserved. IMPORTANT: All rights of this software belong to Phyplus Microelectronics Limited ("Phyplus"). Your use of this Software is limited to those specific rights granted under the terms of the business contract, the confidential agreement, the non-disclosure agreement and any other forms of agreements as a customer or a partner of Phyplus. You may not use this Software unless you agree to abide by the terms of these agreements. You acknowledge that the Software may not be modified, copied, distributed or disclosed unless embedded on a Phyplus Bluetooth Low Energy (BLE) integrated circuit, either as a product or is integrated into your products. Other than for the aforementioned purposes, you may not use, reproduce, copy, prepare derivative works of, modify, distribute, perform, display or sell this Software and/or its documentation for any purposes. YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL PHYPLUS OR ITS SUBSIDIARIES BE LIABLE OR OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. **************************************************************************************************/ /********************************************************************* * INCLUDES */ #include "OSAL.h" #include "gatt.h" #include "hci.h" #include "gapgattserver.h" #include "gattservapp.h" #include "gatt_uuid.h" #include "gatt_profile_uuid.h" #include "linkdb.h" #include "peripheral.h" #include "gapbondmgr.h" #include "devinfoservice.h" #include "battservice.h" #include "hiddev.h" #include "hidkbdservice.h" //#include "touch_key.h" #include "log.h" /********************************************************************* * MACROS */ // Battery measurement period in ms #define DEFAULT_BATT_PERIOD 15000 // TRUE to run scan parameters refresh notify test #define DEFAULT_SCAN_PARAM_NOTIFY_TEST TRUE // Advertising intervals (units of 625us, 160=100ms) #define HID_INITIAL_ADV_INT_MIN 48 #define HID_INITIAL_ADV_INT_MAX 80 #define HID_HIGH_ADV_INT_MIN 32 #define HID_HIGH_ADV_INT_MAX 48 #define HID_LOW_ADV_INT_MIN 1600 #define HID_LOW_ADV_INT_MAX 1600 // Advertising timeouts in sec #define HID_INITIAL_ADV_TIMEOUT 60 #define HID_HIGH_ADV_TIMEOUT 5 #define HID_LOW_ADV_TIMEOUT 0 // Heart Rate Task Events #define START_DEVICE_EVT 0x0001 #define BATT_PERIODIC_EVT 0x0002 #define HID_IDLE_EVT 0x0004 #define HID_SEND_REPORT_EVT 0x0008 #define HID_TEST_EVT 0x0100 #define reportQEmpty() ( firstQIdx == lastQIdx ) /********************************************************************* * CONSTANTS */ #define HID_DEV_DATA_LEN 8 #ifdef HID_DEV_RPT_QUEUE_LEN #define HID_DEV_REPORT_Q_SIZE (HID_DEV_RPT_QUEUE_LEN+1) #else #define HID_DEV_REPORT_Q_SIZE (10+1) #endif /********************************************************************* * TYPEDEFS */ typedef struct { uint8 id; uint8 type; uint8 len; uint8 data[HID_DEV_DATA_LEN]; } hidDevReport_t; /********************************************************************* * GLOBAL VARIABLES */ // Task ID uint8 hidDevTaskId; /********************************************************************* * EXTERNAL VARIABLES */ /********************************************************************* * EXTERNAL FUNCTIONS */ /********************************************************************* * LOCAL VARIABLES */ // GAP State static gaprole_States_t hidDevGapState = GAPROLE_INIT; // TRUE if connection is secure static uint8 hidDevConnSecure = FALSE; // GAP connection handle static uint16 gapConnHandle; // TRUE if pairing in progress static uint8 hidDevPairingStarted = FALSE; // Status of last pairing static uint8 pairingStatus = SUCCESS; static hidRptMap_t *pHidDevRptTbl; static uint8 hidDevRptTblLen; static hidDevCB_t *pHidDevCB; static hidDevCfg_t *pHidDevCfg; // Whether to change to the preferred connection parameters static uint8 updateConnParams = TRUE; // Pending reports static uint8 firstQIdx = 0; static uint8 lastQIdx = 0; static hidDevReport_t hidDevReportQ[HID_DEV_REPORT_Q_SIZE]; // Last report sent out static attHandleValueNoti_t lastNoti = { 0 }; /********************************************************************* * LOCAL FUNCTIONS */ static void hidDev_ProcessOSALMsg( osal_event_hdr_t *pMsg ); static void hidDevProcessGattMsg( gattMsgEvent_t *pMsg ); static void hidDevDisconnected( void ); static void hidDevBattPeriodicTask( void ); static hidRptMap_t *hidDevRptByHandle( uint16 handle ); static hidRptMap_t *hidDevRptById( uint8 id, uint8 type ); static hidRptMap_t *hidDevRptByCccdHandle( uint16 handle ); static void hidDevEnqueueReport( uint8 id, uint8 type, uint8 len, uint8 *pData ); static hidDevReport_t *hidDevDequeueReport( void ); static void hidDevSendReport( uint8 id, uint8 type, uint8 len, uint8 *pData ); static void hidDevHighAdvertising( void ); static void hidDevLowAdvertising( void ); static void hidDevInitialAdvertising( void ); static uint8 hidDevBondCount( void ); static void hidDevStartIdleTimer( void ); static void hidDevStopIdleTimer( void ); /********************************************************************* * PROFILE CALLBACKS */ /********************************************************************* * PUBLIC FUNCTIONS */ /********************************************************************* * @fn HidDev_Init * * @brief Initialization function for the Hid Dev Task. * This is called during initialization and should contain * any application specific initialization (ie. hardware * initialization/setup, table initialization, power up * notificaiton ... ). * * @param task_id - the ID assigned by OSAL. This ID should be * used to send messages and set timers. * * @return none */ void HidDev_Init( uint8 task_id ) { hidDevTaskId = task_id; // Setup the GAP Bond Manager { uint8 syncWL = TRUE; // If a bond is created, the HID Device should write the address of the // HID Host in the HID Device controller's white list and set the HID // Device controller's advertising filter policy to 'process scan and // connection requests only from devices in the White List'. VOID GAPBondMgr_SetParameter( GAPBOND_AUTO_SYNC_WL, sizeof( uint8 ), &syncWL ); } // Set up services GGS_AddService( GATT_ALL_SERVICES ); // GAP GATTServApp_AddService( GATT_ALL_SERVICES ); // GATT attributes DevInfo_AddService( ); Batt_AddService(); Batt_Register(NULL); //touch_init(on_key); // Setup a delayed profile startup osal_set_event( hidDevTaskId, START_DEVICE_EVT ); } /********************************************************************* * @fn HidDev_ProcessEvent * * @brief Hid Dev Task event processor. This function * is called to process all events for the task. Events * include timers, messages and any other user defined events. * * @param task_id - The OSAL assigned task ID. * @param events - events to process. This is a bit map and can * contain more than one event. * * @return events not processed */ uint16 HidDev_ProcessEvent( uint8 task_id, uint16 events ) { VOID task_id; // OSAL required parameter that isn't used in this function LOG("%s\n",__FUNCTION__); if ( events & SYS_EVENT_MSG ) { uint8 *pMsg; if ( (pMsg = osal_msg_receive( hidDevTaskId )) != NULL ) { hidDev_ProcessOSALMsg( (osal_event_hdr_t *)pMsg ); // Release the OSAL message VOID osal_msg_deallocate( pMsg ); } // return unprocessed events return (events ^ SYS_EVENT_MSG); } if ( events & START_DEVICE_EVT ) { // Start the Device //VOID GAPRole_StartDevice( &hidDev_PeripheralCBs ); // Register with bond manager after starting device //GAPBondMgr_Register( (gapBondCBs_t *) &hidDevBondCB ); return ( events ^ START_DEVICE_EVT ); } if ( events & HID_IDLE_EVT ) { if ( hidDevGapState == GAPROLE_CONNECTED ) { // if pairing in progress then restart timer if ( hidDevPairingStarted ) { hidDevStartIdleTimer(); } // else disconnect else { GAPRole_TerminateConnection(); } } return ( events ^ HID_IDLE_EVT ); } if ( events & BATT_PERIODIC_EVT ) { // Perform periodic battery task hidDevBattPeriodicTask(); return ( events ^ BATT_PERIODIC_EVT ); } if ( events & HID_SEND_REPORT_EVT ) { // if connection is secure if ( hidDevConnSecure ) { hidDevReport_t *pReport = hidDevDequeueReport(); if ( pReport != NULL ) { // Send report hidDevSendReport( pReport->id, pReport->type, pReport->len, pReport->data ); } return ( reportQEmpty() ? events ^ HID_SEND_REPORT_EVT : events ); } return ( events ^ HID_SEND_REPORT_EVT ); } return 0; } /********************************************************************* * @fn HidDev_Register * * @brief Register a callback function with HID Dev. * * @param pCfg - Parameter configuration. * @param pfnServiceCB - Callback function. * * @return None. */ void HidDev_Register( hidDevCfg_t *pCfg, hidDevCB_t *pCBs ) { pHidDevCB = pCBs; pHidDevCfg = pCfg; } /********************************************************************* * @fn HidDev_RegisterReports * * @brief Register the report table with HID Dev. * * @param numReports - Length of report table. * @param pRpt - Report table. * * @return None. */ void HidDev_RegisterReports( uint8 numReports, hidRptMap_t *pRpt ) { pHidDevRptTbl = pRpt; hidDevRptTblLen = numReports; } /********************************************************************* * @fn HidDev_Report * * @brief Send a HID report. * * @param id - HID report ID. * @param type - HID report type. * @param len - Length of report. * @param pData - Report data. * * @return None. */ void HidDev_Report( uint8 id, uint8 type, uint8 len, uint8*pData ) { // if connected if ( hidDevGapState == GAPROLE_CONNECTED ) { // if connection is secure if ( hidDevConnSecure ) { // Make sure there're no pending reports if ( reportQEmpty() ) { // send report hidDevSendReport( id, type, len, pData ); return; // we're done } } } // else if not already advertising else if ( hidDevGapState != GAPROLE_ADVERTISING ) { // if bonded if ( hidDevBondCount() > 0 ) { // start high duty cycle advertising hidDevHighAdvertising(); } // else not bonded else { // start initial advertising hidDevInitialAdvertising(); } } // hidDev task will send report when secure connection is established hidDevEnqueueReport( id, type, len, pData ); } /********************************************************************* * @fn HidDev_Close * * @brief Close the connection or stop advertising. * * @return None. */ void HidDev_Close( void ) { uint8 param; // if connected then disconnect if ( hidDevGapState == GAPROLE_CONNECTED ) { GAPRole_TerminateConnection(); } // else stop advertising else { param = FALSE; GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), ¶m ); } } /********************************************************************* * @fn HidDev_SetParameter * * @brief Set a HID Dev parameter. * * @param param - Profile parameter ID * @param len - length of data to right * @param pValue - pointer to data to write. This is dependent on * the parameter ID and WILL be cast to the appropriate * data type (example: data type of uint16 will be cast to * uint16 pointer). * * @return bStatus_t */ bStatus_t HidDev_SetParameter( uint8 param, uint8 len, void *pValue ) { bStatus_t ret = SUCCESS; switch ( param ) { case HIDDEV_ERASE_ALLBONDS: if ( len == 0 ) { // See if the last report sent out wasn't a release key if ( osal_isbufset( lastNoti.value, 0x00, lastNoti.len ) == FALSE ) { // Send a release report before disconnecting, otherwise // the last pressed key would get 'stuck' on the HID Host. osal_memset( lastNoti.value, 0x00, lastNoti.len ); GATT_Notification( gapConnHandle, &lastNoti, FALSE ); } // Drop connection if ( hidDevGapState == GAPROLE_CONNECTED ) { GAPRole_TerminateConnection(); } // Flush report queue firstQIdx = lastQIdx = 0; // Erase bonding info GAPBondMgr_SetParameter( GAPBOND_ERASE_ALLBONDS, 0, NULL ); } else { ret = bleInvalidRange; } break; default: ret = INVALIDPARAMETER; break; } return ( ret ); } /********************************************************************* * @fn HidDev_GetParameter * * @brief Get a HID Dev parameter. * * @param param - Profile parameter ID * @param pValue - pointer to data to get. This is dependent on * the parameter ID and WILL be cast to the appropriate * data type (example: data type of uint16 will be cast to * uint16 pointer). * * @return bStatus_t */ bStatus_t HidDev_GetParameter( uint8 param, void *pValue ) { bStatus_t ret = SUCCESS; switch ( param ) { default: ret = INVALIDPARAMETER; break; } return ( ret ); } /********************************************************************* * @fn HidDev_PasscodeRsp * * @brief Respond to a passcode request. * * @param status - SUCCESS if passcode is available, otherwise * see @ref SMP_PAIRING_FAILED_DEFINES. * @param passcode - integer value containing the passcode. * * @return none */ void HidDev_PasscodeRsp( uint8 status, uint32 passcode ) { // Send passcode response GAPBondMgr_PasscodeRsp( gapConnHandle, status, passcode ); } /********************************************************************* * @fn HidDev_ReadAttrCB * * @brief HID Dev attribute read callback. * * @param connHandle - connection message was received on * @param pAttr - pointer to attribute * @param pValue - pointer to data to be read * @param pLen - length of data to be read * @param offset - offset of the first octet to be read * @param maxLen - maximum length of data to be read * @param method - type of read message * * @return SUCCESS, blePending or Failure */ uint8 HidDev_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen ) { bStatus_t status = SUCCESS; hidRptMap_t *pRpt; uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]); // Only report map is long if ( offset > 0 && uuid != REPORT_MAP_UUID ) { return ( ATT_ERR_ATTR_NOT_LONG ); } if ( uuid == REPORT_UUID || uuid == BOOT_KEY_INPUT_UUID || uuid == BOOT_KEY_OUTPUT_UUID || uuid == BOOT_MOUSE_INPUT_UUID ) { // find report ID in table if ( (pRpt = hidDevRptByHandle(pAttr->handle)) != NULL ) { // execute report callback status = (*pHidDevCB->reportCB)( pRpt->id, pRpt->type, uuid, HID_DEV_OPER_READ, pLen, pValue ); } else { *pLen = 0; } } else if ( uuid == REPORT_MAP_UUID ) { // verify offset if ( offset >= hidReportMapLen ) { status = ATT_ERR_INVALID_OFFSET; } else { // determine read length *pLen = MIN( maxLen, (hidReportMapLen - offset) ); // copy data osal_memcpy( pValue, pAttr->pValue + offset, *pLen ); } } else if ( uuid == HID_INFORMATION_UUID ) { *pLen = HID_INFORMATION_LEN; osal_memcpy( pValue, pAttr->pValue, HID_INFORMATION_LEN ); } else if ( uuid == GATT_REPORT_REF_UUID ) { *pLen = HID_REPORT_REF_LEN; osal_memcpy( pValue, pAttr->pValue, HID_REPORT_REF_LEN ); } else if ( uuid == PROTOCOL_MODE_UUID ) { *pLen = HID_PROTOCOL_MODE_LEN; pValue[0] = pAttr->pValue[0]; } else if ( uuid == GATT_EXT_REPORT_REF_UUID ) { *pLen = HID_EXT_REPORT_REF_LEN; osal_memcpy( pValue, pAttr->pValue, HID_EXT_REPORT_REF_LEN ); } // restart idle timer if ( status == SUCCESS ) { hidDevStartIdleTimer(); } return ( status ); } /********************************************************************* * @fn HidDev_WriteAttrCB * * @brief HID Dev attribute read callback. * * @param connHandle - connection message was received on * @param pAttr - pointer to attribute * @param pValue - pointer to data to be written * @param len - length of data * @param offset - offset of the first octet to be written * * @return Success or Failure */ bStatus_t HidDev_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint8 len, uint16 offset ) { bStatus_t status = SUCCESS; hidRptMap_t *pRpt; // Make sure it's not a blob operation (no attributes in the profile are long) if ( offset > 0 ) { return ( ATT_ERR_ATTR_NOT_LONG ); } uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]); if ( uuid == REPORT_UUID || uuid == BOOT_KEY_OUTPUT_UUID ) { // find report ID in table if ((pRpt = hidDevRptByHandle(pAttr->handle)) != NULL) { // execute report callback status = (*pHidDevCB->reportCB)( pRpt->id, pRpt->type, uuid, HID_DEV_OPER_WRITE, &len, pValue ); } } else if ( uuid == HID_CTRL_PT_UUID ) { // Validate length and value range if ( len == 1 ) { if ( pValue[0] == HID_CMD_SUSPEND || pValue[0] == HID_CMD_EXIT_SUSPEND ) { // execute HID app event callback (*pHidDevCB->evtCB)( (pValue[0] == HID_CMD_SUSPEND) ? HID_DEV_SUSPEND_EVT : HID_DEV_EXIT_SUSPEND_EVT ); } else { status = ATT_ERR_INVALID_VALUE; } } else { status = ATT_ERR_INVALID_VALUE_SIZE; } } else if ( uuid == GATT_CLIENT_CHAR_CFG_UUID ) { status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len, offset, GATT_CLIENT_CFG_NOTIFY ); if ( status == SUCCESS ) { uint16 charCfg = BUILD_UINT16( pValue[0], pValue[1] ); // find report ID in table if ( (pRpt = hidDevRptByCccdHandle(pAttr->handle)) != NULL ) { // execute report callback (*pHidDevCB->reportCB)( pRpt->id, pRpt->type, uuid, (charCfg == GATT_CLIENT_CFG_NOTIFY) ? HID_DEV_OPER_ENABLE : HID_DEV_OPER_DISABLE, &len, pValue ); } } } else if ( uuid == PROTOCOL_MODE_UUID ) { if ( len == HID_PROTOCOL_MODE_LEN ) { if ( pValue[0] == HID_PROTOCOL_MODE_BOOT || pValue[0] == HID_PROTOCOL_MODE_REPORT ) { pAttr->pValue[0] = pValue[0]; // execute HID app event callback (*pHidDevCB->evtCB)( (pValue[0] == HID_PROTOCOL_MODE_BOOT) ? HID_DEV_SET_BOOT_EVT : HID_DEV_SET_REPORT_EVT ); } else { status = ATT_ERR_INVALID_VALUE; } } else { status = ATT_ERR_INVALID_VALUE_SIZE; } } // restart idle timer if (status == SUCCESS) { hidDevStartIdleTimer(); } return ( status ); } /********************************************************************* * @fn hidDev_ProcessOSALMsg * * @brief Process an incoming task message. * * @param pMsg - message to process * * @return none */ static void hidDev_ProcessOSALMsg( osal_event_hdr_t *pMsg ) { switch ( pMsg->event ) { case GATT_MSG_EVENT: hidDevProcessGattMsg( (gattMsgEvent_t *) pMsg ); break; default: break; } } /********************************************************************* * @fn hidDevProcessGattMsg * * @brief Process GATT messages * * @return none */ static void hidDevProcessGattMsg( gattMsgEvent_t *pMsg ) { } /********************************************************************* * @fn hidDevHandleConnStatusCB * * @brief Reset client char config. * * @param connHandle - connection handle * @param changeType - type of change * * @return none */ static void hidDevHandleConnStatusCB( uint16 connHandle, uint8 changeType ) { uint8 i; hidRptMap_t *p = pHidDevRptTbl; uint16 retHandle; gattAttribute_t *pAttr; // Make sure this is not loopback connection if ( connHandle != LOOPBACK_CONNHANDLE ) { if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED ) || ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) && ( !linkDB_Up( connHandle ) ) ) ) { for ( i = hidDevRptTblLen; i > 0; i--, p++ ) { if ( p->cccdHandle != 0 ) { if ( (pAttr = GATT_FindHandle(p->cccdHandle, &retHandle)) != NULL ) { GATTServApp_InitCharCfg( connHandle, (gattCharCfg_t *) pAttr->pValue ); } } } } } } /********************************************************************* * @fn hidDevDisconnected * * @brief Handle disconnect. * * @return none */ static void hidDevDisconnected( void ) { // Stop idle timer hidDevStopIdleTimer(); // Reset client characteristic configuration descriptors Batt_HandleConnStatusCB( gapConnHandle, LINKDB_STATUS_UPDATE_REMOVED ); //ScanParam_HandleConnStatusCB( gapConnHandle, LINKDB_STATUS_UPDATE_REMOVED ); hidDevHandleConnStatusCB( gapConnHandle, LINKDB_STATUS_UPDATE_REMOVED ); // Reset state variables hidDevConnSecure = FALSE; hidProtocolMode = HID_PROTOCOL_MODE_REPORT; hidDevPairingStarted = FALSE; // Reset last report sent out osal_memset( &lastNoti, 0, sizeof( attHandleValueNoti_t ) ); // if bonded and normally connectable start advertising if ( ( hidDevBondCount() > 0 ) && ( pHidDevCfg->hidFlags & HID_FLAGS_NORMALLY_CONNECTABLE ) ) { hidDevLowAdvertising(); } } /********************************************************************* * @fn hidDevGapStateCB * * @brief Notification from the profile of a state change. * * @param newState - new state * * @return none */ void hidDevGapStateCB( gaprole_States_t newState ) { LOG("%s, %d\n",__FUNCTION__, newState); // if connected if ( newState == GAPROLE_CONNECTED ) { // get connection handle GAPRole_GetParameter( GAPROLE_CONNHANDLE, &gapConnHandle ); // connection not secure yet hidDevConnSecure = FALSE; // don't start advertising when connection is closed uint8 param = FALSE; GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), ¶m ); // start idle timer hidDevStartIdleTimer(); } // if disconnected else if ( hidDevGapState == GAPROLE_CONNECTED && newState != GAPROLE_CONNECTED ) { hidDevDisconnected(); updateConnParams = TRUE; if ( pairingStatus == SMP_PAIRING_FAILED_CONFIRM_VALUE ) { // bonding failed due to mismatched confirm values hidDevInitialAdvertising(); pairingStatus = SUCCESS; } } // if started else if ( newState == GAPROLE_STARTED ) { // nothing to do for now! } hidDevGapState = newState; } /********************************************************************* * @fn hidDevPairStateCB * * @brief Pairing state callback. * * @return none */ void hidDevPairStateCB( uint16 connHandle, uint8 state, uint8 status ) { if ( state == GAPBOND_PAIRING_STATE_STARTED ) { hidDevPairingStarted = TRUE; } else if ( state == GAPBOND_PAIRING_STATE_COMPLETE ) { hidDevPairingStarted = FALSE; if ( status == SUCCESS ) { hidDevConnSecure = TRUE; } pairingStatus = status; } else if ( state == GAPBOND_PAIRING_STATE_BONDED ) { if ( status == SUCCESS ) { hidDevConnSecure = TRUE; } } //if(hidDevConnSecure){ // osal_start_reload_timer(hidDevTaskId, HID_TEST_EVT, 1000); //} if ( !reportQEmpty() && hidDevConnSecure ) { // Notify our task to send out pending reports osal_set_event( hidDevTaskId, HID_SEND_REPORT_EVT ); } } /********************************************************************* * @fn hidDevPasscodeCB * * @brief Passcode callback. * * @param deviceAddr - address of device to pair with, and could be either public or random. * @param connectionHandle - connection handle * @param uiInputs - pairing User Interface Inputs - Ask user to input passcode * @param uiOutputs - pairing User Interface Outputs - Display passcode * * @return none */ void hidDevPasscodeCB( uint8 *deviceAddr, uint16 connectionHandle, uint8 uiInputs, uint8 uiOutputs ) { if ( pHidDevCB && pHidDevCB->passcodeCB ) { // execute HID app passcode callback (*pHidDevCB->passcodeCB)( deviceAddr, connectionHandle, uiInputs, uiOutputs ); } else { // Send passcode response GAPBondMgr_PasscodeRsp( connectionHandle, SUCCESS, 0 ); } } /********************************************************************* * @fn hidDevBattPeriodicTask * * @brief Perform a periodic task for battery measurement. * * @param none * * @return none */ static void hidDevBattPeriodicTask( void ) { if ( hidDevGapState == GAPROLE_CONNECTED ) { // perform battery level check Batt_MeasLevel( ); // Restart timer osal_start_timerEx( hidDevTaskId, BATT_PERIODIC_EVT, DEFAULT_BATT_PERIOD ); } } /********************************************************************* * @fn hidDevRptByHandle * * @brief Find the HID report structure for the given handle. * * @param handle - ATT handle * * @return Pointer to HID report structure */ static hidRptMap_t *hidDevRptByHandle( uint16 handle ) { uint8 i; hidRptMap_t *p = pHidDevRptTbl; for ( i = hidDevRptTblLen; i > 0; i--, p++ ) { if ( p->handle == handle && p->mode == hidProtocolMode) { return p; } } return NULL; } /********************************************************************* * @fn hidDevRptByCccdHandle * * @brief Find the HID report structure for the given CCC handle. * * @param handle - ATT handle * * @return Pointer to HID report structure */ static hidRptMap_t *hidDevRptByCccdHandle( uint16 handle ) { uint8 i; hidRptMap_t *p = pHidDevRptTbl; for ( i = hidDevRptTblLen; i > 0; i--, p++ ) { if ( p->cccdHandle == handle) { return p; } } return NULL; } /********************************************************************* * @fn hidDevRptById * * @brief Find the HID report structure for the Report ID and type. * * @param id - HID report ID * @param type - HID report type * * @return Pointer to HID report structure */ static hidRptMap_t *hidDevRptById( uint8 id, uint8 type ) { uint8 i; hidRptMap_t *p = pHidDevRptTbl; for ( i = hidDevRptTblLen; i > 0; i--, p++ ) { if ( p->id == id && p->type == type && p->mode == hidProtocolMode ) { return p; } } return NULL; } /********************************************************************* * @fn hidDevSendReport * * @brief Send a HID report. * * @param id - HID report ID. * @param type - HID report type. * @param len - Length of report. * @param pData - Report data. * * @return None. */ static void hidDevSendReport( uint8 id, uint8 type, uint8 len, uint8 *pData ) { hidRptMap_t *pRpt; gattAttribute_t *pAttr; uint16 retHandle; LOG("%s\n",__FUNCTION__); // Get ATT handle for report if ( (pRpt = hidDevRptById(id, type)) != NULL ) { // if notifications are enabled if ( (pAttr = GATT_FindHandle(pRpt->cccdHandle, &retHandle)) != NULL ) { uint16 value; value = GATTServApp_ReadCharCfg( gapConnHandle, (gattCharCfg_t *) pAttr->pValue ); if ( value & GATT_CLIENT_CFG_NOTIFY ) { // After service discovery and encryption, the HID Device should request to // change to the preferred connection parameters that best suit its use case. if ( updateConnParams ) { GAPRole_SetParameter( GAPROLE_PARAM_UPDATE_REQ, sizeof( uint8 ), &updateConnParams ); updateConnParams = FALSE; } // send notification lastNoti.handle = pRpt->handle; lastNoti.len = len; osal_memcpy(lastNoti.value, pData, len); GATT_Notification( gapConnHandle, &lastNoti, FALSE ); // start idle timer hidDevStartIdleTimer(); } } } } /********************************************************************* * @fn hidDevEnqueueReport * * @brief Enqueue a HID report to be sent later. * * @param id - HID report ID. * @param type - HID report type. * @param len - Length of report. * @param pData - Report data. * * @return None. */ static void hidDevEnqueueReport( uint8 id, uint8 type, uint8 len, uint8 *pData ) { // Enqueue only if bonded if ( hidDevBondCount() > 0 ) { // Update last index lastQIdx = ( lastQIdx + 1 ) % HID_DEV_REPORT_Q_SIZE; if ( lastQIdx == firstQIdx ) { // Queue overflow; discard oldest report firstQIdx = ( firstQIdx + 1 ) % HID_DEV_REPORT_Q_SIZE; } // Save report hidDevReportQ[lastQIdx].id = id; hidDevReportQ[lastQIdx].type = type; hidDevReportQ[lastQIdx].len = len; osal_memcpy( hidDevReportQ[lastQIdx].data, pData, len ); if ( hidDevConnSecure ) { // Notify our task to send out pending reports osal_set_event( hidDevTaskId, HID_SEND_REPORT_EVT ); } } } /********************************************************************* * @fn hidDevDequeueReport * * @brief Dequeue a HID report to be sent out. * * @param id - HID report ID. * @param type - HID report type. * @param len - Length of report. * @param pData - Report data. * * @return None. */ static hidDevReport_t *hidDevDequeueReport( void ) { if ( reportQEmpty() ) { return NULL; } // Update first index firstQIdx = ( firstQIdx + 1 ) % HID_DEV_REPORT_Q_SIZE; return ( &(hidDevReportQ[firstQIdx]) ); } /********************************************************************* * @fn hidDevHighAdvertising * * @brief Start advertising at a high duty cycle. * @param None. * * @return None. */ static void hidDevHighAdvertising( void ) { uint8 param; VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, HID_HIGH_ADV_INT_MIN ); VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, HID_HIGH_ADV_INT_MAX ); VOID GAP_SetParamValue( TGAP_LIM_ADV_TIMEOUT, HID_HIGH_ADV_TIMEOUT ); // Setup adverstising filter policy first param = GAP_FILTER_POLICY_WHITE; VOID GAPRole_SetParameter( GAPROLE_ADV_FILTER_POLICY, sizeof( uint8 ), ¶m ); param = TRUE; GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), ¶m ); } /********************************************************************* * @fn hidDevLowAdvertising * * @brief Start advertising at a low duty cycle. * * @param None. * * @return None. */ static void hidDevLowAdvertising( void ) { uint8 param; VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, HID_LOW_ADV_INT_MIN ); VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, HID_LOW_ADV_INT_MAX ); VOID GAP_SetParamValue( TGAP_LIM_ADV_TIMEOUT, HID_LOW_ADV_TIMEOUT ); // Setup adverstising filter policy first param = GAP_FILTER_POLICY_WHITE; VOID GAPRole_SetParameter( GAPROLE_ADV_FILTER_POLICY, sizeof( uint8 ), ¶m ); param = TRUE; VOID GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), ¶m ); } /********************************************************************* * @fn hidDevInitialAdvertising * * @brief Start advertising for initial connection * * @return None. */ static void hidDevInitialAdvertising( void ) { uint8 param; VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, HID_INITIAL_ADV_INT_MIN ); VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, HID_INITIAL_ADV_INT_MAX ); VOID GAP_SetParamValue( TGAP_LIM_ADV_TIMEOUT, HID_INITIAL_ADV_TIMEOUT ); // Setup adverstising filter policy first param = GAP_FILTER_POLICY_ALL; VOID GAPRole_SetParameter( GAPROLE_ADV_FILTER_POLICY, sizeof( uint8 ), ¶m ); param = TRUE; VOID GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), ¶m ); } /********************************************************************* * @fn hidDevBondCount * * @brief Gets the total number of bonded devices. * * @param None. * * @return number of bonded devices. */ static uint8 hidDevBondCount( void ) { uint8 bondCnt = 0; VOID GAPBondMgr_GetParameter( GAPBOND_BOND_COUNT, &bondCnt ); return ( bondCnt ); } /********************************************************************* * @fn hidDevStartIdleTimer * * @brief Start the idle timer. * * @return None. */ static void hidDevStartIdleTimer( void ) { if ( pHidDevCfg->idleTimeout > 0 ) { osal_start_timerEx( hidDevTaskId, HID_IDLE_EVT, pHidDevCfg->idleTimeout ); } } /********************************************************************* * @fn hidDevStopIdleTimer * * @brief Stop the idle timer. * * @return None. */ static void hidDevStopIdleTimer( void ) { osal_stop_timerEx( hidDevTaskId, HID_IDLE_EVT ); } /********************************************************************* *********************************************************************/