1183 lines
32 KiB
C
1183 lines
32 KiB
C
/********************************** (C) COPYRIGHT *******************************
|
|
* File Name : hiddev.c
|
|
* Author : WCH
|
|
* Version : V1.0
|
|
* Date : 2018/12/10
|
|
* Description : HID 设备任务处理程序
|
|
*********************************************************************************
|
|
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
|
* Attention: This software (modified or not) and binary are used for
|
|
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
|
*******************************************************************************/
|
|
|
|
/*********************************************************************
|
|
* INCLUDES
|
|
*/
|
|
|
|
#include "CONFIG.h"
|
|
#include "battservice.h"
|
|
#include "scanparamservice.h"
|
|
#include "devinfoservice.h"
|
|
#include "hidkbd.h"
|
|
#include "hiddev.h"
|
|
|
|
/*********************************************************************
|
|
* MACROS
|
|
*/
|
|
|
|
// Battery measurement period in (625us)
|
|
#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 160
|
|
#define HID_LOW_ADV_INT_MAX 160
|
|
|
|
// 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
|
|
|
|
/*********************************************************************
|
|
* CONSTANTS
|
|
*/
|
|
|
|
/*********************************************************************
|
|
* TYPEDEFS
|
|
*/
|
|
|
|
/*********************************************************************
|
|
* GLOBAL VARIABLES
|
|
*/
|
|
|
|
// Task ID
|
|
uint8_t hidDevTaskId;
|
|
|
|
/*********************************************************************
|
|
* EXTERNAL VARIABLES
|
|
*/
|
|
|
|
/*********************************************************************
|
|
* EXTERNAL FUNCTIONS
|
|
*/
|
|
|
|
/*********************************************************************
|
|
* LOCAL VARIABLES
|
|
*/
|
|
|
|
// GAP State
|
|
static gapRole_States_t hidDevGapState = GAPROLE_INIT;
|
|
|
|
// TRUE if connection is secure
|
|
static uint8_t hidDevConnSecure = FALSE;
|
|
|
|
// GAP connection handle
|
|
static uint16_t gapConnHandle;
|
|
|
|
// Status of last pairing
|
|
static uint8_t pairingStatus = SUCCESS;
|
|
|
|
static hidRptMap_t *pHidDevRptTbl;
|
|
|
|
static uint8_t hidDevRptTblLen;
|
|
|
|
static hidDevCB_t *pHidDevCB;
|
|
|
|
static hidDevCfg_t *pHidDevCfg;
|
|
|
|
/*********************************************************************
|
|
* LOCAL FUNCTIONS
|
|
*/
|
|
|
|
static void hidDev_ProcessTMOSMsg(tmos_event_hdr_t *pMsg);
|
|
static void hidDevProcessGattMsg(gattMsgEvent_t *pMsg);
|
|
static void hidDevProcessGAPMsg(gapRoleEvent_t *pEvent);
|
|
static void hidDevDisconnected(void);
|
|
static void hidDevGapStateCB(gapRole_States_t newState, gapRoleEvent_t *pEvent);
|
|
static void hidDevParamUpdateCB(uint16_t connHandle, uint16_t connInterval,
|
|
uint16_t connSlaveLatency, uint16_t connTimeout);
|
|
static void hidDevPairStateCB(uint16_t connHandle, uint8_t state, uint8_t status);
|
|
static void hidDevPasscodeCB(uint8_t *deviceAddr, uint16_t connectionHandle,
|
|
uint8_t uiInputs, uint8_t uiOutputs);
|
|
static void hidDevBattCB(uint8_t event);
|
|
static void hidDevScanParamCB(uint8_t event);
|
|
static void hidDevBattPeriodicTask(void);
|
|
|
|
static hidRptMap_t *hidDevRptByHandle(uint16_t handle);
|
|
static hidRptMap_t *hidDevRptById(uint8_t id, uint8_t type);
|
|
static hidRptMap_t *hidDevRptByCccdHandle(uint16_t handle);
|
|
|
|
static uint8_t hidDevSendReport(uint8_t id, uint8_t type, uint8_t len, uint8_t *pData);
|
|
static void hidDevHighAdvertising(void);
|
|
static void hidDevLowAdvertising(void);
|
|
static void hidDevInitialAdvertising(void);
|
|
static uint8_t hidDevBondCount(void);
|
|
static uint8_t HidDev_sendNoti(uint16_t handle, uint8_t len, uint8_t *pData);
|
|
/*********************************************************************
|
|
* PROFILE CALLBACKS
|
|
*/
|
|
|
|
// GAP Role Callbacks
|
|
static gapRolesCBs_t hidDev_PeripheralCBs = {
|
|
hidDevGapStateCB, // Profile State Change Callbacks
|
|
NULL, // When a valid RSSI is read from controller
|
|
hidDevParamUpdateCB
|
|
};
|
|
|
|
// Bond Manager Callbacks
|
|
static 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 TMOS. This ID should be
|
|
* used to send messages and set timers.
|
|
*
|
|
* @return none
|
|
*/
|
|
void HidDev_Init()
|
|
{
|
|
hidDevTaskId = TMOS_ProcessEventRegister(HidDev_ProcessEvent);
|
|
|
|
// Setup the GAP Bond Manager
|
|
{
|
|
uint8_t 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'.
|
|
GAPBondMgr_SetParameter(GAPBOND_AUTO_SYNC_WL, sizeof(uint8_t), &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
|
|
tmos_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 TMOS 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_t HidDev_ProcessEvent(uint8_t task_id, uint16_t events)
|
|
{
|
|
//VOID task_id; // TMOS required parameter that isn't used in this function
|
|
|
|
if(events & SYS_EVENT_MSG)
|
|
{
|
|
uint8_t *pMsg;
|
|
|
|
if((pMsg = tmos_msg_receive(hidDevTaskId)) != NULL)
|
|
{
|
|
hidDev_ProcessTMOSMsg((tmos_event_hdr_t *)pMsg);
|
|
|
|
// Release the TMOS message
|
|
tmos_msg_deallocate(pMsg);
|
|
}
|
|
|
|
// return unprocessed events
|
|
return (events ^ SYS_EVENT_MSG);
|
|
}
|
|
|
|
if(events & START_DEVICE_EVT)
|
|
{
|
|
// Start the Device
|
|
GAPRole_PeripheralStartDevice(hidDevTaskId, &hidDevBondCB, &hidDev_PeripheralCBs);
|
|
|
|
return (events ^ START_DEVICE_EVT);
|
|
}
|
|
|
|
if(events & BATT_PERIODIC_EVT)
|
|
{
|
|
// Perform periodic battery task
|
|
hidDevBattPeriodicTask();
|
|
|
|
return (events ^ BATT_PERIODIC_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_t 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.
|
|
*/
|
|
uint8_t HidDev_Report(uint8_t id, uint8_t type, uint8_t len, uint8_t *pData)
|
|
{
|
|
// if connected
|
|
if(hidDevGapState == GAPROLE_CONNECTED)
|
|
{
|
|
// if connection is secure
|
|
if(hidDevConnSecure)
|
|
{
|
|
// send report
|
|
return hidDevSendReport(id, type, len, pData);
|
|
}
|
|
}
|
|
// 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();
|
|
}
|
|
}
|
|
return bleNotReady;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn HidDev_Close
|
|
*
|
|
* @brief Close the connection or stop advertising.
|
|
*
|
|
* @return None.
|
|
*/
|
|
void HidDev_Close(void)
|
|
{
|
|
uint8_t param;
|
|
|
|
// if connected then disconnect
|
|
if(hidDevGapState == GAPROLE_CONNECTED)
|
|
{
|
|
GAPRole_TerminateLink(gapConnHandle);
|
|
}
|
|
// else stop advertising
|
|
else
|
|
{
|
|
param = FALSE;
|
|
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), ¶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_t will be cast to
|
|
* uint16_t pointer).
|
|
*
|
|
* @return bStatus_t
|
|
*/
|
|
bStatus_t HidDev_SetParameter(uint8_t param, uint8_t len, void *pValue)
|
|
{
|
|
bStatus_t ret = SUCCESS;
|
|
|
|
switch(param)
|
|
{
|
|
case HIDDEV_ERASE_ALLBONDS:
|
|
if(len == 0)
|
|
{
|
|
// Drop connection
|
|
if(hidDevGapState == GAPROLE_CONNECTED)
|
|
{
|
|
GAPRole_TerminateLink(gapConnHandle);
|
|
}
|
|
|
|
// 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_t will be cast to
|
|
* uint16_t pointer).
|
|
*
|
|
* @return bStatus_t
|
|
*/
|
|
bStatus_t HidDev_GetParameter(uint8_t 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_t status, uint32_t 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
|
|
*/
|
|
bStatus_t HidDev_ReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr,
|
|
uint8_t *pValue, uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method)
|
|
{
|
|
bStatus_t status = SUCCESS;
|
|
hidRptMap_t *pRpt;
|
|
|
|
uint16_t 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
|
|
tmos_memcpy(pValue, pAttr->pValue + offset, *pLen);
|
|
}
|
|
}
|
|
else if(uuid == HID_INFORMATION_UUID)
|
|
{
|
|
*pLen = HID_INFORMATION_LEN;
|
|
tmos_memcpy(pValue, pAttr->pValue, HID_INFORMATION_LEN);
|
|
}
|
|
else if(uuid == GATT_REPORT_REF_UUID)
|
|
{
|
|
*pLen = HID_REPORT_REF_LEN;
|
|
tmos_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;
|
|
tmos_memcpy(pValue, pAttr->pValue, HID_EXT_REPORT_REF_LEN);
|
|
}
|
|
|
|
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_t connHandle, gattAttribute_t *pAttr,
|
|
uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method)
|
|
{
|
|
uint16_t uuid;
|
|
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);
|
|
}
|
|
|
|
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_t 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;
|
|
}
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDev_ProcessTMOSMsg
|
|
*
|
|
* @brief Process an incoming task message.
|
|
*
|
|
* @param pMsg - message to process
|
|
*
|
|
* @return none
|
|
*/
|
|
static void hidDev_ProcessTMOSMsg(tmos_event_hdr_t *pMsg)
|
|
{
|
|
switch(pMsg->event)
|
|
{
|
|
case GATT_MSG_EVENT:
|
|
{
|
|
hidDevProcessGattMsg((gattMsgEvent_t *)pMsg);
|
|
break;
|
|
}
|
|
|
|
case GAP_MSG_EVENT:
|
|
{
|
|
hidDevProcessGAPMsg((gapRoleEvent_t *)pMsg);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevProcessGattMsg
|
|
*
|
|
* @brief Process GATT messages
|
|
*
|
|
* @return none
|
|
*/
|
|
static void hidDevProcessGattMsg(gattMsgEvent_t *pMsg)
|
|
{
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevProcessGAPMsg
|
|
*
|
|
* @brief Process an incoming task message.
|
|
*
|
|
* @param pMsg - message to process
|
|
*
|
|
* @return none
|
|
*/
|
|
static void hidDevProcessGAPMsg(gapRoleEvent_t *pEvent)
|
|
{
|
|
switch(pEvent->gap.opcode)
|
|
{
|
|
case GAP_SCAN_REQUEST_EVENT:
|
|
{
|
|
PRINT("recv scan Req addr ");
|
|
for(int i = 0; i < B_ADDR_LEN; i++)
|
|
PRINT("%02x ", pEvent->scanReqEvt.scannerAddr[i]);
|
|
PRINT("\n");
|
|
break;
|
|
}
|
|
|
|
case GAP_PHY_UPDATE_EVENT:
|
|
{
|
|
PRINT("Phy update Rx:%x Tx:%x ..\n", pEvent->linkPhyUpdate.connRxPHYS, pEvent->linkPhyUpdate.connTxPHYS);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevHandleConnStatusCB
|
|
*
|
|
* @brief Reset client char config.
|
|
*
|
|
* @param connHandle - connection handle
|
|
* @param changeType - type of change
|
|
*
|
|
* @return none
|
|
*/
|
|
static void hidDevHandleConnStatusCB(uint16_t connHandle, uint8_t changeType)
|
|
{
|
|
uint8_t i;
|
|
hidRptMap_t *p = pHidDevRptTbl;
|
|
uint16_t 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)
|
|
{
|
|
// 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;
|
|
|
|
// 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, gapRoleEvent_t *pEvent)
|
|
{
|
|
uint8_t param;
|
|
// if connected
|
|
if(newState == GAPROLE_CONNECTED)
|
|
{
|
|
gapEstLinkReqEvent_t *event = (gapEstLinkReqEvent_t *)pEvent;
|
|
|
|
// get connection handle
|
|
gapConnHandle = event->connectionHandle;
|
|
|
|
// connection not secure yet
|
|
hidDevConnSecure = FALSE;
|
|
|
|
// don't start advertising when connection is closed
|
|
param = FALSE;
|
|
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), ¶m);
|
|
}
|
|
// if disconnected
|
|
else if(hidDevGapState == GAPROLE_CONNECTED &&
|
|
newState != GAPROLE_CONNECTED)
|
|
{
|
|
hidDevDisconnected();
|
|
|
|
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!
|
|
}
|
|
|
|
if(pHidDevCB && pHidDevCB->pfnStateChange)
|
|
{
|
|
// execute HID app state change callback
|
|
(*pHidDevCB->pfnStateChange)(newState, pEvent);
|
|
}
|
|
|
|
hidDevGapState = newState;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevParamUpdateCB
|
|
*
|
|
* @brief Parameter update complete callback
|
|
*
|
|
* @param connHandle - connect handle
|
|
* connInterval - connect interval
|
|
* connSlaveLatency - connect slave latency
|
|
* connTimeout - connect timeout
|
|
*
|
|
* @return none
|
|
*/
|
|
static void hidDevParamUpdateCB(uint16_t connHandle, uint16_t connInterval,
|
|
uint16_t connSlaveLatency, uint16_t connTimeout)
|
|
{
|
|
PRINT("Update %d - Int 0x%x - Latency %d\n", connHandle, connInterval, connSlaveLatency);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevPairStateCB
|
|
*
|
|
* @brief Pairing state callback.
|
|
*
|
|
* @return none
|
|
*/
|
|
static void hidDevPairStateCB(uint16_t connHandle, uint8_t state, uint8_t status)
|
|
{
|
|
if(state == GAPBOND_PAIRING_STATE_COMPLETE)
|
|
{
|
|
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
|
|
}
|
|
}
|
|
else if(state == GAPBOND_PAIRING_STATE_BOND_SAVED)
|
|
{
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @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_t *deviceAddr, uint16_t connectionHandle,
|
|
uint8_t uiInputs, uint8_t uiOutputs)
|
|
{
|
|
if(pHidDevCB && pHidDevCB->passcodeCB)
|
|
{
|
|
// execute HID app passcode callback
|
|
(*pHidDevCB->passcodeCB)(deviceAddr, connectionHandle, uiInputs, uiOutputs);
|
|
}
|
|
else
|
|
{
|
|
uint32_t passkey;
|
|
GAPBondMgr_GetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, &passkey);
|
|
|
|
// Send passcode response
|
|
GAPBondMgr_PasscodeRsp(connectionHandle, SUCCESS, passkey);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevBattCB
|
|
*
|
|
* @brief Callback function for battery service.
|
|
*
|
|
* @param event - service event
|
|
*
|
|
* @return none
|
|
*/
|
|
static void hidDevBattCB(uint8_t event)
|
|
{
|
|
if(event == BATT_LEVEL_NOTI_ENABLED)
|
|
{
|
|
tmos_start_task(hidDevTaskId, BATT_PERIODIC_EVT, DEFAULT_BATT_PERIOD);
|
|
}
|
|
else if(event == BATT_LEVEL_NOTI_DISABLED)
|
|
{
|
|
// stop periodic measurement
|
|
tmos_stop_task(hidDevTaskId, BATT_PERIODIC_EVT);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevScanParamCB
|
|
*
|
|
* @brief Callback function for scan parameter service.
|
|
*
|
|
* @param event - service event
|
|
*
|
|
* @return none
|
|
*/
|
|
static void hidDevScanParamCB(uint8_t event)
|
|
{
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevBattPeriodicTask
|
|
*
|
|
* @brief Perform a periodic task for battery measurement.
|
|
*
|
|
* @param none
|
|
*
|
|
* @return none
|
|
*/
|
|
static void hidDevBattPeriodicTask(void)
|
|
{
|
|
// perform battery level check
|
|
Batt_MeasLevel();
|
|
|
|
// Restart timer
|
|
tmos_start_task(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_t handle)
|
|
{
|
|
uint8_t 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_t handle)
|
|
{
|
|
uint8_t 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_t id, uint8_t type)
|
|
{
|
|
uint8_t 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 uint8_t hidDevSendReport(uint8_t id, uint8_t type, uint8_t len, uint8_t *pData)
|
|
{
|
|
hidRptMap_t *pRpt;
|
|
gattAttribute_t *pAttr;
|
|
uint16_t retHandle;
|
|
uint8_t state = bleNoResources;
|
|
|
|
// get att handle for report
|
|
if((pRpt = hidDevRptById(id, type)) != NULL)
|
|
{
|
|
// if notifications are enabled
|
|
if((pAttr = GATT_FindHandle(pRpt->cccdHandle, &retHandle)) != NULL)
|
|
{
|
|
uint16_t value;
|
|
|
|
value = GATTServApp_ReadCharCfg(gapConnHandle, (gattCharCfg_t *)pAttr->pValue);
|
|
if(value & GATT_CLIENT_CFG_NOTIFY)
|
|
{
|
|
// Send report notification
|
|
state = HidDev_sendNoti(pRpt->handle, len, pData);
|
|
}
|
|
}
|
|
}
|
|
return state;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevSendNoti
|
|
*
|
|
* @brief Send a HID notification.
|
|
*
|
|
* @param handle - Attribute handle.
|
|
* @param len - Length of report.
|
|
* @param pData - Report data.
|
|
*
|
|
* @return Success or failure.
|
|
*/
|
|
static uint8_t HidDev_sendNoti(uint16_t handle, uint8_t len, uint8_t *pData)
|
|
{
|
|
uint8_t status;
|
|
attHandleValueNoti_t noti;
|
|
|
|
noti.pValue = GATT_bm_alloc(gapConnHandle, ATT_HANDLE_VALUE_NOTI, len, NULL, 0);
|
|
if(noti.pValue != NULL)
|
|
{
|
|
noti.handle = handle;
|
|
noti.len = len;
|
|
tmos_memcpy(noti.pValue, pData, len);
|
|
|
|
// Send notification
|
|
status = GATT_Notification(gapConnHandle, ¬i, FALSE);
|
|
if(status != SUCCESS)
|
|
{
|
|
GATT_bm_free((gattMsg_t *)¬i, ATT_HANDLE_VALUE_NOTI);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = bleMemAllocError;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevHighAdvertising
|
|
*
|
|
* @brief Start advertising at a high duty cycle.
|
|
|
|
* @param None.
|
|
*
|
|
* @return None.
|
|
*/
|
|
static void hidDevHighAdvertising(void)
|
|
{
|
|
uint8_t param;
|
|
|
|
GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, HID_HIGH_ADV_INT_MIN);
|
|
GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, HID_HIGH_ADV_INT_MAX);
|
|
GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, HID_HIGH_ADV_TIMEOUT);
|
|
|
|
param = TRUE;
|
|
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), ¶m);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevLowAdvertising
|
|
*
|
|
* @brief Start advertising at a low duty cycle.
|
|
*
|
|
* @param None.
|
|
*
|
|
* @return None.
|
|
*/
|
|
static void hidDevLowAdvertising(void)
|
|
{
|
|
uint8_t param;
|
|
|
|
GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, HID_LOW_ADV_INT_MIN);
|
|
GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, HID_LOW_ADV_INT_MAX);
|
|
GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, HID_LOW_ADV_TIMEOUT);
|
|
|
|
param = TRUE;
|
|
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), ¶m);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevInitialAdvertising
|
|
*
|
|
* @brief Start advertising for initial connection
|
|
*
|
|
* @return None.
|
|
*/
|
|
static void hidDevInitialAdvertising(void)
|
|
{
|
|
uint8_t param;
|
|
|
|
GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, HID_INITIAL_ADV_INT_MIN);
|
|
GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, HID_INITIAL_ADV_INT_MAX);
|
|
GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, HID_INITIAL_ADV_TIMEOUT);
|
|
|
|
param = TRUE;
|
|
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), ¶m);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidDevBondCount
|
|
*
|
|
* @brief Gets the total number of bonded devices.
|
|
*
|
|
* @param None.
|
|
*
|
|
* @return number of bonded devices.
|
|
*/
|
|
static uint8_t hidDevBondCount(void)
|
|
{
|
|
uint8_t bondCnt = 0;
|
|
|
|
GAPBondMgr_GetParameter(GAPBOND_BOND_COUNT, &bondCnt);
|
|
|
|
return (bondCnt);
|
|
}
|
|
|
|
/*********************************************************************
|
|
*********************************************************************/
|