DA145XX协议芯片支持配置HOGP功能。即模拟蓝牙键鼠之类的设备,实现和手机绑定后,靠近设备手机自动回连设备的功能。实现HOGP功能需要对默认SDK做特殊配置,具体流程如下:
1、配置宏定义
da1458x_config_basic.h文件:
/****************************************************************************************************************/
/* Enables the BLE security functionality in TASK_APP. If not defined BLE security related code is compiled out.*/
/****************************************************************************************************************/
#define CFG_APP_SECURITY
user_config.h文件:
(1)、对SMP参数的配置,具体根据项目需求配置做配置
(2)、配置广播内容,需要特定的广播内容,才能正常在手机蓝牙系统用界面实现设备信息
/*
****************************************************************************************
*
* Security configuration
*
****************************************************************************************
*/
/************************************************************
* Device IO Capability (@see gap_io_cap)
*
* - GAP_IO_CAP_DISPLAY_ONLY Display Only
* - GAP_IO_CAP_DISPLAY_YES_NO Display Yes No
* - GAP_IO_CAP_KB_ONLY Keyboard Only
* - GAP_IO_CAP_NO_INPUT_NO_OUTPUT No Input No Output
* - GAP_IO_CAP_KB_DISPLAY Keyboard Display
*
* Select only one option.
************************************************************
*/
#define USER_CFG_FEAT_IO_CAP GAP_IO_CAP_NO_INPUT_NO_OUTPUT
/************************************************************
* OOB information (@see gap_oob)
*
* - GAP_OOB_AUTH_DATA_NOT_PRESENT OOB Data not present
* - GAP_OOB_AUTH_DATA_PRESENT OOB data present
*
* Select only one option.
* Note: OOB is only supported with Legacy Pairing
************************************************************
*/
#define USER_CFG_FEAT_OOB GAP_OOB_AUTH_DATA_NOT_PRESENT
/************************************************************
* Authentication Requirements (@see gap_auth_mask)
*
* - GAP_AUTH_NONE None
* - GAP_AUTH_BOND Bond
* - GAP_AUTH_MITM MITM
* - GAP_AUTH_SEC Secure Connection
* - GAP_AUTH_KEY Keypress Notification (Not Supported)
*
* Any combination of the above.
************************************************************
*/
#define USER_CFG_FEAT_AUTH_REQ (GAP_AUTH_BOND | GAP_AUTH_MITM | GAP_AUTH_SEC)
/************************************************************
* Encryption Max key size (7 to 16) - USER_CFG_FEAT_KEY_SIZE
************************************************************
*/
#define USER_CFG_FEAT_KEY_SIZE KEY_LEN
/************************************************************
* Device security requirements (@see gap_sec_req)
*
* - GAP_NO_SEC No security (no authentication and encryption)
* - GAP_SEC1_NOAUTH_PAIR_ENC Unauthenticated pairing with encryption
* - GAP_SEC1_AUTH_PAIR_ENC Authenticated pairing with encryption
* - GAP_SEC1_SEC_PAIR_ENC Authenticated LE Secure Connections pairing with encryption
* - GAP_SEC2_NOAUTH_DATA_SGN Unauthenticated pairing with data signing
* - GAP_SEC2_AUTH_DATA_SGN Authentication pairing with data signing
*
* Select only one option.
************************************************************
*/
#define USER_CFG_FEAT_SEC_REQ GAP_SEC1_NOAUTH_PAIR_ENC
/**************************************************************************************
* Initiator key distribution (@see gap_kdist)
*
* - GAP_KDIST_NONE No Keys to distribute
* - GAP_KDIST_ENCKEY LTK (Encryption key) in distribution
* - GAP_KDIST_IDKEY IRK (ID key)in distribution
* - GAP_KDIST_SIGNKEY CSRK (Signature key) in distribution
*
* Any combination of the above
**************************************************************************************
*/
#define USER_CFG_FEAT_INIT_KDIST (GAP_KDIST_ENCKEY | GAP_KDIST_IDKEY | GAP_KDIST_SIGNKEY)
/**************************************************************************************
* Responder key distribution (@see gap_kdist)
*
* - GAP_KDIST_NONE No Keys to distribute
* - GAP_KDIST_ENCKEY LTK (Encryption key) in distribution
* - GAP_KDIST_IDKEY IRK (ID key)in distribution
* - GAP_KDIST_SIGNKEY CSRK (Signature key) in distribution
*
* Any combination of the above
**************************************************************************************
*/
#define USER_CFG_FEAT_RESP_KDIST (GAP_KDIST_ENCKEY | GAP_KDIST_IDKEY | GAP_KDIST_SIGNKEY)
/// Advertising data
#define USER_ADVERTISE_DATA ("\x03"\
ADV_TYPE_APPEARANCE\
ADV_APPEARANCE_KEYBOARD\
"\x03"\
ADV_TYPE_INCOMPLETE_LIST_16BIT_SERVICE_IDS\
ADV_UUID_HUMAN_INTERFACE_DEVICE_SERVICE)
user_profiles_config.h文件:
配置SDK开启HOGP功能
/***************************************************************************************/
/* Used BLE profiles (used by "rwprf_config.h"). */
/***************************************************************************************/
#define CFG_PRF_HOGPD
/************************************************************
* CUSTS1 security requirements (@see app_prf_srv_perm)
*
* - SRV_PERM_DISABLE Disable access
* - SRV_PERM_ENABLE Enable access
* - SRV_PERM_UNAUTH Access Requires Unauthenticated link
* - SRV_PERM_AUTH Access Requires Authenticated link
* - SRV_PERM_SECURE Access Requires Authenticated Secure Connection Pairing
*
* Select only one option.
************************************************************
*/
#define APP_CUSTS1_SEC_REQ SRV_PERM_UNAUTH
user_callback_config.h文件:
引入部分头文件定义,配置SMP相关的回调函数,部分函数可以调用库,也可以自己根据实现
#if (BLE_APP_SEC)
#include "app_bond_db.h"
#endif // (BLE_APP_SEC)
#if (BLE_HID_DEVICE)
#include "app_hogpd.h"
#endif
static const struct app_callbacks user_app_callbacks = {
.app_on_connection = user_app_connection,
.app_on_disconnect = user_app_disconnect,
.app_on_update_params_rejected = NULL,
.app_on_update_params_complete = NULL,
.app_on_set_dev_config_complete = default_app_on_set_dev_config_complete,
.app_on_adv_nonconn_complete = NULL,
.app_on_adv_undirect_complete = user_app_adv_undirect_complete,
.app_on_adv_direct_complete = NULL,
.app_on_db_init_complete = default_app_on_db_init_complete,
.app_on_scanning_completed = NULL,
.app_on_adv_report_ind = NULL,
.app_on_get_dev_name = default_app_on_get_dev_name,
.app_on_get_dev_appearance = default_app_on_get_dev_appearance,
.app_on_get_dev_slv_pref_params = default_app_on_get_dev_slv_pref_params,
.app_on_set_dev_info = default_app_on_set_dev_info,
.app_on_data_length_change = NULL,
.app_on_update_params_request = default_app_update_params_request,
.app_on_generate_static_random_addr = default_app_generate_static_random_addr,
.app_on_svc_changed_cfg_ind = NULL,
.app_on_get_peer_features = NULL,
#if (BLE_APP_SEC)
.app_on_pairing_request = user_app_on_pairing_request,
.app_on_tk_exch = user_app_on_tk_exch,
.app_on_irk_exch = NULL,
.app_on_csrk_exch = default_app_on_csrk_exch,
.app_on_ltk_exch = default_app_on_ltk_exch,
.app_on_pairing_succeeded = user_app_on_pairing_succeeded,
.app_on_encrypt_ind = NULL,
.app_on_encrypt_req_ind = user_app_on_encrypt_req_ind,
.app_on_security_req_ind = NULL,
.app_on_addr_solved_ind = default_app_on_addr_solved_ind,
.app_on_addr_resolve_failed = default_app_on_addr_resolve_failed,
#if !defined (__DA14531_01__) && !defined (__DA14535__)
.app_on_ral_cmp_evt = default_app_on_ral_cmp_evt,
.app_on_ral_size_ind = NULL,
.app_on_ral_addr_ind = NULL,
#endif // not for DA14531-01, DA14535
#endif // (BLE_APP_SEC)
};
#if (BLE_APP_SEC)
static const struct app_bond_db_callbacks user_app_bond_db_callbacks = {
.app_bdb_init = default_app_bdb_init,
.app_bdb_get_size = default_app_bdb_get_size,
.app_bdb_add_entry = default_app_bdb_add_entry,
.app_bdb_remove_entry = default_app_bdb_remove_entry,
.app_bdb_search_entry = default_app_bdb_search_entry,
.app_bdb_get_number_of_stored_irks = default_app_bdb_get_number_of_stored_irks,
.app_bdb_get_stored_irks = default_app_bdb_get_stored_irks,
.app_bdb_get_device_info_from_slot = default_app_bdb_get_device_info_from_slot,
};
#endif // (BLE_APP_SEC)
#define app_process_catch_rest_cb user_catch_rest_hndl
static const struct arch_main_loop_callbacks user_app_main_loop_callbacks = {
.app_on_init = user_app_init,
// By default the watchdog timer is reloaded and resumed when the system wakes up.
// The user has to take into account the watchdog timer handling (keep it running,
// freeze it, reload it, resume it, etc), when the app_on_ble_powered() is being
// called and may potentially affect the main loop.
.app_on_ble_powered = NULL,
// By default the watchdog timer is reloaded and resumed when the system wakes up.
// The user has to take into account the watchdog timer handling (keep it running,
// freeze it, reload it, resume it, etc), when the app_on_system_powered() is being
// called and may potentially affect the main loop.
.app_on_system_powered = NULL,
.app_before_sleep = NULL,
.app_validate_sleep = NULL,
.app_going_to_sleep = NULL,
.app_resume_from_sleep = NULL,
};
// Place in this structure the app_<profile>_db_create and app_<profile>_enable functions
// for SIG profiles that do not have this function already implemented in the SDK
// or if you want to override the functionality. Check the prf_func array in the SDK
// for your reference of which profiles are supported.
static const struct prf_func_callbacks user_prf_funcs[] =
{
#if BLE_HID_DEVICE
{TASK_ID_HOGPD, app_hogpd_create_db, app_hogpd_enable},
#endif
{TASK_ID_INVALID, NULL, NULL} // DO NOT MOVE. Must always be last
};
2、实现SMP相关自定义函数
user_app_init里的两段代码,第一段使IOS客户端也实现重连功能,第二段会使BLE连接GATT通讯必须要先完成SMP配对后才可进行
//处理配对请求
void user_app_on_pairing_request(uint8_t conidx, struct gapc_bond_req_ind const *param)
{
if(System_Sta.hogp_switch)
{
app_easy_security_send_pairing_rsp(conidx, param);
}
else
{
app_easy_security_reject_encryption(conidx);
}
}
//蓝牙安全认证配对成功回调
void user_app_on_pairing_succeeded(uint8_t conidx)
{
if (app_sec_env[conidx].auth & GAP_AUTH_BOND)
{
app_easy_security_bdb_add_entry(&app_sec_env[0]); //添加绑定信息
}
}
//交换TK回调
void user_app_on_tk_exch(uint8_t conidx,
struct gapc_bond_req_ind const * param)
{
app_easy_security_accept_encryption(conidx);
}
//收到安全连接检查请求
//HOGP回连后,会触发该回调
void user_app_on_encrypt_req_ind(uint8_t conidx,
struct gapc_encrypt_req_ind const *param)
{
const struct app_sec_bond_data_env_tag *pbd = NULL;
pbd = app_easy_security_bdb_search_entry(SEARCH_BY_EDIV_TYPE, (void *) ¶m->ediv, 2);
// If peer has been found in DB
if(pbd)
{
// Store device bond data to security environment
app_sec_env[conidx] = *pbd;
// Accept encryption
app_easy_security_accept_encryption(conidx);
}
// If peer has not been found in DB
else
{
// Reject encryption, disconnect
app_easy_security_reject_encryption(conidx);
}
}
//处理APP事件回调
void user_catch_rest_hndl(ke_msg_id_t const msgid,
void const *param,
ke_task_id_t const dest_id,
ke_task_id_t const src_id)
{
switch(msgid)
{
//添加:26xx HID事件
#if BLE_HID_DEVICE
case HOGPD_REPORT_UPD_RSP:
case HOGPD_PROTO_MODE_REQ_IND:
case HOGPD_NTF_CFG_IND:
case HOGPD_CTNL_PT_IND:
case HOGPD_REPORT_REQ_IND:
{
app_hogpd_process_handler(msgid, param, dest_id, src_id);
break;
}
#endif // BLE_HID_DEVICE
default:
break;
}
}
//APP初始化
void user_app_init(void)
{
//额外添加以下代码:
#if (BLE_HID_DEVICE)
app_set_prf_srv_perm(TASK_ID_DISS, SRV_PERM_UNAUTH);
app_set_prf_srv_perm(TASK_ID_BASS, SRV_PERM_UNAUTH);
app_set_prf_srv_perm(TASK_ID_HOGPD, SRV_PERM_UNAUTH);
#endif
#if (BLE_APP_SEC)
// Set service security requirements
app_set_prf_srv_perm(TASK_ID_CUSTS1, SRV_PERM_UNAUTH);
// Fetch bond data from the external memory
app_set_prf_srv_perm(TASK_ID_GATT_CLIENT, SRV_PERM_UNAUTH);
app_easy_security_bdb_init();
#endif // (BLE_APP_SEC)
}
3、添加相关的库文件到项目中
app_security.c
app_security_task.c
app_easy_security.c
app_bond_db.c
这些文件在sdk\app_modules\src目录下引入
hogpd.c
hogpd_task.c
这些文件在sdk\ble_stack\profiles\hogp目录下引入
4、实现部分HOGP相关文件,配置HOGP相关的参数信息
具体可以参考github上官方的HOGP工程示例
https://github.com/dialog-semiconductor/BLE_SDK6_examples
项目下的示例工程:
interfaces/HID-Gamepad-Digitizer
具体要实现以下文件,可根据自身的业务逻辑进行修改:
app_hogpd.c
/**
****************************************************************************************
*
* \file app_hogpd.c
*
* \brief Keyboard (HID) over GATT Application entry point.
*
* Copyright (C) 2020 Dialog Semiconductor.
* This computer program includes Confidential, Proprietary Information
* of Dialog Semiconductor. All Rights Reserved.
*
* <bluetooth.support@diasemi.com>
*
****************************************************************************************
*/
/**
* \addtogroup USER
* \{
* \addtogroup PROFILE
* \{
* \addtogroup APP_HOGPD
*
* \{
*/
/*
* INCLUDE FILES
****************************************************************************************
*/
#include "rwip_config.h" // SW configuration
#if (BLE_HID_DEVICE)
#include <user_hogpd_config.h>
#include "app_hogpd.h"
#include "prf_utils.h"
//#include "port_platform.h"
#include "app_prf_perm_types.h"
#include "arch_console.h"
uint16_t report_ntf;
uint8_t hogpd_conidx;
#define REPORT_TO_MASK(index) (HOGPD_CFG_REPORT_NTF_EN << index)
#if HID_NUM_OF_REPORTS > HOGPD_NB_REPORT_INST_MAX
#error "Maximum munber of HID reports exceeded. Please increase HOGPD_NB_REPORT_INST_MAX"
#endif
/**
****************************************************************************************
* @brief Enables the HOGPD profile
*
* @param None
*
* @return void
****************************************************************************************
*/
void app_hogpd_enable(uint8_t conidx)
{
// Allocate the message
//arch_printf("app_hogpd_enable\n");
struct hogpd_enable_req * req = KE_MSG_ALLOC(HOGPD_ENABLE_REQ, prf_get_task_from_id(TASK_ID_HOGPD),
TASK_APP,
hogpd_enable_req);
hogpd_conidx = conidx;
// Fill in the parameter structure
req->conidx = hogpd_conidx;
report_ntf = 0;
//report_ntf = 1;
//#ifdef ALWAYS_ENALBE_REPORTS
int i;
for (i=0; i<HID_NUM_OF_REPORTS; i++) {
if((hogpd_reports[i].cfg & HOGPD_CFG_REPORT_IN) == HOGPD_CFG_REPORT_IN) {
report_ntf |= REPORT_TO_MASK(i);
}
}
//#endif
req->ntf_cfg[0] = report_ntf;
// Send the message
ke_msg_send(req);
}
/**
****************************************************************************************
* \brief Creates the HID over GATT database
*
* \param None
****************************************************************************************
*/
void app_hogpd_create_db(void)
{
struct hogpd_db_cfg* db_cfg;
struct gapm_profile_task_add_cmd *req = KE_MSG_ALLOC_DYN(GAPM_PROFILE_TASK_ADD_CMD,
TASK_GAPM,
TASK_APP,
gapm_profile_task_add_cmd,
sizeof(struct hogpd_db_cfg));
// Fill message
req->operation = GAPM_PROFILE_TASK_ADD;
req->prf_task_id = TASK_ID_HOGPD;
req->app_task = TASK_APP;
req->sec_lvl = get_user_prf_srv_perm(TASK_ID_HOGPD);
req->start_hdl = 0;
// Set parameters
db_cfg = (struct hogpd_db_cfg*) req->param;
struct hogpd_hids_cfg *cfg = &db_cfg->cfg[0];
db_cfg->hids_nb = 1;
struct hids_hid_info *hid_info = &cfg->hid_info;
cfg->svc_features |= HOGPD_CFG_BOOT_KB_WR;
cfg->report_nb = HID_NUM_OF_REPORTS;
uint8_t i;
for(i = 0; i < HID_NUM_OF_REPORTS; i++) {
cfg->report_id[i]=hogpd_reports[i].id;
cfg->report_char_cfg[i]=hogpd_reports[i].cfg;
}
for(i = HID_NUM_OF_REPORTS; i< HOGPD_NB_REPORT_INST_MAX;i++)
{
cfg->report_id[i]=0;
cfg->report_char_cfg[i]=0;
}
hid_info->bcdHID = 0x100;
hid_info->bCountryCode = 0;
hid_info->flags = HIDS_REMOTE_WAKE_CAPABLE;
ke_msg_send(req);
}
bool app_hogpd_send_report(uint8_t report_idx, uint8_t *data, uint16_t length, enum hogpd_report_type type)
{
struct hogpd_report_upd_req *req;
// Allocate the message
req = KE_MSG_ALLOC_DYN(HOGPD_REPORT_UPD_REQ,
prf_get_task_from_id(TASK_ID_HOGPD),
TASK_APP,
hogpd_report_upd_req,
length);
if (!req) {
return false;
}
req->conidx = hogpd_conidx;
struct hogpd_report_info *report = &req->report;
// Fill in the parameter structure
// TODO: find the index from the report number
report->hid_idx = 0;
ASSERT_ERROR((type != HOGPD_BOOT_KEYBOARD_INPUT_REPORT && type != HOGPD_BOOT_MOUSE_INPUT_REPORT) || report_idx == 0 );
ASSERT_ERROR((type != HOGPD_BOOT_KEYBOARD_INPUT_REPORT && type != HOGPD_BOOT_MOUSE_INPUT_REPORT) || length <= HOGPD_BOOT_REPORT_MAX_LEN);
ASSERT_ERROR((type == HOGPD_BOOT_KEYBOARD_INPUT_REPORT || type == HOGPD_BOOT_MOUSE_INPUT_REPORT) || length <= HOGPD_REPORT_MAX_LEN);
report->type = type;
report->idx = report_idx;
report->length = length;
memcpy(report->value, data, length);
ke_msg_send(req);
return true;
}
uint8_t app_hogpd_get_protocol_mode(void)
{
struct hogpd_env_tag* hogpd_env = PRF_ENV_GET(HOGPD, hogpd);
return hogpd_env->svcs[0].proto_mode;
}
uint16_t app_hogpd_report_handle(uint8_t report_nb)
{
ASSERT_WARNING(report_nb<HOGPD_NB_REPORT_INST_MAX);
struct hogpd_env_tag* hogpd_env = PRF_ENV_GET(HOGPD, hogpd);
return hogpd_get_att_handle(hogpd_env, 0, HOGPD_IDX_REPORT_VAL, report_nb);
}
#endif
/**
* \}
* \}
* \}
*/
app_hogpd.h
/**
****************************************************************************************
*
* \file app_hogpd.h
*
* \brief Keyboard (HID) Application entry point header file.
*
* Copyright (C) 2020 Dialog Semiconductor.
* This computer program includes Confidential, Proprietary Information
* of Dialog Semiconductor. All Rights Reserved.
*
* <bluetooth.support@diasemi.com>
*
****************************************************************************************
*/
#ifndef _APP_HOGPD_H_
#define _APP_HOGPD_H_
/**
* \addtogroup USER
* \{
* \addtogroup PROFILE
* \{
* \addtogroup APP_HOGPD
*
* \brief HOGPD Profile application implementation
*
* \{
*/
#if (BLE_HID_DEVICE)
#include "hogpd.h"
#include "hogpd_task.h"
/**
****************************************************************************************
* \brief Enables the HOGPD profile
*
* \param conidx
****************************************************************************************
*/
void app_hogpd_enable(uint8_t conidx);
/**
****************************************************************************************
* \brief Creates the HID over GATT database
****************************************************************************************
*/
void app_hogpd_create_db(void);
/**
****************************************************************************************
* \brief
****************************************************************************************
*/
bool app_hogpd_send_report(uint8_t report_idx, uint8_t *data, uint16_t length, enum hogpd_report_type type);
/**
****************************************************************************************
* \brief
****************************************************************************************
*/
uint8_t app_hogpd_get_protocol_mode(void);
uint16_t app_hogpd_report_handle(uint8_t report_nb);
#endif
/**
* \}
* \}
* \}
*/
#endif // _APP_HOGPD_H_
app_hogpd_task.c
/**
****************************************************************************************
*
* \file app_hogpd_task.c
*
* \brief HID Keyboard handlers.
*
* Copyright (C) 2020 Dialog Semiconductor.
* This computer program includes Confidential, Proprietary Information
* of Dialog Semiconductor. All Rights Reserved.
*
* <bluetooth.support@diasemi.com>
*
****************************************************************************************
*/
/**
* \addtogroup USER
* \{
* \addtogroup PROFILE
* \{
* \addtogroup APP_HOGPD
*
* \{
*/
/*
* INCLUDE FILES
****************************************************************************************
*/
#include "rwip_config.h" // SW configuration
#if (BLE_HID_DEVICE)
#include "app_hogpd_task.h"
#include <user_hogpd_config.h>
#include "app_entry_point.h"
#include "uart.h"
extern uint16_t report_ntf;
#define REPORT_MAP_LEN sizeof(report_map)
/**
****************************************************************************************
* \brief
****************************************************************************************
*/
static void app_hogpd_report_upd_rsp_handler(void const *param)
{
struct hogpd_report_upd_rsp *par = (struct hogpd_report_upd_rsp *)param;
//Clear pending ack's for param->report_nb == 0 (normal key report) and == 2 (ext. key report)
switch (par->status) {
case PRF_ERR_UNEXPECTED_LEN:
break;
case PRF_ERR_NTF_DISABLED:
break;
case PRF_ERR_REQ_DISALLOWED:
break;
case PRF_ERR_INVALID_PARAM:
break;
case PRF_ERR_FEATURE_NOT_SUPPORTED:
break;
}
}
__INLINE void app_hogpd_call_store_attibute_callback(uint16_t uuid, int attr_num, int value)
{
//if(hogpd_params.store_attribute_callback !=NULL) {
// hogpd_params.store_attribute_callback(uuid, attr_num, value);
//}
}
/**
****************************************************************************************
* \brief
****************************************************************************************
*/
static void app_hogpd_proto_mode_ind_handler(void const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
struct hogpd_proto_mode_req_ind *par = (struct hogpd_proto_mode_req_ind *)param;
if (hogpd_params.boot_protocol_mode) {
app_hogpd_call_store_attibute_callback(ATT_CHAR_PROTOCOL_MODE, 0, par->proto_mode);
} else if (par->proto_mode == HOGP_BOOT_PROTOCOL_MODE) {
ASSERT_WARNING(0);
}
struct hogpd_proto_mode_cfm *prot_cfm = KE_MSG_ALLOC(HOGPD_PROTO_MODE_CFM, src_id, dest_id,
hogpd_proto_mode_cfm);
prot_cfm->conidx = par->conidx;
prot_cfm->status = GAP_ERR_NO_ERROR;
prot_cfm->hid_idx = par->hid_idx;
prot_cfm->proto_mode = par->proto_mode;
ke_msg_send(prot_cfm);
}
/**
****************************************************************************************
* \brief
****************************************************************************************
*/
static void app_hogpd_ntf_config_ind_handler(void const *param)
{
struct hogpd_ntf_cfg_ind *ind = (struct hogpd_ntf_cfg_ind *)param;
uint16_t i;
uint16_t new_ntf = ind->ntf_cfg[0];
uint16_t ntf_xor = new_ntf ^ report_ntf;
if(ntf_xor & 0x01) {
app_hogpd_call_store_attibute_callback(ATT_CHAR_BOOT_KB_IN_REPORT, 0, new_ntf & 0x01);
}
uint8_t index=9;
for (i=0x8000; i!=0x0020; i >>= 1 ) {
if(ntf_xor & i) {
app_hogpd_call_store_attibute_callback(ATT_CHAR_REPORT, index, (new_ntf & i) ? 1 : 0);
}
index--;
}
report_ntf = new_ntf;
}
/**
****************************************************************************************
* \brief
****************************************************************************************
*/
static void app_hogpd_ctnl_pt_ind_handler(void const *param)
{
struct hogpd_ctnl_pt_ind *ind = (struct hogpd_ctnl_pt_ind *)param;
app_hogpd_call_store_attibute_callback(ATT_CHAR_HID_CTNL_PT, 0, ind->hid_ctnl_pt);
}
/**
****************************************************************************************
* \brief
****************************************************************************************
*/
static void app_hogpd_report_ind_handler(void const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
struct hogpd_report_req_ind *par = (struct hogpd_report_req_ind *)param;
uint8_t dummy_data[] ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
uint8_t i;
uint16_t report_length = 0;
uint8_t *report_data = NULL;
if(par->operation == HOGPD_OP_REPORT_READ) {
switch(par->report.type) {
case HOGPD_REPORT:
for(i = 0; i < HID_NUM_OF_REPORTS; i++) {
if( i == par->report.idx ) {
report_data = dummy_data;
report_length = hogpd_reports[par->report.idx].size;
if(hogpd_reports[i].read_callback != NULL) {
(hogpd_reports[i].read_callback)(KE_IDX_GET(src_id), report_data, &report_length);
}
break;
}
}
break;
case HOGPD_REPORT_MAP:
report_length = REPORT_MAP_LEN;
report_data = (uint8_t *)report_map;
break;
case HOGPD_BOOT_KEYBOARD_INPUT_REPORT:
case HOGPD_BOOT_KEYBOARD_OUTPUT_REPORT:
default:
break;
}
}
if(par->operation == HOGPD_OP_REPORT_WRITE && par->report.type == HOGPD_REPORT) {
for(i = 0; i < HID_NUM_OF_REPORTS; i++) {
if( i == par->report.idx ) {
if(hogpd_reports[i].write_callback != NULL) {
(hogpd_reports[i].write_callback)(KE_IDX_GET(src_id), &par->report);
}
break;
}
}
}
struct hogpd_report_cfm * cfm = KE_MSG_ALLOC_DYN(HOGPD_REPORT_CFM,
src_id, dest_id,
hogpd_report_cfm,
report_length);
cfm->conidx = KE_IDX_GET(src_id);
cfm->status = ATT_ERR_NO_ERROR;
cfm->operation = par->operation;
cfm->report.hid_idx = par->report.hid_idx;
cfm->report.idx = par->report.idx;
cfm->report.type = par->report.type;
cfm->report.length = report_length;
memcpy(cfm->report.value, report_data, report_length);
ke_msg_send(cfm);
}
// Called when the Service Changed indication has been successfully received by the Host
enum process_event_response app_hogpd_process_handler(ke_msg_id_t const msgid, void const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
switch(msgid) {
case HOGPD_REPORT_UPD_RSP:
app_hogpd_report_upd_rsp_handler(param);
break;
case HOGPD_PROTO_MODE_REQ_IND:
app_hogpd_proto_mode_ind_handler(param, dest_id, src_id);
break;
case HOGPD_NTF_CFG_IND:
app_hogpd_ntf_config_ind_handler(param);
break;
case HOGPD_CTNL_PT_IND:
app_hogpd_ctnl_pt_ind_handler(param);
break;
case HOGPD_REPORT_REQ_IND:
app_hogpd_report_ind_handler(param, dest_id, src_id);
break;
default:
return PR_EVENT_UNHANDLED;
}
return PR_EVENT_HANDLED;
}
#endif
/**
* \}
* \}
* \}
*/
app_hogpd_task.h
/**
****************************************************************************************
*
* \file app_hogpd_task.h
*
* \brief HID Keyboard handlers header file.
*
* Copyright (C) 2020 Dialog Semiconductor.
* This computer program includes Confidential, Proprietary Information
* of Dialog Semiconductor. All Rights Reserved.
*
* <bluetooth.support@diasemi.com>
*
****************************************************************************************
*/
#ifndef _APP_HOGPD_TASK_H_
#define _APP_HOGPD_TASK_H_
/**
* \addtogroup USER
* \{
* \addtogroup PROFILE
* \{
* \addtogroup APP_HOGPD
*
* \brief HOGPD Profile application implementation
*
* \{
*/
#include "ke_msg.h"
#include "hogpd.h"
#include "gattc_task.h"
#include "app_hogpd_defs.h"
/*
* FUNCTION DECLARATIONS
****************************************************************************************
*/
/**
****************************************************************************************
* \brief Called when the Service Changed indication has been successfully received by the Host
*
* \param[in] msgid
* \param[in] param
* \param[in] dest_id
* \param[in] src_id
*
* \return enum process_event_response
*****************************************************************************************
*/
enum process_event_response app_hogpd_process_handler(ke_msg_id_t const msgid, void const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id);
/**
* \}
* \}
* \}
*/
#endif // _APP_HOGPD_TASK_H_
user_hogpd_config.h
该文件会影响手机连接读取设备信息的HID配置,手机系统可能会根据这些参数将设备识别为不同的蓝牙外设并进行相关配置
/**
****************************************************************************************
*
* \file user_hogpd_config.h
*
* \brief HOGPDD configuration file.
*
* Copyright (C) 2017 Dialog Semiconductor.
* This computer program includes Confidential, Proprietary Information
* of Dialog Semiconductor. All Rights Reserved.
*
* <bluetooth.support@diasemi.com>
*
****************************************************************************************
*/
#ifndef _USER_HOGPD_CONFIG_H_
#define _USER_HOGPD_CONFIG_H_
/**
****************************************************************************************
* \addtogroup CONFIGURATION
* \{
* \addtogroup PROFILE_CONFIG
* \{
* \addtogroup HOGPD_CFG
*
* \brief BLE HOGPD profile configuration
* \{
****************************************************************************************
*/
#include "app_hogpd_defs.h"
#include "hogpd.h"
#include "hogpd_task.h"
#define CFG_USE_DIGITIZER (0)
#define CFG_USE_JOYSTICKS (0)
#define HID_MOUSE_MOTION_REPORT_ID 0x01 // Mouse motion report
#define HID_MOUSE_MOTION_REPORT_SIZE 8 // The size of the mouse motion report
#define HID_GAMEPAD_AXIS_REPORT_ID 0x02 // Mouse buttons report
#define HID_GAMEPAD_AXIS_REPORT_SIZE 4 // The size of the Mouse buttons report
#define HID_GAMEPAD_BUTTONS_REPORT_ID 0x03 // Advanced buttons report
#define HID_GAMEPAD_BUTTONS_REPORT_SIZE 2 // The size of the advanced buttons report
enum hogpd_indexes {
HID_MOUSE_MOTION_REPORT_IDX = 0,
HID_GAMEPAD_AXIS_REPORT_IDX,
// HID_GAMEPAD_BUTTONS_REPORT_IDX,
HID_NUM_OF_REPORTS // Don't remove this.
};
static const hogpd_reports_t hogpd_reports[HID_NUM_OF_REPORTS] =
{
[HID_MOUSE_MOTION_REPORT_IDX] = {.id = HID_MOUSE_MOTION_REPORT_ID,
.size = HID_MOUSE_MOTION_REPORT_SIZE,
.cfg = HOGPD_CFG_REPORT_IN | HOGPD_REPORT_NTF_CFG_MASK | HOGPD_CFG_REPORT_WR,
.read_callback = NULL,
.write_callback = NULL},
[HID_GAMEPAD_AXIS_REPORT_IDX] = {.id = HID_GAMEPAD_AXIS_REPORT_ID,
.size = HID_GAMEPAD_AXIS_REPORT_SIZE,
.cfg = HOGPD_CFG_REPORT_IN | HOGPD_REPORT_NTF_CFG_MASK | HOGPD_CFG_REPORT_WR,
.read_callback = NULL,
.write_callback = NULL},
// [HID_GAMEPAD_BUTTONS_REPORT_IDX] = {.id = HID_GAMEPAD_BUTTONS_REPORT_ID,
// .size = HID_GAMEPAD_BUTTONS_REPORT_SIZE,
// .cfg = HOGPD_CFG_REPORT_IN | HOGPD_REPORT_NTF_CFG_MASK | HOGPD_CFG_REPORT_WR,
// .read_callback = NULL,
// .write_callback = NULL},
};
static const hogpd_params_t hogpd_params = {
/**
****************************************************************************************
* Use BOOT MODE
****************************************************************************************
*/
.boot_protocol_mode = true,//false,
/**
****************************************************************************************
* Include BATT in HID
****************************************************************************************
*/
.batt_external_report = false,
/**
****************************************************************************************
* Set RemoteWakeup mode. Remote Host may not handle properly remote wakeup when the
* inactivity timeout is on. Some Hosts do not expect to receive LL_TERMINATE_IND from
* Wakeup capable devices while they are sleeping.
****************************************************************************************
*/
.remote_wakeup = false,
/**
****************************************************************************************
* Set normally connectable mode
****************************************************************************************
*/
#ifdef NORMALLY_CONNECTABLE
.normally_connectable = true,
#else
.normally_connectable = false,
#endif
/**
****************************************************************************************
* \brief Callback for storing CCC and attributes
****************************************************************************************
*/
#ifdef HAS_CONNECTION_FSM
.store_attribute_callback = user_store_ccc,
#else
.store_attribute_callback = NULL,
#endif
};
// Report Descriptor == Report Map (HID1_11.pdf section E.6)
static const uint8_t report_map[] =
{
HID_USAGE_PAGE (HID_USAGE_PAGE_GENERIC_DESKTOP), // USAGE_PAGE (Generic Desktop)
HID_USAGE (HID_GEN_DESKTOP_USAGE_MOUSE), // USAGE (Gamepad)
HID_COLLECTION (HID_APPLICATION), // COLLECTION (Application)
HID_REPORT_ID (HID_GAMEPAD_AXIS_REPORT_ID), // REPORT_ID (1)
HID_COLLECTION (HID_LOGICAL), // COLLECTION (Logical)
HID_USAGE (HID_GEN_DESKTOP_USAGE_X), // USAGE (X)
HID_USAGE (HID_GEN_DESKTOP_USAGE_Y), // USAGE (Y)
HID_USAGE (HID_GEN_DESKTOP_USAGE_Z), // USAGE (Z)
HID_USAGE (HID_GEN_DESKTOP_USAGE_RZ), // USAGE (RZ)
//HID_USAGE (HID_GEN_DESKTOP_USAGE_RY), // USAGE (RY)
//HID_USAGE (HID_GEN_DESKTOP_USAGE_RZ), // USAGE (RZ)
HID_LOGICAL_MIN_8 (0x00), // LOGICAL_MINIMUM (0)
HID_LOGICAL_MAX_8 (0xff), // LOGICAL_MAXIMUM (255)
HID_PHYSICAL_MIN_8 (0x00), // PHYSICAL_MINIMUM (0)
HID_PHYSICAL_MAX_8 (0xff), // PHYSICAL_MAXIMUM (255)
HID_REPORT_SIZE (0x08), // REPORT_SIZE (8)
HID_REPORT_COUNT (0x04), // REPORT_COUNT (3)
HID_INPUT (HID_DATA_BIT | HID_VAR_BIT | HID_ABS_BIT), // INPUT (Data,Var,Abs)
HID_END_COLLECTION, // END_COLLECTION
// HID_REPORT_ID (HID_GAMEPAD_BUTTONS_REPORT_ID), // REPORT_ID (2)
// HID_COLLECTION (HID_LOGICAL), // COLLECTION (Logical)
// HID_USAGE_PAGE (HID_USAGE_PAGE_BUTTONS), // USAGE_PAGE (Buttons)
// HID_USAGE_MIN_8 (0x01), // USAGE_MINIMUM (Button A)
// HID_USAGE_MAX_8 (0x08), // USAGE_MAXIMUM (Button R1)
// HID_REPORT_COUNT (0x08), // REPORT_COUNT (16)
// HID_REPORT_SIZE (0x01), // REPORT_SIZE (1)
// HID_LOGICAL_MAX_8 (0x01), // LOGICAL_MAXIMUM (1)
// HID_LOGICAL_MIN_8 (0x00), // LOGICAL_MINIMUM (0)
// HID_INPUT (HID_DATA_BIT | HID_VAR_BIT | HID_ABS_BIT), // Input (Data, Variable, Absolute)
//
// HID_USAGE_PAGE (HID_USAGE_PAGE_BUTTONS), // USAGE_PAGE (Buttons)
// HID_USAGE_MIN_8 (0x09), // USAGE_MINIMUM (Button R2)
// HID_USAGE_MAX_8 (0x0F), // USAGE_MAXIMUM (Button TR)
// HID_REPORT_COUNT (0x07), // REPORT_COUNT (16)
// HID_REPORT_SIZE (0x01), // REPORT_SIZE (1)
// HID_LOGICAL_MAX_8 (0x01), // LOGICAL_MAXIMUM (1)
// HID_LOGICAL_MIN_8 (0x00), // LOGICAL_MINIMUM (0)
// HID_INPUT (HID_DATA_BIT | HID_VAR_BIT | HID_ABS_BIT), // Input (Data, Variable, Absolute)
// HID_REPORT_COUNT (0x01), //1 bit padding
// HID_INPUT (HID_CONST_BIT | HID_ARY_BIT | HID_ABS_BIT), // Input (Cnst,Ary,Abs)
// //HID_REPORT_COUNT (0x01), // Report Count (1)
// //HID_REPORT_SIZE (0x06), // Report Size (6)
// //HID_INPUT (HID_CONST_BIT), // Input (Constant) for padding
// HID_END_COLLECTION, // END_COLLECTION
HID_END_COLLECTION // END_COLLECTION
};
#endif // _USER_HOGPD_CONFIG_H_
app_hogpd_defs.h
该文件无需修改,直接使用官方HOGP示例工程内的文件即可
至此SDK已经具备实现HOGP功能的相关配置。
5、解决6.20以上版本SDKHOGP功能编译错误
使用ARM compiler6编译器,工程默认开启Link-Time Optimization编译后,会导致hogpd_task..c内部的函数编译报错:
Error: L6137E: Symbol gattc_read_req_ind_handler was not preserved by the LTO codegen but is needed by the image.
应该是编译器将该函数优化掉了,主要是这几个函数在SDK的其它地方被配置成了ROM调用,这里实际是这几个函数和那边重名了。要解决这个文件,可以将函数名自行做修改(加了一个my_的前缀),防止其被优化掉:
const struct ke_msg_handler hogpd_default_state[] =
{
{ HOGPD_ENABLE_REQ, (ke_msg_func_t) hogpd_enable_req_handler },
{ HOGPD_REPORT_UPD_REQ, (ke_msg_func_t) hogpd_report_upd_req_handler },
{ HOGPD_REPORT_CFM, (ke_msg_func_t) hogpd_report_cfm_handler },
{ HOGPD_PROTO_MODE_CFM, (ke_msg_func_t) hogpd_proto_mode_cfm_handler },
{ GATTC_ATT_INFO_REQ_IND, (ke_msg_func_t) my_gattc_att_info_req_ind_handler },
{ GATTC_WRITE_REQ_IND, (ke_msg_func_t) my_gattc_write_req_ind_handler },
{ GATTC_READ_REQ_IND, (ke_msg_func_t) my_gattc_read_req_ind_handler },
{ GATTC_CMP_EVT, (ke_msg_func_t) my_gattc_cmp_evt_handler },
};
注意函数的定义处也要做对应修改。修改后,编译即可正常通过。