DA1458x BASS 初始化 -- Battery Service 分析(二)
Overview
BASS 分为两部分。一部分为Profile,另一部分为Application。
同样初始化也是分为两部分,一部分是BASS Profile初始化,另一部分是 BASS Application初始化。
BASS Profile 初始化
本部分主要由bass.c和bass_task.c文件组成。
bass.c和bass_task.c一起构建起了一个rwip系统的TASK。
在Dialog的SDK中,基本每一个TASK都是由两个.c文件(xxx.c和xxx_task.c)文件组成。
xxx.c中的xxx_init函数负责创建TASK,其它的函数为一些功能性函数。
xxx.c — 创建TASK,提供功能性函数用来被callback
xxx_task.c中主要定义了一些结构体变量及对应callback函数,用来注册系统调用。
xxx_task.c — 完成注册系统TASK所必须的结构体数据的定义,
下面我们来看下bass.c中的bass_init函数是如何创建Battery Service的
void bass_init(void)
{
// Reset the find me target environment
memset(&bass_env, 0, sizeof(bass_env));
// Create BASS task
ke_task_create(TASK_BASS, &TASK_DESC_BASS);
// Go to IDLE state
ke_state_set(TASK_BASS, BASS_DISABLED);
}
创建的过程分为三步:
1. 清空全局变量bass_env
2. 创建BASS任务
3. 设置BASS为BASS_DISABLED最后将进入IDLE状态
下面我们一步步来分析
1. 清空全局变量bass_env
定义如下
struct bass_env_tag bass_env __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY
可知bass_env变量在编译链接时将被放置在retention_mem_area0段,这也体现出这个变量的重要性。
通过“Reset the find me target environment”这个注释,我们也能知道这个变量还标示着BASS这个任务。
BASS任务中一些重要的数据,就存储在这个bass_env结构体变量里面。所以,在任务创建之初要给这个变量清零。
struct bass_env_tag结构体定义如下:
/// Battery 'Profile' Server environment variable
struct bass_env_tag
{
/// Connection Info
struct prf_con_info con_info;
/// BAS Start Handles
uint16_t shdl[BASS_NB_BAS_INSTANCES_MAX];
/// Database features
uint8_t features[BASS_NB_BAS_INSTANCES_MAX];
/// Number of BAS
uint8_t bas_nb;
};
2. 创建BASS任务
通过rwip内核的接口ke_task_create函数来创建TASK。
这个函数要传两个参数
@param[in] task_type Task type.
@param[in] p_task_desc Pointer to task descriptor.
- BASS_TASK是任务类型,也标识这任务等级,rw内核会依据这个安排调度。
- TASK_DESC_BASS是BASS任务的描述符,我们需要把它的指针传递进去
注意:
创建rw内核任务的时候,要向ke_task_create传递两个参数。task_type很容易理解,就不说了。关键是p_task_desc这个传参。
我把它称呼为任务描述符指针,它指向一个任务描述符。这个任务描述符里面有内核所想要知道的关于这个任务的一切信息.
内核依据这个任务描述符来对任务发生的时间进行处理。
2.1 任务描述符
我们来看下任务描述符是如何定义的,struct ke_task_desc的原型如下:
/// Task descriptor grouping all information required by the kernel for the scheduling.
struct ke_task_desc
{
/// Pointer to the state handler table (one element for each state).
const struct ke_state_handler* state_handler;
/// Pointer to the default state handler (element parsed after the current state).
const struct ke_state_handler* default_handler;
/// Pointer to the state table (one element for each instance).
ke_state_t* state;
/// Maximum number of states in the task.
uint16_t state_max;
/// Maximum index of supported instances of the task.
uint16_t idx_max;
};
可知,任务描述符都有一个state_handler,用来处理状态变换的事件;
同时任务描述符也有一个default_handler,是用来处理默认的事件操作的;
state用来表述任务状态,这些状态是由用户可以自己定义的,很灵活;
state_max表示这个任务有多少个状态;
idx_max表示这个任务最大支持几个instances,一般都是一个。
2.2 TASK_DESC_BASS
TASK_DESC_BASS是一个很重要的结构体变量,系统关于BASS任务的callback都依赖于这个任务描述符来完成。
我们来看看他是如何定义的。
/// BASS task descriptor
static const struct ke_task_desc TASK_DESC_BASS = {bass_state_handler, &bass_default_handler, bass_state, BASS_STATE_MAX, BASS_IDX_MAX};
TASK_DESC_BASS的填充过程如下:
到了这了这一级还不够,很多函数callback是怎么填进去还没有看出来。我们继续追踪。
(若图片不清晰,请见点击右键在新标签页中打开图片。)
这里我们可以很清晰的看到很多callback函数暴露出来了。
关于状态的函数回调如下:
1. Disabled State 的 handler 是 bass_create_db_req_handler函数;
2. Idle State 的 handler 是 bass_enable_req_handler函数;
3. Connected State 的 handler 有三个,分别对应三个不同的事件:
{BASS_BATT_LEVEL_UPD_REQ, (ke_msg_func_t) bass_batt_level_upd_req_handler},
{GATTC_WRITE_CMD_IND, (ke_msg_func_t) gattc_write_cmd_ind_handler},
{GATTC_CMP_EVT, (ke_msg_func_t) gattc_cmp_evt_handler},
默认函数回调如下:
/// Default State handlers definition
const struct ke_msg_handler bass_default_state[] =
{
{GAPC_DISCONNECT_IND, (ke_msg_func_t)gapc_disconnect_ind_handler},
};
可知默认函数回调是gapc_disconnect_ind_handler函数
至此我们漫长的2.2小节结束了,可以来到文章开头profile BASS的初始化。
设置BASS为BASS_DISABLED最后将进入IDLE状态
我们知道系统boot时会RW内核初始化的过程中将会调用bass_init函数,而bass_init函数通过调用ke_state_set(TASK_BASS, BASS_DISABLED);
将BASS_TASK状态设置为BASS_DISABLED。
再之后APP BASS运行起来后会调用app_bass_create_db函数,该函数向内核发消息(BASS_CREATE_DB_REQ)。
之后RW kernel就会调用对应的创建BASS Database的函数即bass_create_db_req_handler函数。
下面我们来看一下这个函数的代码
static int bass_create_db_req_handler(ke_msg_id_t const msgid,
struct bass_create_db_req const *param,
ke_task_id_t const dest_id,
ke_task_id_t const src_id)
{
// Service content flag
uint8_t cfg_flag = BAS_CFG_FLAG_MANDATORY_MASK;
// Status
uint8_t status = PRF_ERR_OK;
#ifndef USE_ONE_BAS_INSTANCE
// Counter
uint8_t i;
#endif
// Battery Level characteristic value permissions
uint16_t perm;
// Battery Level characteristic value properties
uint8_t prop;
arch_printf("bass_create_db_req_handler\r\n");
// Save profile id
bass_env.con_info.prf_id = TASK_BASS;
// Check number of BAS instances
if (param->bas_nb <= BASS_NB_BAS_INSTANCES_MAX)
{
// Save number of BAS
bass_env.bas_nb = param->bas_nb;
#ifndef USE_ONE_BAS_INSTANCE
for (i = 0; ((i < param->bas_nb) && (status == PRF_ERR_OK)); i++)
#else
const int i = 0;
#endif
{
// Save database configuration
bass_env.features[i] = param->features[i];
// Check if notifications are supported
if (bass_env.features[i] == BAS_BATT_LVL_NTF_SUP)
{
cfg_flag |= BAS_CFG_FLAG_NTF_SUP_MASK;
}
// Check if multiple instances
if (bass_env.bas_nb > 1)
{
cfg_flag |= BAS_CFG_FLAG_MTP_BAS_MASK;
}
//Create BAS in the DB
status = attm_svc_create_db(&bass_env.shdl[i], (uint8_t *)&cfg_flag, BAS_IDX_NB, NULL,
dest_id, &bas_att_db[0]);
//Disable the service and set optional features
if (status == PRF_ERR_OK)
{
//Disable service
status = attmdb_svc_set_permission(bass_env.shdl[i], PERM(SVC, DISABLE));
//Set optional properties and permissions
if (bass_env.features[i] == BAS_BATT_LVL_NTF_SUP)
{
prop = ATT_CHAR_PROP_RD | ATT_CHAR_PROP_NTF;
perm = PERM(RD, ENABLE) | PERM(NTF, ENABLE);
attmdb_att_partial_value_update(bass_env.shdl[i] + BAS_IDX_BATT_LVL_CHAR, 0, 1, &prop);
attmdb_att_set_permission(bass_env.shdl[i] + BAS_IDX_BATT_LVL_VAL, perm);
}
}
// Reset configuration flag
cfg_flag = BAS_CFG_FLAG_MANDATORY_MASK;
}
if (status == PRF_ERR_OK)
{
//If we are here, database has been fulfilled with success, go to idle state
ke_state_set(TASK_BASS, BASS_IDLE);
}
}
else
{
status = PRF_ERR_INVALID_PARAM;
}
// Send confirmation to application
struct bass_create_db_cfm * cfm = KE_MSG_ALLOC(BASS_CREATE_DB_CFM, src_id, TASK_BASS,
bass_create_db_cfm);
cfm->status = status;
ke_msg_send(cfm);
return (KE_MSG_CONSUMED);
}
由此我们可以的知道其通过attm_svc_create_db函数创建service database,而这个函数就是通过我们上一章DA1458x BASS Database的组成结构 – Battery Service 分析(一)重点讲的bas_att_db的变量来创建service database的。
attm_svc_create_db函数原型如下:(其具体代码在官方的obj文件中,源码不可见)
/**
****************************************************************************************
* @brief Function use to ease service database creation.
* 函数用来简化service database的创建。
*
* Use @see attmdb_add_service function of attmdb module to create service database,
* then use @see attmdb_add_attribute function of attmdb module to create attributes
* according to database description array given in parameter.
* 使用attmdb模块的@see attmdb_add_service函数创建service数据库,
* 然后使用attmdb模块的@see attmdb_add_attribute函数根据参数中给出的数据库描述数组创建属性。
*
* @note: database description array shall be const to reduce memory consuption (only ROM)
* database description array应该使用const类型来减少内存消耗(只有ROM)
* @note: It supports only 16 bits UUIDs
* 它只支持16位的UUID
*
* @param[in|out] shdl Service start handle.服务启动句柄
* @param[in|out] cfg_flag Configuration Flag, each bit matches with an attribute of
* att_db (Max: 32 attributes); if the bit is set to 1, the
* attribute will be added in the service.
* 配置标志,每一个bit与一个att_db的属性匹配(最多32个属性);
* 如果该位设置为1,则属性将被添加到service中。
* @param[in] max_nb_att Number of attributes in the service
* service中的属性数目
* @param[in|out] att_tbl Array which will be fulfilled with the difference between
* each characteristic handle and the service start handle.
* This array is useful if several characteristics are optional
* within the service, can be set to NULL if not needed.
* 数组将满足每个特征句柄和服务启动句柄之间的区别。
* 如果服务中有几个特征是可选的,那么这个数组很有用,如果不需要,可以设置为NULL。
* @param[in] dest_id Task ID linked to the service. This task will be notified
* each time the service content is modified by a peer device.
* Task ID,它会这个service链接。当这个service的内容被
* peer device(对应的BLE连接设备)修改时,会通知这个task。
* @param[in|out] att_db Table containing all attributes information
* 包含所有属性信息的表(const类型的结构体数组,很重要)
*
* @return Command status code:
* - @ref ATT_ERR_NO_ERROR: If database creation succeeds.创建成功
* - @ref ATT_ERR_INVALID_HANDLE: If start_hdl given in parameter + nb of attribute override
* some existing services handles.
* services handle已经存在
* - @ref ATT_ERR_INSUFF_RESOURCE: There is not enough memory to allocate service buffer.
* or of new attribute cannot be added because all expected
* attributes already added or buffer overflow detected during
* allocation
* 没有足够的内存分配service buffer,或者无法添加新属性,
* 因为在分配过程中检测到所有预期的属性已经添加或buffer溢出
****************************************************************************************
*/
uint8_t attm_svc_create_db(uint16_t *shdl, uint8_t *cfg_flag, uint8_t max_nb_att,
uint8_t *att_tbl, ke_task_id_t const dest_id,
const struct attm_desc *att_db);
由此我们通过bass_create_db_req_handler函数,彻底完成了 Profile部分的Battery service的初始化。
上一篇: Python if语法,for循环,while循环
下一篇: Python实现输出100以内的质数