904 lines
34 KiB
C
904 lines
34 KiB
C
|
/**
|
||
|
****************************************************************************************
|
||
|
* @addtogroup HOGPD
|
||
|
* @{
|
||
|
****************************************************************************************
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* INCLUDE FILES
|
||
|
****************************************************************************************
|
||
|
*/
|
||
|
#include "rwip_config.h"
|
||
|
|
||
|
#if (BLE_HID_DEVICE)
|
||
|
|
||
|
#include "gap.h"
|
||
|
#include "gattc_task.h"
|
||
|
#include "attm.h"
|
||
|
#include "hogpd.h"
|
||
|
#include "hogpd_task.h"
|
||
|
#include "prf_utils.h"
|
||
|
|
||
|
#include "ke_mem.h"
|
||
|
|
||
|
/*
|
||
|
* DEFINES
|
||
|
****************************************************************************************
|
||
|
*/
|
||
|
#define HIDS_CFG_FLAG_MANDATORY_MASK ((uint32_t)0x000FD)
|
||
|
#define HIDS_MANDATORY_ATT_NB (7)
|
||
|
#define HIDS_CFG_FLAG_MAP_EXT_MASK ((uint32_t)0x00102)
|
||
|
#define HIDS_MAP_EXT_ATT_NB (2)
|
||
|
#define HIDS_CFG_FLAG_PROTO_MODE_MASK ((uint32_t)0x00600)
|
||
|
#define HIDS_PROTO_MODE_ATT_NB (2)
|
||
|
#define HIDS_CFG_FLAG_KEYBOARD_MASK ((uint32_t)0x0F800)
|
||
|
#define HIDS_KEYBOARD_ATT_NB (5)
|
||
|
#define HIDS_CFG_FLAG_MOUSE_MASK ((uint32_t)0x70000)
|
||
|
#define HIDS_MOUSE_ATT_NB (3)
|
||
|
|
||
|
#define HIDS_CFG_REPORT_MANDATORY_MASK ((uint32_t)0x7)
|
||
|
#define HIDS_REPORT_MANDATORY_ATT_NB (3)
|
||
|
#define HIDS_CFG_REPORT_IN_MASK ((uint32_t)0x8)
|
||
|
#define HIDS_REPORT_IN_ATT_NB (1)
|
||
|
// number of attribute index for a report
|
||
|
#define HIDS_REPORT_NB_IDX (4)
|
||
|
|
||
|
/*
|
||
|
* HIDS ATTRIBUTES DEFINITION
|
||
|
****************************************************************************************
|
||
|
*/
|
||
|
|
||
|
/// Full HIDS Database Description - Used to add attributes into the database
|
||
|
const struct attm_desc hids_att_db[HOGPD_IDX_NB] =
|
||
|
{
|
||
|
// HID Service Declaration
|
||
|
[HOGPD_IDX_SVC] = {ATT_DECL_PRIMARY_SERVICE, PERM(RD, ENABLE), 0, 0},
|
||
|
|
||
|
// HID Service Declaration
|
||
|
[HOGPD_IDX_INCL_SVC] = {ATT_DECL_INCLUDE, PERM(RD, ENABLE), 0, 0},
|
||
|
|
||
|
// HID Information Characteristic Declaration
|
||
|
[HOGPD_IDX_HID_INFO_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE),0, 0},
|
||
|
// HID Information Characteristic Value
|
||
|
[HOGPD_IDX_HID_INFO_VAL] = {ATT_CHAR_HID_INFO, PERM(RD, ENABLE), 0, sizeof(struct hids_hid_info)},
|
||
|
|
||
|
// HID Control Point Characteristic Declaration
|
||
|
[HOGPD_IDX_HID_CTNL_PT_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0},
|
||
|
// HID Control Point Characteristic Value
|
||
|
[HOGPD_IDX_HID_CTNL_PT_VAL] = {ATT_CHAR_HID_CTNL_PT, PERM(WRITE_COMMAND, ENABLE), PERM(RI, ENABLE), sizeof(uint8_t)},
|
||
|
|
||
|
// Report Map Characteristic Declaration
|
||
|
[HOGPD_IDX_REPORT_MAP_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0},
|
||
|
// Report Map Characteristic Value
|
||
|
[HOGPD_IDX_REPORT_MAP_VAL] = {ATT_CHAR_REPORT_MAP, PERM(RD, ENABLE), PERM(RI, ENABLE), HOGPD_REPORT_MAP_MAX_LEN},
|
||
|
// Report Map Characteristic - External Report Reference Descriptor
|
||
|
[HOGPD_IDX_REPORT_MAP_EXT_REP_REF] = {ATT_DESC_EXT_REPORT_REF, PERM(RD, ENABLE), 0, sizeof(uint16_t)},
|
||
|
|
||
|
// Protocol Mode Characteristic Declaration
|
||
|
[HOGPD_IDX_PROTO_MODE_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0},
|
||
|
// Protocol Mode Characteristic Value
|
||
|
[HOGPD_IDX_PROTO_MODE_VAL] = {ATT_CHAR_PROTOCOL_MODE, (PERM(RD, ENABLE) | PERM(WRITE_COMMAND, ENABLE)), PERM(RI, ENABLE), sizeof(uint8_t)},
|
||
|
|
||
|
// Boot Keyboard Input Report Characteristic Declaration
|
||
|
[HOGPD_IDX_BOOT_KB_IN_REPORT_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0},
|
||
|
// Boot Keyboard Input Report Characteristic Value
|
||
|
[HOGPD_IDX_BOOT_KB_IN_REPORT_VAL] = {ATT_CHAR_BOOT_KB_IN_REPORT, (PERM(RD, ENABLE) | PERM(NTF, ENABLE)), PERM(RI, ENABLE), HOGPD_BOOT_REPORT_MAX_LEN},
|
||
|
// Boot Keyboard Input Report Characteristic - Client Characteristic Configuration Descriptor
|
||
|
[HOGPD_IDX_BOOT_KB_IN_REPORT_NTF_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, PERM(RD, ENABLE)|PERM(WRITE_REQ, ENABLE), 0, 0},
|
||
|
|
||
|
// Boot Keyboard Output Report Characteristic Declaration
|
||
|
[HOGPD_IDX_BOOT_KB_OUT_REPORT_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0},
|
||
|
// Boot Keyboard Output Report Characteristic Value
|
||
|
[HOGPD_IDX_BOOT_KB_OUT_REPORT_VAL] = {ATT_CHAR_BOOT_KB_OUT_REPORT, (PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE) | PERM(WRITE_COMMAND, ENABLE)), PERM(RI, ENABLE), HOGPD_BOOT_REPORT_MAX_LEN},
|
||
|
|
||
|
// Boot Mouse Input Report Characteristic Declaration
|
||
|
[HOGPD_IDX_BOOT_MOUSE_IN_REPORT_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0},
|
||
|
// Boot Mouse Input Report Characteristic Value
|
||
|
[HOGPD_IDX_BOOT_MOUSE_IN_REPORT_VAL] = {ATT_CHAR_BOOT_MOUSE_IN_REPORT, (PERM(RD, ENABLE) | PERM(NTF, ENABLE)), PERM(RI, ENABLE), HOGPD_BOOT_REPORT_MAX_LEN,},
|
||
|
// Boot Mouse Input Report Characteristic - Client Characteristic Configuration Descriptor
|
||
|
[HOGPD_IDX_BOOT_MOUSE_IN_REPORT_NTF_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, PERM(RD, ENABLE)|PERM(WRITE_REQ, ENABLE) | PERM(WRITE_COMMAND, ENABLE), 0, 0},
|
||
|
|
||
|
// Report Characteristic Declaration
|
||
|
[HOGPD_IDX_REPORT_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0},
|
||
|
// Report Characteristic Value
|
||
|
[HOGPD_IDX_REPORT_VAL] = {ATT_CHAR_REPORT, PERM(RD, ENABLE), PERM(RI, ENABLE), HOGPD_REPORT_MAX_LEN},
|
||
|
// Report Characteristic - Report Reference Descriptor
|
||
|
[HOGPD_IDX_REPORT_REP_REF] = {ATT_DESC_REPORT_REF, PERM(RD, ENABLE), 0, sizeof(struct hids_report_ref)},
|
||
|
// Report Characteristic - Client Characteristic Configuration Descriptor
|
||
|
[HOGPD_IDX_REPORT_NTF_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, PERM(RD, ENABLE)|PERM(WRITE_REQ, ENABLE) | PERM(WRITE_COMMAND, ENABLE), 0, 0},
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
****************************************************************************************
|
||
|
* @brief Initialization of the HOGPD module.
|
||
|
* This function performs all the initializations of the Profile module.
|
||
|
* - Creation of database (if it's a service)
|
||
|
* - Allocation of profile required memory
|
||
|
* - Initialization of task descriptor to register application
|
||
|
* - Task State array
|
||
|
* - Number of tasks
|
||
|
* - Default task handler
|
||
|
*
|
||
|
* @param[out] env Collector or Service allocated environment data.
|
||
|
* @param[in|out] start_hdl Service start handle (0 - dynamically allocated), only applies for services.
|
||
|
* @param[in] app_task Application task number.
|
||
|
* @param[in] sec_lvl Security level (AUTH, EKS and MI field of @see enum attm_value_perm_mask)
|
||
|
* @param[in] param Configuration parameters of profile collector or service (32 bits aligned)
|
||
|
*
|
||
|
* @return status code to know if profile initialization succeed or not.
|
||
|
****************************************************************************************
|
||
|
*/
|
||
|
static uint8_t hogpd_init (struct prf_task_env* env, uint16_t* start_hdl, uint16_t app_task, uint8_t sec_lvl, struct hogpd_db_cfg* params)
|
||
|
{
|
||
|
struct hogpd_env_tag* hogpd_env = NULL;
|
||
|
// Service content flag - Without Report Characteristics
|
||
|
uint32_t cfg_flag[HOGPD_NB_HIDS_INST_MAX][(HOGPD_ATT_MAX/32) +1];
|
||
|
// Start handle used to allocate service.
|
||
|
uint16_t shdl;
|
||
|
|
||
|
// Total number of attributes
|
||
|
uint8_t tot_nb_att = 0;
|
||
|
|
||
|
// array of service description used to allocate the service
|
||
|
struct attm_desc * hids_db[HOGPD_NB_HIDS_INST_MAX];
|
||
|
// Status
|
||
|
uint8_t status = GAP_ERR_NO_ERROR;
|
||
|
// Service Instance Counter, Counter
|
||
|
uint8_t svc_idx, report_idx;
|
||
|
// Report Char. Report Ref value
|
||
|
struct hids_report_ref report_ref;
|
||
|
|
||
|
//-------------------- allocate memory required for the profile ---------------------
|
||
|
hogpd_env = (struct hogpd_env_tag* ) ke_malloc(sizeof(struct hogpd_env_tag), KE_MEM_ATT_DB);
|
||
|
|
||
|
// ensure that everything is initialized
|
||
|
memset(hids_db, 0, sizeof(hids_db));
|
||
|
memset(cfg_flag, 0, sizeof(cfg_flag));
|
||
|
memset(hogpd_env, 0, sizeof(struct hogpd_env_tag));
|
||
|
|
||
|
|
||
|
// Check number of HID instances
|
||
|
if ((params->hids_nb == 0) || (params->hids_nb > HOGPD_NB_HIDS_INST_MAX))
|
||
|
{
|
||
|
// Invalid number of service instances
|
||
|
status = PRF_ERR_INVALID_PARAM;
|
||
|
}
|
||
|
|
||
|
// For each required HIDS instance
|
||
|
for (svc_idx = 0; ((svc_idx < params->hids_nb) && (status == GAP_ERR_NO_ERROR)); svc_idx++)
|
||
|
{
|
||
|
// Check number of Report Char. instances
|
||
|
if (params->cfg[svc_idx].report_nb > HOGPD_NB_REPORT_INST_MAX)
|
||
|
{
|
||
|
// Too many Report Char. Instances
|
||
|
status = PRF_ERR_INVALID_PARAM;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// retrieve features
|
||
|
hogpd_env->svcs[svc_idx].features = params->cfg[svc_idx].svc_features & HOGPD_CFG_MASK;
|
||
|
hogpd_env->svcs[svc_idx].nb_report = params->cfg[svc_idx].report_nb;
|
||
|
|
||
|
hids_db[svc_idx] = (struct attm_desc *) ke_malloc(HOGPD_ATT_MAX * sizeof(struct attm_desc), KE_MEM_NON_RETENTION);
|
||
|
|
||
|
cfg_flag[svc_idx][0] = HIDS_CFG_FLAG_MANDATORY_MASK;
|
||
|
hogpd_env->svcs[svc_idx].nb_att += HIDS_MANDATORY_ATT_NB;
|
||
|
|
||
|
// copy default definition of the HID attribute database
|
||
|
memcpy(hids_db[svc_idx], hids_att_db, sizeof(struct attm_desc) * HOGPD_ATT_UNIQ_NB);
|
||
|
|
||
|
//--------------------------------------------------------------------
|
||
|
// Compute cfg_flag[i] without Report Characteristics
|
||
|
//--------------------------------------------------------------------
|
||
|
if ((params->cfg[svc_idx].svc_features & HOGPD_CFG_MAP_EXT_REF) == HOGPD_CFG_MAP_EXT_REF)
|
||
|
{
|
||
|
cfg_flag[svc_idx][0] |= HIDS_CFG_FLAG_MAP_EXT_MASK;
|
||
|
hogpd_env->svcs[svc_idx].nb_att += HIDS_MAP_EXT_ATT_NB;
|
||
|
// store reference handle in database
|
||
|
hids_db[svc_idx][HOGPD_IDX_INCL_SVC].max_size = params->cfg[svc_idx].ext_ref.inc_svc_hdl;
|
||
|
}
|
||
|
|
||
|
if ((params->cfg[svc_idx].svc_features & HOGPD_CFG_PROTO_MODE) == HOGPD_CFG_PROTO_MODE)
|
||
|
{
|
||
|
cfg_flag[svc_idx][0] |= HIDS_CFG_FLAG_PROTO_MODE_MASK;
|
||
|
hogpd_env->svcs[svc_idx].nb_att += HIDS_PROTO_MODE_ATT_NB;
|
||
|
}
|
||
|
|
||
|
if ((params->cfg[svc_idx].svc_features & HOGPD_CFG_KEYBOARD) == HOGPD_CFG_KEYBOARD)
|
||
|
{
|
||
|
cfg_flag[svc_idx][0] |= HIDS_CFG_FLAG_KEYBOARD_MASK;
|
||
|
hogpd_env->svcs[svc_idx].nb_att += HIDS_KEYBOARD_ATT_NB;
|
||
|
|
||
|
if ((params->cfg[svc_idx].svc_features & HOGPD_CFG_BOOT_KB_WR) == HOGPD_CFG_BOOT_KB_WR)
|
||
|
{
|
||
|
// Adds write permissions on report
|
||
|
hids_db[svc_idx][HOGPD_IDX_BOOT_KB_IN_REPORT_VAL].perm |= (PERM(WRITE_REQ, ENABLE));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((params->cfg[svc_idx].svc_features & HOGPD_CFG_MOUSE) == HOGPD_CFG_MOUSE)
|
||
|
{
|
||
|
cfg_flag[svc_idx][0] |= HIDS_CFG_FLAG_MOUSE_MASK;
|
||
|
hogpd_env->svcs[svc_idx].nb_att += HIDS_MOUSE_ATT_NB;
|
||
|
|
||
|
if ((params->cfg[svc_idx].svc_features & HOGPD_CFG_BOOT_MOUSE_WR) == HOGPD_CFG_BOOT_MOUSE_WR)
|
||
|
{
|
||
|
// Adds write permissions on report
|
||
|
hids_db[svc_idx][HOGPD_IDX_BOOT_MOUSE_IN_REPORT_VAL].perm |= (PERM(WRITE_REQ, ENABLE));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// set report handle offset
|
||
|
hogpd_env->svcs[svc_idx].report_hdl_offset = hogpd_env->svcs[svc_idx].nb_att;
|
||
|
|
||
|
//--------------------------------------------------------------------
|
||
|
// Update cfg_flag_rep[i] with Report Characteristics
|
||
|
//--------------------------------------------------------------------
|
||
|
for (report_idx = 0; report_idx < params->cfg[svc_idx].report_nb; report_idx++)
|
||
|
{
|
||
|
uint16_t perm = 0;
|
||
|
uint16_t report_offset = HIDS_REPORT_NB_IDX*report_idx;
|
||
|
|
||
|
// update config for current report
|
||
|
cfg_flag[svc_idx][(HOGPD_IDX_REPORT_CHAR + report_offset) / 32] |= (1 << ((HOGPD_IDX_REPORT_CHAR + report_offset) % 32));
|
||
|
cfg_flag[svc_idx][(HOGPD_IDX_REPORT_VAL + report_offset) / 32] |= (1 << ((HOGPD_IDX_REPORT_VAL + report_offset) % 32));
|
||
|
cfg_flag[svc_idx][(HOGPD_IDX_REPORT_REP_REF + report_offset) / 32] |= (1 << ((HOGPD_IDX_REPORT_REP_REF + report_offset) % 32));
|
||
|
hogpd_env->svcs[svc_idx].nb_att += HIDS_REPORT_MANDATORY_ATT_NB;
|
||
|
|
||
|
// copy default definition of the HID report database
|
||
|
memcpy(&(hids_db[svc_idx][HOGPD_IDX_REPORT_CHAR + report_offset]), &(hids_att_db[HOGPD_IDX_REPORT_CHAR]), sizeof(struct attm_desc) * HIDS_REPORT_NB_IDX);
|
||
|
|
||
|
// according to the report type, update value property
|
||
|
switch (params->cfg[svc_idx].report_char_cfg[report_idx] & HOGPD_CFG_REPORT_FEAT)
|
||
|
{
|
||
|
// Input Report
|
||
|
case HOGPD_CFG_REPORT_IN:
|
||
|
{
|
||
|
// add notification permission on report
|
||
|
perm = PERM(RD, ENABLE) | PERM(NTF, ENABLE);
|
||
|
// Report Char. supports NTF => Client Characteristic Configuration Descriptor
|
||
|
cfg_flag[svc_idx][(HOGPD_IDX_REPORT_NTF_CFG + report_offset) / 32] |= (1 << ((HOGPD_IDX_REPORT_NTF_CFG + report_offset) % 32));
|
||
|
hogpd_env->svcs[svc_idx].nb_att += HIDS_REPORT_IN_ATT_NB;
|
||
|
|
||
|
// update feature flag
|
||
|
hogpd_env->svcs[svc_idx].features |= (HOGPD_CFG_REPORT_NTF_EN << report_idx);
|
||
|
|
||
|
// check if attribute value could be written
|
||
|
if ((params->cfg[svc_idx].report_char_cfg[report_idx] & HOGPD_CFG_REPORT_WR) == HOGPD_CFG_REPORT_WR)
|
||
|
{
|
||
|
perm |= PERM(WRITE_REQ, ENABLE);
|
||
|
}
|
||
|
} break;
|
||
|
|
||
|
// Output Report
|
||
|
case HOGPD_CFG_REPORT_OUT:
|
||
|
{
|
||
|
perm = PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE) | PERM(WRITE_COMMAND, ENABLE);
|
||
|
} break;
|
||
|
|
||
|
// Feature Report
|
||
|
case HOGPD_CFG_REPORT_FEAT:
|
||
|
{
|
||
|
perm = PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE);
|
||
|
} break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
status = PRF_ERR_INVALID_PARAM;
|
||
|
} break;
|
||
|
}
|
||
|
|
||
|
hids_db[svc_idx][HOGPD_IDX_REPORT_VAL + (HIDS_REPORT_NB_IDX*report_idx)].perm = perm;
|
||
|
}
|
||
|
|
||
|
// increment total number of attributes to allocate.
|
||
|
tot_nb_att += hogpd_env->svcs[svc_idx].nb_att;
|
||
|
}
|
||
|
|
||
|
// Check that attribute list can be allocated.
|
||
|
if(status == ATT_ERR_NO_ERROR)
|
||
|
{
|
||
|
status = attm_reserve_handle_range(start_hdl, tot_nb_att);
|
||
|
}
|
||
|
|
||
|
// used start handle calculated when handle range reservation has been performed
|
||
|
shdl = *start_hdl;
|
||
|
hogpd_env->start_hdl = *start_hdl;
|
||
|
hogpd_env->hids_nb = params->hids_nb;
|
||
|
// Initialize the Report ID
|
||
|
report_ref.report_id = 0;
|
||
|
|
||
|
// allocate services
|
||
|
for (svc_idx = 0; ((svc_idx < params->hids_nb) && (status == GAP_ERR_NO_ERROR)); svc_idx++)
|
||
|
{
|
||
|
uint16_t handle;
|
||
|
status = attm_svc_create_db(&shdl, ATT_SVC_HID, (uint8_t *)&(cfg_flag[svc_idx][0]),
|
||
|
HOGPD_ATT_MAX, NULL, env->task, hids_db[svc_idx],
|
||
|
(sec_lvl & (PERM_MASK_SVC_DIS | PERM_MASK_SVC_AUTH | PERM_MASK_SVC_EKS)));
|
||
|
// update start handle for next service
|
||
|
shdl += hogpd_env->svcs[svc_idx].nb_att;
|
||
|
|
||
|
// Set HID Information Char. Value
|
||
|
if(status == GAP_ERR_NO_ERROR)
|
||
|
{
|
||
|
handle = hogpd_get_att_handle(hogpd_env, svc_idx, HOGPD_IDX_HID_INFO_VAL, 0);
|
||
|
ASSERT_ERR(handle != ATT_INVALID_HDL);
|
||
|
status = attm_att_set_value(handle, sizeof(struct hids_hid_info), 0, (uint8_t *)¶ms->cfg[svc_idx].hid_info);
|
||
|
}
|
||
|
|
||
|
// Set Report Map Char. External Report Ref value
|
||
|
if((status == GAP_ERR_NO_ERROR)
|
||
|
&& ((params->cfg[svc_idx].svc_features & HOGPD_CFG_MAP_EXT_REF) == HOGPD_CFG_MAP_EXT_REF))
|
||
|
{
|
||
|
handle = hogpd_get_att_handle(hogpd_env, svc_idx, HOGPD_IDX_REPORT_MAP_EXT_REP_REF, 0);
|
||
|
ASSERT_ERR(handle != ATT_INVALID_HDL);
|
||
|
status = attm_att_set_value(handle, sizeof(uint16_t), 0, (uint8_t *)¶ms->cfg[svc_idx].ext_ref.rep_ref_uuid);
|
||
|
}
|
||
|
|
||
|
// Set Report External Ref value
|
||
|
for(report_idx = 0 ; (status == GAP_ERR_NO_ERROR)
|
||
|
&& (report_idx < params->cfg[svc_idx].report_nb) ; report_idx++)
|
||
|
{
|
||
|
// Save the Report ID
|
||
|
report_ref.report_id = params->cfg[svc_idx].report_id[report_idx];
|
||
|
// Set Report Type
|
||
|
report_ref.report_type = (params->cfg[svc_idx].report_char_cfg[report_idx] & HOGPD_CFG_REPORT_FEAT);
|
||
|
|
||
|
handle = hogpd_get_att_handle(hogpd_env, svc_idx, HOGPD_IDX_REPORT_REP_REF, report_idx);
|
||
|
ASSERT_ERR(handle != ATT_INVALID_HDL);
|
||
|
// Set value in the database
|
||
|
status = attm_att_set_value(handle, sizeof(struct hids_report_ref), 0, (uint8_t *)&report_ref);
|
||
|
}
|
||
|
|
||
|
|
||
|
// by default in Report protocol mode.
|
||
|
hogpd_env->svcs[svc_idx].proto_mode = HOGP_REPORT_PROTOCOL_MODE;
|
||
|
|
||
|
}
|
||
|
|
||
|
//-------------------- Update profile task information ---------------------
|
||
|
if (status == ATT_ERR_NO_ERROR)
|
||
|
{
|
||
|
// allocate HOGPD required environment variable
|
||
|
env->env = (prf_env_t*) hogpd_env;
|
||
|
hogpd_env->prf_env.app_task = app_task
|
||
|
| (PERM_GET(sec_lvl, SVC_MI) ? PERM(PRF_MI, ENABLE) : PERM(PRF_MI, DISABLE));
|
||
|
hogpd_env->prf_env.prf_task = env->task | PERM(PRF_MI, DISABLE);
|
||
|
// initialize environment variable
|
||
|
env->id = TASK_ID_HOGPD;
|
||
|
hogpd_task_init(&(env->desc));
|
||
|
|
||
|
// service is ready, go into an Idle state
|
||
|
ke_state_set(env->task, HOGPD_IDLE);
|
||
|
}
|
||
|
else if(hogpd_env != NULL)
|
||
|
{
|
||
|
ke_free(hogpd_env);
|
||
|
}
|
||
|
|
||
|
// ensure that temporary allocated databases description is correctly free
|
||
|
for (svc_idx = 0; svc_idx < HOGPD_NB_HIDS_INST_MAX; svc_idx++)
|
||
|
{
|
||
|
if(hids_db[svc_idx] != NULL)
|
||
|
{
|
||
|
ke_free(hids_db[svc_idx]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (status);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
****************************************************************************************
|
||
|
* @brief Destruction of the HOGPD module - due to a reset for instance.
|
||
|
* This function clean-up allocated memory (attribute database is destroyed by another
|
||
|
* procedure)
|
||
|
*
|
||
|
* @param[in|out] env Collector or Service allocated environment data.
|
||
|
****************************************************************************************
|
||
|
*/
|
||
|
static void hogpd_destroy(struct prf_task_env* env)
|
||
|
{
|
||
|
struct hogpd_env_tag* hogpd_env = (struct hogpd_env_tag*) env->env;
|
||
|
|
||
|
// free profile environment variables
|
||
|
env->env = NULL;
|
||
|
ke_free(hogpd_env);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
****************************************************************************************
|
||
|
* @brief Handles Connection creation
|
||
|
*
|
||
|
* @param[in|out] env Collector or Service allocated environment data.
|
||
|
* @param[in] conidx Connection index
|
||
|
****************************************************************************************
|
||
|
*/
|
||
|
static void hogpd_create(struct prf_task_env* env, uint8_t conidx)
|
||
|
{
|
||
|
/* Nothing to do */
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
****************************************************************************************
|
||
|
* @brief Handles Disconnection
|
||
|
*
|
||
|
* @param[in|out] env Collector or Service allocated environment data.
|
||
|
* @param[in] conidx Connection index
|
||
|
* @param[in] reason Detach reason
|
||
|
****************************************************************************************
|
||
|
*/
|
||
|
static void hogpd_cleanup(struct prf_task_env* env, uint8_t conidx, uint8_t reason)
|
||
|
{
|
||
|
struct hogpd_env_tag* hogpd_env = (struct hogpd_env_tag*) env->env;
|
||
|
uint8_t svc_idx;
|
||
|
ASSERT_ERR(conidx < BLE_CONNECTION_MAX);
|
||
|
|
||
|
// Reset the notification configuration to ensure that no notification will be sent on
|
||
|
// a disconnected link
|
||
|
for (svc_idx = 0; svc_idx < hogpd_env->hids_nb; svc_idx++)
|
||
|
{
|
||
|
hogpd_env->svcs[svc_idx].ntf_cfg[conidx] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* GLOBAL VARIABLE DEFINITIONS
|
||
|
****************************************************************************************
|
||
|
*/
|
||
|
|
||
|
/// HOGPD Task interface required by profile manager
|
||
|
const struct prf_task_cbs hogpd_itf =
|
||
|
{
|
||
|
(prf_init_fnct) hogpd_init,
|
||
|
hogpd_destroy,
|
||
|
hogpd_create,
|
||
|
hogpd_cleanup,
|
||
|
};
|
||
|
|
||
|
|
||
|
/*
|
||
|
* GLOBAL FUNCTIONS DEFINITIONS
|
||
|
****************************************************************************************
|
||
|
*/
|
||
|
|
||
|
const struct prf_task_cbs* hogpd_prf_itf_get(void)
|
||
|
{
|
||
|
return &hogpd_itf;
|
||
|
}
|
||
|
|
||
|
|
||
|
uint16_t hogpd_get_att_handle(struct hogpd_env_tag* hogpd_env, uint8_t svc_idx, uint8_t att_idx, uint8_t report_idx)
|
||
|
{
|
||
|
uint16_t handle = ATT_INVALID_HDL;
|
||
|
uint8_t i = 0;
|
||
|
|
||
|
// Sanity check
|
||
|
if((svc_idx < hogpd_env ->hids_nb) && (att_idx < HOGPD_IDX_NB)
|
||
|
&& ((att_idx < HOGPD_ATT_UNIQ_NB) || (report_idx < hogpd_env->svcs[svc_idx].nb_report)))
|
||
|
{
|
||
|
handle = hogpd_env->start_hdl;
|
||
|
|
||
|
for(i = 0 ; i < svc_idx ; i++)
|
||
|
{
|
||
|
// update start handle for next service - only useful if multiple service, else not used.
|
||
|
handle += hogpd_env->svcs[i].nb_att;
|
||
|
}
|
||
|
|
||
|
// increment index according to expected index
|
||
|
if(att_idx < HOGPD_ATT_UNIQ_NB)
|
||
|
{
|
||
|
handle += att_idx;
|
||
|
|
||
|
// check if Keyboard feature active
|
||
|
if((hogpd_env->svcs[svc_idx].features & HOGPD_CFG_KEYBOARD) == 0)
|
||
|
{
|
||
|
if(att_idx > HOGPD_IDX_BOOT_KB_OUT_REPORT_VAL)
|
||
|
{
|
||
|
handle -= HIDS_KEYBOARD_ATT_NB;
|
||
|
}
|
||
|
// Error Case
|
||
|
else if ((att_idx >= HOGPD_IDX_BOOT_KB_IN_REPORT_CHAR)
|
||
|
&& (att_idx <= HOGPD_IDX_BOOT_KB_OUT_REPORT_VAL))
|
||
|
{
|
||
|
handle = ATT_INVALID_HANDLE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check if Mouse feature active
|
||
|
if((hogpd_env->svcs[svc_idx].features & HOGPD_CFG_MOUSE) == 0)
|
||
|
{
|
||
|
if(att_idx > HOGPD_IDX_BOOT_MOUSE_IN_REPORT_NTF_CFG)
|
||
|
{
|
||
|
handle -= HIDS_MOUSE_ATT_NB;
|
||
|
}
|
||
|
// Error Case
|
||
|
else if ((att_idx >= HOGPD_IDX_BOOT_MOUSE_IN_REPORT_CHAR)
|
||
|
&& (att_idx <= HOGPD_IDX_BOOT_MOUSE_IN_REPORT_NTF_CFG))
|
||
|
{
|
||
|
handle = ATT_INVALID_HANDLE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check if Protocol Mode feature active
|
||
|
if((hogpd_env->svcs[svc_idx].features & HOGPD_CFG_PROTO_MODE) == 0)
|
||
|
{
|
||
|
if(att_idx > HOGPD_IDX_PROTO_MODE_VAL)
|
||
|
{
|
||
|
handle -= HIDS_PROTO_MODE_ATT_NB;
|
||
|
}
|
||
|
// Error Case
|
||
|
else if ((att_idx >= HOGPD_IDX_PROTO_MODE_CHAR)
|
||
|
&& (att_idx <= HOGPD_IDX_PROTO_MODE_VAL))
|
||
|
{
|
||
|
handle = ATT_INVALID_HANDLE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check if Ext Ref feature active
|
||
|
if((hogpd_env->svcs[svc_idx].features & HOGPD_CFG_MAP_EXT_REF) == 0)
|
||
|
{
|
||
|
if(att_idx > HOGPD_IDX_REPORT_MAP_EXT_REP_REF)
|
||
|
{
|
||
|
handle -= HIDS_MAP_EXT_ATT_NB;
|
||
|
}
|
||
|
else if(att_idx > HOGPD_IDX_INCL_SVC)
|
||
|
{
|
||
|
handle -= 1;
|
||
|
}
|
||
|
// Error Case
|
||
|
else if ((att_idx == HOGPD_IDX_INCL_SVC)
|
||
|
|| (att_idx == HOGPD_IDX_REPORT_MAP_EXT_REP_REF))
|
||
|
{
|
||
|
handle = ATT_INVALID_HANDLE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
handle += hogpd_env->svcs[svc_idx].report_hdl_offset;
|
||
|
|
||
|
// increment attribute handle with other reports
|
||
|
for(i = 0 ; i < report_idx; i++)
|
||
|
{
|
||
|
handle += HIDS_REPORT_MANDATORY_ATT_NB;
|
||
|
// check if it's a Report input
|
||
|
if((hogpd_env->svcs[svc_idx].features & (HOGPD_CFG_REPORT_NTF_EN << i)) != 0)
|
||
|
{
|
||
|
handle += HIDS_REPORT_IN_ATT_NB;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Error check
|
||
|
if((att_idx == HOGPD_IDX_REPORT_NTF_CFG)
|
||
|
&& ((hogpd_env->svcs[svc_idx].features & (HOGPD_CFG_REPORT_NTF_EN << report_idx)) != 0))
|
||
|
{
|
||
|
handle = ATT_INVALID_HANDLE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// update handle cursor
|
||
|
handle += att_idx - HOGPD_ATT_UNIQ_NB;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return handle;
|
||
|
}
|
||
|
|
||
|
uint8_t hogpd_get_att_idx(struct hogpd_env_tag* hogpd_env, uint16_t handle, uint8_t *svc_idx, uint8_t *att_idx, uint8_t *report_idx)
|
||
|
{
|
||
|
uint16_t hdl_cursor = hogpd_env->start_hdl;
|
||
|
uint8_t status = PRF_APP_ERROR;
|
||
|
|
||
|
// invalid index
|
||
|
*att_idx = HOGPD_IDX_NB;
|
||
|
|
||
|
// Browse list of services
|
||
|
// handle must be greater than current index
|
||
|
for(*svc_idx = 0 ; (*svc_idx < hogpd_env->hids_nb) && (handle >= hdl_cursor) ; (*svc_idx)++)
|
||
|
{
|
||
|
// check if handle is on current service
|
||
|
if(handle >= (hdl_cursor + hogpd_env->svcs[*svc_idx].nb_att))
|
||
|
{
|
||
|
hdl_cursor += hogpd_env->svcs[*svc_idx].nb_att;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// if we are here, we are sure that handle is valid
|
||
|
status = GAP_ERR_NO_ERROR;
|
||
|
*report_idx = 0;
|
||
|
|
||
|
// check if handle is in reports or not
|
||
|
if(handle < (hdl_cursor + hogpd_env->svcs[*svc_idx].report_hdl_offset))
|
||
|
{
|
||
|
if(handle == hdl_cursor)
|
||
|
{
|
||
|
*att_idx = HOGPD_IDX_SVC;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// check if Ext Ref feature active
|
||
|
if((hogpd_env->svcs[*svc_idx].features & HOGPD_CFG_MAP_EXT_REF) != 0)
|
||
|
{
|
||
|
hdl_cursor += 1;
|
||
|
if(handle == hdl_cursor)
|
||
|
{
|
||
|
*att_idx = HOGPD_IDX_INCL_SVC;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check if handle is in mandatory range
|
||
|
hdl_cursor += HIDS_MANDATORY_ATT_NB;
|
||
|
if(handle <= hdl_cursor)
|
||
|
{
|
||
|
*att_idx = HOGPD_IDX_REPORT_MAP_VAL - (hdl_cursor - handle - 1);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// check if Ext Ref feature active
|
||
|
if((hogpd_env->svcs[*svc_idx].features & HOGPD_CFG_MAP_EXT_REF) != 0)
|
||
|
{
|
||
|
hdl_cursor += 1;
|
||
|
if(handle == hdl_cursor)
|
||
|
{
|
||
|
*att_idx = HOGPD_IDX_REPORT_MAP_EXT_REP_REF;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check if Protocol Mode feature active
|
||
|
if((hogpd_env->svcs[*svc_idx].features & HOGPD_CFG_PROTO_MODE) != 0)
|
||
|
{
|
||
|
hdl_cursor += HIDS_PROTO_MODE_ATT_NB;
|
||
|
if(handle <= hdl_cursor)
|
||
|
{
|
||
|
*att_idx = HOGPD_IDX_PROTO_MODE_VAL - (hdl_cursor - handle - 1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check if Keyboard feature active
|
||
|
if((hogpd_env->svcs[*svc_idx].features & HOGPD_CFG_KEYBOARD) != 0)
|
||
|
{
|
||
|
hdl_cursor += HIDS_KEYBOARD_ATT_NB;
|
||
|
if(handle <= hdl_cursor)
|
||
|
{
|
||
|
*att_idx = HOGPD_IDX_BOOT_KB_OUT_REPORT_VAL - (hdl_cursor - handle - 1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check if Mouse feature active
|
||
|
if((hogpd_env->svcs[*svc_idx].features & HOGPD_CFG_MOUSE) != 0)
|
||
|
{
|
||
|
hdl_cursor += HIDS_MOUSE_ATT_NB;
|
||
|
if(handle <= hdl_cursor)
|
||
|
{
|
||
|
*att_idx = HOGPD_IDX_BOOT_MOUSE_IN_REPORT_NTF_CFG - (hdl_cursor - handle - 1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// not expected
|
||
|
ASSERT_ERR(0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// add handle offset
|
||
|
hdl_cursor += hogpd_env->svcs[*svc_idx].report_hdl_offset;
|
||
|
|
||
|
for(*report_idx = 0 ; (*report_idx < hogpd_env->svcs[*svc_idx].nb_report) ; (*report_idx)++)
|
||
|
{
|
||
|
hdl_cursor += HIDS_REPORT_MANDATORY_ATT_NB;
|
||
|
if(handle <= hdl_cursor)
|
||
|
{
|
||
|
*att_idx = HOGPD_IDX_REPORT_REP_REF - (hdl_cursor - handle - 1);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if((hogpd_env->svcs[*svc_idx].features & (HOGPD_CFG_REPORT_NTF_EN << *report_idx)) != 0)
|
||
|
{
|
||
|
hdl_cursor += HIDS_REPORT_IN_ATT_NB;
|
||
|
if(handle == hdl_cursor)
|
||
|
{
|
||
|
*att_idx = HOGPD_IDX_REPORT_NTF_CFG;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// not expected
|
||
|
ASSERT_ERR(*att_idx != HOGPD_IDX_NB);
|
||
|
}
|
||
|
// loop not expected here
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return (status);
|
||
|
}
|
||
|
|
||
|
|
||
|
uint8_t hogpd_ntf_send(uint8_t conidx, const struct hogpd_report_info* report)
|
||
|
{
|
||
|
struct hogpd_env_tag* hogpd_env = PRF_ENV_GET(HOGPD, hogpd);
|
||
|
uint8_t status = GAP_ERR_NO_ERROR;
|
||
|
uint16_t handle = ATT_INVALID_HANDLE;
|
||
|
uint8_t att_idx = HOGPD_IDX_NB;
|
||
|
uint16_t max_report_len= 0;
|
||
|
uint16_t feature_mask = 0;
|
||
|
uint8_t exp_prot_mode = 0;
|
||
|
|
||
|
// According to the report type retrieve:
|
||
|
// - Attribute index
|
||
|
// - Attribute max length
|
||
|
// - Feature to use
|
||
|
// - Expected protocol mode
|
||
|
switch(report->type)
|
||
|
{
|
||
|
// An Input Report
|
||
|
case HOGPD_REPORT:
|
||
|
{
|
||
|
att_idx = HOGPD_IDX_REPORT_VAL;
|
||
|
max_report_len = HOGPD_REPORT_MAX_LEN;
|
||
|
feature_mask = HOGPD_CFG_REPORT_NTF_EN << report->idx;
|
||
|
exp_prot_mode = HOGP_REPORT_PROTOCOL_MODE;
|
||
|
}break;
|
||
|
// Boot Keyboard input report
|
||
|
case HOGPD_BOOT_KEYBOARD_INPUT_REPORT:
|
||
|
{
|
||
|
att_idx = HOGPD_IDX_BOOT_KB_IN_REPORT_VAL;
|
||
|
max_report_len = HOGPD_BOOT_REPORT_MAX_LEN;
|
||
|
feature_mask = HOGPD_CFG_KEYBOARD;
|
||
|
exp_prot_mode = HOGP_BOOT_PROTOCOL_MODE;
|
||
|
}break;
|
||
|
// Boot Mouse input report
|
||
|
case HOGPD_BOOT_MOUSE_INPUT_REPORT:
|
||
|
{
|
||
|
att_idx = HOGPD_IDX_BOOT_MOUSE_IN_REPORT_VAL;
|
||
|
max_report_len = HOGPD_BOOT_REPORT_MAX_LEN;
|
||
|
feature_mask = HOGPD_CFG_MOUSE;
|
||
|
exp_prot_mode = HOGP_BOOT_PROTOCOL_MODE;
|
||
|
}break;
|
||
|
|
||
|
default: /* Nothing to do */ break;
|
||
|
}
|
||
|
|
||
|
handle = hogpd_get_att_handle(hogpd_env, report->hid_idx, att_idx, report->idx);
|
||
|
|
||
|
// check if attribute is found
|
||
|
if(handle == ATT_INVALID_HANDLE)
|
||
|
{
|
||
|
// check if it's an unsupported feature
|
||
|
if((feature_mask != 0) && (report->hid_idx < hogpd_env->hids_nb)
|
||
|
&& (report->idx < hogpd_env->svcs[report->hid_idx].nb_report)
|
||
|
&& ((hogpd_env->svcs[report->hid_idx].features & feature_mask) == 0))
|
||
|
{
|
||
|
status = PRF_ERR_FEATURE_NOT_SUPPORTED;
|
||
|
}
|
||
|
// or an invalid param
|
||
|
else
|
||
|
{
|
||
|
status = PRF_ERR_INVALID_PARAM;
|
||
|
}
|
||
|
}
|
||
|
// check if length is valid
|
||
|
else if(report->length > max_report_len)
|
||
|
{
|
||
|
status = PRF_ERR_UNEXPECTED_LEN;
|
||
|
}
|
||
|
// check if notification is enabled
|
||
|
else if ((hogpd_env->svcs[report->hid_idx].ntf_cfg[conidx] & feature_mask) == 0)
|
||
|
{
|
||
|
status = PRF_ERR_NTF_DISABLED;
|
||
|
}
|
||
|
// check if protocol mode is valid
|
||
|
else if((hogpd_env->svcs[report->hid_idx].proto_mode != exp_prot_mode)
|
||
|
&& ((hogpd_env->svcs[report->hid_idx].features & HOGPD_CFG_PROTO_MODE) != 0))
|
||
|
{
|
||
|
status = PRF_ERR_REQ_DISALLOWED;
|
||
|
}
|
||
|
|
||
|
if(status == GAP_ERR_NO_ERROR)
|
||
|
{
|
||
|
// Allocate the GATT notification message
|
||
|
struct gattc_send_evt_cmd *report_ntf = KE_MSG_ALLOC_DYN(GATTC_SEND_EVT_CMD,
|
||
|
KE_BUILD_ID(TASK_GATTC, conidx), prf_src_task_get(&(hogpd_env->prf_env), conidx),
|
||
|
gattc_send_evt_cmd, report->length);
|
||
|
|
||
|
// Fill in the parameter structure
|
||
|
report_ntf->operation = GATTC_NOTIFY;
|
||
|
report_ntf->handle = handle;
|
||
|
// pack measured value in database
|
||
|
report_ntf->length = report->length;
|
||
|
memcpy(report_ntf->value, report->value, report->length);
|
||
|
// send notification to peer device
|
||
|
ke_msg_send(report_ntf);
|
||
|
}
|
||
|
|
||
|
return (status);
|
||
|
}
|
||
|
|
||
|
uint8_t hogpd_ntf_cfg_ind_send(uint8_t conidx, uint8_t svc_idx, uint8_t att_idx, uint8_t report_idx, uint16_t ntf_cfg)
|
||
|
{
|
||
|
// Status
|
||
|
uint8_t status = GAP_ERR_NO_ERROR;
|
||
|
uint16_t mask = 0;
|
||
|
|
||
|
struct hogpd_env_tag* hogpd_env = PRF_ENV_GET(HOGPD, hogpd);
|
||
|
|
||
|
// set notification config update mask
|
||
|
switch(att_idx)
|
||
|
{
|
||
|
// An Input Report
|
||
|
case HOGPD_IDX_REPORT_NTF_CFG:
|
||
|
{
|
||
|
mask = HOGPD_CFG_REPORT_NTF_EN << report_idx;
|
||
|
}break;
|
||
|
// Boot Keyboard input report
|
||
|
case HOGPD_IDX_BOOT_KB_IN_REPORT_NTF_CFG:
|
||
|
{
|
||
|
mask = HOGPD_CFG_KEYBOARD;
|
||
|
}break;
|
||
|
// Boot Mouse input report
|
||
|
case HOGPD_IDX_BOOT_MOUSE_IN_REPORT_NTF_CFG:
|
||
|
{
|
||
|
mask = HOGPD_CFG_MOUSE;
|
||
|
}break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
ASSERT_ERR(0);
|
||
|
}break;
|
||
|
}
|
||
|
|
||
|
// Stop notification
|
||
|
if (ntf_cfg == PRF_CLI_STOP_NTFIND)
|
||
|
{
|
||
|
hogpd_env->svcs[svc_idx].ntf_cfg[conidx] &= ~mask;
|
||
|
}
|
||
|
// Start notification
|
||
|
else if(ntf_cfg == PRF_CLI_START_NTF)
|
||
|
{
|
||
|
hogpd_env->svcs[svc_idx].ntf_cfg[conidx] |= mask;
|
||
|
}
|
||
|
// Provided value is incorrect
|
||
|
else
|
||
|
{
|
||
|
status = PRF_APP_ERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
// inform application about updated notification configuration
|
||
|
if(status == GAP_ERR_NO_ERROR)
|
||
|
{
|
||
|
// Allocate the GATT notification message
|
||
|
struct hogpd_ntf_cfg_ind *ntf_cfg_ind = KE_MSG_ALLOC(HOGPD_NTF_CFG_IND,
|
||
|
prf_dst_task_get(&(hogpd_env->prf_env), conidx),
|
||
|
prf_src_task_get(&(hogpd_env->prf_env), conidx),
|
||
|
hogpd_ntf_cfg_ind);
|
||
|
|
||
|
// Fill in the parameter structure
|
||
|
ntf_cfg_ind->conidx = conidx;
|
||
|
|
||
|
for(svc_idx = 0 ; svc_idx < HOGPD_NB_HIDS_INST_MAX; svc_idx++)
|
||
|
{
|
||
|
ntf_cfg_ind->ntf_cfg[svc_idx] = hogpd_env->svcs[svc_idx].ntf_cfg[conidx];
|
||
|
}
|
||
|
|
||
|
// send indication to application
|
||
|
ke_msg_send(ntf_cfg_ind);
|
||
|
}
|
||
|
|
||
|
return (status);
|
||
|
}
|
||
|
|
||
|
#endif /* BLE_HID_DEVICE */
|
||
|
|
||
|
/// @} HOGPD
|