状态机进阶(smp 状态机介绍)
在Bluedroid中的线程介绍 那篇文章中,我们介绍了最简单的一种状态机实现方式。这个状态机的缺点也很明显,如果事件和状态都很多的话,那么状态转换表会很大(事件个数),状态转换表也要很多个(状态个数)。
在smp模块中,我们可以看到如何设计实现状态和事件都比较多的状态机。
smp状态机需求分析
- smp状态机的状态很多(17个状态)
- smp状态机的事件很多(41个事件)
- BLE分为master和slave role,smp状态机需要同时支持这两个role的功能。
- smp的每个状态处理的事件很少,状态之间跳转简单(流程简单一步一步往下走,但是步骤很多)
smp状态机设计思路
状态很多,每个状态处理的事件很少。可以把每个状态拆分成小的状态转换表,只需要当前状态要处理的事件的项。不需要每个状态实现一个大表(包含所有的事件的状态转换表),因为大表的话,很多项都是无用的项,还占内存。
状态机的三要素就是:状态,事件,处理函数。
原先大表的话,事件这一列可以省略只有action和next state两列。拆成小表后就需要事件这一列了。
处理函数还是跟前面一样,通过枚举值查对应的函数;
拆分后的状态表的查询比较简单,通过当前状态和role(区分master和slave),就可以获取状态转换表;
最关键的地方是如何把事件映射到小表的对应列上。映射做好后,就可以使用小表做事件处理和状态跳转了。下面结合代码详细介绍。
代码分析
stack/smp/smp_main.cc
action list
enum {
SMP_PROC_SEC_REQ,
SMP_SEND_PAIR_REQ,
SMP_SEND_PAIR_RSP,
SMP_SEND_CONFIRM,
SMP_SEND_PAIR_FAIL,
SMP_SEND_RAND,
SMP_SEND_ENC_INFO,
SMP_SEND_ID_INFO,
SMP_SEND_LTK_REPLY,
SMP_PROC_PAIR_CMD,
SMP_PROC_PAIR_FAIL,
SMP_PROC_CONFIRM,
SMP_PROC_RAND,
...
}
static const tSMP_ACT smp_sm_action[] = {
smp_proc_sec_req,
smp_send_pair_req,
smp_send_pair_rsp,
smp_send_confirm,
smp_send_pair_fail,
smp_send_rand,
smp_send_enc_info,
smp_send_id_info,
smp_send_ltk_reply,
smp_proc_pair_cmd,
smp_proc_pair_fail,
smp_proc_confirm,
smp_proc_rand,
...
}
上面的代码我们看到action和前面介绍的一样,enum和处理函数一一对应,在状态转换表中填enum的值,可以通过这个值来找到对应的处理函数。
状态表
static const uint8_t smp_master_idle_table[][SMP_SM_NUM_COLS] = {
/* Event Action Next State */
/* L2C_CONN */
{SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_STATE_WAIT_APP_RSP},
/* SEC_REQ */
{SMP_PROC_SEC_REQ, SMP_SEND_APP_CBACK, SMP_STATE_WAIT_APP_RSP},
/* L2C_DISC */
{SMP_IDLE_TERMINATE, SMP_SM_NO_ACTION, SMP_STATE_IDLE},
/* AUTH_CMPL */
{SMP_PAIRING_CMPL, SMP_SM_NO_ACTION, SMP_STATE_IDLE},
/* CR_LOC_SC_OOB_DATA */
{SMP_CREATE_PRIVATE_KEY, SMP_SM_NO_ACTION,SMP_STATE_CREATE_LOCAL_SEC_CONN_OOB_DATA}
};
static const tSMP_SM_TBL smp_state_table[][2] = {
/* SMP_STATE_IDLE */
{smp_master_idle_table, smp_slave_idle_table},
/* SMP_STATE_WAIT_APP_RSP */
{smp_master_wait_for_app_response_table,
¦smp_slave_wait_for_app_response_table},
/* SMP_STATE_SEC_REQ_PENDING */
{
NULL, smp_slave_s