From 7500f5489a242e2da2d8f343096643e575eb6a2a Mon Sep 17 00:00:00 2001 From: Richard Goulter Date: Wed, 20 Dec 2023 22:59:19 +0700 Subject: [PATCH] add app, profile --- app/hidkbd.c | 530 +++++++++++++ app/hidkbd_main.c | 77 ++ app/include/hidkbd.h | 62 ++ profile/battservice.c | 584 ++++++++++++++ profile/devinfoservice.c | 588 ++++++++++++++ profile/hiddev.c | 1182 ++++++++++++++++++++++++++++ profile/hidkbdservice.c | 637 +++++++++++++++ profile/include/battservice.h | 172 ++++ profile/include/devinfoservice.h | 109 +++ profile/include/hiddev.h | 441 +++++++++++ profile/include/hidkbdservice.h | 112 +++ profile/include/scanparamservice.h | 133 ++++ profile/scanparamservice.c | 405 ++++++++++ 13 files changed, 5032 insertions(+) create mode 100644 app/hidkbd.c create mode 100644 app/hidkbd_main.c create mode 100644 app/include/hidkbd.h create mode 100644 profile/battservice.c create mode 100644 profile/devinfoservice.c create mode 100644 profile/hiddev.c create mode 100644 profile/hidkbdservice.c create mode 100644 profile/include/battservice.h create mode 100644 profile/include/devinfoservice.h create mode 100644 profile/include/hiddev.h create mode 100644 profile/include/hidkbdservice.h create mode 100644 profile/include/scanparamservice.h create mode 100644 profile/scanparamservice.c diff --git a/app/hidkbd.c b/app/hidkbd.c new file mode 100644 index 0000000..eed7f8e --- /dev/null +++ b/app/hidkbd.c @@ -0,0 +1,530 @@ +/********************************** (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; +} + +/********************************************************************* +*********************************************************************/ diff --git a/app/hidkbd_main.c b/app/hidkbd_main.c new file mode 100644 index 0000000..589aec3 --- /dev/null +++ b/app/hidkbd_main.c @@ -0,0 +1,77 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : main.c + * Author : WCH + * Version : V1.0 + * Date : 2020/08/06 + * 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. + *******************************************************************************/ + +/******************************************************************************/ +/* 头文件包含 */ +#include "CONFIG.h" +#include "HAL.h" +#include "hiddev.h" +#include "hidkbd.h" + +/********************************************************************* + * GLOBAL TYPEDEFS + */ +__attribute__((aligned(4))) uint32_t MEM_BUF[BLE_MEMHEAP_SIZE / 4]; + +#if(defined(BLE_MAC)) && (BLE_MAC == TRUE) +const uint8_t MacAddr[6] = {0x84, 0xC2, 0xE4, 0x03, 0x02, 0x03}; +#endif + +/********************************************************************* + * @fn Main_Circulation + * + * @brief 主循环 + * + * @return none + */ +__HIGH_CODE +__attribute__((noinline)) +void Main_Circulation() +{ + while(1) + { + TMOS_SystemProcess(); + } +} + +/********************************************************************* + * @fn main + * + * @brief 主函数 + * + * @return none + */ +int main(void) +{ +#if(defined(DCDC_ENABLE)) && (DCDC_ENABLE == TRUE) + PWR_DCDCCfg(ENABLE); +#endif + SetSysClock(CLK_SOURCE_PLL_60MHz); +#if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE) + GPIOA_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU); + GPIOB_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU); +#endif +#ifdef DEBUG + GPIOA_SetBits(bTXD1); + GPIOA_ModeCfg(bTXD1, GPIO_ModeOut_PP_5mA); + UART1_DefInit(); +#endif + PRINT("%s\n", VER_LIB); + CH59x_BLEInit(); + HAL_Init(); + GAPRole_PeripheralInit(); + HidDev_Init(); + HidEmu_Init(); + Main_Circulation(); +} + +/******************************** endfile @ main ******************************/ diff --git a/app/include/hidkbd.h b/app/include/hidkbd.h new file mode 100644 index 0000000..d21d616 --- /dev/null +++ b/app/include/hidkbd.h @@ -0,0 +1,62 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : hidkbd.h + * 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. + *******************************************************************************/ + +#ifndef HIDKBD_H +#define HIDKBD_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************************************************* + * INCLUDES + */ + +/********************************************************************* + * CONSTANTS + */ + +// Task Events +#define START_DEVICE_EVT 0x0001 +#define START_REPORT_EVT 0x0002 +#define START_PARAM_UPDATE_EVT 0x0004 +#define START_PHY_UPDATE_EVT 0x0008 +/********************************************************************* + * MACROS + */ + +/********************************************************************* + * FUNCTIONS + */ + +/********************************************************************* + * GLOBAL VARIABLES + */ + +/* + * Task Initialization for the BLE Application + */ +extern void HidEmu_Init(void); + +/* + * Task Event Processor for the BLE Application + */ +extern uint16_t HidEmu_ProcessEvent(uint8_t task_id, uint16_t events); + +/********************************************************************* +*********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/profile/battservice.c b/profile/battservice.c new file mode 100644 index 0000000..d92bf7d --- /dev/null +++ b/profile/battservice.c @@ -0,0 +1,584 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : battservice.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 "hiddev.h" +#include "battservice.h" + +/********************************************************************* + * MACROS + */ + +/********************************************************************* + * CONSTANTS + */ + +// ADC voltage levels +#define BATT_ADC_LEVEL_3V 409 +#define BATT_ADC_LEVEL_2V 273 + +#define BATT_LEVEL_VALUE_IDX 2 // Position of battery level in attribute array +#define BATT_LEVEL_VALUE_CCCD_IDX 3 // Position of battery level CCCD in attribute array + +#define BATT_LEVEL_VALUE_LEN 1 +/********************************************************************* + * TYPEDEFS + */ + +/********************************************************************* + * GLOBAL VARIABLES + */ +// Battery service +const uint8_t battServUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(BATT_SERV_UUID), HI_UINT16(BATT_SERV_UUID)}; + +// Battery level characteristic +const uint8_t battLevelUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(BATT_LEVEL_UUID), HI_UINT16(BATT_LEVEL_UUID)}; + +/********************************************************************* + * EXTERNAL VARIABLES + */ + +/********************************************************************* + * EXTERNAL FUNCTIONS + */ + +/********************************************************************* + * LOCAL VARIABLES + */ + +// Application callback +static battServiceCB_t battServiceCB; + +// Measurement setup callback +static battServiceSetupCB_t battServiceSetupCB = NULL; + +// Measurement teardown callback +static battServiceTeardownCB_t battServiceTeardownCB = NULL; + +// Measurement calculation callback +static battServiceCalcCB_t battServiceCalcCB = NULL; + +static uint16_t battMinLevel = BATT_ADC_LEVEL_2V; // For VDD/3 measurements +static uint16_t battMaxLevel = BATT_ADC_LEVEL_3V; // For VDD/3 measurements + +// Critical battery level setting +static uint8_t battCriticalLevel; + +// ADC channel to be used for reading +//static uint8_t battServiceAdcCh = HAL_ADC_CHANNEL_VDD; + +/********************************************************************* + * Profile Attributes - variables + */ + +// Battery Service attribute +static const gattAttrType_t battService = {ATT_BT_UUID_SIZE, battServUUID}; + +// Battery level characteristic +static uint8_t battLevelProps = GATT_PROP_READ | GATT_PROP_NOTIFY; +static uint8_t battLevel = 100; +static gattCharCfg_t battLevelClientCharCfg[GATT_MAX_NUM_CONN]; + +// HID Report Reference characteristic descriptor, battery level +static uint8_t hidReportRefBattLevel[HID_REPORT_REF_LEN] = { + HID_RPT_ID_BATT_LEVEL_IN, HID_REPORT_TYPE_INPUT}; + +/********************************************************************* + * Profile Attributes - Table + */ + +static gattAttribute_t battAttrTbl[] = { + // Battery Service + { + {ATT_BT_UUID_SIZE, primaryServiceUUID}, /* type */ + GATT_PERMIT_READ, /* permissions */ + 0, /* handle */ + (uint8_t *)&battService /* pValue */ + }, + + // Battery Level Declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &battLevelProps}, + + // Battery Level Value + { + {ATT_BT_UUID_SIZE, battLevelUUID}, + GATT_PERMIT_READ, + 0, + &battLevel}, + + // Battery Level Client Characteristic Configuration + { + {ATT_BT_UUID_SIZE, clientCharCfgUUID}, + GATT_PERMIT_READ | GATT_PERMIT_WRITE, + 0, + (uint8_t *)&battLevelClientCharCfg}, + + // HID Report Reference characteristic descriptor, batter level input + { + {ATT_BT_UUID_SIZE, reportRefUUID}, + GATT_PERMIT_READ, + 0, + hidReportRefBattLevel} +}; + +/********************************************************************* + * LOCAL FUNCTIONS + */ +static bStatus_t battReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr, + uint8_t *pValue, uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method); +static bStatus_t battWriteAttrCB(uint16_t connHandle, gattAttribute_t *pAttr, + uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method); +static void battNotifyCB(linkDBItem_t *pLinkItem); +static uint8_t battMeasure(void); +static void battNotifyLevel(void); + +/********************************************************************* + * PROFILE CALLBACKS + */ +// Battery Service Callbacks +gattServiceCBs_t battCBs = { + battReadAttrCB, // Read callback function pointer + battWriteAttrCB, // Write callback function pointer + NULL // Authorization callback function pointer +}; + +/********************************************************************* + * PUBLIC FUNCTIONS + */ + +/********************************************************************* + * @fn Batt_AddService + * + * @brief Initializes the Battery Service by registering + * GATT attributes with the GATT server. + * + * @return Success or Failure + */ +bStatus_t Batt_AddService(void) +{ + uint8_t status = SUCCESS; + + // Initialize Client Characteristic Configuration attributes + GATTServApp_InitCharCfg(INVALID_CONNHANDLE, battLevelClientCharCfg); + + // Register GATT attribute list and CBs with GATT Server App + status = GATTServApp_RegisterService(battAttrTbl, + GATT_NUM_ATTRS(battAttrTbl), + GATT_MAX_ENCRYPT_KEY_SIZE, + &battCBs); + + return (status); +} + +/********************************************************************* + * @fn Batt_Register + * + * @brief Register a callback function with the Battery Service. + * + * @param pfnServiceCB - Callback function. + * + * @return None. + */ +extern void Batt_Register(battServiceCB_t pfnServiceCB) +{ + battServiceCB = pfnServiceCB; +} + +/********************************************************************* + * @fn Batt_SetParameter + * + * @brief Set a Battery Service parameter. + * + * @param param - Profile parameter ID + * @param len - length of data to right + * @param value - 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 Batt_SetParameter(uint8_t param, uint8_t len, void *value) +{ + bStatus_t ret = SUCCESS; + + switch(param) + { + case BATT_PARAM_CRITICAL_LEVEL: + battCriticalLevel = *((uint8_t *)value); + + // If below the critical level and critical state not set, notify it + if(battLevel < battCriticalLevel) + { + battNotifyLevel(); + } + break; + + default: + ret = INVALIDPARAMETER; + break; + } + + return (ret); +} + +/********************************************************************* + * @fn Batt_GetParameter + * + * @brief Get a Battery Service parameter. + * + * @param param - Profile parameter ID + * @param value - 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 Batt_GetParameter(uint8_t param, void *value) +{ + bStatus_t ret = SUCCESS; + switch(param) + { + case BATT_PARAM_LEVEL: + *((uint8_t *)value) = battLevel; + break; + + case BATT_PARAM_CRITICAL_LEVEL: + *((uint8_t *)value) = battCriticalLevel; + break; + + case BATT_PARAM_SERVICE_HANDLE: + *((uint16_t *)value) = GATT_SERVICE_HANDLE(battAttrTbl); + break; + + case BATT_PARAM_BATT_LEVEL_IN_REPORT: + { + hidRptMap_t *pRpt = (hidRptMap_t *)value; + + pRpt->id = hidReportRefBattLevel[0]; + pRpt->type = hidReportRefBattLevel[1]; + pRpt->handle = battAttrTbl[BATT_LEVEL_VALUE_IDX].handle; + pRpt->cccdHandle = battAttrTbl[BATT_LEVEL_VALUE_CCCD_IDX].handle; + pRpt->mode = HID_PROTOCOL_MODE_REPORT; + } + break; + + default: + ret = INVALIDPARAMETER; + break; + } + + return (ret); +} + +/********************************************************************* + * @fn Batt_MeasLevel + * + * @brief Measure the battery level and update the battery + * level value in the service characteristics. If + * the battery level-state characteristic is configured + * for notification and the battery level has changed + * since the last measurement, then a notification + * will be sent. + * + * @return Success + */ +bStatus_t Batt_MeasLevel(void) +{ + uint8_t level; + + level = battMeasure(); + + // If level has gone down + if(level < battLevel) + { + // Update level + battLevel = level; + + // Send a notification + battNotifyLevel(); + } + + return SUCCESS; +} + +/********************************************************************* + * @fn Batt_Setup + * + * @brief Set up which ADC source is to be used. Defaults to VDD/3. + * + * @param adc_ch - ADC Channel, e.g. HAL_ADC_CHN_AIN6 + * @param minVal - max battery level + * @param maxVal - min battery level + * @param sCB - HW setup callback + * @param tCB - HW tear down callback + * @param cCB - percentage calculation callback + * + * @return none. + */ +void Batt_Setup(uint8_t adc_ch, uint16_t minVal, uint16_t maxVal, + battServiceSetupCB_t sCB, battServiceTeardownCB_t tCB, + battServiceCalcCB_t cCB) +{ + //battServiceAdcCh = adc_ch; + battMinLevel = minVal; + battMaxLevel = maxVal; + + battServiceSetupCB = sCB; + battServiceTeardownCB = tCB; + battServiceCalcCB = cCB; +} + +/********************************************************************* + * @fn battReadAttrCB + * + * @brief Read an attribute. + * + * @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 + */ +static bStatus_t battReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr, + uint8_t *pValue, uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method) +{ + uint16_t uuid; + bStatus_t status = SUCCESS; + + // 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]); + + // Measure battery level if reading level + if(uuid == BATT_LEVEL_UUID) + { + uint8_t level; + + level = battMeasure(); + + // If level has gone down + if(level < battLevel) + { + // Update level + battLevel = level; + } + + *pLen = 1; + pValue[0] = battLevel; + } + else if(uuid == GATT_REPORT_REF_UUID) + { + *pLen = HID_REPORT_REF_LEN; + tmos_memcpy(pValue, pAttr->pValue, HID_REPORT_REF_LEN); + } + else + { + status = ATT_ERR_ATTR_NOT_FOUND; + } + + return (status); +} + +/********************************************************************* + * @fn battWriteAttrCB + * + * @brief Validate attribute data prior to a write operation + * + * @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 + */ +static bStatus_t battWriteAttrCB(uint16_t connHandle, gattAttribute_t *pAttr, + uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method) +{ + bStatus_t status = SUCCESS; + + uint16_t uuid = BUILD_UINT16(pAttr->type.uuid[0], pAttr->type.uuid[1]); + switch(uuid) + { + case 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]); + + if(battServiceCB) + { + (*battServiceCB)((charCfg == GATT_CFG_NO_OPERATION) ? BATT_LEVEL_NOTI_DISABLED : BATT_LEVEL_NOTI_ENABLED); + } + } + break; + + default: + status = ATT_ERR_ATTR_NOT_FOUND; + break; + } + + return (status); +} + +/********************************************************************* + * @fn battNotifyCB + * + * @brief Send a notification of the level state characteristic. + * + * @param connHandle - linkDB item + * + * @return None. + */ +static void battNotifyCB(linkDBItem_t *pLinkItem) +{ + if(pLinkItem->stateFlags & LINK_CONNECTED) + { + uint16_t value = GATTServApp_ReadCharCfg(pLinkItem->connectionHandle, + battLevelClientCharCfg); + if(value & GATT_CLIENT_CFG_NOTIFY) + { + attHandleValueNoti_t noti; + + noti.pValue = GATT_bm_alloc(pLinkItem->connectionHandle, ATT_HANDLE_VALUE_NOTI, + BATT_LEVEL_VALUE_LEN, NULL, 0); + if(noti.pValue != NULL) + { + noti.handle = battAttrTbl[BATT_LEVEL_VALUE_IDX].handle; + noti.len = BATT_LEVEL_VALUE_LEN; + noti.pValue[0] = battLevel; + + if(GATT_Notification(pLinkItem->connectionHandle, ¬i, FALSE) != SUCCESS) + { + GATT_bm_free((gattMsg_t *)¬i, ATT_HANDLE_VALUE_NOTI); + } + } + } + } +} + +/********************************************************************* + * @fn battMeasure + * + * @brief Measure the battery level with the ADC and return + * it as a percentage 0-100%. + * + * @return Battery level. + */ +static uint8_t battMeasure(void) +{ + uint16_t adc; + uint8_t percent; + + // Call measurement setup callback + if(battServiceSetupCB != NULL) + { + battServiceSetupCB(); + } + + // Configure ADC and perform a read + adc = 300; + // Call measurement teardown callback + if(battServiceTeardownCB != NULL) + { + battServiceTeardownCB(); + } + + if(adc >= battMaxLevel) + { + percent = 100; + } + else if(adc <= battMinLevel) + { + percent = 0; + } + else + { + if(battServiceCalcCB != NULL) + { + percent = battServiceCalcCB(adc); + } + else + { + uint16_t range = battMaxLevel - battMinLevel + 1; + + // optional if you want to keep it even, otherwise just take floor of divide + // range += (range & 1); + range >>= 2; // divide by 4 + + percent = (uint8_t)((((adc - battMinLevel) * 25) + (range - 1)) / range); + } + } + + return percent; +} + +/********************************************************************* + * @fn battNotifyLevelState + * + * @brief Send a notification of the battery level state + * characteristic if a connection is established. + * + * @return None. + */ +static void battNotifyLevel(void) +{ + // Execute linkDB callback to send notification + linkDB_PerformFunc(battNotifyCB); +} + +/********************************************************************* + * @fn Batt_HandleConnStatusCB + * + * @brief Battery Service link status change handler function. + * + * @param connHandle - connection handle + * @param changeType - type of change + * + * @return none + */ +void Batt_HandleConnStatusCB(uint16_t connHandle, uint8_t changeType) +{ + // Make sure this is not loopback connection + if(connHandle != LOOPBACK_CONNHANDLE) + { + // Reset Client Char Config if connection has dropped + if((changeType == LINKDB_STATUS_UPDATE_REMOVED) || + ((changeType == LINKDB_STATUS_UPDATE_STATEFLAGS) && + (!linkDB_Up(connHandle)))) + { + GATTServApp_InitCharCfg(connHandle, battLevelClientCharCfg); + } + } +} + +/********************************************************************* +*********************************************************************/ diff --git a/profile/devinfoservice.c b/profile/devinfoservice.c new file mode 100644 index 0000000..b52d9ea --- /dev/null +++ b/profile/devinfoservice.c @@ -0,0 +1,588 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : devinfoservice.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" + +/********************************************************************* + * MACROS + */ + +/********************************************************************* + * CONSTANTS + */ + +/********************************************************************* + * TYPEDEFS + */ + +/********************************************************************* + * GLOBAL VARIABLES + */ +// Device information service +const uint8_t devInfoServUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(DEVINFO_SERV_UUID), HI_UINT16(DEVINFO_SERV_UUID)}; + +// System ID +const uint8_t devInfoSystemIdUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(SYSTEM_ID_UUID), HI_UINT16(SYSTEM_ID_UUID)}; + +// Model Number String +const uint8_t devInfoModelNumberUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(MODEL_NUMBER_UUID), HI_UINT16(MODEL_NUMBER_UUID)}; + +// Serial Number String +const uint8_t devInfoSerialNumberUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(SERIAL_NUMBER_UUID), HI_UINT16(SERIAL_NUMBER_UUID)}; + +// Firmware Revision String +const uint8_t devInfoFirmwareRevUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(FIRMWARE_REV_UUID), HI_UINT16(FIRMWARE_REV_UUID)}; + +// Hardware Revision String +const uint8_t devInfoHardwareRevUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(HARDWARE_REV_UUID), HI_UINT16(HARDWARE_REV_UUID)}; + +// Software Revision String +const uint8_t devInfoSoftwareRevUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(SOFTWARE_REV_UUID), HI_UINT16(SOFTWARE_REV_UUID)}; + +// Manufacturer Name String +const uint8_t devInfoMfrNameUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(MANUFACTURER_NAME_UUID), HI_UINT16(MANUFACTURER_NAME_UUID)}; + +// IEEE 11073-20601 Regulatory Certification Data List +const uint8_t devInfo11073CertUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(IEEE_11073_CERT_DATA_UUID), HI_UINT16(IEEE_11073_CERT_DATA_UUID)}; + +// PnP ID +const uint8_t devInfoPnpIdUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(PNP_ID_UUID), HI_UINT16(PNP_ID_UUID)}; + +/********************************************************************* + * EXTERNAL VARIABLES + */ + +/********************************************************************* + * EXTERNAL FUNCTIONS + */ + +/********************************************************************* + * LOCAL VARIABLES + */ + +/********************************************************************* + * Profile Attributes - variables + */ + +// Device Information Service attribute +static const gattAttrType_t devInfoService = {ATT_BT_UUID_SIZE, devInfoServUUID}; + +// System ID characteristic +static uint8_t devInfoSystemIdProps = GATT_PROP_READ; +static uint8_t devInfoSystemId[DEVINFO_SYSTEM_ID_LEN] = {0, 0, 0, 0, 0, 0, 0, 0}; + +// Model Number String characteristic +static uint8_t devInfoModelNumberProps = GATT_PROP_READ; +static const uint8_t devInfoModelNumber[] = "Model Number"; + +// Serial Number String characteristic +static uint8_t devInfoSerialNumberProps = GATT_PROP_READ; +static const uint8_t devInfoSerialNumber[] = "Serial Number"; + +// Firmware Revision String characteristic +static uint8_t devInfoFirmwareRevProps = GATT_PROP_READ; +static const uint8_t devInfoFirmwareRev[] = "Firmware Revision"; + +// Hardware Revision String characteristic +static uint8_t devInfoHardwareRevProps = GATT_PROP_READ; +static const uint8_t devInfoHardwareRev[] = "Hardware Revision"; + +// Software Revision String characteristic +static uint8_t devInfoSoftwareRevProps = GATT_PROP_READ; +static const uint8_t devInfoSoftwareRev[] = "Software Revision"; + +// Manufacturer Name String characteristic +static uint8_t devInfoMfrNameProps = GATT_PROP_READ; +static const uint8_t devInfoMfrName[] = "Manufacturer Name"; + +// IEEE 11073-20601 Regulatory Certification Data List characteristic +static uint8_t devInfo11073CertProps = GATT_PROP_READ; +static const uint8_t devInfo11073Cert[] = { + DEVINFO_11073_BODY_EXP, // authoritative body type + 0x00, // authoritative body structure type + // authoritative body data follows below: + 'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l'}; + +// System ID characteristic +static uint8_t devInfoPnpIdProps = GATT_PROP_READ; +static uint8_t devInfoPnpId[DEVINFO_PNP_ID_LEN] = { + 1, // Vendor ID source (1=Bluetooth SIG) + LO_UINT16(0x07D7), HI_UINT16(0x07D7), // Vendor ID (WCH) + LO_UINT16(0x0000), HI_UINT16(0x0000), // Product ID (vendor-specific) + LO_UINT16(0x0110), HI_UINT16(0x0110) // Product version (JJ.M.N) +}; + +/********************************************************************* + * Profile Attributes - Table + */ + +static gattAttribute_t devInfoAttrTbl[] = { + // Device Information Service + { + {ATT_BT_UUID_SIZE, primaryServiceUUID}, /* type */ + GATT_PERMIT_READ, /* permissions */ + 0, /* handle */ + (uint8_t *)&devInfoService /* pValue */ + }, + + // System ID Declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &devInfoSystemIdProps}, + + // System ID Value + { + {ATT_BT_UUID_SIZE, devInfoSystemIdUUID}, + GATT_PERMIT_READ, + 0, + (uint8_t *)devInfoSystemId}, + + // Model Number String Declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &devInfoModelNumberProps}, + + // Model Number Value + { + {ATT_BT_UUID_SIZE, devInfoModelNumberUUID}, + GATT_PERMIT_READ, + 0, + (uint8_t *)devInfoModelNumber}, + + // Serial Number String Declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &devInfoSerialNumberProps}, + + // Serial Number Value + { + {ATT_BT_UUID_SIZE, devInfoSerialNumberUUID}, + GATT_PERMIT_READ, + 0, + (uint8_t *)devInfoSerialNumber}, + + // Firmware Revision String Declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &devInfoFirmwareRevProps}, + + // Firmware Revision Value + { + {ATT_BT_UUID_SIZE, devInfoFirmwareRevUUID}, + GATT_PERMIT_READ, + 0, + (uint8_t *)devInfoFirmwareRev}, + + // Hardware Revision String Declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &devInfoHardwareRevProps}, + + // Hardware Revision Value + { + {ATT_BT_UUID_SIZE, devInfoHardwareRevUUID}, + GATT_PERMIT_READ, + 0, + (uint8_t *)devInfoHardwareRev}, + + // Software Revision String Declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &devInfoSoftwareRevProps}, + + // Software Revision Value + { + {ATT_BT_UUID_SIZE, devInfoSoftwareRevUUID}, + GATT_PERMIT_READ, + 0, + (uint8_t *)devInfoSoftwareRev}, + + // Manufacturer Name String Declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &devInfoMfrNameProps}, + + // Manufacturer Name Value + { + {ATT_BT_UUID_SIZE, devInfoMfrNameUUID}, + GATT_PERMIT_READ, + 0, + (uint8_t *)devInfoMfrName}, + + // IEEE 11073-20601 Regulatory Certification Data List Declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &devInfo11073CertProps}, + + // IEEE 11073-20601 Regulatory Certification Data List Value + { + {ATT_BT_UUID_SIZE, devInfo11073CertUUID}, + GATT_PERMIT_READ, + 0, + (uint8_t *)devInfo11073Cert}, + + // PnP ID Declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &devInfoPnpIdProps}, + + // PnP ID Value + { + {ATT_BT_UUID_SIZE, devInfoPnpIdUUID}, + GATT_PERMIT_READ, + 0, + (uint8_t *)devInfoPnpId}}; + +/********************************************************************* + * LOCAL FUNCTIONS + */ +static bStatus_t devInfo_ReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr, + uint8_t *pValue, uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method); + +/********************************************************************* + * PROFILE CALLBACKS + */ +// Device Info Service Callbacks +gattServiceCBs_t devInfoCBs = { + devInfo_ReadAttrCB, // Read callback function pointer + NULL, // Write callback function pointer + NULL // Authorization callback function pointer +}; + +/********************************************************************* + * NETWORK LAYER CALLBACKS + */ + +/********************************************************************* + * PUBLIC FUNCTIONS + */ + +/********************************************************************* + * @fn DevInfo_AddService + * + * @brief Initializes the Device Information service by registering + * GATT attributes with the GATT server. + * + * @return Success or Failure + */ +bStatus_t DevInfo_AddService(void) +{ + // Register GATT attribute list and CBs with GATT Server App + return GATTServApp_RegisterService(devInfoAttrTbl, + GATT_NUM_ATTRS(devInfoAttrTbl), + GATT_MAX_ENCRYPT_KEY_SIZE, + &devInfoCBs); +} + +/********************************************************************* + * @fn DevInfo_SetParameter + * + * @brief Set a Device Information parameter. + * + * @param param - Profile parameter ID + * @param len - length of data to write + * @param value - 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 DevInfo_SetParameter(uint8_t param, uint8_t len, void *value) +{ + bStatus_t ret = SUCCESS; + + switch(param) + { + case DEVINFO_SYSTEM_ID: + tmos_memcpy(devInfoSystemId, value, len); + break; + + default: + ret = INVALIDPARAMETER; + break; + } + + return (ret); +} + +/********************************************************************* + * @fn DevInfo_GetParameter + * + * @brief Get a Device Information parameter. + * + * @param param - Profile parameter ID + * @param value - 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 DevInfo_GetParameter(uint8_t param, void *value) +{ + bStatus_t ret = SUCCESS; + + switch(param) + { + case DEVINFO_SYSTEM_ID: + tmos_memcpy(value, devInfoSystemId, sizeof(devInfoSystemId)); + break; + + case DEVINFO_MODEL_NUMBER: + tmos_memcpy(value, devInfoModelNumber, sizeof(devInfoModelNumber)); + break; + case DEVINFO_SERIAL_NUMBER: + tmos_memcpy(value, devInfoSerialNumber, sizeof(devInfoSerialNumber)); + break; + + case DEVINFO_FIRMWARE_REV: + tmos_memcpy(value, devInfoFirmwareRev, sizeof(devInfoFirmwareRev)); + break; + + case DEVINFO_HARDWARE_REV: + tmos_memcpy(value, devInfoHardwareRev, sizeof(devInfoHardwareRev)); + break; + + case DEVINFO_SOFTWARE_REV: + tmos_memcpy(value, devInfoSoftwareRev, sizeof(devInfoSoftwareRev)); + break; + + case DEVINFO_MANUFACTURER_NAME: + tmos_memcpy(value, devInfoMfrName, sizeof(devInfoMfrName)); + break; + + case DEVINFO_11073_CERT_DATA: + tmos_memcpy(value, devInfo11073Cert, sizeof(devInfo11073Cert)); + break; + + case DEVINFO_PNP_ID: + tmos_memcpy(value, devInfoPnpId, sizeof(devInfoPnpId)); + break; + + default: + ret = INVALIDPARAMETER; + break; + } + + return (ret); +} + +/********************************************************************* + * @fn devInfo_ReadAttrCB + * + * @brief Read an attribute. + * + * @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 + */ +static bStatus_t devInfo_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; + uint16_t uuid = BUILD_UINT16(pAttr->type.uuid[0], pAttr->type.uuid[1]); + + switch(uuid) + { + case SYSTEM_ID_UUID: + // verify offset + if(offset >= sizeof(devInfoSystemId)) + { + status = ATT_ERR_INVALID_OFFSET; + } + else + { + // determine read length + *pLen = MIN(maxLen, (sizeof(devInfoSystemId) - offset)); + + // copy data + tmos_memcpy(pValue, &devInfoSystemId[offset], *pLen); + } + break; + + case MODEL_NUMBER_UUID: + // verify offset + if(offset >= (sizeof(devInfoModelNumber) - 1)) + { + status = ATT_ERR_INVALID_OFFSET; + } + else + { + // determine read length (exclude null terminating character) + *pLen = MIN(maxLen, ((sizeof(devInfoModelNumber) - 1) - offset)); + + // copy data + tmos_memcpy(pValue, &devInfoModelNumber[offset], *pLen); + } + break; + + case SERIAL_NUMBER_UUID: + // verify offset + if(offset >= (sizeof(devInfoSerialNumber) - 1)) + { + status = ATT_ERR_INVALID_OFFSET; + } + else + { + // determine read length (exclude null terminating character) + *pLen = MIN(maxLen, ((sizeof(devInfoSerialNumber) - 1) - offset)); + + // copy data + tmos_memcpy(pValue, &devInfoSerialNumber[offset], *pLen); + } + break; + + case FIRMWARE_REV_UUID: + // verify offset + if(offset >= (sizeof(devInfoFirmwareRev) - 1)) + { + status = ATT_ERR_INVALID_OFFSET; + } + else + { + // determine read length (exclude null terminating character) + *pLen = MIN(maxLen, ((sizeof(devInfoFirmwareRev) - 1) - offset)); + + // copy data + tmos_memcpy(pValue, &devInfoFirmwareRev[offset], *pLen); + } + break; + + case HARDWARE_REV_UUID: + // verify offset + if(offset >= (sizeof(devInfoHardwareRev) - 1)) + { + status = ATT_ERR_INVALID_OFFSET; + } + else + { + // determine read length (exclude null terminating character) + *pLen = MIN(maxLen, ((sizeof(devInfoHardwareRev) - 1) - offset)); + + // copy data + tmos_memcpy(pValue, &devInfoHardwareRev[offset], *pLen); + } + break; + + case SOFTWARE_REV_UUID: + // verify offset + if(offset >= (sizeof(devInfoSoftwareRev) - 1)) + { + status = ATT_ERR_INVALID_OFFSET; + } + else + { + // determine read length (exclude null terminating character) + *pLen = MIN(maxLen, ((sizeof(devInfoSoftwareRev) - 1) - offset)); + + // copy data + tmos_memcpy(pValue, &devInfoSoftwareRev[offset], *pLen); + } + break; + + case MANUFACTURER_NAME_UUID: + // verify offset + if(offset >= (sizeof(devInfoMfrName) - 1)) + { + status = ATT_ERR_INVALID_OFFSET; + } + else + { + // determine read length (exclude null terminating character) + *pLen = MIN(maxLen, ((sizeof(devInfoMfrName) - 1) - offset)); + + // copy data + tmos_memcpy(pValue, &devInfoMfrName[offset], *pLen); + } + break; + + case IEEE_11073_CERT_DATA_UUID: + // verify offset + if(offset >= sizeof(devInfo11073Cert)) + { + status = ATT_ERR_INVALID_OFFSET; + } + else + { + // determine read length + *pLen = MIN(maxLen, (sizeof(devInfo11073Cert) - offset)); + + // copy data + tmos_memcpy(pValue, &devInfo11073Cert[offset], *pLen); + } + break; + + case PNP_ID_UUID: + // verify offset + if(offset >= sizeof(devInfoPnpId)) + { + status = ATT_ERR_INVALID_OFFSET; + } + else + { + // determine read length + *pLen = MIN(maxLen, (sizeof(devInfoPnpId) - offset)); + + // copy data + tmos_memcpy(pValue, &devInfoPnpId[offset], *pLen); + } + break; + + default: + *pLen = 0; + status = ATT_ERR_ATTR_NOT_FOUND; + break; + } + + return (status); +} + +/********************************************************************* +*********************************************************************/ diff --git a/profile/hiddev.c b/profile/hiddev.c new file mode 100644 index 0000000..fde9aad --- /dev/null +++ b/profile/hiddev.c @@ -0,0 +1,1182 @@ +/********************************** (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); +} + +/********************************************************************* +*********************************************************************/ diff --git a/profile/hidkbdservice.c b/profile/hidkbdservice.c new file mode 100644 index 0000000..2815fab --- /dev/null +++ b/profile/hidkbdservice.c @@ -0,0 +1,637 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : hidkbdservice.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 "hidkbdservice.h" +#include "hiddev.h" +#include "battservice.h" + +/********************************************************************* + * MACROS + */ + +/********************************************************************* + * CONSTANTS + */ + +/********************************************************************* + * TYPEDEFS + */ + +/********************************************************************* + * GLOBAL VARIABLES + */ +// HID service +const uint8_t hidServUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(HID_SERV_UUID), HI_UINT16(HID_SERV_UUID)}; + +// HID Boot Keyboard Input Report characteristic +const uint8_t hidBootKeyInputUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(BOOT_KEY_INPUT_UUID), HI_UINT16(BOOT_KEY_INPUT_UUID)}; + +// HID Boot Keyboard Output Report characteristic +const uint8_t hidBootKeyOutputUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(BOOT_KEY_OUTPUT_UUID), HI_UINT16(BOOT_KEY_OUTPUT_UUID)}; + +// HID Information characteristic +const uint8_t hidInfoUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(HID_INFORMATION_UUID), HI_UINT16(HID_INFORMATION_UUID)}; + +// HID Report Map characteristic +const uint8_t hidReportMapUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(REPORT_MAP_UUID), HI_UINT16(REPORT_MAP_UUID)}; + +// HID Control Point characteristic +const uint8_t hidControlPointUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(HID_CTRL_PT_UUID), HI_UINT16(HID_CTRL_PT_UUID)}; + +// HID Report characteristic +const uint8_t hidReportUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(REPORT_UUID), HI_UINT16(REPORT_UUID)}; + +// HID Protocol Mode characteristic +const uint8_t hidProtocolModeUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(PROTOCOL_MODE_UUID), HI_UINT16(PROTOCOL_MODE_UUID)}; + +/********************************************************************* + * EXTERNAL VARIABLES + */ + +/********************************************************************* + * EXTERNAL FUNCTIONS + */ + +/********************************************************************* + * LOCAL VARIABLES + */ + +// HID Information characteristic value +static const uint8_t hidInfo[HID_INFORMATION_LEN] = { + LO_UINT16(0x0111), HI_UINT16(0x0111), // bcdHID (USB HID version) + 0x00, // bCountryCode + HID_FEATURE_FLAGS // Flags +}; + +// HID Report Map characteristic value +static const uint8_t hidReportMap[] = { + 0x05, 0x01, // Usage Pg (Generic Desktop) + 0x09, 0x06, // Usage (Keyboard) + 0xA1, 0x01, // Collection: (Application) + // + 0x05, 0x07, // Usage Pg (Key Codes) + 0x19, 0xE0, // Usage Min (224) + 0x29, 0xE7, // Usage Max (231) + 0x15, 0x00, // Log Min (0) + 0x25, 0x01, // Log Max (1) + // + // Modifier byte + 0x75, 0x01, // Report Size (1) + 0x95, 0x08, // Report Count (8) + 0x81, 0x02, // Input: (Data, Variable, Absolute) + // + // Reserved byte + 0x95, 0x01, // Report Count (1) + 0x75, 0x08, // Report Size (8) + 0x81, 0x01, // Input: (Constant) + // + // LED report + 0x95, 0x05, // Report Count (5) + 0x75, 0x01, // Report Size (1) + 0x05, 0x08, // Usage Pg (LEDs) + 0x19, 0x01, // Usage Min (1) + 0x29, 0x05, // Usage Max (5) + 0x91, 0x02, // Output: (Data, Variable, Absolute) + // + // LED report padding + 0x95, 0x01, // Report Count (1) + 0x75, 0x03, // Report Size (3) + 0x91, 0x01, // Output: (Constant) + // + // Key arrays (6 bytes) + 0x95, 0x06, // Report Count (6) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Log Min (0) + 0x25, 0x65, // Log Max (101) + 0x05, 0x07, // Usage Pg (Key Codes) + 0x19, 0x00, // Usage Min (0) + 0x29, 0x65, // Usage Max (101) + 0x81, 0x00, // Input: (Data, Array) + // + 0xC0 // End Collection +}; + +// HID report map length +uint16_t hidReportMapLen = sizeof(hidReportMap); + +// HID report mapping table +static hidRptMap_t hidRptMap[HID_NUM_REPORTS]; + +/********************************************************************* + * Profile Attributes - variables + */ + +// HID Service attribute +static const gattAttrType_t hidService = {ATT_BT_UUID_SIZE, hidServUUID}; + +// Include attribute (Battery service) +static uint16_t include = GATT_INVALID_HANDLE; + +// HID Information characteristic +static uint8_t hidInfoProps = GATT_PROP_READ; + +// HID Report Map characteristic +static uint8_t hidReportMapProps = GATT_PROP_READ; + +// HID External Report Reference Descriptor +static uint8_t hidExtReportRefDesc[ATT_BT_UUID_SIZE] = + {LO_UINT16(BATT_LEVEL_UUID), HI_UINT16(BATT_LEVEL_UUID)}; + +// HID Control Point characteristic +static uint8_t hidControlPointProps = GATT_PROP_WRITE_NO_RSP; +static uint8_t hidControlPoint; + +// HID Protocol Mode characteristic +static uint8_t hidProtocolModeProps = GATT_PROP_READ | GATT_PROP_WRITE_NO_RSP; +uint8_t hidProtocolMode = HID_PROTOCOL_MODE_REPORT; + +// HID Report characteristic, key input +static uint8_t hidReportKeyInProps = GATT_PROP_READ | GATT_PROP_NOTIFY; +static uint8_t hidReportKeyIn; +static gattCharCfg_t hidReportKeyInClientCharCfg[GATT_MAX_NUM_CONN]; + +// HID Report Reference characteristic descriptor, key input +static uint8_t hidReportRefKeyIn[HID_REPORT_REF_LEN] = + {HID_RPT_ID_KEY_IN, HID_REPORT_TYPE_INPUT}; + +// HID Report characteristic, LED output +static uint8_t hidReportLedOutProps = GATT_PROP_READ | GATT_PROP_WRITE | GATT_PROP_WRITE_NO_RSP; +static uint8_t hidReportLedOut; + +// HID Report Reference characteristic descriptor, LED output +static uint8_t hidReportRefLedOut[HID_REPORT_REF_LEN] = + {HID_RPT_ID_LED_OUT, HID_REPORT_TYPE_OUTPUT}; + +// HID Boot Keyboard Input Report +static uint8_t hidReportBootKeyInProps = GATT_PROP_READ | GATT_PROP_NOTIFY; +static uint8_t hidReportBootKeyIn; +static gattCharCfg_t hidReportBootKeyInClientCharCfg[GATT_MAX_NUM_CONN]; + +// HID Boot Keyboard Output Report +static uint8_t hidReportBootKeyOutProps = GATT_PROP_READ | GATT_PROP_WRITE | GATT_PROP_WRITE_NO_RSP; +static uint8_t hidReportBootKeyOut; + +// Feature Report +static uint8_t hidReportFeatureProps = GATT_PROP_READ | GATT_PROP_WRITE; +static uint8_t hidReportFeature; + +// HID Report Reference characteristic descriptor, Feature +static uint8_t hidReportRefFeature[HID_REPORT_REF_LEN] = + {HID_RPT_ID_FEATURE, HID_REPORT_TYPE_FEATURE}; + +/********************************************************************* + * Profile Attributes - Table + */ + +static gattAttribute_t hidAttrTbl[] = { + // HID Service + { + {ATT_BT_UUID_SIZE, primaryServiceUUID}, /* type */ + GATT_PERMIT_READ, /* permissions */ + 0, /* handle */ + (uint8_t *)&hidService /* pValue */ + }, + + // Included service (battery) + { + {ATT_BT_UUID_SIZE, includeUUID}, + GATT_PERMIT_READ, + 0, + (uint8_t *)&include}, + + // HID Information characteristic declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &hidInfoProps}, + + // HID Information characteristic + { + {ATT_BT_UUID_SIZE, hidInfoUUID}, + GATT_PERMIT_ENCRYPT_READ, + 0, + (uint8_t *)hidInfo}, + + // HID Control Point characteristic declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &hidControlPointProps}, + + // HID Control Point characteristic + { + {ATT_BT_UUID_SIZE, hidControlPointUUID}, + GATT_PERMIT_ENCRYPT_WRITE, + 0, + &hidControlPoint}, + + // HID Protocol Mode characteristic declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &hidProtocolModeProps}, + + // HID Protocol Mode characteristic + { + {ATT_BT_UUID_SIZE, hidProtocolModeUUID}, + GATT_PERMIT_ENCRYPT_READ | GATT_PERMIT_ENCRYPT_WRITE, + 0, + &hidProtocolMode}, + + // HID Report Map characteristic declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &hidReportMapProps}, + + // HID Report Map characteristic + { + {ATT_BT_UUID_SIZE, hidReportMapUUID}, + GATT_PERMIT_ENCRYPT_READ, + 0, + (uint8_t *)hidReportMap}, + + // HID External Report Reference Descriptor + { + {ATT_BT_UUID_SIZE, extReportRefUUID}, + GATT_PERMIT_READ, + 0, + hidExtReportRefDesc + + }, + + // HID Report characteristic, key input declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &hidReportKeyInProps}, + + // HID Report characteristic, key input + { + {ATT_BT_UUID_SIZE, hidReportUUID}, + GATT_PERMIT_ENCRYPT_READ, + 0, + &hidReportKeyIn}, + + // HID Report characteristic client characteristic configuration + { + {ATT_BT_UUID_SIZE, clientCharCfgUUID}, + GATT_PERMIT_READ | GATT_PERMIT_ENCRYPT_WRITE, + 0, + (uint8_t *)&hidReportKeyInClientCharCfg}, + + // HID Report Reference characteristic descriptor, key input + { + {ATT_BT_UUID_SIZE, reportRefUUID}, + GATT_PERMIT_READ, + 0, + hidReportRefKeyIn}, + + // HID Report characteristic, LED output declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &hidReportLedOutProps}, + + // HID Report characteristic, LED output + { + {ATT_BT_UUID_SIZE, hidReportUUID}, + GATT_PERMIT_ENCRYPT_READ | GATT_PERMIT_ENCRYPT_WRITE, + 0, + &hidReportLedOut}, + + // HID Report Reference characteristic descriptor, LED output + { + {ATT_BT_UUID_SIZE, reportRefUUID}, + GATT_PERMIT_READ, + 0, + hidReportRefLedOut}, + + // HID Boot Keyboard Input Report declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &hidReportBootKeyInProps}, + + // HID Boot Keyboard Input Report + { + {ATT_BT_UUID_SIZE, hidBootKeyInputUUID}, + GATT_PERMIT_ENCRYPT_READ, + 0, + &hidReportBootKeyIn}, + + // HID Boot Keyboard Input Report characteristic client characteristic configuration + { + {ATT_BT_UUID_SIZE, clientCharCfgUUID}, + GATT_PERMIT_READ | GATT_PERMIT_ENCRYPT_WRITE, + 0, + (uint8_t *)&hidReportBootKeyInClientCharCfg}, + + // HID Boot Keyboard Output Report declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &hidReportBootKeyOutProps}, + + // HID Boot Keyboard Output Report + { + {ATT_BT_UUID_SIZE, hidBootKeyOutputUUID}, + GATT_PERMIT_ENCRYPT_READ | GATT_PERMIT_ENCRYPT_WRITE, + 0, + &hidReportBootKeyOut}, + + // Feature Report declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &hidReportFeatureProps}, + + // Feature Report + { + {ATT_BT_UUID_SIZE, hidReportUUID}, + GATT_PERMIT_ENCRYPT_READ | GATT_PERMIT_ENCRYPT_WRITE, + 0, + &hidReportFeature}, + + // HID Report Reference characteristic descriptor, feature + { + {ATT_BT_UUID_SIZE, reportRefUUID}, + GATT_PERMIT_READ, + 0, + hidReportRefFeature}, +}; + +// Attribute index enumeration-- these indexes match array elements above +enum +{ + HID_SERVICE_IDX, // HID Service + HID_INCLUDED_SERVICE_IDX, // Included Service + HID_INFO_DECL_IDX, // HID Information characteristic declaration + HID_INFO_IDX, // HID Information characteristic + HID_CONTROL_POINT_DECL_IDX, // HID Control Point characteristic declaration + HID_CONTROL_POINT_IDX, // HID Control Point characteristic + HID_PROTOCOL_MODE_DECL_IDX, // HID Protocol Mode characteristic declaration + HID_PROTOCOL_MODE_IDX, // HID Protocol Mode characteristic + HID_REPORT_MAP_DECL_IDX, // HID Report Map characteristic declaration + HID_REPORT_MAP_IDX, // HID Report Map characteristic + HID_EXT_REPORT_REF_DESC_IDX, // HID External Report Reference Descriptor + HID_REPORT_KEY_IN_DECL_IDX, // HID Report characteristic, key input declaration + HID_REPORT_KEY_IN_IDX, // HID Report characteristic, key input + HID_REPORT_KEY_IN_CCCD_IDX, // HID Report characteristic client characteristic configuration + HID_REPORT_REF_KEY_IN_IDX, // HID Report Reference characteristic descriptor, key input + HID_REPORT_LED_OUT_DECL_IDX, // HID Report characteristic, LED output declaration + HID_REPORT_LED_OUT_IDX, // HID Report characteristic, LED output + HID_REPORT_REF_LED_OUT_IDX, // HID Report Reference characteristic descriptor, LED output + HID_BOOT_KEY_IN_DECL_IDX, // HID Boot Keyboard Input Report declaration + HID_BOOT_KEY_IN_IDX, // HID Boot Keyboard Input Report + HID_BOOT_KEY_IN_CCCD_IDX, // HID Boot Keyboard Input Report characteristic client characteristic configuration + HID_BOOT_KEY_OUT_DECL_IDX, // HID Boot Keyboard Output Report declaration + HID_BOOT_KEY_OUT_IDX, // HID Boot Keyboard Output Report + HID_FEATURE_DECL_IDX, // Feature Report declaration + HID_FEATURE_IDX, // Feature Report + HID_REPORT_REF_FEATURE_IDX // HID Report Reference characteristic descriptor, feature +}; + +/********************************************************************* + * LOCAL FUNCTIONS + */ + +/********************************************************************* + * PROFILE CALLBACKS + */ + +// Service Callbacks +gattServiceCBs_t hidKbdCBs = { + HidDev_ReadAttrCB, // Read callback function pointer + HidDev_WriteAttrCB, // Write callback function pointer + NULL // Authorization callback function pointer +}; + +/********************************************************************* + * PUBLIC FUNCTIONS + */ + +/********************************************************************* + * @fn Hid_AddService + * + * @brief Initializes the HID Service by registering + * GATT attributes with the GATT server. + * + * @return Success or Failure + */ +bStatus_t Hid_AddService(void) +{ + uint8_t status = SUCCESS; + + // Initialize Client Characteristic Configuration attributes + GATTServApp_InitCharCfg(INVALID_CONNHANDLE, hidReportKeyInClientCharCfg); + GATTServApp_InitCharCfg(INVALID_CONNHANDLE, hidReportBootKeyInClientCharCfg); + + // Register GATT attribute list and CBs with GATT Server App + status = GATTServApp_RegisterService(hidAttrTbl, GATT_NUM_ATTRS(hidAttrTbl), GATT_MAX_ENCRYPT_KEY_SIZE, &hidKbdCBs); + + // Set up included service + Batt_GetParameter(BATT_PARAM_SERVICE_HANDLE, + &GATT_INCLUDED_HANDLE(hidAttrTbl, HID_INCLUDED_SERVICE_IDX)); + + // Construct map of reports to characteristic handles + // Each report is uniquely identified via its ID and type + + // Key input report + hidRptMap[0].id = hidReportRefKeyIn[0]; + hidRptMap[0].type = hidReportRefKeyIn[1]; + hidRptMap[0].handle = hidAttrTbl[HID_REPORT_KEY_IN_IDX].handle; + hidRptMap[0].cccdHandle = hidAttrTbl[HID_REPORT_KEY_IN_CCCD_IDX].handle; + hidRptMap[0].mode = HID_PROTOCOL_MODE_REPORT; + + // LED output report + hidRptMap[1].id = hidReportRefLedOut[0]; + hidRptMap[1].type = hidReportRefLedOut[1]; + hidRptMap[1].handle = hidAttrTbl[HID_REPORT_LED_OUT_IDX].handle; + hidRptMap[1].cccdHandle = 0; + hidRptMap[1].mode = HID_PROTOCOL_MODE_REPORT; + + // Boot keyboard input report + // Use same ID and type as key input report + hidRptMap[2].id = hidReportRefKeyIn[0]; + hidRptMap[2].type = hidReportRefKeyIn[1]; + hidRptMap[2].handle = hidAttrTbl[HID_BOOT_KEY_IN_IDX].handle; + hidRptMap[2].cccdHandle = hidAttrTbl[HID_BOOT_KEY_IN_CCCD_IDX].handle; + hidRptMap[2].mode = HID_PROTOCOL_MODE_BOOT; + + // Boot keyboard output report + // Use same ID and type as LED output report + hidRptMap[3].id = hidReportRefLedOut[0]; + hidRptMap[3].type = hidReportRefLedOut[1]; + hidRptMap[3].handle = hidAttrTbl[HID_BOOT_KEY_OUT_IDX].handle; + hidRptMap[3].cccdHandle = 0; + hidRptMap[3].mode = HID_PROTOCOL_MODE_BOOT; + + // Feature report + hidRptMap[4].id = hidReportRefFeature[0]; + hidRptMap[4].type = hidReportRefFeature[1]; + hidRptMap[4].handle = hidAttrTbl[HID_FEATURE_IDX].handle; + hidRptMap[4].cccdHandle = 0; + hidRptMap[4].mode = HID_PROTOCOL_MODE_REPORT; + + // Battery level input report + Batt_GetParameter(BATT_PARAM_BATT_LEVEL_IN_REPORT, &(hidRptMap[5])); + + // Setup report ID map + HidDev_RegisterReports(HID_NUM_REPORTS, hidRptMap); + + return (status); +} + +/********************************************************************* + * @fn Hid_SetParameter + * + * @brief Set a HID Kbd parameter. + * + * @param id - HID report ID. + * @param type - HID report type. + * @param uuid - attribute uuid. + * @param len - length of data to right. + * @param pValue - pointer to data to write. This is dependent on + * the input parameters and WILL be cast to the appropriate + * data type (example: data type of uint16_t will be cast to + * uint16_t pointer). + * + * @return GATT status code. + */ +uint8_t Hid_SetParameter(uint8_t id, uint8_t type, uint16_t uuid, uint8_t len, void *pValue) +{ + bStatus_t ret = SUCCESS; + + switch(uuid) + { + case REPORT_UUID: + if(type == HID_REPORT_TYPE_OUTPUT) + { + if(len == 1) + { + hidReportLedOut = *((uint8_t *)pValue); + } + else + { + ret = ATT_ERR_INVALID_VALUE_SIZE; + } + } + else if(type == HID_REPORT_TYPE_FEATURE) + { + if(len == 1) + { + hidReportFeature = *((uint8_t *)pValue); + } + else + { + ret = ATT_ERR_INVALID_VALUE_SIZE; + } + } + else + { + ret = ATT_ERR_ATTR_NOT_FOUND; + } + break; + + case BOOT_KEY_OUTPUT_UUID: + if(len == 1) + { + hidReportBootKeyOut = *((uint8_t *)pValue); + } + else + { + ret = ATT_ERR_INVALID_VALUE_SIZE; + } + break; + + default: + // ignore the request + break; + } + + return (ret); +} + +/********************************************************************* + * @fn Hid_GetParameter + * + * @brief Get a HID Kbd parameter. + * + * @param id - HID report ID. + * @param type - HID report type. + * @param uuid - attribute uuid. + * @param pLen - length of data to be read + * @param pValue - pointer to data to get. This is dependent on + * the input parameters and WILL be cast to the appropriate + * data type (example: data type of uint16_t will be cast to + * uint16_t pointer). + * + * @return GATT status code. + */ +uint8_t Hid_GetParameter(uint8_t id, uint8_t type, uint16_t uuid, uint16_t *pLen, void *pValue) +{ + switch(uuid) + { + case REPORT_UUID: + if(type == HID_REPORT_TYPE_OUTPUT) + { + *((uint8_t *)pValue) = hidReportLedOut; + *pLen = 1; + } + else if(type == HID_REPORT_TYPE_FEATURE) + { + *((uint8_t *)pValue) = hidReportFeature; + *pLen = 1; + } + else + { + *pLen = 0; + } + break; + + case BOOT_KEY_OUTPUT_UUID: + *((uint8_t *)pValue) = hidReportBootKeyOut; + *pLen = 1; + break; + + default: + *pLen = 0; + break; + } + + return (SUCCESS); +} + +/********************************************************************* +*********************************************************************/ diff --git a/profile/include/battservice.h b/profile/include/battservice.h new file mode 100644 index 0000000..7e2eced --- /dev/null +++ b/profile/include/battservice.h @@ -0,0 +1,172 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : battservice.h + * Author : WCH + * Version : V1.0 + * Date : 2018/12/11 + * 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. + *******************************************************************************/ + +#ifndef BATTSERVICE_H +#define BATTSERVICE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************************************************* + * INCLUDES + */ + +/********************************************************************* + * CONSTANTS + */ + +// Battery Service Get/Set Parameters +#define BATT_PARAM_LEVEL 0 +#define BATT_PARAM_CRITICAL_LEVEL 1 +#define BATT_PARAM_SERVICE_HANDLE 2 +#define BATT_PARAM_BATT_LEVEL_IN_REPORT 3 + +// Callback events +#define BATT_LEVEL_NOTI_ENABLED 1 +#define BATT_LEVEL_NOTI_DISABLED 2 + +// HID Report IDs for the service +#define HID_RPT_ID_BATT_LEVEL_IN 4 // Battery Level input report ID + +/********************************************************************* + * TYPEDEFS + */ + +// Battery Service callback function +typedef void (*battServiceCB_t)(uint8_t event); + +// Battery measure HW setup function +typedef void (*battServiceSetupCB_t)(void); + +// Battery measure percentage calculation function +typedef uint8_t (*battServiceCalcCB_t)(uint16_t adcVal); + +// Battery measure HW teardown function +typedef void (*battServiceTeardownCB_t)(void); + +/********************************************************************* + * MACROS + */ + +/********************************************************************* + * Profile Callbacks + */ + +/********************************************************************* + * API FUNCTIONS + */ + +/********************************************************************* + * @fn Batt_AddService + * + * @brief Initializes the Battery service by registering + * GATT attributes with the GATT server. + * + * @return Success or Failure + */ +extern bStatus_t Batt_AddService(void); + +/********************************************************************* + * @fn Batt_Register + * + * @brief Register a callback function with the Battery Service. + * + * @param pfnServiceCB - Callback function. + * + * @return None. + */ +extern void Batt_Register(battServiceCB_t pfnServiceCB); + +/********************************************************************* + * @fn Batt_SetParameter + * + * @brief Set a Battery Service parameter. + * + * @param param - Profile parameter ID + * @param len - length of data to right + * @param value - 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 + */ +extern bStatus_t Batt_SetParameter(uint8_t param, uint8_t len, void *value); + +/********************************************************************* + * @fn Batt_GetParameter + * + * @brief Get a Battery parameter. + * + * @param param - Profile parameter ID + * @param value - 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 + */ +extern bStatus_t Batt_GetParameter(uint8_t param, void *value); + +/********************************************************************* + * @fn Batt_MeasLevel + * + * @brief Measure the battery level and update the battery + * level value in the service characteristics. If + * the battery level-state characteristic is configured + * for notification and the battery level has changed + * since the last measurement, then a notification + * will be sent. + * + * @return Success or Failure + */ +extern bStatus_t Batt_MeasLevel(void); + +/********************************************************************* + * @fn Batt_Setup + * + * @brief Set up which ADC source is to be used. Defaults to VDD/3. + * + * @param adc_ch - ADC Channel, e.g. HAL_ADC_CHN_AIN6 + * @param minVal - max battery level + * @param maxVal - min battery level + * @param sCB - HW setup callback + * @param tCB - HW tear down callback + * @param cCB - percentage calculation callback + * + * @return none. + */ +extern void Batt_Setup(uint8_t adc_ch, uint16_t minVal, uint16_t maxVal, + battServiceSetupCB_t sCB, battServiceTeardownCB_t tCB, + battServiceCalcCB_t cCB); + +/********************************************************************* + * @fn Batt_HandleConnStatusCB + * + * @brief Battery Service link status change handler function. + * + * @param connHandle - connection handle + * @param changeType - type of change + * + * @return none + */ +void Batt_HandleConnStatusCB(uint16_t connHandle, uint8_t changeType); + +/********************************************************************* +*********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* BATTSERVICE_H */ diff --git a/profile/include/devinfoservice.h b/profile/include/devinfoservice.h new file mode 100644 index 0000000..683d243 --- /dev/null +++ b/profile/include/devinfoservice.h @@ -0,0 +1,109 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : devinfoservice.h + * Author : WCH + * Version : V1.0 + * Date : 2018/12/11 + * 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. + *******************************************************************************/ + +#ifndef DEVINFOSERVICE_H +#define DEVINFOSERVICE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************************************************* + * INCLUDES + */ + +/********************************************************************* + * CONSTANTS + */ + +// Device Information Service Parameters +#define DEVINFO_SYSTEM_ID 0 +#define DEVINFO_MODEL_NUMBER 1 +#define DEVINFO_SERIAL_NUMBER 2 +#define DEVINFO_FIRMWARE_REV 3 +#define DEVINFO_HARDWARE_REV 4 +#define DEVINFO_SOFTWARE_REV 5 +#define DEVINFO_MANUFACTURER_NAME 6 +#define DEVINFO_11073_CERT_DATA 7 +#define DEVINFO_PNP_ID 8 + +// IEEE 11073 authoritative body values +#define DEVINFO_11073_BODY_EMPTY 0 +#define DEVINFO_11073_BODY_IEEE 1 +#define DEVINFO_11073_BODY_CONTINUA 2 +#define DEVINFO_11073_BODY_EXP 254 + +// System ID length +#define DEVINFO_SYSTEM_ID_LEN 8 + +// PnP ID length +#define DEVINFO_PNP_ID_LEN 7 + +/********************************************************************* + * TYPEDEFS + */ + +/********************************************************************* + * MACROS + */ + +/********************************************************************* + * Profile Callbacks + */ + +/********************************************************************* + * API FUNCTIONS + */ + +/* + * DevInfo_AddService- Initializes the Device Information service by registering + * GATT attributes with the GATT server. + * + */ + +extern bStatus_t DevInfo_AddService(void); + +/********************************************************************* + * @fn DevInfo_SetParameter + * + * @brief Set a Device Information parameter. + * + * @param param - Profile parameter ID + * @param len - length of data to right + * @param value - 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 DevInfo_SetParameter(uint8_t param, uint8_t len, void *value); + +/* + * DevInfo_GetParameter - Get a Device Information parameter. + * + * param - Profile parameter ID + * value - 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). + */ +extern bStatus_t DevInfo_GetParameter(uint8_t param, void *value); + +/********************************************************************* +*********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* DEVINFOSERVICE_H */ diff --git a/profile/include/hiddev.h b/profile/include/hiddev.h new file mode 100644 index 0000000..46d032b --- /dev/null +++ b/profile/include/hiddev.h @@ -0,0 +1,441 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : hiddev.h + * Author : WCH + * Version : V1.0 + * Date : 2018/12/11 + * 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. + *******************************************************************************/ + +#ifndef HIDDEV_H +#define HIDDEV_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************************************************* + * INCLUDES + */ + +/********************************************************************* + * MACROS + */ + +/********************************************************************* + * CONSTANTS + */ + +// HID Device Parameters +#define HIDDEV_ERASE_ALLBONDS 0 // Erase all of the bonded devices. Write Only. No Size. + +// HID read/write operation +#define HID_DEV_OPER_WRITE 0 // Write operation +#define HID_DEV_OPER_READ 1 // Read operation +#define HID_DEV_OPER_ENABLE 2 // Notification enabled for report ID +#define HID_DEV_OPER_DISABLE 3 // Notifications disabled for report ID + +// HID callback events +#define HID_DEV_SUSPEND_EVT 0 // HID suspend +#define HID_DEV_EXIT_SUSPEND_EVT 1 // HID exit suspend +#define HID_DEV_SET_BOOT_EVT 2 // HID set boot mode +#define HID_DEV_SET_REPORT_EVT 3 // HID set report mode + +/* HID Report type */ +#define HID_REPORT_TYPE_INPUT 1 +#define HID_REPORT_TYPE_OUTPUT 2 +#define HID_REPORT_TYPE_FEATURE 3 + +/* HID information flags */ +#define HID_FLAGS_REMOTE_WAKE 0x01 // RemoteWake +#define HID_FLAGS_NORMALLY_CONNECTABLE 0x02 // NormallyConnectable + +/* Control point commands */ +#define HID_CMD_SUSPEND 0x00 // Suspend +#define HID_CMD_EXIT_SUSPEND 0x01 // Exit Suspend + +/* HID protocol mode values */ +#define HID_PROTOCOL_MODE_BOOT 0x00 // Boot Protocol Mode +#define HID_PROTOCOL_MODE_REPORT 0x01 // Report Protocol Mode + +/* Attribute value lengths */ +#define HID_PROTOCOL_MODE_LEN 1 // HID Protocol Mode +#define HID_INFORMATION_LEN 4 // HID Information +#define HID_REPORT_REF_LEN 2 // HID Report Reference Descriptor +#define HID_EXT_REPORT_REF_LEN 2 // External Report Reference Descriptor + +// HID Keyboard/Keypad Usage IDs (subset of the codes available in the USB HID Usage Tables spec) +#define HID_KEYBOARD_RESERVED 0 // 0x00 - No event inidicated +#define HID_KEYBOARD_A 4 // 0x04 - Keyboard a and A +#define HID_KEYBOARD_B 5 // 0x05 - Keyboard b and B +#define HID_KEYBOARD_C 6 // 0x06 - Keyboard c and C +#define HID_KEYBOARD_D 7 // 0x07 - Keyboard d and D +#define HID_KEYBOARD_E 8 // 0x08 - Keyboard e and E +#define HID_KEYBOARD_F 9 // 0x09 - Keyboard f and F +#define HID_KEYBOARD_G 10 // 0x0A - Keyboard g and G +#define HID_KEYBOARD_H 11 // 0x0B - Keyboard h and H +#define HID_KEYBOARD_I 12 // 0x0C - Keyboard i and I +#define HID_KEYBOARD_J 13 // 0x0D - Keyboard j and J +#define HID_KEYBOARD_K 14 // 0x0E - Keyboard k and K +#define HID_KEYBOARD_L 15 // 0x0F - Keyboard l and L +#define HID_KEYBOARD_M 16 // 0x10 - Keyboard m and M +#define HID_KEYBOARD_N 17 // 0x11 - Keyboard n and N +#define HID_KEYBOARD_O 18 // 0x12 - Keyboard o and O +#define HID_KEYBOARD_P 19 // 0x13 - Keyboard p and p +#define HID_KEYBOARD_Q 20 // 0x14 - Keyboard q and Q +#define HID_KEYBOARD_R 21 // 0x15 - Keyboard r and R +#define HID_KEYBOARD_S 22 // 0x16 - Keyboard s and S +#define HID_KEYBOARD_T 23 // 0x17 - Keyboard t and T +#define HID_KEYBOARD_U 24 // 0x18 - Keyboard u and U +#define HID_KEYBOARD_V 25 // 0x19 - Keyboard v and V +#define HID_KEYBOARD_W 26 // 0x1A - Keyboard w and W +#define HID_KEYBOARD_X 27 // 0x1B - Keyboard x and X +#define HID_KEYBOARD_Y 28 // 0x1C - Keyboard y and Y +#define HID_KEYBOARD_Z 29 // 0x1D - Keyboard z and Z +#define HID_KEYBOARD_1 30 // 0x1E - Keyboard 1 and ! +#define HID_KEYBOARD_2 31 // 0x1F - Keyboard 2 and @ +#define HID_KEYBOARD_3 32 // 0x20 - Keyboard 3 and # +#define HID_KEYBOARD_4 33 // 0x21 - Keyboard 4 and % +#define HID_KEYBOARD_5 34 // 0x22 - Keyboard 5 and % +#define HID_KEYBOARD_6 35 // 0x23 - Keyboard 6 and ^ +#define HID_KEYBOARD_7 36 // 0x24 - Keyboard 7 and & +#define HID_KEYBOARD_8 37 // 0x25 - Keyboard 8 and * +#define HID_KEYBOARD_9 38 // 0x26 - Keyboard 9 and ( +#define HID_KEYBOARD_0 39 // 0x27 - Keyboard 0 and ) +#define HID_KEYBOARD_RETURN 40 // 0x28 - Keyboard Return (ENTER) +#define HID_KEYBOARD_ESCAPE 41 // 0x29 - Keyboard ESCAPE +#define HID_KEYBOARD_DELETE 42 // 0x2A - Keyboard DELETE (Backspace) +#define HID_KEYBOARD_TAB 43 // 0x2B - Keyboard Tab +#define HID_KEYBOARD_SPACEBAR 44 // 0x2C - Keyboard Spacebar +#define HID_KEYBOARD_MINUS 45 // 0x2D - Keyboard - and (underscore) +#define HID_KEYBOARD_EQUAL 46 // 0x2E - Keyboard = and + +#define HID_KEYBOARD_LEFT_BRKT 47 // 0x2F - Keyboard [ and { +#define HID_KEYBOARD_RIGHT_BRKT 48 // 0x30 - Keyboard ] and } +#define HID_KEYBOARD_BACK_SLASH 49 // 0x31 - Keyboard \ and | +#define HID_KEYBOARD_SEMI_COLON 51 // 0x33 - Keyboard ; and : +#define HID_KEYBOARD_SGL_QUOTE 52 // 0x34 - Keyboard ' and " +#define HID_KEYBOARD_GRV_ACCENT 53 // 0x35 - Keyboard Grave Accent and Tilde +#define HID_KEYBOARD_COMMA 54 // 0x36 - Keyboard , and < +#define HID_KEYBOARD_DOT 55 // 0x37 - Keyboard . and > +#define HID_KEYBOARD_FWD_SLASH 56 // 0x38 - Keyboard / and ? +#define HID_KEYBOARD_CAPS_LOCK 57 // 0x39 - Keyboard Caps Lock +#define HID_KEYBOARD_F1 58 // 0x3A - Keyboard F1 +#define HID_KEYBOARD_F2 59 // 0x3B - Keyboard F2 +#define HID_KEYBOARD_F3 60 // 0x3C - Keyboard F3 +#define HID_KEYBOARD_F4 61 // 0x3D - Keyboard F4 +#define HID_KEYBOARD_F5 62 // 0x3E - Keyboard F5 +#define HID_KEYBOARD_F6 63 // 0x3F - Keyboard F6 +#define HID_KEYBOARD_F7 64 // 0x40 - Keyboard F7 +#define HID_KEYBOARD_F8 65 // 0x41 - Keyboard F8 +#define HID_KEYBOARD_F9 66 // 0x42 - Keyboard F9 +#define HID_KEYBOARD_F10 67 // 0x43 - Keyboard F10 +#define HID_KEYBOARD_F11 68 // 0x44 - Keyboard F11 +#define HID_KEYBOARD_F12 69 // 0x45 - Keyboard F12 +#define HID_KEYBOARD_PRNT_SCREEN 70 // 0x46 - Keyboard Print Screen +#define HID_KEYBOARD_SCROLL_LOCK 71 // 0x47 - Keyboard Scroll Lock +#define HID_KEYBOARD_PAUSE 72 // 0x48 - Keyboard Pause +#define HID_KEYBOARD_INSERT 73 // 0x49 - Keyboard Insert +#define HID_KEYBOARD_HOME 74 // 0x4A - Keyboard Home +#define HID_KEYBOARD_PAGE_UP 75 // 0x4B - Keyboard PageUp +#define HID_KEYBOARD_DELETE_FWD 76 // 0x4C - Keyboard Delete Forward +#define HID_KEYBOARD_END 77 // 0x4D - Keyboard End +#define HID_KEYBOARD_PAGE_DOWN 78 // 0x4E - Keyboard PageDown +#define HID_KEYBOARD_RIGHT_ARROW 79 // 0x4F - Keyboard RightArrow +#define HID_KEYBOARD_LEFT_ARROW 80 // 0x50 - Keyboard LeftArrow +#define HID_KEYBOARD_DOWN_ARROW 81 // 0x51 - Keyboard DownArrow +#define HID_KEYBOARD_UP_ARROW 82 // 0x52 - Keyboard UpArrow +#define HID_KEYBPAD_NUM_LOCK 83 // 0x53 - Keypad Num Lock and Clear +#define HID_KEYBPAD_DIVIDE 84 // 0x54 - Keypad / +#define HID_KEYBOARD_MULTIPLY 85 // 0x55 - Keypad * +#define HID_KEYBOARD_SUBTRACT 86 // 0x56 - Keypad - +#define HID_KEYBPAD_ADD 87 // 0x57 - Keypad + +#define HID_KEYBPAD_ENTER 88 // 0x58 - Keypad ENTER +#define HID_KEYBPAD_1 89 // 0x59 - Keypad 1 and End +#define HID_KEYBPAD_2 90 // 0x5A - Keypad 2 and Down Arrow +#define HID_KEYBPAD_3 91 // 0x5B - Keypad 3 and PageDn +#define HID_KEYBPAD_4 92 // 0x5C - Keypad 4 and Lfet Arrow +#define HID_KEYBPAD_5 93 // 0x5D - Keypad 5 +#define HID_KEYBPAD_6 94 // 0x5E - Keypad 6 and Right Arrow +#define HID_KEYBPAD_7 95 // 0x5F - Keypad 7 and Home +#define HID_KEYBPAD_8 96 // 0x60 - Keypad 8 and Up Arrow +#define HID_KEYBPAD_9 97 // 0x61 - Keypad 9 and PageUp +#define HID_KEYBPAD_0 98 // 0x62 - Keypad 0 and Insert +#define HID_KEYBPAD_DOT 99 // 0x63 - Keypad . and Delete +#define HID_KEYBOARD_MUTE 127 // 0x7F - Keyboard Mute +#define HID_KEYBOARD_VOLUME_UP 128 // 0x80 - Keyboard Volume up +#define HID_KEYBOARD_VOLUME_DOWN 129 // 0x81 - Keyboard Volume down +#define HID_KEYBOARD_LEFT_CTRL 224 // 0xE0 - Keyboard LeftContorl +#define HID_KEYBOARD_LEFT_SHIFT 225 // 0xE1 - Keyboard LeftShift +#define HID_KEYBOARD_LEFT_ALT 226 // 0xE2 - Keyboard LeftAlt +#define HID_KEYBOARD_LEFT_GUI 227 // 0xE3 - Keyboard LeftGUI +#define HID_KEYBOARD_RIGHT_CTRL 228 // 0xE4 - Keyboard LeftContorl +#define HID_KEYBOARD_RIGHT_SHIFT 229 // 0xE5 - Keyboard LeftShift +#define HID_KEYBOARD_RIGHT_ALT 230 // 0xE6 - Keyboard LeftAlt +#define HID_KEYBOARD_RIGHT_GUI 231 // 0xE7 - Keyboard RightGUI + +#define HID_MOUSE_BUTTON_LEFT 253 +#define HID_MOUSE_BUTTON_MIDDLE 254 +#define HID_MOUSE_BUTTON_RIGHT 255 + +// HID Consumer Usage IDs (subset of the codes available in the USB HID Usage Tables spec) +#define HID_CONSUMER_POWER 48 // 0x30 - Power +#define HID_CONSUMER_RESET 49 // 0x31 - Reset +#define HID_CONSUMER_SLEEP 50 // 0x32 - Sleep + +#define HID_CONSUMER_MENU 64 // 0x40 - Menu +#define HID_CONSUMER_SELECTION 128 // 0x80 - Selection +#define HID_CONSUMER_ASSIGN_SEL 129 // 0x81 - Assign Selection +#define HID_CONSUMER_MODE_STEP 130 // 0x82 - Mode Step +#define HID_CONSUMER_RECALL_LAST 131 // 0x83 - Recall Last +#define HID_CONSUMER_QUIT 148 // 0x94 - Quit +#define HID_CONSUMER_HELP 149 // 0x95 - Help +#define HID_CONSUMER_CHANNEL_UP 156 // 0x9C - Channel Increment +#define HID_CONSUMER_CHANNEL_DOWN 157 // 0x9D - Channel Decrement + +#define HID_CONSUMER_PLAY 176 // 0xB0 - Play +#define HID_CONSUMER_PAUSE 177 // 0xB1 - Pause +#define HID_CONSUMER_RECORD 178 // 0xB2 - Record +#define HID_CONSUMER_FAST_FORWARD 179 // 0xB3 - Fast Forward +#define HID_CONSUMER_REWIND 180 // 0xB4 - Rewind +#define HID_CONSUMER_SCAN_NEXT_TRK 181 // 0xB5 - Scan Next Track +#define HID_CONSUMER_SCAN_PREV_TRK 182 // 0xB6 - Scan Previous Track +#define HID_CONSUMER_STOP 183 // 0xB7 - Stop +#define HID_CONSUMER_EJECT 184 // 0xB8 - Eject +#define HID_CONSUMER_RANDOM_PLAY 185 // 0xB9 - Random Play +#define HID_CONSUMER_SELECT_DISC 186 // 0xBA - Select Disk +#define HID_CONSUMER_ENTER_DISC 187 // 0xBB - Enter Disc +#define HID_CONSUMER_REPEAT 188 // 0xBC - Repeat +#define HID_CONSUMER_STOP_EJECT 204 // 0xCC - Stop/Eject +#define HID_CONSUMER_PLAY_PAUSE 205 // 0xCD - Play/Pause +#define HID_CONSUMER_PLAY_SKIP 206 // 0xCE - Play/Skip + +#define HID_CONSUMER_VOLUME 224 // 0xE0 - Volume +#define HID_CONSUMER_BALANCE 225 // 0xE1 - Balance +#define HID_CONSUMER_MUTE 226 // 0xE2 - Mute +#define HID_CONSUMER_BASS 227 // 0xE3 - Bass +#define HID_CONSUMER_VOLUME_UP 233 // 0xE9 - Volume Increment +#define HID_CONSUMER_VOLUME_DOWN 234 // 0xEA - Volume Decrement + +/********************************************************************* + * TYPEDEFS + */ + +// HID report mapping table +typedef struct +{ + uint16_t handle; // Handle of report characteristic + uint16_t cccdHandle; // Handle of CCCD for report characteristic + uint8_t id; // Report ID + uint8_t type; // Report type + uint8_t mode; // Protocol mode (report or boot) +} hidRptMap_t; + +// HID dev configuration structure +typedef struct +{ + uint32_t idleTimeout; // Idle timeout in milliseconds + uint8_t hidFlags; // HID feature flags + +} hidDevCfg_t; + +/********************************************************************* + * Global Variables + */ + +// These variables are defined in the service .c file that uses HID Dev + +// HID report map length +extern uint16_t hidReportMapLen; + +// HID protocol mode +extern uint8_t hidProtocolMode; + +/********************************************************************* + * Profile Callbacks + */ + +// HID Report callback +typedef uint8_t (*hidDevReportCB_t)(uint8_t id, uint8_t type, uint16_t uuid, + uint8_t oper, uint16_t *pLen, uint8_t *pData); + +// HID event callback +typedef void (*hidDevEvtCB_t)(uint8_t evt); + +// HID passcode callback +typedef void (*hidDevPasscodeCB_t)(uint8_t *deviceAddr, uint16_t connectionHandle, + uint8_t uiInputs, uint8_t uiOutputs); + +typedef struct +{ + hidDevReportCB_t reportCB; + hidDevEvtCB_t evtCB; + hidDevPasscodeCB_t passcodeCB; + gapRolesStateNotify_t pfnStateChange; //!< Whenever the device changes state +} hidDevCB_t; + +/********************************************************************* + * @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 + */ +extern void HidDev_Init(void); + +/********************************************************************* + * @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 + */ +extern uint16_t HidDev_ProcessEvent(uint8_t task_id, uint16_t events); + +/********************************************************************* + * @fn HidDev_Register + * + * @brief Register a callback function with HID Dev. + * + * @param pCfg - Parameter configuration. + * @param pCBs - Callback function. + * + * @return None. + */ +extern void HidDev_Register(hidDevCfg_t *pCfg, hidDevCB_t *pCBs); + +/********************************************************************* + * @fn HidDev_RegisterReports + * + * @brief Register the report table with HID Dev. + * + * @param numReports - Length of report table. + * @param pRpt - Report table. + * + * @return None. + */ +extern void HidDev_RegisterReports(uint8_t numReports, hidRptMap_t *pRpt); + +/********************************************************************* + * @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. + */ +extern uint8_t HidDev_Report(uint8_t id, uint8_t type, uint8_t len, uint8_t *pData); + +/********************************************************************* + * @fn HidDev_Close + * + * @brief Close the connection or stop advertising. + * + * @return None. + */ +extern void HidDev_Close(void); + +/********************************************************************* + * @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 + */ +extern bStatus_t HidDev_SetParameter(uint8_t param, uint8_t len, void *pValue); + +/********************************************************************* + * @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 + */ +extern bStatus_t HidDev_GetParameter(uint8_t param, void *pValue); + +/********************************************************************* + * @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 + */ +extern void HidDev_PasscodeRsp(uint8_t status, uint32_t 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 + */ +extern 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); + +/********************************************************************* + * @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 + */ +extern bStatus_t HidDev_WriteAttrCB(uint16_t connHandle, gattAttribute_t *pAttr, + uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method); + +/********************************************************************* +*********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* HIDDEV_H */ diff --git a/profile/include/hidkbdservice.h b/profile/include/hidkbdservice.h new file mode 100644 index 0000000..7990d7d --- /dev/null +++ b/profile/include/hidkbdservice.h @@ -0,0 +1,112 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : hidkbdservice.h + * 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. + *******************************************************************************/ + +#ifndef HIDKBDSERVICE_H +#define HIDKBDSERVICE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************************************************* + * INCLUDES + */ + +/********************************************************************* + * CONSTANTS + */ + +// Number of HID reports defined in the service +#define HID_NUM_REPORTS 7 + +// HID Report IDs for the service +#define HID_RPT_ID_KEY_IN 0 // Keyboard input report ID +#define HID_RPT_ID_MOUSE_IN 1 // Mouse input report ID +#define HID_RPT_ID_LED_OUT 0 // LED output report ID +#define HID_RPT_ID_FEATURE 0 // Feature report ID + +// HID feature flags +#define HID_FEATURE_FLAGS HID_FLAGS_REMOTE_WAKE + +/********************************************************************* + * TYPEDEFS + */ + +/********************************************************************* + * MACROS + */ + +/********************************************************************* + * Profile Callbacks + */ + +/********************************************************************* + * API FUNCTIONS + */ + +/********************************************************************* + * @fn Hid_AddService + * + * @brief Initializes the HID service for keyboard by registering + * GATT attributes with the GATT server. + * + * @param none + * + * @return Success or Failure + */ +extern bStatus_t Hid_AddService(void); + +/********************************************************************* + * @fn Hid_SetParameter + * + * @brief Set a HID Kbd parameter. + * + * @param id - HID report ID. + * @param type - HID report type. + * @param uuid - attribute uuid. + * @param len - length of data to right. + * @param pValue - pointer to data to write. This is dependent on + * the input parameters and WILL be cast to the appropriate + * data type (example: data type of uint16_t will be cast to + * uint16_t pointer). + * + * @return GATT status code. + */ +extern uint8_t Hid_SetParameter(uint8_t id, uint8_t type, uint16_t uuid, uint8_t len, + void *pValue); + +/********************************************************************* + * @fn Hid_GetParameter + * + * @brief Get a HID Kbd parameter. + * + * @param id - HID report ID. + * @param type - HID report type. + * @param uuid - attribute uuid. + * @param pLen - length of data to be read. + * @param pValue - pointer to data to get. This is dependent on + * the input parameters and WILL be cast to the appropriate + * data type (example: data type of uint16_t will be cast to + * uint16_t pointer). + * + * @return GATT status code. + */ +extern uint8_t Hid_GetParameter(uint8_t id, uint8_t type, uint16_t uuid, uint16_t *pLen, void *pValue); + +/********************************************************************* +*********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* HIDKBDSERVICE_H */ diff --git a/profile/include/scanparamservice.h b/profile/include/scanparamservice.h new file mode 100644 index 0000000..cb9bb7c --- /dev/null +++ b/profile/include/scanparamservice.h @@ -0,0 +1,133 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : scanparamservice.h + * Author : WCH + * Version : V1.0 + * Date : 2018/12/11 + * 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. + *******************************************************************************/ + +#ifndef SCANPARAMSERVICE_H +#define SCANPARAMSERVICE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************************************************* + * INCLUDES + */ + +/********************************************************************* + * CONSTANTS + */ + +// Scan Characteristic Lengths +#define SCAN_INTERVAL_WINDOW_CHAR_LEN 4 +#define SCAN_PARAM_REFRESH_LEN 1 + +// Scan Parameter Refresh Values +#define SCAN_PARAM_REFRESH_REQ 0x00 + +// Callback events +#define SCAN_INTERVAL_WINDOW_SET 1 + +// Get/Set parameters +#define SCAN_PARAM_PARAM_INTERVAL 0 +#define SCAN_PARAM_PARAM_WINDOW 1 + +/********************************************************************* + * TYPEDEFS + */ + +/********************************************************************* + * MACROS + */ + +/********************************************************************* + * Profile Callbacks + */ + +// Scan Parameters Service callback function +typedef void (*scanParamServiceCB_t)(uint8_t event); + +/********************************************************************* + * API FUNCTIONS + */ + +/********************************************************************* + * @fn ScanParam_AddService + * + * @brief Initializes the Service by registering + * GATT attributes with the GATT server. + * + * @return Success or Failure + */ +extern bStatus_t ScanParam_AddService(void); + +/********************************************************************* + * @fn ScanParam_Register + * + * @brief Register a callback function with the Scan Parameters Service. + * + * @param pfnServiceCB - Callback function. + * + * @return None. + */ +extern void ScanParam_Register(scanParamServiceCB_t pfnServiceCB); + +/********************************************************************* + * @fn ScanParam_SetParameter + * + * @brief Set a Scan Parameters Service parameter. + * + * @param param - Profile parameter ID + * @param len - length of data to right + * @param value - 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 + */ +extern bStatus_t ScanParam_SetParameter(uint8_t param, uint8_t len, void *value); + +/********************************************************************* + * @fn ScanParam_GetParameter + * + * @brief Get a Scan Parameters Service parameter. + * + * @param param - Profile parameter ID + * @param value - 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 + */ +extern bStatus_t ScanParam_GetParameter(uint8_t param, void *value); + +/********************************************************************* + * @fn ScanParam_RefreshNotify + * + * @brief Notify the peer to refresh the scan parameters. + * + * @param connHandle - connection handle + * + * @return None + */ +extern void ScanParam_RefreshNotify(uint16_t connHandle); + +extern void ScanParam_HandleConnStatusCB(uint16_t connHandle, uint8_t changeType); + +/********************************************************************* +*********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* SCANPARAMSERVICE_H */ diff --git a/profile/scanparamservice.c b/profile/scanparamservice.c new file mode 100644 index 0000000..52d7a1a --- /dev/null +++ b/profile/scanparamservice.c @@ -0,0 +1,405 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : scanparamservice.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 "scanparamservice.h" + +/********************************************************************* + * MACROS + */ + +/********************************************************************* + * CONSTANTS + */ + +/********************************************************************* + * TYPEDEFS + */ + +/********************************************************************* + * GLOBAL VARIABLES + */ +// Scan parameters service +const uint8_t scanParamServUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(SCAN_PARAM_SERV_UUID), HI_UINT16(SCAN_PARAM_SERV_UUID)}; + +// Scan interval window characteristic +const uint8_t scanIntervalWindowUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(SCAN_INTERVAL_WINDOW_UUID), HI_UINT16(SCAN_INTERVAL_WINDOW_UUID)}; + +// Scan parameter refresh characteristic +const uint8_t scanParamRefreshUUID[ATT_BT_UUID_SIZE] = { + LO_UINT16(SCAN_REFRESH_UUID), HI_UINT16(SCAN_REFRESH_UUID)}; + +/********************************************************************* + * EXTERNAL VARIABLES + */ + +/********************************************************************* + * EXTERNAL FUNCTIONS + */ + +/********************************************************************* + * LOCAL VARIABLES + */ + +// Application callback +static scanParamServiceCB_t scanParamServiceCB; + +/********************************************************************* + * Profile Attributes - variables + */ + +// Scan Parameters Service attribute +static const gattAttrType_t scanParamService = {ATT_BT_UUID_SIZE, scanParamServUUID}; + +// Scan Interval Window characteristic +static uint8_t scanIntervalWindowProps = GATT_PROP_WRITE_NO_RSP; +static uint8_t scanIntervalWindow[SCAN_INTERVAL_WINDOW_CHAR_LEN]; + +// Scan Parameter Refresh characteristic +static uint8_t scanParamRefreshProps = GATT_PROP_NOTIFY; +static uint8_t scanParamRefresh[SCAN_PARAM_REFRESH_LEN]; +static gattCharCfg_t scanParamRefreshClientCharCfg[GATT_MAX_NUM_CONN]; + +/********************************************************************* + * Profile Attributes - Table + */ + +static gattAttribute_t scanParamAttrTbl[] = { + // Scan Parameters Service attribute + { + {ATT_BT_UUID_SIZE, primaryServiceUUID}, /* type */ + GATT_PERMIT_READ, /* permissions */ + 0, /* handle */ + (uint8_t *)&scanParamService /* pValue */ + }, + + // Scan Interval Window declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &scanIntervalWindowProps}, + + // Scan Interval Window characteristic + { + {ATT_BT_UUID_SIZE, scanIntervalWindowUUID}, + GATT_PERMIT_ENCRYPT_WRITE, + 0, + scanIntervalWindow}, + + // Scan Parameter Refresh declaration + { + {ATT_BT_UUID_SIZE, characterUUID}, + GATT_PERMIT_READ, + 0, + &scanParamRefreshProps}, + + // Scan Parameter Refresh characteristic + { + {ATT_BT_UUID_SIZE, scanParamRefreshUUID}, + 0, + 0, + scanParamRefresh}, + + // Scan Parameter Refresh characteristic client characteristic configuration + { + {ATT_BT_UUID_SIZE, clientCharCfgUUID}, + GATT_PERMIT_READ | GATT_PERMIT_ENCRYPT_WRITE, + 0, + (uint8_t *)&scanParamRefreshClientCharCfg} +}; + +// Attribute index enumeration-- these indexes match array elements above +enum +{ + SCAN_PARAM_SERVICE_IDX, // Scan Parameters Service + SCAN_PARAM_INTERVAL_DECL_IDX, // Scan Interval Window declaration + SCAN_PARAM_INTERVAL_IDX, // Scan Interval Window characteristic + SCAN_PARAM_REFRESH_DECL_IDX, // Scan Parameter Refresh declaration + SCAN_PARAM_REFRESH_IDX, // Scan Parameter Refresh characteristic + SCAN_PARAM_REFRESH_CCCD_IDX // Scan Parameter Refresh characteristic client characteristic configuration +}; + +/********************************************************************* + * LOCAL FUNCTIONS + */ +static bStatus_t scanParamWriteAttrCB(uint16_t connHandle, gattAttribute_t *pAttr, + uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method); +static bStatus_t scanParamReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr, + uint8_t *pValue, uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method); + +/********************************************************************* + * PROFILE CALLBACKS + */ + +// Service Callbacks +gattServiceCBs_t scanParamCBs = { + scanParamReadAttrCB, // Read callback function pointer + scanParamWriteAttrCB, // Write callback function pointer + NULL // Authorization callback function pointer +}; + +/********************************************************************* + * PUBLIC FUNCTIONS + */ + +/********************************************************************* + * @fn ScanParam_AddService + * + * @brief Initializes the Battery Service by registering + * GATT attributes with the GATT server. + * + * @return Success or Failure + */ +bStatus_t ScanParam_AddService(void) +{ + uint8_t status = SUCCESS; + + // Initialize Client Characteristic Configuration attributes + GATTServApp_InitCharCfg(INVALID_CONNHANDLE, scanParamRefreshClientCharCfg); + + // Register GATT attribute list and CBs with GATT Server App + status = GATTServApp_RegisterService(scanParamAttrTbl, GATT_NUM_ATTRS(scanParamAttrTbl), GATT_MAX_ENCRYPT_KEY_SIZE, + &scanParamCBs); + + return (status); +} + +/********************************************************************* + * @fn ScanParam_Register + * + * @brief Register a callback function with the Battery Service. + * + * @param pfnServiceCB - Callback function. + * + * @return None. + */ +extern void ScanParam_Register(scanParamServiceCB_t pfnServiceCB) +{ + scanParamServiceCB = pfnServiceCB; +} + +/********************************************************************* + * @fn ScanParam_SetParameter + * + * @brief Set a Battery Service parameter. + * + * @param param - Profile parameter ID + * @param len - length of data to right + * @param value - 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 ScanParam_SetParameter(uint8_t param, uint8_t len, void *value) +{ + bStatus_t ret = SUCCESS; + + switch(param) + { + default: + ret = INVALIDPARAMETER; + break; + } + + return (ret); +} + +/********************************************************************* + * @fn ScanParam_GetParameter + * + * @brief Get a Battery Service parameter. + * + * @param param - Profile parameter ID + * @param value - 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 ScanParam_GetParameter(uint8_t param, void *value) +{ + bStatus_t ret = SUCCESS; + switch(param) + { + case SCAN_PARAM_PARAM_INTERVAL: + *((uint16_t *)value) = BUILD_UINT16(scanIntervalWindow[0], + scanIntervalWindow[1]); + break; + + case SCAN_PARAM_PARAM_WINDOW: + *((uint16_t *)value) = BUILD_UINT16(scanIntervalWindow[2], + scanIntervalWindow[3]); + break; + + default: + ret = INVALIDPARAMETER; + break; + } + + return (ret); +} + +/********************************************************************* + * @fn ScanParam_RefreshNotify + * + * @brief Notify the peer to refresh the scan parameters. + * + * @param connHandle - connection handle + * + * @return None + */ +void ScanParam_RefreshNotify(uint16_t connHandle) +{ + uint16_t value; + + value = GATTServApp_ReadCharCfg(connHandle, scanParamRefreshClientCharCfg); + if(value & GATT_CLIENT_CFG_NOTIFY) + { + attHandleValueNoti_t noti; + + noti.pValue = GATT_bm_alloc(connHandle, ATT_HANDLE_VALUE_NOTI, + SCAN_PARAM_REFRESH_LEN, NULL, 0); + if(noti.pValue != NULL) + { + // send notification + noti.handle = scanParamAttrTbl[SCAN_PARAM_REFRESH_CCCD_IDX].handle; + noti.len = SCAN_PARAM_REFRESH_LEN; + noti.pValue[0] = SCAN_PARAM_REFRESH_REQ; + + if(GATT_Notification(connHandle, ¬i, FALSE) != SUCCESS) + { + GATT_bm_free((gattMsg_t *)¬i, ATT_HANDLE_VALUE_NOTI); + } + } + } +} + +/********************************************************************* + * @fn scanParamReadAttrCB + * + * @brief GATT 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 + */ +static bStatus_t scanParamReadAttrCB(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; + + return (status); +} + +/********************************************************************* + * @fn scanParamWriteAttrCB + * + * @brief Validate attribute data prior to a write operation + * + * @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 + */ +static bStatus_t scanParamWriteAttrCB(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; + + // 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]); + + // Only one writeable attribute + if(uuid == SCAN_INTERVAL_WINDOW_UUID) + { + if(len == SCAN_INTERVAL_WINDOW_CHAR_LEN) + { + uint16_t interval = BUILD_UINT16(pValue[0], pValue[1]); + uint16_t window = BUILD_UINT16(pValue[0], pValue[1]); + + // Validate values + if(window <= interval) + { + tmos_memcpy(pAttr->pValue, pValue, len); + + (*scanParamServiceCB)(SCAN_INTERVAL_WINDOW_SET); + } + 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); + } + else + { + status = ATT_ERR_ATTR_NOT_FOUND; + } + + return (status); +} + +/********************************************************************* + * @fn ScanParam_HandleConnStatusCB + * + * @brief Service link status change handler function. + * + * @param connHandle - connection handle + * @param changeType - type of change + * + * @return none + */ +void ScanParam_HandleConnStatusCB(uint16_t connHandle, uint8_t changeType) +{ + // Make sure this is not loopback connection + if(connHandle != LOOPBACK_CONNHANDLE) + { + // Reset Client Char Config if connection has dropped + if((changeType == LINKDB_STATUS_UPDATE_REMOVED) || + ((changeType == LINKDB_STATUS_UPDATE_STATEFLAGS) && + (!linkDB_Up(connHandle)))) + { + GATTServApp_InitCharCfg(connHandle, scanParamRefreshClientCharCfg); + } + } +}