一 链路层的数据结构
在链路层上传输的数据格式如下图所示:
广播报文和数据报文都包含 Preamble(前导码), AccessAddress(接入地址),PDU(数据),CRC。其中Constant Tone Extension是可选的。
- 前导码
- 接入地址
- PDU
- Header
- Length
- payload
- MIC(可选)
- CRC
- Constant Tone Extension(可选)
1 前导码
报文最开始的8bit是01010101(0x55)或者10101010(0xAA)。这是很简单的交替序列。接收机可以用它来配置自动增益控制,以及确定 “0”、"1"比特所使用的的频率。
这段序列之所以非常重要,是因为芯片必须能够应对输入信号强度的可能范围。接收机需要应付 -10dBm到-90dBm的信号强度,也就是80dB的动态范围。从接收机的角度讲,也就是1pW到0.1pW的能量。自动增益控制器必须检测出输入信号的能量等级,并调整增益,使得信号刚好处于接收机能够轻容工作的范围之中。
2M PHY的前导码为2个字节
2 接入地址(Access Address)
接入地址的第一个比特决定了前导是01010101还是10101010。如果接入地址的第一个比特为0,则使用01010101序列;如果是1则使用10101010序列。这保证了任一报文的前9个比特都是交替的,即要么为101010101要么为010101010。
LL层使用接入地址来区分当前发送的数据是广播包还是数据包,广播包接入地址固定为0x8E89BED6,数据包使用是在连接时生成的接入地址。
- 连接时主设备分配一个32位的随机数并发送给从设备,这就是接入地址。
- 接入地址被用来表示一个连接,在连接期间保持不变。
- 对于同一个从设备,断开从新连接接入地址也会重新生成。
3 数据(PDU)
根据使用的通道不同可以分为广播报文和数据报文。广播报文在广播通道上传输(37, 38, 39), 数据报文在数据通道上传输(0到36)。
二 广播通道上的PDU
广播通道上的PDU格式:
- 两个字节的header
- 1到255字节数据
广播通道上的Header格式
- PDU Type(4bit) 广播报文类型
- RFU(1bit) 未使用
- ChSel(1bit) 未使用
- TxAdd(1bit) 发送地址类型
- 0: public地址
- 1: 随机地址
- 静态随机地址,地址最高两位为11
- 私有随机地址
- 不可解析私有随机地址,地址的最高两位为00
- 可解析私有随机地址,地址的最高两位为1(最高位)0(次高位)
- RxAdd(1bit) 接收地址类型,规则和TxAdd一样
- length(8bit),长度,取值范围1到255
1 广播报文的类型
- ADV_IND,通用广播,外设发出的一般称为通用广播,广播中包含mac地址设备名字等信息。
- ADV_DERECT_IND,定向广播,某个外设在工作的时候就希望某个主设备来连接它,就可以向这个中央设备发起一个定向广播。
- ADV_NONCONN_IND,不可连接的广播,比如货架上的广告机,会不断的向外发送广播信息。
- ADV_SCAN_IND,可发现广播,包含了比通用广播更加丰富的信息。
- SCAN_REQ,扫描请求,当中央设备发现通用广播中包含的信息并不全,这个时候主设备可以发一个扫描请求。
- SCAN_RSP,外设响应主设备的扫描请求,返回给中央设备更多的信息。
- CONNECT_REQ,连接请求,当主设备得到足够的信息之后就可以发送一个连接请求。来建立连接。
- ADV_EXT_IND, 通用扩展广播
- AUX 开头的广播类型和ADV_EXT_IND是蓝牙5.0新引入的,暂且不表。
2 广播通道上的报文
2.1 ADV_IND 通用广播
- AdvA, 6个字节广播者的mac地址
- 最多31个字节的广播数据, AdvData来自上层
通用广播是用途最广的广播方式。进行通用广播的设备能够被扫描设备扫描到,或者在接收到连接请求时作为从设备进入连接态。
2.2 ADV_DERECT_IND 定向广播
- AdvA, 6字节的广播者地址
- TargetA,接收广播者的地址
有时候,设备间需要快速建立连接。如果从设备想这么做,就需要进行广播。定向广播事件就是为了尽可能快的建立连接。这种报文只能包含两个地址,广播者和发起者的地址。发起设备收到发给自己的定向广播报文后,可以立即发送连接请求作为回应。
定向广播有特殊的时序要求。完整的广播事件必须每3.75ms重发一次。这一要求使得扫描设备只需要扫描3.75ms便可收到定向广播设备的消息。定向广播不可以持续1.28s以上的时间。
当使用定向广播时,设备不能被主动发现。
该报文不包含任何上层数据
2.3 ADV_NONCONN_IND 不可连接的广播
- AdvA, 6个字节广播者的mac地址
- 最多31个字节的广播数据, AdvData来自上层
不想被连接的设备使用不可连接广播。这种广播的典型应用包括设备只想广播数据,而不想被扫描或者连接。不可连接广播不会进入连接态,只能在就绪态和广播态之间进行切换。
2.4 ADV_SCAN_IND 可发现广播
- AdvA, 6个字节广播者的mac地址
- 最多31个字节的广播数据, AdvData来自上层
可发现广播允许其他设备扫描该设备,但是不能进入连接态。这意味着该设备可以被发现,既可以广播数据,又可以响应扫描,但不能建立连接。这是一种适用于广播数据的广播形式,动态数据可以包含在广播数据中,而静态数据可以包含于扫描响应数据中。
2.5 SCAN_REQ 扫描请求
- ScanA, 请求者的地址
- AdvA, 广播者地址
2.6 SCAN_RSP 扫描响应
- AdvA, 广播者的地址
- ScanRspData, 来自上层的扫描数据
2.7 CONNECT_REQ 连接请求
连接过程
连接请求的数据包
- InitA: 发起连接者的mac地址,BLE的MAC地址,随机地址的最高两位应该为11b
- AdvA: 广播者的地址mac地址
- AA: 接入地址。LL层使用接入地址来区分当前发送的数据是广播包还是数据包,广播包接入地址固定为0x8E89BED6,数据包使用就是该值。
- CRCInit(CRC initialization value): CRC初始值,主设备提供的一个随机数,防止多个连接AA相同。
- WinSize(transmitWindowSize): 发送窗口大小, 单位是1.25ms
- WinOffset(transmitWindowOffset): 发送窗口偏移, 单位是1.25ms
- Interval(connInterval): 连接间隔 单位是1.25ms
- Latency(connSlaveLatency): 从设备延时,表示从设备可以跳过多少个连接事件。
- Timeout(connSupervisionTimeout): 监控超时。单位是ms
- ChM(Channel Map): 信道图,表示当前环境中哪一个信道可用,每一个bit表示一个信道1表示可用,0表示不可用。比如ff ff ff ff 1f(0x1fffffffff), 二进制为0001111111111111111111111111111111111111b
- Hop(hopIncrement): 跳频算法的hop值,比如110b, 6
- masterSCA: 00100b, 休眠时钟精度, 151 ppm to 250 ppm
三 数据通道上的报文
数据通道上的报文格式:
数据通道上的Header格式:
- LLID(2bit),逻辑链路标识符,用来判断数据报文属于下列哪种类型
- 0b00, 保留
- 0b01, 来自L2CAP的延续帧,或者是一个空包。
- 0b10, 来自L2CAP的开始帧,或者是一个完整的报文。
- 0b11, 控制报文。
- 链路层控制报文(11),用来管理连接,
- 高层报文开始(10),也可用于一个完整的报文
- 高层报文延续(01),也可以是一个空包
- NESN(1bit),预期序列号
- SN(1bit),序列号
- MD(1bit),更多数据
- CP(1bit),是否包含CTEinfo
- RFU(3bit),保留
- length(8bit),长度
- CTEinfo(8bit), 指明Constant Tone Extension的类型和长度
- payload,数据,长度为0到251字节,
- MIC,消息完整性校验,4个字节,可选
为了使数据传输变得可靠,所有的数据均带有序列号(SN)。连接建立之后,第一个数据包的SN为0;每次发送新的数据包时,其SN与上一个数据包的SN不同。这使得接收方能够判断接收数据包的性质: 如果SN与之前的一样,接收方认为该报文为重传报文,如果SN和上一个数据包的SN不同,则认为是新报文。
数据包的确认还需要用到另外一个比特,预期序列号(NESN)。NESN的发送方用其通知对方字节预期接收的数据包的SN。
如果设备成功接收SN为0的报文,在其确认报文中,应该将NESN设为1,向对方表明自己希望下一个数据包的SN为1,如果不讲NESN设置为1,那么发送方就认为从设备没有成功接收到自己的报文,将进行重传。因此NESN作为一个标志来判断数据包是否被正确接收还是需要重传。
数据信道报文的Header里还有一个更多数据位(MD), 用来通知对端设备自己还有其它的数据准备发送。如果收到设置了MD位的数据包,应该在当前连接事件中继续与对端设备通信。这样一来,只要还有数据要发送,连接事件就会自动扩展;一旦不再有数据发送,连接事件会迅速关闭。如果把MD设置为0,设备可以快速、优雅的结束连接事件,从而节省能量。
另外,NESN还可以用来进行流量控制。设备一旦没有足够的缓存空间来处理消息,可以不更新NESN。这迫使对端设备重新发送当前消息,从而把对缓存的要求从接收端转嫁到发送端。
下面是一个具体的例子:
- 主设备发送第一包数据(SN=0,NESN=0,MD=1)
- 从设备接收到之后回复(SN=0,NESN=1,MD=1),但是这一包数据主设备没有收到。
- 主设备没有收到从设备的回复,因此重传这一包数据(SN=0,NESN=0,MD=01
- 从设备回复(SN=0,NESN=1,MD=1)
- 主设备收到从设备的回复,开始发送下一包数据(SN=1,NESN=1,MD=0)
- 从设备回复(SN=1,NESN=0,MD=0)
- 主设备收到从设备的回复,结束当前的连接事件。注意上述过程都在同一个连接事件内完成的。
- 在下一个连接事件中,主设备有数据需要发送(SN=0,NESN=0,MD=0)
- 从设备回复(SN=0,NESN=1,MD=0)
- 主设备收到从设备回复,结束当前连接事件。
3.1 数据报文
LLID为0b01或者0b10。
Header 中的 LLID 字段设置为 0b01,长度字段设置为 0b00000000 被称为空 PDU。 主机的链路层可以向从机发送一个空的 PDU,以允许从机响应任何数据的 PDU,包括一个空的 PDU。
如果LLID字段为0b10,长度字段不应该被设置为0b00000000。
如果链路层接收到 Data_Total_Length 等于 0b00000000 且 Packet_Boundary_Flag 设置为 0b00(即开始片段)的 HCI ACL 数据包,则链路层不能简单地通过空中传输该片段,而是必须将其与一个或 更多的以下延续片段形成一个 PDU,LLID 设置为 0b10 和非零长度。
数据报文的Payload来自上层。
3.2 控制报文
控制报文是用来控制连接链路的。
数据格式如下:
- Opcode,操作码,1个字节
- CTRData,控制数据,0到250个字节
控制报文中的 CtrData 字段由操作码字段指定。 对于给定的操作码,CtrData 字段的长度是固定的。
操作码类型如下(详细信息参看: 核心卷,vol6, PartB, 2.4.2):
如果接收到不支持控制报文或保留供将来使用的报文,则链路层应以 LL_UNKNOWN_RSP PDU 进行响应。 LL_UNKNOWN_RSP PDU 的 UnknownType 字段应设置为接收到的 PDU 中操作码的值。
LL_UNKNOWN_RSP 的CtrData数据格式:
如果接收到的控制报文长度错误或 CtrData 字段无效,链路层可以继续执行该命令,并对数据进行特定于实现的解释(例如,如果PDU太长,它可以忽略额外数据;如果字段超出范围,则可以使用最接近的允许值)。 如果它不继续该规程,它应以LL_UNKNOWN_RSP PDU响应,或者,如果相关规程允许,则响应 LL_REJECT_IND 或 LL_REJECT_EXT_IND PDU。 LL_UNKNOWN_RSP PDU 的 UnknownType 字段或 LL_REJECT_EXT_IND PDU 的 RejectOpcode 字段应设置为接收到的 PDU 中的 Opcode 值。
LL_REJECT_IND的CtrData数据格式:
LL_REJECT_EXT_IND 的CtrData数据格式: