L2CAP 详解
本文主要介绍了 L2CAP 在 BLE 里的内容,包括 L2CAP 里 Protocol multiplexing 的概念,BLE 使用的两种操作模式:Basic L2CAP Mode 和 LE Credit Based Flow Control Mode ,最后还包含 SDU 和 PDU 的分段重组。
Protocol multiplexing
两个蓝牙设备建立连接后,在 Controller 层面就建立起了一条逻辑链路(Logic Link),BLE 设备之间的逻辑链路称为 LE-U 。
BLE 设备有很多种类的应用消息需要在这条逻辑链路上传输,因此需要一种机制来识别不同的应用协议,L2CAP 的协议多路复用就是用来解决这个问题的。L2CAP 在逻辑链路上进一步地抽象出了信道(channel)的概念,每个信道的两端都由 CID(Channel ID)标识,不同的应用协议绑定不同的 CID。如果对 TCP/IP 协议比较熟悉的话,可以将 CID 与 TCP/IP 协议里的端口对比理解,网络中的 IP 数据包根据 IP 地址找到了主机,然后又根据不同的端口号,进一步找到接收该数据包的进程。蓝牙协议也是如此,L2CAP 接收到数据包后,根据 CID 将数据包分发给不同的应用协议。
TCP/IP 里有一些默认的端口号,例如 80 代表 HTTP 协议,20 代表 SSH 协议,蓝牙协议里也规定了一些默认端口号,例如 0x0004 代表 ATT 协议,0x0006 代表 SM 协议。这也就是说,应用协议如果想接收 ATT 协议数据,监听自身 0x0004 的 CID 即可,从该 CID 上接收的数据就是 ATT 协议数据。
Mode
L2CAP 根据 Controller 类型的不同,会分别进入不同的模式工作,BLE 设备只会进入下述这两种模式。
Basic L2CAP Mode
Basic L2CAP Mode 是默认模式,也是 BLE 设备最常用的模式,一个信道的两端设备直接收发数据,没有流控,没有重传,是最简单的模式。其数据包格式(B-frame)如下:
- Length:后续负载(Information payload)的字节大小。
- Channel ID:该数据包的目的端口。
- Information payload:负载 PDU 数据。
负载理论上可拥有 0 ~ 65535 个字节,实际上不能超过配置的 MTU(Maximum Transmission Unit),最小的 MTU 是 23 字节。
LE Credit Based Flow Control Mode
Credit Based Flow Control Mode 的数据包格式(K-frame)如下:
- Length:整个 L2CAP PDU 的字节大小减 4,不包含 Length 字段和 Channel ID 字节。
- Channel ID:该数据包的目的端口。
- L2CAP SDU Length:由于 L2CAP 传输的 PDU 大小不能超过配置的 MPS(Maximum PDU payload Size),因此若有大于 MPS 的上层应用服务数据 SDU(Service Data Unit)需要传输,需要将 SDU 分段成多个 K-frame 连续发送。第一个发送的 K-frame 携带 L2CAP SDU Length 字段,代表该 SDU 的全部大小;后续的 K-frame 不携带该字段。
- Information Payload:负载 PDU 数据,不能大于 MPS 。
该模式里的两个 CID 想建立连接,需要发送 L2CAP 的信号数据包(Signaling packet),在 BLE 里占用 0x0005 CID 。
该模式里的流控是通过信用值(Credit)来实现的,一个设备想和另一个设备建立 L2CAP 连接,需发送 L2CAP_LE_CREDIT_BASED_CONNECTION_REQ 请求,请求里包含一个 Initial Credits 值,代表对端可以发送的 K-frame 数量。另一个设备返回的 L2CAP_LE_CREDIT_BASED_CONNECTION_RSP 响应里,也包含一个 Initial Credits 值,这样建立连接的两个设备都拥有了自己可以发送的 K-frame 数量。
设备每发送一个 K-frame,信用值减一,若信用值为 0,则代表该设备不能发送 K-frame 了,也就达到了流控的目的。对端设备可以在任意时刻发送 L2CAP_FLOW_CONTROL_CREDIT_IND ,该数据包里有一个 Credits 值,代表允许对端继续发送的 K-frame 数量。收到该数据包后,设备自身的信用值需要加上该 Credits 值。
对端设备的信用值(允许发送 K-frame 数量)完全由本地设备控制,从而达到流控的目的。
分段重组
Host 端在初始化时一般会发送 HCI_LE_Read_Buffer_Size command 命令获取 Controller 存储 ACL 数据的缓冲区大小 size,若 L2CAP 通过 HCI 发送的 ACL 数据大于 size,则就需要将 L2CAP 数据分段,并利用 HCI ACL 里的 PB Flag 字段标识,0x00 代表第一个分段包,0x01 代表连续的分段包。
Host 从 Controller 接收 ACL 数据包时也需要进行重组,若 PB Flag 值为 0x10,则代表第一个分段包,0x01 代表连续的分段包。