目录
- 前言
- 一、逻辑链路层及自适应协议层(L2CAP)
- 二、常用的L2CAP术语
- 三、L2CAP的工作模式
- 四、L2CAP通道
- 五、L2CAP帧类型
- 六、Fragmentation/Recombination
- 七、Segmentation/Reassembly
- 八、L2CAP MTU
- 九、Controller to Host Flow Control
- 十、总结
前言
本文记录对蓝牙协议栈逻辑链路及自适应协议层的学习还有后面如何在TI的蓝牙芯片上去实现L2CAP CoC的数据透传,为什么用L2CAP CoC进行数据透传呢?因为它每次透传的数据量远远能比GATT大,如果堆够用的话可以传输64KB,而GATT数据透传限于ATT-MTU,最多每次能传输的也就247个字节。
一、逻辑链路层及自适应协议层(L2CAP)
该层属于主机的内容,位于HCI层的上一层,我们可以看下下面这个结构图1-1加强一下印象:
它的作用是为主机的GAP, GATT, SM, Application和链路层之间搭建起通信的桥梁,因此它有不可或缺的协议复用能力。同时,在主机和协议栈数据交换中它还有数据分段和重组的能力。所有从主机或者APP发出的数据都在这一层被封装成L2CAP packet。L2CAP允许更高层的协议(比如GATT、SM)和APP去发送和接收上层data packets(L2CAP服务数据单元,SDU),最多能传输64KB。L2CAP也允许每个通道的流控制和重发机制。
二、常用的L2CAP术语
L2CAP Channel: L2CAP通道,对端设备两节点之间的逻辑连接,用它们的信道标识符(CIDs)做通道的区分。
SDU: 服务数据单元,L2CAP与上层交换的数据包,它不包含L2CAP的帧头。
PDU: 协议数据单元,包括了L2CAP协议信息域、控制信息、上层信息数据,这个数据包就包含了L2CAP的帧头。一个SDU可能被分割成多个PDU进行传输。
MTU: 最大数据传输单元,上层应用可以接收的payload最大字节数,注意这个跟ATT的MTU是不一样的。
MPS: L2CAP可以接收的payload最大字节数。
Credit: 本蓝牙设备可以接收的LE帧数量。Credits取值范围是1~65535,在两个设备之间使用流控制。
L2CAP Basic Header: 为每个PDU预先准备的L2CAP协议信息。它包括了CID和长度。
PSM: 协议服务复用器,占用两个字节,用于定义L2CAP信道数据的解析。有动态的PSM和固定的PSMs。固定的PSMs是由SIG定义的,而动态的PSMs可以由GATT发现。
Fragmentation/Reconbination: 分包重组,分包(分段)是将单个L2CAP PDU分解成更小的数据段以供发送器发送的过程。重组是控制器将片段重新组装成完整的L2CAP PDU的过程。分包重组是由控制器实现的,并且基于LE数据长度扩展特征(可以看下Local supported feature)。
Segmentation/Reassembly: 分段是将单个L2CAP SDU分解成多个成为SDU段的L2CAP数据包的过程。在接收侧按照与此操作相反的方式进行重新组装。每个段都封装在一个适当的L2CAP报头中。分段和重组都是由L2CAP处理,并且对上下层都是透明的。
注:SDU的最大字节数等于协议栈里边定义的L2CAP_SDU_SIZE。
代码如下(示例):
三、L2CAP的工作模式
蓝牙5协议栈L2CAP支持两种不同的工作模式:Basic L2CAP Mode和LE Credit Based Flow Control Mode。
四、L2CAP通道
L2CAP由3种通道类型:Connection-Oriented、Connectionless data、L2CAP signaling。L2CAP的每个端点由信道标识符CID引用。Connectionless data channels不适用于蓝牙5协议栈。另外,通道可以划分为固定通道和动态通道。
固定通道执行特殊的L2CAP功能,使用的CIDs范围在0x0001和0x003F之间。每个固定通道的特征(比如MTU)都是独立的。
下面罗列了协议栈和应用程序可以使用的CIDs
CID | Description | Usage |
---|---|---|
0x0004 | Attribute Protocol(ATT) | Sending ATT information |
0x0005 | LE Signaling Channel | Sending L2CAP commands |
0x0006 | Security Manager Protocol(SMP) | Sending Pairing/Security Information |
0x0040~0x007F | Dynamically Allocated | LE Credit Based Flow control packets |
比如,GATT层的数据交互使用的通道是0x0004,SMP(配对、安全)使用0x0006通道。应用程序不能直接访问ATT、SMP、信号通道,因为它们已经被相关的Host Layers使用了。上面提到的LE signaling channel使用在L2CAP连接参数更新流程、使用在动态通道上建立LE Credit基本连接,以及使用在交换Credits上。
动态分配的通道ID用于识别逻辑链路和本地端点。本地端点的取值范围是0x0040到0xFFFF。这个本地端点用于面向连接的L2CAP通道(COC,全称Connection-orientated channel)。这些动态分配的通道是可以被应用程序访问和管理的,以便在L2CAP-COC上定义自己的协议。L2CAP通道是双向的,类似于套接字。动态分配一个通道,有以下3个参数:PSM、MTU、CID。前面提到,固定的PSMs是由BLE SIG定义的,它们的范围是0x00010x007F。动态的PSMs的范围是0x00800x00FF。PSMs可在GATT服务端设备上保持固定,而GATT客户端可以从GATT服务端中获取PSM。
五、L2CAP帧类型
在蓝牙协议栈里面有两个L2CAP帧类型。
(1)Basic Frame:基本帧,在基本模式下由固定通道使用
(2)LE information Frame:低功耗信息帧,用于LE credit基本流控制模式下的动态通道。
注:L2CAP处理来自主机或者APP的SDU数据帧。
下面是不同帧类型的帧头区别:
L2CAP Frame Type | Header Size | Header Contents |
---|---|---|
Basic frame | 4 | Length:2 octets CID: 2 octets |
LE information frame | 6 | Length:2 octets CID: 2 octets SDU length: 2 octets |
六、Fragmentation/Recombination
从L2CAP的角度来看,所有数据包都作为完整的数据包发送到控制器和从控制器接收。这也意味着分段/重组(如果使能了LE DATA Length Extension)是由控制器执行的,并且对L2CAP不可见。当使用分段时,较大的数据包将被拆分成多个LL数据包然后在对端设备的LL层进行重组(LL层属于控制器的一部分)。
七、Segmentation/Reassembly
当工作在基本模式下,L2CAP不执行分段或者重组。但是,动态通道工作在LE Credit基本流控模式下,是有可能发生数据包的分段和重组的。
八、L2CAP MTU
L2CAP MTU是L2CAP层能够处理的最大数据字节大小。然而,L2CAP使用的MTU是不同的,取决于模式和通道类型。
(1)信号通道会使用L2CAP_SIG_MTU_SIZE
(2)固定通道数据包的MTU限定最大是MAX_PDU_SIZE - L2CAP_HDR_SIZE
(3)COC数据包的最大字节数取决于PSM的MTU,同时被L2CAP_SDU_SIZE限制
当对于固定通道,MTU由更高级别的协议(如ATT)定义。在COC上,MTU受L2CAP_SDU_SIZE和对端设备支持的MTU的最小值约束。
九、Controller to Host Flow Control
MAX_NUM_PDU定义了一次可以入列到控制器的TX数据包的最大个数。尝试发送更多数据包将导致调用的API接口返回失败,并且数据包也没有真正入列到控制器中。
应用程序可能不清楚在给定时间内有多少个数据包在等着发送,因此我们在写代码调用API的时候最好就是检查API的返回值。还有,一般L2CAP有流控制通知功能,我们可以使用它来提高L2CAP数据通讯的效率。这里那ti的蓝牙芯片来举例,可以使用L2CAP_RegisterFlowCtrlTask()这个API使能流控,这样协议栈会有事件L2CAP_NUM_CTRL_DATA_PKT_EVT通知APP可发送的数据包数量,这个事件会在每次有新的缓冲区可用时触发。
十、总结
使用L2CAP总结如下:
主机和从机都可以请求建立L2CAP COC通道。一旦L2CAP连接建立成功之后,协议栈会发送L2CAP_SIGNAL_EVENT到应用层,具体的事件是L2CAP_CHANNEL_ESTABLISHED_EVT,这个是双方都会触发的事件。主机和从机双方需要交换L2CAP Credits。连接建立之后,设备(双方)可以分发Credit。最后,我们就可以利用L2CAP CoC通道发送数据了,可以调用接口L2CAP_SendSDU()这个API发送数据,当成功将数据发送到空中之后,协议栈会给应用层发送L2CAP_SIGNAL_EVENT,具体事件是L2CAP_SEND_SDU_DONE_EVT。而接收数据呢?当蓝牙接收到数据之后,协议栈会给应用层发送L2CAP_DATA_EVENT事件,并携带接收到的数据。