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.

  1. BASS_TASK是任务类型,也标识这任务等级,rw内核会依据这个安排调度。
  2. 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的填充过程如下:
TASK_DESC_BASS

到了这了这一级还不够,很多函数callback是怎么填进去还没有看出来。我们继续追踪。

TASK_DESC_BASS_1

(若图片不清晰,请见点击右键在新标签页中打开图片。)

这里我们可以很清晰的看到很多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的初始化。

猜你喜欢

转载自blog.csdn.net/daoshuti/article/details/78538391