/**************************************************************************************************

    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 "scanparamservice.h"


#include "hiddev.h"
#include "hidkbd.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 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
*/

// HID report mapping table
extern hidRptMap_t  hidRptMap[];


/*********************************************************************
    LOCAL VARIABLES
*/

// GAP State
gaprole_States_t hidDevGapState = GAPROLE_INIT;

// TRUE if connection is secure
static uint8 hidDevConnSecure = FALSE;

// GAP connection handle
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 );
void hidDevBattCB( uint8 event );
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 );
static void HidDev_scanParamCB(uint8_t event);

/*********************************************************************
    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 = FALSE;//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();
    Batt_Register(NULL);
    // Register for Scan Parameters service callback.
    ScanParam_Register(HidDev_scanParamCB);
    //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 );
        GAPBondMgr_SetParameter(GAPBOND_ERASE_ALLBONDS,0,NULL);
        LOG("start Device EVT\n\r");
        return ( events ^ START_DEVICE_EVT );
    }

    if ( events & HID_IDLE_EVT )
    {
        if ( hidDevGapState == GAPROLE_CONNECTED )
        {
            // if pairing in progress then restart timer
            if ( hidDevPairingStarted )
            {
                hidDevStartIdleTimer();
                LOG("hidDevStartIdleTimer \n\r");
            }
            // 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 )
        {
            LOG("Send Hid Report\n\r");
            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 );
                LOG("send key action\n\r");
                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
    @param       method - type of read message

    @return      SUCCESS, blePending or Failure
*/
uint8 HidDev_ReadAttrCB( uint16 connHandle, gattAttribute_t* pAttr,
                         uint8* pValue, uint16* 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, uint16 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 )
            {
                if(hidRptMap[5].cccdHandle==pRpt->cccdHandle)
                {
                    LOG("Audio cfg%4X\n\r",charCfg);
                }

                // 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 ), &param );
        uint8 peerAddress[B_ADDR_LEN];
        GAPRole_GetParameter(GAPROLE_CONN_BD_ADDR, peerAddress);
        LOG("Master Mac:%02X,%02X,%02X,%02X,%02X,%02X\n\r",peerAddress[5],peerAddress[4],peerAddress[3],peerAddress[2],peerAddress[1],peerAddress[0]);
        // start idle timer
        hidDevStartIdleTimer();
    }
    // if disconnected
    else if ( hidDevGapState == GAPROLE_CONNECTED &&
              newState != GAPROLE_CONNECTED )
    {
//  GAPBondMgr_SetParameter(GAPBOND_ERASE_ALLBONDS,0,NULL);
        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;
            LOG("Pair Success\n\r");
        }
        else
        {
            LOG("Pair Fail\n\r");
        }

        pairingStatus = status;
    }
    else if ( state == GAPBOND_PAIRING_STATE_BONDED )
    {
        if ( status == SUCCESS )
        {
            hidDevConnSecure = TRUE;
        }
    }

    if ( !reportQEmpty() && hidDevConnSecure )
    {
        LOG("Set Send Report EVENT\n\r");
        // 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      hidDevBattCB

    @brief   Callback function for battery service.

    @param   event - service event

    @return  none
*/
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
*/
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)
        {
            if(i==HID_VOICE_START_IN_CCCD_IDX)
            {
                LOG("Voice Notify Enable!!!!!\n\r");
            }

            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();
            }
            else
            {
                LOG("notify fail\n\r");
            }
        }
    }
}

/*********************************************************************
    @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_ALL;//GAP_FILTER_POLICY_WHITE teddy modify
    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 );
}


/*********************************************************************
    @fn      HidDev_scanParamCB

    @brief   Callback function for scan parameter service.

    @param   event - service event

    @return  none
*/
static void HidDev_scanParamCB(uint8_t event)
{
    // Do nothing.
}


/*********************************************************************
*********************************************************************/
