2. L2CAP信道类型与CID分配机制
L2CAP(Logical Link Control and Adaptation Protocol)位于蓝牙协议栈的基带层之上,负责向上层协议(如RFCOMM、ATT、SMP)提供逻辑信道抽象。根据连接方式与用途,L2CAP信道分为三大类:面向连接信道(COC)、无连接信道(CL)和固定信道。每类信道通过唯一的信道标识符(CID)进行寻址与区分。
核心要点:CID是L2CAP层实现多路复用的关键机制。每个信道在本地和远端各分配一个CID,构成
(本地CID,远端CID)二元组,唯一标识一条逻辑信道。
2.1 信道类型总览
下表从连接建立方式、典型用途、CID范围三个维度对比三类信道:
| 信道类型 | 连接建立方式 | 典型用途 | CID范围 |
|---|---|---|---|
| 面向连接信道(COC) | 动态建立(L2CAP CONNECTION REQ/RSP) | RFCOMM、BNEP、EATT | 0x0040 – 0xFFFF(动态分配) |
| 无连接信道(CL) | 无需建立,直接发送数据报 | 广播数据、L2CAP组播 | 0x0002(固定CID) |
| 固定信道 | 预分配,L2CAP层初始化时即存在 | Signaling、SMP、ATT | 0x0001 – 0x003F(固定CID) |
开发提示:在Linux内核源码
net/bluetooth/l2cap_core.c 中,l2cap_chan_create() 函数负责为COC信道动态分配CID,分配算法采用线性扫描,从0x0040开始查找未使用的CID。
2.2 面向连接信道(COC)
COC是L2CAP层最常用的信道类型,提供可靠的、面向连接的数据传输服务。其生命周期包含四个阶段:建立、配置、数据传输、关闭。
2.2.1 COC建立流程
COC的建立通过L2CAP信令信道(CID 0x0001)交换控制帧完成。典型流程如下:
- 请求端发送
L2CAP_CONNECTION_REQ,携带PSM(Protocol/Service Multiplexer)和本地CID。 - 响应端收到后,分配本地CID,回复
L2CAP_CONNECTION_RSP,包含结果码和远端CID。 - 双方进入
CONFIG阶段,交换L2CAP_CONFIG_REQ/RSP,协商MTU、Flush Timeout等参数。 - 配置完成后,信道进入
OPEN状态,开始数据传输。
性能陷阱:COC建立过程需要2次RTT(连接请求/响应 + 配置请求/响应)。在低延迟场景(如LE Audio)中,建议使用EATT(Enhanced ATT)的多信道COC模式,通过并行建立多个COC减少连接建立开销。
2.2.2 COC CID分配机制
COC的CID由L2CAP层动态分配,分配规则如下:
- 起始值:0x0040(BR/EDR)或 0x0040(LE,但LE COC从0x0040开始,与BR/EDR共用范围)。
- 分配方向:每个方向独立分配。发起端分配本地CID,响应端分配远端CID。
- 回收复用:信道关闭后,CID标记为可用,后续新信道可复用该CID。
- 范围限制:0x0040 – 0xFFFF,理论上最多支持 65472 个COC信道(实际受内存和控制器资源限制)。
2.3 无连接信道(CL)
无连接信道使用固定CID 0x0002,提供不可靠的、无连接的数据报服务。其特点包括:
- 无需建立连接:发送方直接构造L2CAP帧,目标CID固定为0x0002。
- 广播支持:可用于向多个设备发送广播数据(如蓝牙信标)。
- 无确认机制:数据包可能丢失,上层协议需自行处理可靠性。
- 协议数据单元(PDU)格式:包含协议/服务复用器(PSM)字段,用于区分上层协议。
应用场景:无连接信道常用于蓝牙低功耗(BLE)的广播数据和基于L2CAP的组播应用。在Linux中,通过
L2CAP_CONNINFO 套接字选项可查询CL信道状态。
2.4 固定信道
固定信道在L2CAP层初始化时即预分配,无需动态建立。其CID范围固定为 0x0001 – 0x003F,每个CID对应一个特定的协议或功能。
2.4.1 主要固定信道
| CID | 信道名称 | 用途 | 协议 |
|---|---|---|---|
| 0x0001 | L2CAP Signaling | L2CAP控制帧交换(连接、配置、断开) | L2CAP |
| 0x0002 | 无连接信道(CL) | 无连接数据报 | L2CAP |
| 0x0003 | AMP Manager | AMP(Alternate MAC/PHY)管理 | AMP |
| 0x0004 | BR/EDR Security Manager (SMP) | 配对与密钥分发(BR/EDR) | SMP |
| 0x0005 | LE Security Manager (SMP) | 配对与密钥分发(LE) | SMP |
| 0x0006 | ATT | 属性协议(Attribute Protocol) | ATT |
| 0x0007 | LE Signaling | LE L2CAP控制帧 | L2CAP |
| 0x0008 | LE Security Manager (SMP) - LE | LE安全管理器 | SMP |
内核视角:在Linux内核中,固定信道的初始化在
l2cap_chan_create() 中通过 l2cap_fixed_chan_register() 完成。每个固定信道对应一个 struct l2cap_chan 实例,其 chan_type 字段设置为 L2CAP_CHAN_FIXED。
2.4.2 固定信道与COC的对比
| 对比维度 | 固定信道 | COC |
|---|---|---|
| CID分配 | 预分配(0x0001 – 0x003F) | 动态分配(0x0040 – 0xFFFF) |
| 建立开销 | 无(初始化即存在) | 需要连接/配置信令交换 |
| 可靠性 | 取决于底层(固定信道本身无重传) | 支持重传与流控 |
| 典型延迟 | 低(无建立延迟) | 中(2次RTT建立延迟) |
| 适用场景 | 控制信令、安全协议、属性协议 | 数据流、文件传输、音频流 |
2.5 CID分配机制详解
CID是L2CAP层实现多路复用的核心标识符。每个L2CAP信道在本地和远端各有一个CID,构成(本地CID,远端CID)二元组,唯一标识一条逻辑信道。
2.5.1 CID空间划分
CID是一个16位无符号整数,其空间划分如下:
- 0x0000:保留,未使用。
- 0x0001 – 0x003F:固定信道CID,由蓝牙规范预定义。
- 0x0040 – 0xFFFF:动态CID,用于COC信道,由L2CAP层动态分配。
2.5.2 动态CID分配算法
Linux内核中,动态CID分配采用线性扫描 + 位图管理策略:
/* l2cap_core.c - CID分配核心逻辑 */
static u16 l2cap_alloc_cid(struct l2cap_chan *chan)
{
u16 cid;
/* 从0x0040开始扫描 */
for (cid = L2CAP_CID_DYN_START; cid < L2CAP_CID_DYN_END; cid++) {
/* 检查CID是否已被使用 */
if (!__l2cap_get_chan_by_scid(chan->conn, cid)) {
chan->scid = cid;
return cid;
}
}
return 0; /* 分配失败 */
}
性能调优点:线性扫描算法在CID使用率高时(如同时存在数千个COC信道),分配延迟会线性增长。优化方案包括:
① 使用位图(bitmap)记录空闲CID,分配复杂度降为O(1)。
② 引入CID缓存池,预分配常用CID范围。
③ 在多信道EATT场景中,建议将CID分配与信道优先级关联,优先分配低CID值。
① 使用位图(bitmap)记录空闲CID,分配复杂度降为O(1)。
② 引入CID缓存池,预分配常用CID范围。
③ 在多信道EATT场景中,建议将CID分配与信道优先级关联,优先分配低CID值。
2.6 信道类型选择策略
在实际开发中,选择哪种信道类型取决于应用需求:
| 应用场景 | 推荐信道类型 | 理由 |
|---|---|---|
| 属性读写(GATT) | 固定信道(ATT,CID=0x0006) | 低延迟,无需建立连接 |
| 安全配对 | 固定信道(SMP,CID=0x0005/0x0008) | 控制信令,固定CID保证可达 |
| 文件传输 | COC | 需要可靠传输和流控 |
| 音频流 | COC(EATT多信道) | 低延迟、多信道并行传输 |
| 广播数据 | 无连接信道(CL,CID=0x0002) | 无需建立连接,支持多设备 |
2.7 性能调优要点
调优总结:CID分配效率直接影响L2CAP层吞吐量。在多信道并发场景(如EATT同时使用5个COC信道),建议:
① 使用位图分配替代线性扫描。
② 将CID分配与NUMA节点绑定,减少跨内存访问延迟。
③ 在中断上下文中避免CID分配操作,使用工作队列异步处理。
① 使用位图分配替代线性扫描。
② 将CID分配与NUMA节点绑定,减少跨内存访问延迟。
③ 在中断上下文中避免CID分配操作,使用工作队列异步处理。