【android bluetooth 协议分析 06】【l2cap详解 9】【L2cap通道生命周期】
本篇 我们开始对 L2CAP 信道状态机的行为进行梳理,覆盖了 Classic L2CAP 和 Credit-based L2CAP 的连接、配置、数据传输、断开流程,以及 Credit-based Reconfig 场景。
在开始本篇介绍之前, 我们先来认识一下 有限状态机(FSM) 模型。
1. FSM 模型
1. 什么是有限状态机(FSM)?
有限状态机是一种抽象的计算模型,用于描述系统在不同状态之间的切换行为。它广泛应用于嵌入式系统、网络协议、编译器、游戏开发、操作系统等场景。
1. 构成要素
一个 FSM 通常由以下几个核心组成:
要素 | 含义 |
---|---|
状态(State) | 系统可能处于的某种情况(例如“待机”、“连接中”) |
事件(Event) | 触发状态改变的外部或内部动作(例如“连接请求”) |
动作(Action) | 状态转换时执行的操作(例如“初始化连接参数”) |
状态转移(Transition) | 根据当前状态和事件,决定下一个状态 |
2. 状态机示意图(简化)
[Idle] : 初始状态为 Idle|Connect Request : 此时来了一个 事件:连接请求↓[Connecting]: 此时状态切换为 Connecting|Connection Success : 此时来了一个事件: 连接成功↓[Connected] : 此时状态切换为 Connected
3. 类型分类
类型 | 特点 |
---|---|
Mealy 状态机 | 输出取决于当前状态和输入(动作发生在边上) |
Moore 状态机 | 输出仅取决于当前状态(动作发生在状态上) |
大多数实际系统会使用 混合模型,即部分动作跟随事件触发,部分跟随状态变化。
4. 类比:FSM 就像一个红绿灯控制器
状态 | 事件 | 行动 | 下一个状态 |
---|---|---|---|
红灯 | 时间到 | 变黄灯 | 黄灯 |
黄灯 | 时间到 | 变绿灯 | 绿灯 |
绿灯 | 时间到 | 变红灯 | 红灯 |
这个系统只有有限几个状态(红、黄、绿),每个状态在某个条件满足(如时间到)后会跳转到另一个状态。这就是一个经典的 FSM。
5. FSM 在系统中的应用示例
例如一个音乐 App:
- 状态:
首页
,播放页
,设置页
- 事件:点击按钮
- 动作:更新 UI,切换页面
6. 状态机的优势
优点 | 说明 |
---|---|
结构清晰 | 每种状态和转移逻辑一目了然 |
易于维护 | 修改状态逻辑不影响其他部分 |
可视化强 | 状态图可以清楚展示系统流程 |
适合代码自动生成 | 如使用工具(UML、statechart)辅助开发 |
7. 简化代码示例
switch (state) {case STATE_CLOSED:if (event == CONNECT_REQ) {send_connect_rsp();state = STATE_CONNECTING;}break;case STATE_CONNECTING:if (event == CONNECT_SUCCESS) {init_config();state = STATE_CONFIG;}break;
}
8. 小结
项 | 内容 |
---|---|
FSM 是什么? | 用来描述系统在不同状态之间如何根据事件进行转换的模型 |
有什么用? | 描述复杂交互逻辑(如蓝牙连接、协议握手、APP导航等) |
好处是? | 模块清晰,逻辑明确,易于调试和可视化 |
2. FSM和l2cap 的关系
在 aosp 中 l2cap 对于信道的的管理就是使用的 FSM 模型。 只有了解了 FSM 是什么,我们才能清晰去剖析 aosp 的 l2cap 源码。
在 Android AOSP 的蓝牙协议栈中,每条 L2CAP 信道(channel)就是一个 FSM:
- 状态:
CLOSED
,WAIT_CONNECT_RSP
,CONFIG
,OPEN
,DISCONNECTING
等。 - 事件:
L2CEVT_L2CAP_CONNECT_REQ
,L2CEVT_L2CAP_CONFIG_RSP
,L2CEVT_LP_DISCONNECT_IND
等。 - 动作:调用回调通知上层、发送数据包给对端、启动定时器等。
- 状态转移:配置完成后进入
OPEN
,断开请求后进入DISCONNECTING
。
3. 剖析l2cap 源码
铺垫了这么多,我们现在看,如何一步步和我们的 源码对应起来。
在 l2cap相关的源码中,我们会看到 很多 对于 p_ccb->chnl_state 通道 的赋值和判断。容易让人晕。那这些都代码什么呢? 我们该如何快速梳理清楚这些信息呢?
我们在之前 的文章 通俗易懂l2cap 状态机里面其实就已经介绍过。 还记得这张图吗?
- 图中每一个 圆圈代表 一个状态。 也就是对 p_ccb->chnl_state 的不同赋值。
在aosp 中 l2cap 的状态 是通过 tL2C_CHNL_STATE 来定义的。
p_ccb->chnl_state
每个 状态 之间的切换, 的触发点都是 我们的事件来触发的。 而aosp 中的事件都是用 tL2CEVT 来描述的。
状态和事件的关系是什么?
在 l2c_csm.cc
的状态机模型中:
-
状态(State)
描述的是 当前连接的生命周期阶段(比如正在等待安全认证、正在配置、已经打开等)。 -
事件(Event)
是 触发状态变化的外部/内部信号(比如收到连接请求、认证成功、配置完成等)。
状态 + 事件 = 动作(Action) + 下一状态(Next State)
这是典型的 有限状态机(FSM) 模型。
举个例子
来自 l2c_csm.cc
case CST_ORIG_W4_SEC_COMP:switch (event) {case L2CEVT_SEC_COMP:// 认证成功,下一状态是等待 L2CAP 连接响应...p_ccb->chnl_state = CST_W4_L2CAP_CONNECT_RSP;break;
这段逻辑明确说明:
- 当前状态:
CST_ORIG_W4_SEC_COMP
- 当前事件:
L2CEVT_SEC_COMP
- 新状态:
CST_W4_L2CAP_CONNECT_RSP
我们先来梳理一下 l2cap 中所有的状态
1. tL2C_CHNL_STATE 介绍
// system/stack/l2cap/l2c_int.h
typedef enum {CST_CLOSED, /* Channel is in closed state */CST_ORIG_W4_SEC_COMP, /* Originator waits security clearence */CST_TERM_W4_SEC_COMP, /* Acceptor waits security clearence */CST_W4_L2CAP_CONNECT_RSP, /* Waiting for peer conenct response */CST_W4_L2CA_CONNECT_RSP, /* Waiting for upper layer connect rsp */CST_CONFIG, /* Negotiating configuration */CST_OPEN, /* Data transfer state */CST_W4_L2CAP_DISCONNECT_RSP, /* Waiting for peer disconnect rsp */CST_W4_L2CA_DISCONNECT_RSP /* Waiting for upper layer disc rsp */
} tL2C_CHNL_STATE;
每个状态都代表 L2CAP 信道的某个生命周期阶段
状态枚举值 | 生命周期阶段 | 状态含义(职责) | 典型进入条件 | 典型离开条件 |
---|---|---|---|---|
CST_CLOSED | 初始 / 终止阶段 | 信道未建立或已关闭;表示不活动状态。 | - 信道刚分配 - 信道断开完成 | - 发起连接请求(主动连接) - 收到连接请求(被动连接) |
CST_ORIG_W4_SEC_COMP | 连接准备阶段(主动) | 主动连接发起者正在等待安全认证完成。 | - 应用层请求连接 - 本地需要安全认证 | - 安全验证通过或失败 |
CST_TERM_W4_SEC_COMP | 连接准备阶段(被动) | 被动连接接收者正在等待安全认证完成。 | - 接收到连接请求 - 本地需要安全认证 | - 安全验证通过或失败 |
CST_W4_L2CAP_CONNECT_RSP | 建链阶段(主动) | 主动发起连接后,正在等待对端回应 L2CAP_CONNECT_RSP 。 | - 安全验证通过后,发送 L2CAP_CONNECT_REQ | - 收到 L2CAP_CONNECT_RSP (成功、失败、pending) |
CST_W4_L2CA_CONNECT_RSP | 建链阶段(被动) | 被动收到连接请求后,等待上层回应是否接受连接。 | - 收到 L2CAP_CONNECT_REQ - 安全验证通过 | - 上层调用 L2CA_ConnectRsp() 发送 L2CAP_CONNECT_RSP |
CST_CONFIG | 配置阶段 | 信道已建立,正在协商配置参数(MTU、QoS等)。 | - 双方连接确认无误 | - 本地和对端配置完成 |
CST_OPEN | 数据传输阶段 | 配置成功,信道处于“开放”状态,可进行数据收发。 | - 配置阶段完成(收发双方 config req/resp 都完成) | - 发起或收到断开请求 |
CST_W4_L2CAP_DISCONNECT_RSP | 断开阶段(主动) | 主动发起断开后,等待对端响应 L2CAP_DISCONNECT_RSP 。 | - 本地调用 L2CA_DisconnectReq() | - 收到对端 L2CAP_DISCONNECT_RSP |
CST_W4_L2CA_DISCONNECT_RSP | 断开阶段(被动) | 对端发起断开请求后,本地等待上层调用 L2CA_DisconnectRsp() 响应断开。 | - 收到对端 L2CAP_DISCONNECT_REQ | - 本地上层响应断开(调用 L2CA_DisconnectRsp() ) |
1. 生命周期流程简述(配合状态表)
- 连接前:
CST_CLOSED
- 主动连接发起:
CST_ORIG_W4_SEC_COMP
→CST_W4_L2CAP_CONNECT_RSP
- 被动连接接收:
CST_TERM_W4_SEC_COMP
→CST_W4_L2CA_CONNECT_RSP
- 配置阶段:
CST_CONFIG
- 连接建立完成:
CST_OPEN
- 断开过程(主动):
CST_W4_L2CAP_DISCONNECT_RSP
- 断开过程(被动):
CST_W4_L2CA_DISCONNECT_RSP
- 最终状态:
CST_CLOSED
2.CST_ORIG_W4_SEC_COMP/CST_TERM_W4_SEC_COMP 介绍
在 tL2C_CHNL_STATE
枚举中:
CST_ORIG_W4_SEC_COMP
:Originator waits security clearanceCST_TERM_W4_SEC_COMP
:Terminator (i.e. acceptor) waits security clearance
从命名和注释来看,这两个状态都属于 等待安全认证 的阶段,但它们分别用于 主动连接发起方(Originator) 和 被动连接接收方(Terminator),即:
状态 | 所属角色 | 场景说明 |
---|---|---|
CST_ORIG_W4_SEC_COMP | 主动发起方 | 我们调用 L2CA_ConnectReq() 发起连接请求,需要先进行安全认证 |
CST_TERM_W4_SEC_COMP | 被动接收方 | 对方发起连接请求,我们收到 L2CAP_CONNECT_REQ ,需要本地进行安全认证 |
1. 为什么一个是“主动”,一个是“被动”?
L2CAP 连接建立是一个主被动分工明确的过程,需要配合 SDP、Security Manager(安全认证)等模块完成握手过程。在安全认证阶段,根据发起连接的一方不同:
-
我们主动发起连接(Originator):我们触发连接流程,也就需要我们等待“本地的安全认证”完成之后再继续(如发送
L2CAP_CONNECT_REQ
)。- 对应状态:
CST_ORIG_W4_SEC_COMP
- 对应状态:
-
我们被动接收连接(Terminator):我们收到对方发来的
L2CAP_CONNECT_REQ
,但我们需要先验证是否允许连接(通过安全管理器),再回复L2CAP_CONNECT_RSP
。- 对应状态:
CST_TERM_W4_SEC_COMP
- 对应状态:
2. 状态之间的本质区别
状态名 | 安全认证触发方 | 安全认证后动作 |
---|---|---|
CST_ORIG_W4_SEC_COMP | 我们主动调用 | 安全认证成功 → 发送 L2CAP_CONNECT_REQ |
CST_TERM_W4_SEC_COMP | 我们被动接收 | 安全认证成功 → 继续处理 CONNECT_REQ 并向上层汇报 |
3. 举个现实场景类比
你可以把这个过程想象成:
-
你(车机)打电话给别人(手机):你要通过保安(安全模块)验证你能否联系到别人,验证中你就是 Originator。
-
别人(手机)给你打电话:你收到请求,但也得问一下保安(安全模块)你是否愿意接,验证中你就是 Terminator。
上面已经结束了所有的 l2cap 的事件, 接下来介绍, 触发状态切换的 事件都有哪些:
2. tL2CEVT 介绍
// system/stack/l2cap/l2c_int.h
typedef enum : uint16_t {/* Lower layer */L2CEVT_LP_CONNECT_CFM = 0, /* connect confirm */L2CEVT_LP_CONNECT_CFM_NEG = 1, /* connect confirm (failed) */L2CEVT_LP_CONNECT_IND = 2, /* connect indication */L2CEVT_LP_DISCONNECT_IND = 3, /* disconnect indication *//* Security */L2CEVT_SEC_COMP = 7, /* cleared successfully */L2CEVT_SEC_COMP_NEG = 8, /* procedure failed *//* Peer connection */L2CEVT_L2CAP_CONNECT_REQ = 10, /* request */L2CEVT_L2CAP_CONNECT_RSP = 11, /* response */L2CEVT_L2CAP_CONNECT_RSP_PND = 12, /* response pending */L2CEVT_L2CAP_CONNECT_RSP_NEG = 13, /* response (failed) *//* Peer configuration */L2CEVT_L2CAP_CONFIG_REQ = 14, /* request */L2CEVT_L2CAP_CONFIG_RSP = 15, /* response */L2CEVT_L2CAP_CONFIG_RSP_NEG = 16, /* response (failed) */L2CEVT_L2CAP_DISCONNECT_REQ = 17, /* Peer disconnect request */L2CEVT_L2CAP_DISCONNECT_RSP = 18, /* Peer disconnect response */L2CEVT_L2CAP_INFO_RSP = 19, /* Peer information response */L2CEVT_L2CAP_DATA = 20, /* Peer data *//* Upper layer */L2CEVT_L2CA_CONNECT_REQ = 21, /* connect request */L2CEVT_L2CA_CONNECT_RSP = 22, /* connect response */L2CEVT_L2CA_CONNECT_RSP_NEG = 23, /* connect response (failed)*/L2CEVT_L2CA_CONFIG_REQ = 24, /* config request */L2CEVT_L2CA_CONFIG_RSP = 25, /* config response */L2CEVT_L2CA_DISCONNECT_REQ = 27, /* disconnect request */L2CEVT_L2CA_DISCONNECT_RSP = 28, /* disconnect response */L2CEVT_L2CA_DATA_READ = 29, /* data read */L2CEVT_L2CA_DATA_WRITE = 30, /* data write */L2CEVT_TIMEOUT = 32, /* Timeout */L2CEVT_SEC_RE_SEND_CMD = 33, /* btm_sec has enough info to proceed */L2CEVT_ACK_TIMEOUT = 34, /* RR delay timeout */L2CEVT_L2CA_SEND_FLOW_CONTROL_CREDIT = 35, /* Upper layer credit packet \*//* Peer credit based connection */L2CEVT_L2CAP_RECV_FLOW_CONTROL_CREDIT = 36, /* credit packet */L2CEVT_L2CAP_CREDIT_BASED_CONNECT_REQ =37, /* credit based connection request */L2CEVT_L2CAP_CREDIT_BASED_CONNECT_RSP =38, /* accepted credit based connection */L2CEVT_L2CAP_CREDIT_BASED_CONNECT_RSP_NEG =39, /* rejected credit based connection */L2CEVT_L2CAP_CREDIT_BASED_RECONFIG_REQ =40, /* credit based reconfig request*/L2CEVT_L2CAP_CREDIT_BASED_RECONFIG_RSP =41, /* credit based reconfig response *//* Upper layer credit based connection */L2CEVT_L2CA_CREDIT_BASED_CONNECT_REQ = 42, /* connect request */L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP = 43, /* connect response */L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP_NEG = 44, /* connect response (failed)*/L2CEVT_L2CA_CREDIT_BASED_RECONFIG_REQ = 45, /* reconfig request */
} tL2CEVT;
1. L2CAP 事件对照表
事件名 | 值 | 分类 | 方向 | 说明 | 适用连接类型 |
---|---|---|---|---|---|
L2CEVT_LP_CONNECT_CFM | 0 | Lower Layer | 下层→L2CAP | 连接成功确认(主动发起方收到) | Classic & Credit-based |
L2CEVT_LP_CONNECT_CFM_NEG | 1 | Lower Layer | 下层→L2CAP | 连接失败确认 | Classic & Credit-based |
L2CEVT_LP_CONNECT_IND | 2 | Lower Layer | 下层→L2CAP | 接收到连接请求(被动方) | Classic & Credit-based |
L2CEVT_LP_DISCONNECT_IND | 3 | Lower Layer | 下层→L2CAP | 接收到断开连接通知 | Classic & Credit-based |
事件名 | 值 | 分类 | 方向 | 说明 | 适用连接类型 |
---|---|---|---|---|---|
L2CEVT_SEC_COMP | 7 | 安全 | 安全层→L2CAP | 安全认证完成 | Classic & Credit-based |
L2CEVT_SEC_COMP_NEG | 8 | 安全 | 安全层→L2CAP | 安全认证失败 | Classic & Credit-based |
事件名 | 值 | 分类 | Peer → L2CAP | 说明 | 适用连接类型 |
---|---|---|---|---|---|
L2CEVT_L2CAP_CONNECT_REQ | 10 | Peer信令 | ✅ | 接收到 L2CAP 连接请求 | Classic |
L2CEVT_L2CAP_CONNECT_RSP | 11 | Peer信令 | ✅ | 对连接请求的响应 | Classic |
L2CEVT_L2CAP_CONNECT_RSP_PND | 12 | Peer信令 | ✅ | 延迟连接响应(pending) | Classic |
L2CEVT_L2CAP_CONNECT_RSP_NEG | 13 | Peer信令 | ✅ | 连接失败响应 | Classic |
L2CEVT_L2CAP_CONFIG_REQ | 14 | Peer信令 | ✅ | 接收到配置请求 | Classic |
L2CEVT_L2CAP_CONFIG_RSP | 15 | Peer信令 | ✅ | 接收到配置响应 | Classic |
L2CEVT_L2CAP_CONFIG_RSP_NEG | 16 | Peer信令 | ✅ | 接收到配置失败响应 | Classic |
L2CEVT_L2CAP_DISCONNECT_REQ | 17 | Peer信令 | ✅ | 对方发起断开 | Classic |
L2CEVT_L2CAP_DISCONNECT_RSP | 18 | Peer信令 | ✅ | 对方响应断开请求 | Classic |
L2CEVT_L2CAP_INFO_RSP | 19 | Peer信令 | ✅ | Peer 信息响应(Info Response) | Classic |
L2CEVT_L2CAP_DATA | 20 | 数据 | ✅ | 接收到 peer 数据帧 | Classic & Credit-based |
事件名 | 值 | 分类 | 应用层→L2CAP | 说明 | 适用连接类型 |
---|---|---|---|---|---|
L2CEVT_L2CA_CONNECT_REQ | 21 | 上层 | ✅ | 应用层发起连接请求 | Classic |
L2CEVT_L2CA_CONNECT_RSP | 22 | 上层 | ✅ | 应用层同意连接 | Classic |
L2CEVT_L2CA_CONNECT_RSP_NEG | 23 | 上层 | ✅ | 应用层拒绝连接 | Classic |
L2CEVT_L2CA_CONFIG_REQ | 24 | 上层 | ✅ | 应用层配置请求 | Classic |
L2CEVT_L2CA_CONFIG_RSP | 25 | 上层 | ✅ | 应用层配置响应 | Classic |
L2CEVT_L2CA_DISCONNECT_REQ | 27 | 上层 | ✅ | 应用层发起断开 | Classic |
L2CEVT_L2CA_DISCONNECT_RSP | 28 | 上层 | ✅ | 应用层响应断开 | Classic |
L2CEVT_L2CA_DATA_READ | 29 | 上层 | ✅ | 上层读取到数据 | Classic & Credit-based |
L2CEVT_L2CA_DATA_WRITE | 30 | 上层 | ✅ | 上层发送数据 | Classic & Credit-based |
事件名 | 值 | 分类 | 系统 | 说明 | 适用连接类型 |
---|---|---|---|---|---|
L2CEVT_TIMEOUT | 32 | 定时器 | ⏰ | 超时事件 | Classic & Credit-based |
L2CEVT_SEC_RE_SEND_CMD | 33 | 安全 | 安全层 | 安全认证完成,可重新发送命令 | Classic & Credit-based |
L2CEVT_ACK_TIMEOUT | 34 | 定时器 | ⏰ | 等待 ACK 超时 | Classic |
2. Credit-based L2CAP 专用事件
事件名 | 值 | 分类 | 说明 | 方向 | 适用 |
---|---|---|---|---|---|
L2CEVT_L2CA_SEND_FLOW_CONTROL_CREDIT | 35 | 上层 | 本地应用补充 credit | 应用→L2CAP | Credit-based |
L2CEVT_L2CAP_RECV_FLOW_CONTROL_CREDIT | 36 | Peer | 接收到对方发送的 credit | Peer→L2CAP | Credit-based |
L2CEVT_L2CAP_CREDIT_BASED_CONNECT_REQ | 37 | Peer | Credit-based 连接请求 | Peer→L2CAP | Credit-based |
L2CEVT_L2CAP_CREDIT_BASED_CONNECT_RSP | 38 | Peer | Credit-based 连接成功响应 | Peer→L2CAP | Credit-based |
L2CEVT_L2CAP_CREDIT_BASED_CONNECT_RSP_NEG | 39 | Peer | Credit-based 连接失败响应 | Peer→L2CAP | Credit-based |
L2CEVT_L2CAP_CREDIT_BASED_RECONFIG_REQ | 40 | Peer | 重新配置请求 | Peer→L2CAP | Credit-based |
L2CEVT_L2CAP_CREDIT_BASED_RECONFIG_RSP | 41 | Peer | 重新配置响应 | Peer→L2CAP | Credit-based |
L2CEVT_L2CA_CREDIT_BASED_CONNECT_REQ | 42 | 上层 | 应用层发起 Credit-based 连接 | 应用→L2CAP | Credit-based |
L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP | 43 | 上层 | 应用层接受连接 | 应用→L2CAP | Credit-based |
L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP_NEG | 44 | 上层 | 应用层拒绝连接 | 应用→L2CAP | Credit-based |
L2CEVT_L2CA_CREDIT_BASED_RECONFIG_REQ | 45 | 上层 | 应用发起 reconfig 请求 | 应用→L2CAP | Credit-based |
3. 单个状态梳理
上面我已经帮大家梳理出了 状态和事件。 但是还是不够清晰。 接下来,我将 按照 某一个状态, 接收那些事件(信号) , 需要 执行那些 操作(action), 操作执行完毕后,应该切换到 那个状态。在梳理一次。
1. Classic L2CAP 状态机分析
状态:CST_CLOSED
Event | 说明 | 条件 | Action | Next State |
---|---|---|---|---|
L2CEVT_L2CA_CONNECT_REQ | 上层发起 Classic L2CAP 连接请求 | 需要分配 local CID 和 peer CID | 启动物理连接(l2cu_create_conn) | CST_ORIG_W4_SEC_COMP |
L2CEVT_LP_CONNECT_IND | 接收到物理连接建立请求 | —— | 回复 L2CAP connect indication,启动安全验证 | CST_TERM_W4_SEC_COMP |
状态:CST_ORIG_W4_SEC_COMP
Event | 说明 | 条件 | Action | Next State |
L2CEVT_SEC_COMP | 安全检查成功 | —— | 发送 L2CAP connect request | CST_W4_L2CAP_CONNECT_RSP |
L2CEVT_SEC_COMP_NEG | 安全检查失败 | —— | 通知上层失败 | CST_CLOSED |
状态:CST_TERM_W4_SEC_COMP
Event | 说明 | 条件 | Action | Next State |
L2CEVT_SEC_COMP | 安全检查成功 | —— | 通知上层有连接请求 | CST_W4_L2CA_CONNECT_RSP |
L2CEVT_SEC_COMP_NEG | 安全检查失败 | —— | 回复 connect negative | CST_CLOSED |
状态:CST_W4_L2CAP_CONNECT_RSP
Event | 说明 | 条件 | Action | Next State |
L2CEVT_L2CAP_CONNECT_RSP | 接收到 peer 的 connect response(成功) | —— | 发送 config request | CST_CONFIG |
L2CEVT_L2CAP_CONNECT_RSP_NEG | peer 拒绝连接 | —— | 通知上层失败 | CST_CLOSED |
L2CEVT_L2CAP_CONNECT_RSP_PND | peer 暂时无法接收 | —— | 等待 | 保持不变 |
状态:CST_W4_L2CA_CONNECT_RSP
Event | 说明 | 条件 | Action | Next State |
L2CEVT_L2CA_CONNECT_RSP | 上层接受连接 | —— | 发送 connect response + config request | CST_CONFIG |
L2CEVT_L2CA_CONNECT_RSP_NEG | 上层拒绝连接 | —— | 回复 connect negative | CST_CLOSED |
状态:CST_CONFIG
Event | 说明 | 条件 | Action | Next State |
L2CEVT_L2CAP_CONFIG_REQ | peer 配置本地 | 接收参数并记录 | 回复 config rsp | 根据配置完成情况维持或进入 OPEN |
L2CEVT_L2CAP_CONFIG_RSP | peer 回应我方配置 | —— | 检查是否都完成 | 若都完成 → CST_OPEN ,否则保持 |
L2CEVT_L2CAP_CONFIG_RSP_NEG | peer 拒绝配置 | —— | 处理失败并通知 | CST_CLOSED |
状态:CST_OPEN
Event | 说明 | 条件 | Action | Next State |
L2CEVT_L2CAP_DISCONNECT_REQ | peer 请求断开 | —— | 回复 disconnect rsp | CST_CLOSED |
L2CEVT_L2CA_DISCONNECT_REQ | 上层请求断开 | —— | 发送 disconnect req | CST_W4_L2CAP_DISCONNECT_RSP |
L2CEVT_L2CAP_DATA | peer 数据到达 | —— | 调用上层回调 | 保持 |
状态:CST_W4_L2CAP_DISCONNECT_RSP
Event | 说明 | 条件 | Action | Next State |
L2CEVT_L2CAP_DISCONNECT_RSP | peer 回复断开成功 | —— | 通知上层 | CST_CLOSED |
状态:CST_W4_L2CA_DISCONNECT_RSP
Event | 说明 | 条件 | Action | Next State |
L2CEVT_L2CA_DISCONNECT_RSP | 上层确认断开 | —— | 清理资源 | CST_CLOSED |
2. Credit-based L2CAP 状态机分析
状态:CST_CLOSED
Event | 说明 | 条件 | Action | Next State |
L2CEVT_L2CA_CREDIT_BASED_CONNECT_REQ | 上层请求 credit-based 连接 | 分配 local CID,初始化 FCR 模式 | 发送 connect req,启动物理连接 | CST_ORIG_W4_SEC_COMP |
L2CEVT_L2CAP_CREDIT_BASED_CONNECT_REQ | peer 请求 credit-based 连接 | 检查服务 UUID 是否支持 | 启动安全验证流程 | CST_TERM_W4_SEC_COMP |
状态:CST_ORIG_W4_SEC_COMP
Event | 说明 | 条件 | Action | Next State |
L2CEVT_SEC_COMP | 安全验证成功 | —— | 发送 CB connect 请求 | CST_W4_L2CAP_CONNECT_RSP |
L2CEVT_SEC_COMP_NEG | 安全验证失败 | —— | 通知上层失败 | CST_CLOSED |
状态:CST_TERM_W4_SEC_COMP
Event | 说明 | 条件 | Action | Next State |
L2CEVT_SEC_COMP | 安全验证成功 | —— | 通知上层(connect indication) | CST_W4_L2CA_CONNECT_RSP |
L2CEVT_SEC_COMP_NEG | 安全验证失败 | —— | 回复 connect negative | CST_CLOSED |
状态:CST_W4_L2CAP_CONNECT_RSP
Event | 说明 | 条件 | Action | Next State |
L2CEVT_L2CAP_CREDIT_BASED_CONNECT_RSP | peer 同意连接 | —— | 通知上层成功 | CST_OPEN |
L2CEVT_L2CAP_CREDIT_BASED_CONNECT_RSP_NEG | peer 拒绝连接 | —— | 通知上层失败 | CST_CLOSED |
状态:CST_W4_L2CA_CONNECT_RSP
Event | 说明 | 条件 | Action | Next State |
L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP | 上层接受连接 | —— | 回复 connect rsp | CST_OPEN |
L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP_NEG | 上层拒绝连接 | —— | 回复 connect negative | CST_CLOSED |
状态:CST_OPEN
Event | 说明 | 条件 | Action | Next State |
L2CEVT_L2CA_DISCONNECT_REQ | 上层请求断开 | —— | 发送 disconnect req | CST_W4_L2CAP_DISCONNECT_RSP |
L2CEVT_L2CAP_DISCONNECT_REQ | peer 请求断开 | —— | 回复 disconnect rsp | CST_CLOSED |
L2CEVT_L2CAP_DATA | peer 数据 | —— | 通知上层 | 保持 |
L2CEVT_L2CAP_RECV_FLOW_CONTROL_CREDIT | 接收信用额度 | —— | 更新 flow control | 保持 |
L2CEVT_L2CA_SEND_FLOW_CONTROL_CREDIT | 上层发出信用 | —— | 发送 credit | 保持 |
3. Credit-based Reconfig 状态
状态:CST_OPEN
Event | 说明 | 条件 | Action | Next State |
L2CEVT_L2CAP_CREDIT_BASED_RECONFIG_REQ | peer 请求 Reconfig | —— | 回复 reconfig rsp | 保持 |
L2CEVT_L2CAP_CREDIT_BASED_RECONFIG_RSP | peer 回应我方 Reconfig 请求 | —— | 更新配置 | 保持 |
L2CEVT_L2CA_CREDIT_BASED_RECONFIG_REQ | 上层请求 Reconfig | —— | 发送 reconfig 请求 | 保持 |
4. 小结
本篇开篇介绍了 FSM模型,详细介绍了 tL2C_CHNL_STATE 和 tL2CEVT 。 单个状态下,在接收到事件后,如何切换到下一个状态。也帮助大家梳理了一遍。 大家应该对 l2cap 有一个大体了了解了。 我详细有了这些梳理。 我们在对照代码来看, 应该不是特别吃力了。