531 lines
16 KiB
C
531 lines
16 KiB
C
/********************************** (C) COPYRIGHT *******************************
|
|
* File Name : hidkbd.c
|
|
* Author : WCH
|
|
* Version : V1.0
|
|
* Date : 2018/12/10
|
|
* Description : 蓝牙键盘应用程序,初始化广播连接参数,然后广播,直至连接主机后,定时上传键值
|
|
*********************************************************************************
|
|
* 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 "devinfoservice.h"
|
|
#include "battservice.h"
|
|
#include "hidkbdservice.h"
|
|
#include "hiddev.h"
|
|
#include "hidkbd.h"
|
|
|
|
/*********************************************************************
|
|
* MACROS
|
|
*/
|
|
// HID keyboard input report length
|
|
#define HID_KEYBOARD_IN_RPT_LEN 8
|
|
|
|
// HID LED output report length
|
|
#define HID_LED_OUT_RPT_LEN 1
|
|
|
|
/*********************************************************************
|
|
* CONSTANTS
|
|
*/
|
|
// Param update delay
|
|
#define START_PARAM_UPDATE_EVT_DELAY 12800
|
|
|
|
// Param update delay
|
|
#define START_PHY_UPDATE_DELAY 1600
|
|
|
|
// HID idle timeout in msec; set to zero to disable timeout
|
|
#define DEFAULT_HID_IDLE_TIMEOUT 60000
|
|
|
|
// Minimum connection interval (units of 1.25ms)
|
|
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL 8
|
|
|
|
// Maximum connection interval (units of 1.25ms)
|
|
#define DEFAULT_DESIRED_MAX_CONN_INTERVAL 8
|
|
|
|
// Slave latency to use if parameter update request
|
|
#define DEFAULT_DESIRED_SLAVE_LATENCY 0
|
|
|
|
// Supervision timeout value (units of 10ms)
|
|
#define DEFAULT_DESIRED_CONN_TIMEOUT 500
|
|
|
|
// Default passcode
|
|
#define DEFAULT_PASSCODE 0
|
|
|
|
// Default GAP pairing mode
|
|
#define DEFAULT_PAIRING_MODE GAPBOND_PAIRING_MODE_WAIT_FOR_REQ
|
|
|
|
// Default MITM mode (TRUE to require passcode or OOB when pairing)
|
|
#define DEFAULT_MITM_MODE FALSE
|
|
|
|
// Default bonding mode, TRUE to bond
|
|
#define DEFAULT_BONDING_MODE TRUE
|
|
|
|
// Default GAP bonding I/O capabilities
|
|
#define DEFAULT_IO_CAPABILITIES GAPBOND_IO_CAP_NO_INPUT_NO_OUTPUT
|
|
|
|
// Battery level is critical when it is less than this %
|
|
#define DEFAULT_BATT_CRITICAL_LEVEL 6
|
|
|
|
/*********************************************************************
|
|
* TYPEDEFS
|
|
*/
|
|
|
|
/*********************************************************************
|
|
* GLOBAL VARIABLES
|
|
*/
|
|
|
|
// Task ID
|
|
static uint8_t hidEmuTaskId = INVALID_TASK_ID;
|
|
|
|
/*********************************************************************
|
|
* EXTERNAL VARIABLES
|
|
*/
|
|
|
|
/*********************************************************************
|
|
* EXTERNAL FUNCTIONS
|
|
*/
|
|
|
|
/*********************************************************************
|
|
* LOCAL VARIABLES
|
|
*/
|
|
|
|
// GAP Profile - Name attribute for SCAN RSP data
|
|
static uint8_t scanRspData[] = {
|
|
0x0D, // length of this data
|
|
GAP_ADTYPE_LOCAL_NAME_COMPLETE, // AD Type = Complete local name
|
|
'B',
|
|
'L',
|
|
'E',
|
|
' ',
|
|
'K',
|
|
'e',
|
|
'y',
|
|
'b',
|
|
'o',
|
|
'a',
|
|
'r',
|
|
'd', // connection interval range
|
|
0x05, // length of this data
|
|
GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE,
|
|
LO_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL), // 100ms
|
|
HI_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL),
|
|
LO_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL), // 1s
|
|
HI_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL),
|
|
|
|
// service UUIDs
|
|
0x05, // length of this data
|
|
GAP_ADTYPE_16BIT_MORE,
|
|
LO_UINT16(HID_SERV_UUID),
|
|
HI_UINT16(HID_SERV_UUID),
|
|
LO_UINT16(BATT_SERV_UUID),
|
|
HI_UINT16(BATT_SERV_UUID),
|
|
|
|
// Tx power level
|
|
0x02, // length of this data
|
|
GAP_ADTYPE_POWER_LEVEL,
|
|
0 // 0dBm
|
|
};
|
|
|
|
// Advertising data
|
|
static uint8_t advertData[] = {
|
|
// flags
|
|
0x02, // length of this data
|
|
GAP_ADTYPE_FLAGS,
|
|
GAP_ADTYPE_FLAGS_LIMITED | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
|
|
|
|
// appearance
|
|
0x03, // length of this data
|
|
GAP_ADTYPE_APPEARANCE,
|
|
LO_UINT16(GAP_APPEARE_HID_KEYBOARD),
|
|
HI_UINT16(GAP_APPEARE_HID_KEYBOARD)};
|
|
|
|
// Device name attribute value
|
|
static CONST uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "HID Keyboard";
|
|
|
|
// HID Dev configuration
|
|
static hidDevCfg_t hidEmuCfg = {
|
|
DEFAULT_HID_IDLE_TIMEOUT, // Idle timeout
|
|
HID_FEATURE_FLAGS // HID feature flags
|
|
};
|
|
|
|
static uint16_t hidEmuConnHandle = GAP_CONNHANDLE_INIT;
|
|
|
|
/*********************************************************************
|
|
* LOCAL FUNCTIONS
|
|
*/
|
|
|
|
static void hidEmu_ProcessTMOSMsg(tmos_event_hdr_t *pMsg);
|
|
static void hidEmuSendKbdReport(uint8_t keycode);
|
|
static uint8_t hidEmuRcvReport(uint8_t len, uint8_t *pData);
|
|
static uint8_t hidEmuRptCB(uint8_t id, uint8_t type, uint16_t uuid,
|
|
uint8_t oper, uint16_t *pLen, uint8_t *pData);
|
|
static void hidEmuEvtCB(uint8_t evt);
|
|
static void hidEmuStateCB(gapRole_States_t newState, gapRoleEvent_t *pEvent);
|
|
|
|
/*********************************************************************
|
|
* PROFILE CALLBACKS
|
|
*/
|
|
|
|
static hidDevCB_t hidEmuHidCBs = {
|
|
hidEmuRptCB,
|
|
hidEmuEvtCB,
|
|
NULL,
|
|
hidEmuStateCB};
|
|
|
|
/*********************************************************************
|
|
* PUBLIC FUNCTIONS
|
|
*/
|
|
|
|
/*********************************************************************
|
|
* @fn HidEmu_Init
|
|
*
|
|
* @brief Initialization function for the HidEmuKbd App 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 HidEmu_Init()
|
|
{
|
|
hidEmuTaskId = TMOS_ProcessEventRegister(HidEmu_ProcessEvent);
|
|
|
|
// Setup the GAP Peripheral Role Profile
|
|
{
|
|
uint8_t initial_advertising_enable = TRUE;
|
|
|
|
// Set the GAP Role Parameters
|
|
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);
|
|
|
|
GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);
|
|
GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);
|
|
}
|
|
|
|
// Set the GAP Characteristics
|
|
GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, (void *)attDeviceName);
|
|
|
|
// Setup the GAP Bond Manager
|
|
{
|
|
uint32_t passkey = DEFAULT_PASSCODE;
|
|
uint8_t pairMode = DEFAULT_PAIRING_MODE;
|
|
uint8_t mitm = DEFAULT_MITM_MODE;
|
|
uint8_t ioCap = DEFAULT_IO_CAPABILITIES;
|
|
uint8_t bonding = DEFAULT_BONDING_MODE;
|
|
GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey);
|
|
GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(uint8_t), &pairMode);
|
|
GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(uint8_t), &mitm);
|
|
GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
|
|
GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(uint8_t), &bonding);
|
|
}
|
|
|
|
// Setup Battery Characteristic Values
|
|
{
|
|
uint8_t critical = DEFAULT_BATT_CRITICAL_LEVEL;
|
|
Batt_SetParameter(BATT_PARAM_CRITICAL_LEVEL, sizeof(uint8_t), &critical);
|
|
}
|
|
|
|
// Set up HID keyboard service
|
|
Hid_AddService();
|
|
|
|
// Register for HID Dev callback
|
|
HidDev_Register(&hidEmuCfg, &hidEmuHidCBs);
|
|
|
|
// Setup a delayed profile startup
|
|
tmos_set_event(hidEmuTaskId, START_DEVICE_EVT);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn HidEmu_ProcessEvent
|
|
*
|
|
* @brief HidEmuKbd Application 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 HidEmu_ProcessEvent(uint8_t task_id, uint16_t events)
|
|
{
|
|
static uint8_t send_char = 4;
|
|
|
|
if(events & SYS_EVENT_MSG)
|
|
{
|
|
uint8_t *pMsg;
|
|
|
|
if((pMsg = tmos_msg_receive(hidEmuTaskId)) != NULL)
|
|
{
|
|
hidEmu_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)
|
|
{
|
|
return (events ^ START_DEVICE_EVT);
|
|
}
|
|
|
|
if(events & START_PARAM_UPDATE_EVT)
|
|
{
|
|
// Send connect param update request
|
|
GAPRole_PeripheralConnParamUpdateReq(hidEmuConnHandle,
|
|
DEFAULT_DESIRED_MIN_CONN_INTERVAL,
|
|
DEFAULT_DESIRED_MAX_CONN_INTERVAL,
|
|
DEFAULT_DESIRED_SLAVE_LATENCY,
|
|
DEFAULT_DESIRED_CONN_TIMEOUT,
|
|
hidEmuTaskId);
|
|
|
|
return (events ^ START_PARAM_UPDATE_EVT);
|
|
}
|
|
|
|
if(events & START_PHY_UPDATE_EVT)
|
|
{
|
|
// start phy update
|
|
PRINT("Send Phy Update %x...\n", GAPRole_UpdatePHY(hidEmuConnHandle, 0,
|
|
GAP_PHY_BIT_LE_2M, GAP_PHY_BIT_LE_2M, 0));
|
|
|
|
return (events ^ START_PHY_UPDATE_EVT);
|
|
}
|
|
|
|
if(events & START_REPORT_EVT)
|
|
{
|
|
hidEmuSendKbdReport(send_char);
|
|
send_char += 3;
|
|
if(send_char >= 29)
|
|
send_char = 4;
|
|
hidEmuSendKbdReport(0x00);
|
|
tmos_start_task(hidEmuTaskId, START_REPORT_EVT, 2000);
|
|
return (events ^ START_REPORT_EVT);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidEmu_ProcessTMOSMsg
|
|
*
|
|
* @brief Process an incoming task message.
|
|
*
|
|
* @param pMsg - message to process
|
|
*
|
|
* @return none
|
|
*/
|
|
static void hidEmu_ProcessTMOSMsg(tmos_event_hdr_t *pMsg)
|
|
{
|
|
switch(pMsg->event)
|
|
{
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidEmuSendKbdReport
|
|
*
|
|
* @brief Build and send a HID keyboard report.
|
|
*
|
|
* @param keycode - HID keycode.
|
|
*
|
|
* @return none
|
|
*/
|
|
static void hidEmuSendKbdReport(uint8_t keycode)
|
|
{
|
|
uint8_t buf[HID_KEYBOARD_IN_RPT_LEN];
|
|
|
|
buf[0] = 0; // Modifier keys
|
|
buf[1] = 0; // Reserved
|
|
buf[2] = keycode; // Keycode 1
|
|
buf[3] = 0; // Keycode 2
|
|
buf[4] = 0; // Keycode 3
|
|
buf[5] = 0; // Keycode 4
|
|
buf[6] = 0; // Keycode 5
|
|
buf[7] = 0; // Keycode 6
|
|
|
|
HidDev_Report(HID_RPT_ID_KEY_IN, HID_REPORT_TYPE_INPUT,
|
|
HID_KEYBOARD_IN_RPT_LEN, buf);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidEmuStateCB
|
|
*
|
|
* @brief GAP state change callback.
|
|
*
|
|
* @param newState - new state
|
|
*
|
|
* @return none
|
|
*/
|
|
static void hidEmuStateCB(gapRole_States_t newState, gapRoleEvent_t *pEvent)
|
|
{
|
|
switch(newState & GAPROLE_STATE_ADV_MASK)
|
|
{
|
|
case GAPROLE_STARTED:
|
|
{
|
|
uint8_t ownAddr[6];
|
|
GAPRole_GetParameter(GAPROLE_BD_ADDR, ownAddr);
|
|
GAP_ConfigDeviceAddr(ADDRTYPE_STATIC, ownAddr);
|
|
PRINT("Initialized..\n");
|
|
}
|
|
break;
|
|
|
|
case GAPROLE_ADVERTISING:
|
|
if(pEvent->gap.opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT)
|
|
{
|
|
PRINT("Advertising..\n");
|
|
}
|
|
break;
|
|
|
|
case GAPROLE_CONNECTED:
|
|
if(pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT)
|
|
{
|
|
gapEstLinkReqEvent_t *event = (gapEstLinkReqEvent_t *)pEvent;
|
|
|
|
// get connection handle
|
|
hidEmuConnHandle = event->connectionHandle;
|
|
tmos_start_task(hidEmuTaskId, START_PARAM_UPDATE_EVT, START_PARAM_UPDATE_EVT_DELAY);
|
|
PRINT("Connected..\n");
|
|
}
|
|
break;
|
|
|
|
case GAPROLE_CONNECTED_ADV:
|
|
if(pEvent->gap.opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT)
|
|
{
|
|
PRINT("Connected Advertising..\n");
|
|
}
|
|
break;
|
|
|
|
case GAPROLE_WAITING:
|
|
if(pEvent->gap.opcode == GAP_END_DISCOVERABLE_DONE_EVENT)
|
|
{
|
|
PRINT("Waiting for advertising..\n");
|
|
}
|
|
else if(pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT)
|
|
{
|
|
PRINT("Disconnected.. Reason:%x\n", pEvent->linkTerminate.reason);
|
|
}
|
|
else if(pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT)
|
|
{
|
|
PRINT("Advertising timeout..\n");
|
|
}
|
|
// Enable advertising
|
|
{
|
|
uint8_t initial_advertising_enable = TRUE;
|
|
// Set the GAP Role Parameters
|
|
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);
|
|
}
|
|
break;
|
|
|
|
case GAPROLE_ERROR:
|
|
PRINT("Error %x ..\n", pEvent->gap.opcode);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidEmuRcvReport
|
|
*
|
|
* @brief Process an incoming HID keyboard report.
|
|
*
|
|
* @param len - Length of report.
|
|
* @param pData - Report data.
|
|
*
|
|
* @return status
|
|
*/
|
|
static uint8_t hidEmuRcvReport(uint8_t len, uint8_t *pData)
|
|
{
|
|
// verify data length
|
|
if(len == HID_LED_OUT_RPT_LEN)
|
|
{
|
|
// set LEDs
|
|
return SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return ATT_ERR_INVALID_VALUE_SIZE;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidEmuRptCB
|
|
*
|
|
* @brief HID Dev report callback.
|
|
*
|
|
* @param id - HID report ID.
|
|
* @param type - HID report type.
|
|
* @param uuid - attribute uuid.
|
|
* @param oper - operation: read, write, etc.
|
|
* @param len - Length of report.
|
|
* @param pData - Report data.
|
|
*
|
|
* @return GATT status code.
|
|
*/
|
|
static uint8_t hidEmuRptCB(uint8_t id, uint8_t type, uint16_t uuid,
|
|
uint8_t oper, uint16_t *pLen, uint8_t *pData)
|
|
{
|
|
uint8_t status = SUCCESS;
|
|
|
|
// write
|
|
if(oper == HID_DEV_OPER_WRITE)
|
|
{
|
|
if(uuid == REPORT_UUID)
|
|
{
|
|
// process write to LED output report; ignore others
|
|
if(type == HID_REPORT_TYPE_OUTPUT)
|
|
{
|
|
status = hidEmuRcvReport(*pLen, pData);
|
|
}
|
|
}
|
|
|
|
if(status == SUCCESS)
|
|
{
|
|
status = Hid_SetParameter(id, type, uuid, *pLen, pData);
|
|
}
|
|
}
|
|
// read
|
|
else if(oper == HID_DEV_OPER_READ)
|
|
{
|
|
status = Hid_GetParameter(id, type, uuid, pLen, pData);
|
|
}
|
|
// notifications enabled
|
|
else if(oper == HID_DEV_OPER_ENABLE)
|
|
{
|
|
tmos_start_task(hidEmuTaskId, START_REPORT_EVT, 500);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* @fn hidEmuEvtCB
|
|
*
|
|
* @brief HID Dev event callback.
|
|
*
|
|
* @param evt - event ID.
|
|
*
|
|
* @return HID response code.
|
|
*/
|
|
static void hidEmuEvtCB(uint8_t evt)
|
|
{
|
|
// process enter/exit suspend or enter/exit boot mode
|
|
return;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*********************************************************************/
|