Files
examples/cc25xx-example/ti/BLE-CC254x/Projects/ble/Profiles/HIDDev/hiddev.c
2017-06-10 17:57:47 +08:00

1407 lines
37 KiB
C

/**************************************************************************************************
Filename: hiddev.c
Revised: $Date: 2013-05-06 13:33:47 -0700 (Mon, 06 May 2013) $
Revision: $Revision: 34153 $
Description: This file contains the common HID Device profile
for use with the CC2540 Bluetooth Low Energy Protocol Stack.
Copyright 2011-2013 Texas Instruments Incorporated. All rights reserved.
IMPORTANT: Your use of this Software is limited to those specific rights
granted under the terms of a software license agreement between the user
who downloaded the software, his/her employer (which must be your employer)
and Texas Instruments Incorporated (the "License"). You may not use this
Software unless you agree to abide by the terms of the License. The License
limits your use, and you acknowledge, that the Software may not be modified,
copied or distributed unless embedded on a Texas Instruments microcontroller
or used solely and exclusively in conjunction with a Texas Instruments radio
frequency transceiver, which is integrated into your product. Other than for
the foregoing purpose, you may not use, reproduce, copy, prepare derivative
works of, modify, distribute, perform, display or sell this Software and/or
its documentation for any purpose.
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
TEXAS INSTRUMENTS OR ITS LICENSORS 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.
Should you have any questions regarding your right to use this Software,
contact Texas Instruments Incorporated at www.TI.com.
**************************************************************************************************/
/*********************************************************************
* INCLUDES
*/
#include "bcomdef.h"
#include "OSAL.h"
#include "OnBoard.h"
#include "gatt.h"
#include "hci.h"
#include "gapgattserver.h"
#include "gattservapp.h"
#include "gatt_uuid.h"
#include "linkdb.h"
#include "peripheral.h"
#include "gapbondmgr.h"
#include "devinfoservice.h"
#include "battservice.h"
#include "scanparamservice.h"
#include "hiddev.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 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 hidDevGapStateCB( gaprole_States_t newState );
static void hidDevPairStateCB( uint16 connHandle, uint8 state, uint8 status );
static void hidDevPasscodeCB( uint8 *deviceAddr, uint16 connectionHandle,
uint8 uiInputs, uint8 uiOutputs );
static void hidDevBattCB( uint8 event );
static void hidDevScanParamCB( uint8 event );
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
*/
// GAP Role Callbacks
static gapRolesCBs_t hidDev_PeripheralCBs =
{
hidDevGapStateCB, // Profile State Change Callbacks
NULL // When a valid RSSI is read from controller
};
// Bond Manager Callbacks
static const gapBondCBs_t hidDevBondCB =
{
hidDevPasscodeCB,
hidDevPairStateCB
};
/*********************************************************************
* 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( );
ScanParam_AddService( );
// Register for Battery service callback
Batt_Register( hidDevBattCB );
// Register for Scan Parameters service callback
ScanParam_Register( hidDevScanParamCB );
// 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
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 ), &param );
}
}
/*********************************************************************
* @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
*
* @return Success 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 != HID_REPORT_MAP_UUID )
{
return ( ATT_ERR_ATTR_NOT_LONG );
}
if ( uuid == HID_REPORT_UUID ||
uuid == HID_BOOT_KEY_INPUT_UUID ||
uuid == HID_BOOT_KEY_OUTPUT_UUID ||
uuid == HID_BOOT_MOUSE_INPUT_UUID )
{
// require encryption
if ( hidDevConnSecure == FALSE )
{
return ( ATT_ERR_INSUFFICIENT_ENCRYPT );
}
// 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 == HID_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 )
{
// require encryption
if ( hidDevConnSecure == FALSE )
{
return ( ATT_ERR_INSUFFICIENT_ENCRYPT );
}
*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 == HID_PROTOCOL_MODE_UUID )
{
// require encryption
if ( hidDevConnSecure == FALSE )
{
return ( ATT_ERR_INSUFFICIENT_ENCRYPT );
}
*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 == HID_REPORT_UUID ||
uuid == HID_BOOT_KEY_OUTPUT_UUID )
{
// require encryption
if ( hidDevConnSecure == FALSE )
{
return ( ATT_ERR_INSUFFICIENT_ENCRYPT );
}
// 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_CONTROL_POINT_UUID )
{
// require encryption
if ( hidDevConnSecure == FALSE )
{
return ( ATT_ERR_INSUFFICIENT_ENCRYPT );
}
// 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 == HID_PROTOCOL_MODE_UUID )
{
// require encryption
if ( hidDevConnSecure == FALSE )
{
return ( ATT_ERR_INSUFFICIENT_ENCRYPT );
}
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
*/
static void hidDevGapStateCB( gaprole_States_t 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 ), &param );
// 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
*/
static 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 DEFAULT_SCAN_PARAM_NOTIFY_TEST == TRUE
ScanParam_RefreshNotify( gapConnHandle );
#endif
}
}
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
*/
static 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 hidDevBattCB
*
* @brief Callback function for battery service.
*
* @param event - service event
*
* @return none
*/
static void hidDevBattCB( uint8 event )
{
if ( event == BATT_LEVEL_NOTI_ENABLED )
{
// if connected start periodic measurement
if ( hidDevGapState == GAPROLE_CONNECTED )
{
osal_start_timerEx( hidDevTaskId, BATT_PERIODIC_EVT, DEFAULT_BATT_PERIOD );
}
}
else if ( event == BATT_LEVEL_NOTI_DISABLED )
{
// stop periodic measurement
osal_stop_timerEx( hidDevTaskId, BATT_PERIODIC_EVT );
}
}
/*********************************************************************
* @fn hidDevScanParamCB
*
* @brief Callback function for scan parameter service.
*
* @param event - service event
*
* @return none
*/
static void hidDevScanParamCB( uint8 event )
{
}
/*********************************************************************
* @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;
// 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 ), &param );
param = TRUE;
GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );
}
/*********************************************************************
* @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 ), &param );
param = TRUE;
VOID GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );
}
/*********************************************************************
* @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 ), &param );
param = TRUE;
VOID GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );
}
/*********************************************************************
* @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 );
}
/*********************************************************************
*********************************************************************/