欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

DA1458x BASS 初始化 -- Battery Service 分析(二)

程序员文章站 2024-03-15 14:37:05
...

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的填充过程如下:
DA1458x BASS 初始化 -- Battery Service 分析(二)

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

DA1458x BASS 初始化 -- Battery Service 分析(二)

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

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