关于RSL10 SDK中Event Kernel部分的代码分析(上)
[复制链接]
本人在一个多月以前就开始分析官方给的工程范例了,然而对其中的函数回调用法没有看懂,自己的项目实验也就因此卡住了,拖到了现在。估计有许多网友也遇到了同样的问题,所以在此把这段时间的学习分析心得总结出来,希望对大家开发RSL10程序有帮助。
RSL10 的 BLE 程序要依赖于 Event Kernel, 这个 Kernel 是个啥?
随便找 BLE 应用的若干例子,在其 app.c 中可以见到,主函数到了最后是一个循环,里面都含有这三个调用:
Kernel_Schedule();
Sys_Watchdog_Refresh();
SYS_WAIT_FOR_EVENT;
第二个是刷新WDT, 第三个是使用 ARM 的 WFE 指令进入低功耗模式,等待硬件唤醒,然后再次执行 Kernel_Schedule(). 明显,这是一个 RTOS 吗?这个和FreeRTOS的调度器不一样。关键的是,代码其它地方也看不到多任务系统的特征——每个任务独立的堆栈。所以,大胆猜测:这个调度器,是根据事件去调用若干函数而已。
在 ble_peripheral_server_hrp 这个例子里面,在 main() 开始就调用的 Device_Initialize() 函数中,执行了一条
ke_task_create(TASK_APP, &TASK_DESC_APP);
乍一看,像是一个 RTOS 创建了一个任务呢,可是这个调用的参数又很诡异,不像。在 ON 的文档《RSL10 Firmware Reference》中,竟然没有对这个函数的说明。纵观整个代码,除了创建这个 TASK_APP 也没有创建别的“任务”了。这当然不对,如果是支持多任务的话,多个任务(线程)函数总要有吧。
因为 ke_task_create() 没有提供源代码,从头文件给出的定义来看:
/****************************************************************************************
* [url=home.php?mod=space&uid=159083]@brief[/url] Create a task.
*
* @param[in] task_type Task type.
* @param[in] p_task_desc Pointer to task descriptor.
*
* [url=home.php?mod=space&uid=784970]@return[/url] Status
***************************************************************************************/
uint8_t ke_task_create(uint8_t task_type, struct ke_task_desc const * p_task_desc);
第一个参数是 8-bit 的 task 类型, 第二个参数指向一个 task descriptor (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;
};
推测,一个 task 可以有多种状态,每个状态可以有不同的 handler 函数(用途是处理消息)。一个 task 类型可以有多个实例。
在 ble_peripheral_server_hrp 中,ke_task_create() 使用的 TASK_DESC_APP 这个结构常量定义为:
static const struct ke_task_desc TASK_DESC_APP = {
.state_handler = NULL,
.default_handler = &appm_default_handler,
.state = appm_state,
.state_max = 0,
.idx_max = APP_IDX_MAX
};
其中 state_handler 是空指针,不起作用。默认的 handler 是 appm_default_handler, 这是 ke_state_handler 结构类型:
struct ke_state_handler
{
/// Pointer to the message handler table of this state.
const struct ke_msg_handler *msg_table;
/// Number of messages handled in this state.
uint16_t msg_cnt;
};
其中包含一个指针和一个16-bit数,指针是指向 ke_msg_handler 结构类型。
struct ke_msg_handler
{
/// Id of the handled message.
ke_msg_id_t id;
/// Pointer to the handler function for the msgid above.
ke_msg_func_t func;
};
在这个例子中,appm_default_handler 定义是:
static const struct ke_state_handler appm_default_handler
= KE_STATE_HANDLER(appm_default_state);
static const struct ke_msg_handler appm_default_state[] =
{
{ KE_MSG_DEFAULT_HANDLER, (ke_msg_func_t)MsgHandler_Notify }
};
KE_STATE_HANDLER 这个宏定义是
#define KE_STATE_HANDLER(hdl) {hdl, sizeof(hdl)/sizeof(struct ke_msg_handler)}
代码看着不明显,我总结成个图:
ble_peripheral_server_hrp 中 appm_default_state 这个表很简单,只有一项,handler 函数是 MsgHandler_Notify(), 这个函数是 SDK 中提供的,有源代码。但并不是说只用一个 handler 函数就足够。另外看一个例子:peripheral_server 中,appm_default_state[] 表有5项:
const struct ke_msg_handler appm_default_state[] =
{
/* Note: Put the default handler on top as this is used for handling any
* messages without a defined handler */
{ KE_MSG_DEFAULT_HANDLER, (ke_msg_func_t)Msg_Handler },
BLE_MESSAGE_HANDLER_LIST,
BASS_MESSAGE_HANDLER_LIST,
CS_MESSAGE_HANDLER_LIST,
APP_MESSAGE_HANDLER_LIST
};
这么看来,表的用法还不清楚,每个 handler 函数各自的作用也不清楚。
我用 GDB 调试了 ble_peripheral_server_hrp 的执行,在 ke_task_create() 函数入口设置断点,发现在 BLE_InitNoTL() 中调用了很多次 ke_task_create(). 其中 task type 为 0x0A 到 0x31 的调用,第二个参数指向的地址位于 SRAM 中 prf_env 全局变量内,值为全0。其它的调用,第二个参数指向的是 flash 的地址,是程序中的只读常量,有:
TASK_DESC_GAPM
TASK_DESC_GAPC
TASK_DESC_GATTM
TASK_DESC_GATTC
TASK_DESC_L2CC
TASK_DESC_LLD
TASK_DESC_LLC
TASK_DESC_LLM
这些常量来自于二进制库,可以在 GDB 下查看,如,查看 TASK_DESC_GAPC 这个任务描述结构
(gdb) print *(struct ke_task_desc *)TASK_DESC_GAPC
$2 = {state_handler = 0x0, default_handler = 0x124afc <gapc_default_handler>,
state = 0x200020b0 <gapc_state> "????????", state_max = 64, idx_max = 8}
(gdb) print *(struct ke_state_handler *)gapc_default_handler
$4 = {msg_table = 0x124b04 <gapc_default_state>, msg_cnt = 38}
(gdb) print *(struct ke_msg_handler *)gapc_default_state
$5 = {id = 65535, func = 0x118f73 <gapc_default_msg_handler+1>}
gapc_default_state 表有38项,第一项的 handler 是 gapc_default_msg_handler
要看最后一项,可以这样
(gdb) print ((struct ke_msg_handler *)gapc_default_state)[37]
$25 = {id = 3639,
func = 0x119a0d <gapc_set_max_rx_size_and_time_cmd_handler+1>}
直接地,还可以写成
(gdb) print ((struct ke_task_desc *)TASK_DESC_GAPC)->default_handler->msg_table[37]
这样可以发现很多的 handler 函数了,它们又是怎样被使用的呢?
现在来看下 handler 函数的类型定义:
/// Format of a task message handler function
typedef int (*ke_msg_func_t)(ke_msg_id_t const msgid, void const *param,
ke_task_id_t const dest_id, ke_task_id_t const src_id);
当一个 handler 被调用的时候,会传给它 4 个参数。这正好是一个消息要包含的内容。在手册里给出了描述:
在 ke_msg.h 头文件中定义了
/// Message structure.
struct ke_msg
{
struct co_list_hdr hdr; ///< List header for chaining
ke_msg_id_t id; ///< Message id.
ke_task_id_t dest_id; ///< Destination kernel identifier.
ke_task_id_t src_id; ///< Source kernel identifier.
uint16_t param_len; ///< Parameter embedded struct length.
uint32_t param[1]; ///< Parameter embedded struct. Must be word-aligned.
};
其中的 struct co_list_hdr 类型成员是用来构造链表的,暂且不管。后面有三个字段较为关键:消息的ID, 消息目标(task)的id, 和消息来源(task)的id. 剩下的是消息带的数据(payload)。
前面已经说过 task 类型,那么 task id 又是啥?
根据定义
/// Task Identifier. Composed by the task type and the task index.
typedef uint16_t ke_task_id_t;
/// Builds the task identifier from the type and the index of that task.
#define KE_BUILD_ID(type, index) ( (ke_task_id_t)(((index) << 8)|(type)) )
知道了 task id 是一个16-bit数据,低8位是 task 类型,高8位是 "task index" ——如何用?我假设是实例的索引(一个task类型可以有多个实例)。
消息的 ID 也是个16-bit数据, 在头文件中写了注释
/// Message Identifier. The number of messages is limited to 0xFFFF.
/// The message ID is divided in two parts:
/// bits[15~8]: task index (no more than 255 tasks support)
/// bits[7~0]: message index(no more than 255 messages per task)
typedef uint16_t ke_msg_id_t;
这样一来,消息 ID 包含 task index 和 message index 两部分。但是不清楚了,此 task 又对应接收方还是发送方,还是可能有其它?
注释的说明和手册有所不同:
按照手册说的,一个 task 的第一个消息 ID, 高8位就是 task 类型。task type 和 task index 必然不是同一个东西,不然 task id 就不会包含两者了。晕了!
还是找代码来验证吧。
使用了 TASK_FIRST_MSG 宏的地方,例如:
/// Messages for Battery Server
enum bass_msg_id
{
/// Start the Battery Server - at connection used to restore bond data
BASS_ENABLE_REQ = TASK_FIRST_MSG(TASK_ID_BASS),
/// Confirmation of the Battery Server start
BASS_ENABLE_RSP,
/// Battery Level Value Update Request
BASS_BATT_LEVEL_UPD_REQ,
/// Inform APP if Battery Level value has been notified or not
BASS_BATT_LEVEL_UPD_RSP,
/// Inform APP that Battery Level Notification Configuration has been changed - use to update bond data
BASS_BATT_LEVEL_NTF_CFG_IND,
};
定义这些消息ID的高8位是 TASK_ID_BASS, 低8位则从0开始计数。
再查找 TASK_ID_BASS 的定义,在 rwip_task.h 当中:
/// Tasks types definition, this value shall be in [0-254] range
enum TASK_API_ID
{
// Link Layer Tasks
TASK_ID_LLM = 0,
TASK_ID_LLC = 1,
TASK_ID_LLD = 2,
TASK_ID_DBG = 3,
// BT Controller Tasks
TASK_ID_LM = 4,
TASK_ID_LC = 5,
TASK_ID_LB = 6,
TASK_ID_LD = 7,
TASK_ID_HCI = 8,
TASK_ID_DISPLAY = 9,
// -----------------------------------------------------------------------------------
// --------------------- BLE HL TASK API Identifiers ---------------------------------
// -----------------------------------------------------------------------------------
TASK_ID_L2CC = 10, // L2CAP Controller Task
TASK_ID_GATTM = 11, // Generic Attribute Profile Manager Task
TASK_ID_GATTC = 12, // Generic Attribute Profile Controller Task
TASK_ID_GAPM = 13, // Generic Access Profile Manager
TASK_ID_GAPC = 14, // Generic Access Profile Controller
TASK_ID_APP = 15, // Application API
TASK_ID_AHI = 16, // Application Host Interface
// -----------------------------------------------------------------------------------
// --------------------- BLE Profile TASK API Identifiers ----------------------------
// -----------------------------------------------------------------------------------
TASK_ID_DISS = 20, // Device Information Service Server Task
TASK_ID_DISC = 21, // Device Information Service Client Task
TASK_ID_PROXM = 22, // Proximity Monitor Task
TASK_ID_PROXR = 23, // Proximity Reporter Task
TASK_ID_FINDL = 24, // Find Me Locator Task
TASK_ID_FINDT = 25, // Find Me Target Task
TASK_ID_HTPC = 26, // Health Thermometer Collector Task
TASK_ID_HTPT = 27, // Health Thermometer Sensor Task
TASK_ID_BLPS = 28, // Blood Pressure Sensor Task
TASK_ID_BLPC = 29, // Blood Pressure Collector Task
TASK_ID_HRPS = 30, // Heart Rate Sensor Task
TASK_ID_HRPC = 31, // Heart Rate Collector Task
TASK_ID_TIPS = 32, // Time Server Task
TASK_ID_TIPC = 33, // Time Client Task
TASK_ID_SCPPS = 34, // Scan Parameter Profile Server Task
TASK_ID_SCPPC = 35, // Scan Parameter Profile Client Task
TASK_ID_BASS = 36, // Battery Service Server Task
TASK_ID_BASC = 37, // Battery Service Client Task
这里的代码才是 task id 的诠释。那么,task type 呢?
在 rwip_config.h 中定义了
/// Tasks types definition
enum KE_TASK_TYPE
{
#if (BT_EMB_PRESENT)
// BT Controller Tasks
TASK_LM,
TASK_LC,
TASK_LB,
TASK_LD,
TASK_HCI,
#endif // (BT_EMB_PRESENT)
#if (BLE_EMB_PRESENT)
// Link Layer Tasks
TASK_LLM ,
TASK_LLC ,
TASK_LLD ,
#endif // (BLE_EMB_PRESENT)
#if ((BLE_EMB_PRESENT) || (BT_EMB_PRESENT))
TASK_DBG,
#endif // ((BLE_EMB_PRESENT) || (BT_EMB_PRESENT))
#if (DISPLAY_SUPPORT)
TASK_DISPLAY,
#endif // (DISPLAY_SUPPORT)
#if (BLE_APP_PRESENT)
TASK_APP,
#endif // (BLE_APP_PRESENT)
#if (BLE_HOST_PRESENT)
TASK_L2CC, // L2CAP Controller Task
TASK_GATTM, // Generic Attribute Profile Manager Task
TASK_GATTC, // Generic Attribute Profile Controller Task
TASK_GAPM, // Generic Access Profile Manager
TASK_GAPC, // Generic Access Profile Controller
// allocate a certain number of profiles task
TASK_PRF_MAX = (TASK_GAPC + BLE_NB_PROFILES),
#ifdef BLE_AUDIO_AM0_TASK
TASK_AM0, // BLE Audio Mode 0 Task
#endif // BLE_AUDIO_AM0_TASK
#endif // (BLE_HOST_PRESENT)
#if (AHI_TL_SUPPORT)
TASK_AHI,
#endif // (AHI_TL_SUPPORT)
/// Maximum number of tasks
TASK_MAX,
TASK_NONE = 0xFF,
};
可见,task type 和 task index 都是预定义的常量。而且 task index 值的意义是固定的,并非动态分配。task type 值对应的任务类型可以配置,不完全固定。然后,task type 和 task index 看起来不能随意组合成一个有效的 task id. 消息ID里面包含的,是 task index 而非 task type, 文档的叙述不确切。
来看一个工程代码中涉及到 task id 的调用:
ke_timer_set(LED_TIMER, TASK_APP, TIMER_2S_SETTING);
这个函数原型是:
/* @param[in] timer_id Timer identifier (message identifier type).
* @param[in] task_id Task identifier which will be notified
* @param[in] delay Delay in time units. ******/
void ke_timer_set(ke_msg_id_t const timer_id, ke_task_id_t const task, uint32_t delay);
其中的第二个参数是 ke_task_id_t 类型的,在调用中传入参数是 TASK_APP, 这是一个 task type 值。这样的话,高8位,即 task index 被置0了。又晕了。
再看一处调用:
cmd = KE_MSG_ALLOC(GAPM_RESET_CMD, TASK_GAPM, TASK_APP, gapm_reset_cmd);
隐含调用的是函数
void *ke_msg_alloc(ke_msg_id_t const id, ke_task_id_t const dest_id,
ke_task_id_t const src_id, uint16_t const param_len);
仍然是 TASK_GAPM, TASK_APP 两个 task type 值被用作了需要 task id 的参数。
既然如此用法是能工作的,那么 task index 在实际中并不是很重要?尚且不明白的是,task type, task index 在消息处理过程中的作用是什么。
我将 ble_peripheral_server_hrp 例子稍做修改,用自己的 handler 替换原来的,以便插入调试代码:
int My_MsgHandler(ke_msg_id_t const msg_id, void *param,
ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
PRINTF("[%04X: %04X<-%04X]\n", msg_id, dest_id, src_id);
return MsgHandler_Notify(msg_id, param, dest_id, src_id);
}
然后重新编译,烧写,运行。在 RTT Viewer 中看到:
00>
00> ble_peripheral_server_bond has started 20:34:28!
00> [0D01: 0004<-0008]
00> conidx=0 [0D00: 0004<-0008]
00> conidx=0 [0D00: 0004<-0008]
00> conidx=0 [0D1C: 0004<-0008]
00> conidx=0 [0D00: 0004<-0008]
00> conidx=0 [0D1C: 0004<-0008]
00> conidx=0 [0D00: 0004<-0008]
00> conidx=0 [0D1C: 0004<-0008]
00> conidx=0 [0D00: 0004<-0008]
00> conidx=0 [0F01: 0004<-00FF]
00> [0F01: 0004<-00FF]
00> [0F01: 0004<-00FF]
00> [0F01: 0004<-00FF]
00> [1E32: 0004<-00FF]
00> [0F01: 0004<-00FF]
00> [0F01: 0004<-00FF]
00> [0F01: 0004<-00FF]
00> [0F01: 0004<-00FF]
00> [0F01: 0004<-00FF]
00> [1E32: 0004<-00FF]
My_MsgHandler 处理过的以上消息,参数中 task id 对应的 task index 都是0, 即仅含有 task type. 但是消息ID都包含了task index. 那么消息ID的task index是发送方还是接收方?看似都有可能。
由 Kernel_Schedule() 来处理所有的消息,选择恰当的 handler, 那么它首先要决定的是在哪个 task (type)的 handler 函数列表中查找。根据上面测试结果,应当是由消息的 dest_id 包含的 task type 来决定。
如前所述,一个 task 的 handler 函数可以不止一个,在 ke_msg_handler 结构中有一个id成员表示了消息ID, 那就意味着有一种根据消息ID进行匹配的机制。由于这部分代码是二进制的,不容易看它是如何匹配的。
用 GDB 查看了一下 TASK_GATTM 的 default_handler 列表,一共8个,包括消息ID与 handler 函数
id = 65535, |
func = 0x1129bf <gattm_default_msg_handler+1> |
id = 2816, |
func = 0x112b0f <gattm_add_svc_req_handler+1> |
id = 2818, |
func = 0x112ae5 <gattm_svc_get_permission_req_handler+1> |
id = 2820, |
func = 0x112abb <gattm_svc_set_permission_req_handler+1> |
id = 2822, |
func = 0x112a75 <gattm_att_get_permission_req_handler+1> |
id = 2824, |
func = 0x112a49 <gattm_att_set_permission_req_handler+1> |
id = 2826, |
func = 0x1129f1 <gattm_att_get_value_req_handler+1> |
id = 2828, |
func = 0x1129c3 <gattm_att_set_value_req_handler+1>
|
除了第一项的ID为0xFFFF外,后面7项ID的高8位都是0x0B, 也就是对应 TASK_ID_GATTM.
在 gattm_task.h 中找到消息ID的定义:
/// GATT Task messages
enum gattm_msg_id
{
/* Database Management */
/// Add service in database request
GATTM_ADD_SVC_REQ = TASK_FIRST_MSG(TASK_ID_GATTM),
/// Add service in database response
GATTM_ADD_SVC_RSP,
/* Service management */
/// Get permission settings of service request
GATTM_SVC_GET_PERMISSION_REQ,
/// Get permission settings of service response
GATTM_SVC_GET_PERMISSION_RSP,
/// Set permission settings of service request
GATTM_SVC_SET_PERMISSION_REQ,
/// Set permission settings of service response
GATTM_SVC_SET_PERMISSION_RSP,
/* Attribute Manipulation */
/// Get permission settings of attribute request
GATTM_ATT_GET_PERMISSION_REQ,
/// Get permission settings of attribute response
GATTM_ATT_GET_PERMISSION_RSP,
/// Set permission settings of attribute request
GATTM_ATT_SET_PERMISSION_REQ,
/// Set permission settings of attribute response
GATTM_ATT_SET_PERMISSION_RSP,
/// Get attribute value request
GATTM_ATT_GET_VALUE_REQ,
/// Get attribute value response
GATTM_ATT_GET_VALUE_RSP,
/// Set attribute value request
GATTM_ATT_SET_VALUE_REQ,
/// Set attribute value response
GATTM_ATT_SET_VALUE_RSP,
/* Debug messages */
/// DEBUG ONLY: Destroy Attribute database request
GATTM_DESTROY_DB_REQ,
/// DEBUG ONLY: Destroy Attribute database response
GATTM_DESTROY_DB_RSP,
/// DEBUG ONLY: Retrieve list of services request
GATTM_SVC_GET_LIST_REQ,
/// DEBUG ONLY: Retrieve list of services response
GATTM_SVC_GET_LIST_RSP,
/// DEBUG ONLY: Retrieve information of attribute request
GATTM_ATT_GET_INFO_REQ,
/// DEBUG ONLY: Retrieve information of attribute response
GATTM_ATT_GET_INFO_RSP,
};
翻译出来,表中消息ID与handler函数是:
GATTM_ADD_SVC_REQ ----- gattm_add_svc_req_handler
GATTM_SVC_GET_PERMISSION_REQ ----- gattm_svc_get_permission_req_handler
GATTM_SVC_SET_PERMISSION_REQ ----- gattm_svc_set_permission_req_handler
GATTM_ATT_GET_PERMISSION_REQ ----- gattm_att_get_permission_req_handler
GATTM_ATT_SET_PERMISSION_REQ ----- gattm_att_set_permission_req_handler
GATTM_ATT_GET_VALUE_REQ ----- gattm_att_get_value_req_handler
GATTM_ATT_SET_VALUE_REQ ----- gattm_att_set_value_req_handler
刚好名称都对应上了。看来第一个 gattm_default_msg_handler 是用来处理余下的没有对应ID的消息的。反汇编了一下,发现它什么都没有做,就算处理完了。
(gdb) disass gattm_default_msg_handler
Dump of assembler code for function gattm_default_msg_handler:
0x001129be <+0>: movs r0, #0
0x001129c0 <+2>: bx lr
End of assembler dump.
这表明,在正常情况下是不应当有除了以上7种ID的消息之外的任何消息,发送给 GATTM_TASK 的。
至此,我大概明白了Event Kernel 涉及到的 task 是怎样一个概念,消息发送给一个 task 时,调度器如何选择其默认的 handler 函数列表中的函数来处理消息。
|