声明:BLE低功耗蓝牙系列博客来自个人的学习总结,其中肯定会包含很多错误,如果发现欢迎帮忙指正。BLE内容比较多,我现在还没有完全学完,好在其分层设计,所以可以每学完一个部分就可以做相应的总结。需要说明的是该系列博客的大部分内容来自《低功耗蓝牙开发权威指南》这本书,还会包含韦东山蓝牙系列课程的部分内容。该系列博客可以提供些什么?该系列博客是对BLE相关的知识点做归纳总结,注释个人在学习过程中的观点、理解。以后的内容计划有BLE应用的使用和实现过程,尽可能的在博客内容加入协议分析的过程,总之是从作者的学习历程记录BLE学习、理解的过程。通过对BLE低功耗蓝牙的学习弥补了我对无线通讯技术的空白,今后持续学习BLE这项技术,博客也会不断完善,必要时进行调整,修改。
HCI是英文Host Controller Interface的缩写。低功耗蓝牙框架如下图,上半部分称为主机Host,下半部分称为控制器Controller。主机和控制器分别是两个CPU:控制器包含物理层和链路层,主要负责无线数据的收发和链路管理,主机则主要是协议栈的处理(其实Host和Controller的工作也可以由一个CPU来完成,但一般不这么做)。
主机控制器接口(HCI)是主机与控制器之间的接口,主要完成两个任务:
一个是发送命令给控制器和接收来自控制器的事件;
另一个是发送和接收来自对端设备的数据;
很好理解,主机使用控制器主要是为了传输无线信号的,使用时也需要对控制器做各种设置,参数获取等。低功耗蓝牙主机与控制器之间传输的数据分为三类:
command:主机向控制器发出的各种命令
event:控制器返回的信息
acl data:与对端设备传输的数据
蓝牙规范定义的主机与控制器之间的硬件接口有这几种:
5线串口
三线串口
SDIO
USB
HCI既是两个设备之间的物理上的接口,也是逻辑上的接口。逻辑接口定义了命令、事件和数据的封包格式。物理接口则规定了主机和控制器之间如何传输数据包。
这里需要说明的是,HCI是主机与控制器之间交互通道,两个蓝牙设备之间的无线信号传输不全都通过控制器反馈到主机,因为有的无线信息交互是在控制器间完成的。
UART(5线):5线UART定义了两条流控线,管脚定义为(TX、RX、GND、RTS、CTS),低功耗蓝牙中,UART接口一般采用8n1。数据加上一个字节的头部表示数据类型,一字节头部定义如下:
命令=0x1
数据=0x2
事件=0x4
3线UART:它在设计上舍弃了硬件流控线,连线更加简单。但由于没有硬件流控,它使用SLIP协议传输数据。
USB:usb接口通过各个端点进行主机/设备之间的交互
端点0x0:主机给设备发送命令
中断端点0x81:轮询来自设备的事件
端点0x2:主机向设备输出块数据
端点0x82:设备向主机输入块数据(来自主机轮询)
关于逻辑接口,需要理解三个相关概念:
信道
数据包格式
流控
一旦控制器与其它设备建立了连接,控制器的底层HCI接口就创建了一个HCI信道,使用一个连接句柄来标识这个HCI信道。使用这个句柄发送和接收来自对端的数据。连接结束时句柄消失。这里信道和链路层的信道不是一个概念,HCI信道的存在使得控制器可以同时与多台设备建立连接,主机通过控制器返回的连接句柄与不同设备进行数据传输 。
命令数据包:主机通过向控制器发送命令数据包来执行命令。这些命令通常用于配置控制器的状态,或者请求控制器完成操作。
HCI命令数据包包括,用于确认发送命令的操作码、参数的长度字段和命令参数。每个命令都有自己的一套独特的参数。
(BT4.0)
在低功耗蓝牙中一共有三种基本命令类型,分别实现如下功能:
1.配置控制器的状态
2.请求执行特定的操作
3.管理连接
事件数据包:控制器发送主机的事件数据包主要用于发送信息和反馈之前输入的命令。
(BT4.0)
HCI事件数据包由事件类型编码、参数的长度字段和事件参数组成。每一个事件都有一套独特的参数。
低功耗蓝牙主要有下列三种基本事件类型:
1. 通用命令完成事件:当主机发送给控制器的命令执行完毕时,控制器立即返回一个通用命令完成事件。事件的参数包含了之前发送的命令操作码和执行命令的返回参数。每当控制器执行无线传输无关的任务时,都将使用通用命令完成事件。
2. 通用命令状态事件:需执行无线传输操作,通常返回的是通用命令状态事件;一段时间后才会返回特定命令完成事件。
3. 特定命令完成事件:有些命令需要一段时间才能完成执行,它们都有对应的、并且是唯一的特定命令完成事件。例如,LE Create Connection命令首先返回一个命令状态事件,随后等待连接建立完成或者失败,再返回LE Connection Complete事件。
数据包:数据包是指主机与控制器之间传输的应用数据。控制器接收来自主机的数据包,将其传给对端设备。对端设备收到数据之后,将其从控制器发往主机。
(BT4.0)
数据包总是以连接句柄作为第一个字段,在LE Connection Complete事件中该字段占用了12位。如果主机一直没有收到LE Connection Complete事件,则不能发送任何数据给对端设备。HCI数据包有两种标记:数据包边界标记和广播标记。数据包边界标记用来确认上层协议(L2CAP)的信息是一个开始数据还是一个延续包。
HCI接口有两种流控形式如下,而且一般只考虑对控制器的流控,因为大部分主机都能处理大量数据:
命令流控
数据流控
1. 控制器使用命令流控同时处理多个HCI命令。要启动命令流控,所有命令完成事件和命令状态事件都必须包含一个Num HCI Command Packets参数。该参数用来表示控制器能够缓存多少条命令。控制器每次向主机发送命令完成事件或命令状态事件,都会在其中包含缓存区的剩余空间。控制器可以随时发送操作码为No Operation的命令完成事件,其中携带新的Num HCI Command Packets参数。
2. 主机通过LE Read Buffer Size命令获知缓存区的剩余数量。每发送一个数据包给控制器,就消耗控制器中的一个缓存,控制器发送Number Of Completed Packet事件给主机,当中包含连接句柄的列表和发送数据包的数量,之后释放相应缓存区。因此,主机不仅知道控制器释放了多少个缓存区,也知道哪些数据发送到了对端设备。
Number of Completed Packets的数据解析结果如下:
Command前有2字节的头部OpCode,用来表示是哪一个Command。
OGF表示“OpCode Group Field”,即“哪一组命令”,它占据高6位。
OCF表示“OpCode Command Field”,即“这组里的哪一个命令”,它占据低10位。
在蓝牙协议里(Core_v4.2.pdf),每一个OGF用一节来描述,在这节里会描述该组(OGF)的所有命令(OCF),位置如下图:
定义的命令很多,低功耗蓝牙只是用器其中一小部分,以下面几种功能分类进行介绍:
控制器的配置
广播和观察
发起连接
连接管理
利用控制器与对端设备通讯之前,主机可以先对控制器进行下列相关操作:重置控制器的状态、读取设备地址、设置事件掩码、读取流控缓存、读取控制器支持的列表、生成随机数、加密数据、加密数据、设置随机地址以及配置白名单等。
重置控制器为已知状态:在执行其它操作之前,有必要重置下控制器的状态,让控制器重置为就绪态,这样所有可以配置的参数恢复为默认值。主机可以通过发送Reset命令来重置控制器。这里需要注意两点:一是重置命令不会重置物理链路,如果要重置重置链路的话,需要执行另外的链路重置操作。
读取设备地址:主机通过向控制器发送Read BD_ADDR命令来读取设备地址,后者返回一个含有固定设备地址的Command Complete事件。如果控制器没有固定地址,则返回全零地址00:00:00:00:00:00。此时,主机需要为控制器生成一个随机地址,否则无法进行数据传输。
设置事件掩码:控制器定义了很多事件类型。考虑到未来的蓝牙规范还会添加新的事件和功能,控制器必须知道主机能够接受和处理哪些事件。如果主机不能理解控制器的事件,将会导致互操作性问题。解决问题的一个办法是设置事件掩码,让主机告诉控制器哪些事件能接收,哪些不能接收。这样控制器只会发那些能被接收的事件。
读取缓存区大小
读取控制器支持的功能
读取控制器支持的状态
随机数
加密数据
设置随机地址
白名单
两个低功耗蓝牙设备之间的最基本的通信模型就是广播和观察模型,二者采用广播和扫描来传输数据。
广播
控制器有两类数据可以通过广播发送:广播数据和扫描响应数据。另外,控制器还有一系列的参数,用来设置如何发送以及何时发送广播报文。
而且广播一共有四种:可连接的非定向广播、可连接的定向广播、可扫描的非定向广播、不可连接的非定向广播;
主机使用LE Set Advertising Parameter命令来设置广播参数,配置参数包括:
广播的最小间隔时间和最大间隔时间,范围从20ms~10.24s;
可以设置地址类型,固定地址或者为随机地址。如果为定向广播,那么在广播数据包中应含有对端设备的地址;
广播信道映射,信道映射用来决定使用哪三个广播信道;
广播过滤策略,过滤策略用来过滤不符合规则的广播数据包,过滤策略可以设置如下规则之一:
1.接受任何设备的扫描请求或连接请求;
2.仅仅接受白名单中特定设备的扫描请求,但是接受任何设备的连接请求;
3.接受任何设备的扫描请求,但仅仅接受白名单中的特定设备的连接请求;
4.仅仅接受白名单中的特定设备的扫描请求和连接请求;
主机可以使用LE Read Advertising Channel TX Power命令获取广播时的发射功率,另外我们还将发射功率值放在广播数据包中或者扫描响应数据包,从而实现靠近配对。
主机使用LE Set Advertising Data命令和LE Set Scan Response Date命令设置广播数据包和扫描响应数据包。
使用LE Set Advertising Enabled来启动或关闭广播。广播一旦启动,控制器将使用配置好的参数进行广播。
被动扫描
使用LE Set Scan Parameter命令来进行设置控制器的扫描参数,可配置的参数如下:
扫描类型 -- 可设置为被动扫描或主动扫描
扫描间隔 -- 控制器间隔多长时间扫描一次
扫描窗口 -- 每次扫描的持续时间
扫描策略 -- 接受任何的广播数据包或者仅仅接受白名单设备的广播数据包
参数设置完成后,主机就可以使用LE Set Scan Enable命令启动扫描。如果控制器接收到的符合过滤策略和其他规则的广播数据包,则发送一个LE Advertising Report事件给主机。
主动扫描
主动扫描不但可以捕获到对端设备的广播数据包,还可以捕获可能的扫描响应包。
在参数配置和启动扫描方面,主动扫描和被动扫描使用的命令完全一样。但控制器要发送SCAN_REQ数据包给对端设备,以便获得扫描响应数据包,而这些数据包需要包含设备地址,因此,在使用LE Set Scan Parameter命令时需要配置一个额外参数,决定链路层的数据包使用固定地址抑或是随机地址。控制器收到了SCAN_RSP数据包将向主机发送一个LE Advertising Resport事件。
要建立连接,其中一个蓝牙设备应处于可连接的广播状态,由另一个设备发起连接。
与白名单设备发起连接:最常用的连接方式是主机先将对端设备添加到白名单中,然后再与白名单中的设备进行连接,通过这种方式,控制器可以同时和多个设备发起连接。
主机使用LE Add Device To White List以及其它白名单管理命令实现该功能。一旦主机打算与白名单中的设备连接,则控制器发送LE Create Connetion命令:
LE Create Connection命令可配置如下参数:
扫描间隔和扫描窗口 -- 与主动扫描参数共同决定控制器侦听广播的频率(不是很理解这句话的含义?)
发起者过滤策略 -- 可设置为"使用白名单"
发起者地址类型 -- 该参数设置CONNECT_REQ数据包的地址类型时固定地址或随机地址
发起连接参数 -- 该参数用于设置主设备传输数据给从设备的频率、从设备可以忽视主设备的等待时间(latency)、监控超时以及每个连接中发往从设备或者 来自从 设备的预期的数据传输质量。
发起连接参数对白名单的所有设备是完全相同的。当与不同类型设备进行连接,那么主机应该为LE Create Connection命令设置最坏情况下的连接参数。
如果控制器收到白名单中某一设备的可连接广播数据包,则向对端设备发送CONNECT_REQ数据包,其中包含所有连接所需的信息。另外控制器还向主机发送LE Create Conection Complete事件。如果对端设备接收到CONECT_REQ数据包,它也会向其主机发送LE Create Conection Complete事件。
该LE Create Conection Complete事件包含连接句柄,用于标示主机和控制器之间传输的数据包。该事件还包括当前控制器的角色信息(主、从),已经从设备地址、间隔时间、等待时间、监控超时和主设备的时钟精度等。
当连接已经建立并且发送了LE Create Conection Complete事件,所有的广播或者其它的发起连接请求将会自动停止。如果主机想继续广播或与其它设备进行连接,则需再次执行相应的命令。
与单一设备发起连接:与单一设备进行连接,主机同样使用LE Create Connection命令。区别在于发起者的过滤策略将设置为"忽略白名单",另外还需设置对端设备的地址等相关参数。其它和白名单的连接过程完全类似。
取消连接请求:当主机使用LE Create Connection命令向对端设备发起连接请求,如果连接请求的接受方长时间没有响应,主机可以发送LE Create Connection Cancel命令,该命令会返回Command Complete事件。另外,不管连接成功与否,LE Create Connection命令的都会返回LE Connection Complete事件。
备注:这里可能出现竞态条件,主机向控制器发送LE Create Connection Cancel命令的同时,可能控制器已经向对端设备发送了CONECT_REQ,但尚未返回LE Connection Complete事件。在这种情况下,LE Create Connection Cancel命令返回Command Complete事件,但还可能收到一个连接建立的LE Connection Complete事件。所以即便试图取消某个连接,该连接也完全有可能成功建立。
建立连接之后,设备就可以对连接进行管理:包括切换至低功耗模式、增加或减少间隔时间,设置加密或者终止连接等。
更新连接:如果当前的连接参数不再适用时,主设备可以进行修改。主设备使用LE Connection Update命令修改连接参数。连接参数包括新的连接间隔、等待时间、监控超时已经连接事件长度。
控制器收到命令先返回Command Status事件,随后才发送链路连接更新请求数据包给设备。该数据包包括了瞬时时刻。一旦计数器倒数结束,就必须更新连接参数,随后控制器返回LE Connection Update Complete事件表示连接参数已经更新。
更新信道映射图:主机无法知道某个信道是不是好信道,控制器可以通过监控信道中的数据包的错误率来判断哪些未知信道的确很糟糕。但主机可能存有本地信道使用情况的信息,并且希望将该信息告知控制器。
LE Set Host Channel Classification命令将设置链路层信道图,返回Command Complete事件,控制器可以在任何时候启动链路层控制规程修改信道映射图。另外,主机也可以使用LE Read Channel Map命令获得当前的信道映射图。
交换功能列表:主设备的主机使用LE Read Remote Used Feature命令请求获取对端设备的支持功能列表。该命令除了返回Command Status事件外,还会让控制器发送链路层功能请求(LL_FEATURE_REQ)以及获得对端设备的链路层功能响应包(LL_FEATURE_RSP)。随后控制器在返回的LE Read Used Feature Complete事件中包含了对端设备的功能信息。
交换版本信息:主机使用LE Read Remote Version Information命令交换版本信息。但当再次执行此命令时控制器并不会执行相关的链路规程。这是因为版本信息会被当做一种静态信息缓存在控制器中。
加密连接:只要连接双方都有一共享秘钥,主机就可以加密传输数据。共享秘钥由安全管理器(初始化配对过程、秘钥分配都由它管理)设置。连接加密涉及两组命令和事件:一组与主设备相关,另一组与从设备相关。
主设备的主机使用LE start Encryption命令要求链路层启动加密,该命令包含了连接的加密密钥。当加密规程开始后,控制器将返回Command Status事件。然后链路层开始加密。加密规程启动后,控制器返回Encryption Change事件,告知主机加密已经完成或者加密过程出现了问题。
在从设备的视角,从设备的主机首先收到LE Long Term Key Request事件,通知从设备启动加密。主机接着发送LE Long Term Key Request Reply命令作为响应,其中包括了加密连接的密钥。由于这是条命令,所以会返回相应的Command Complete事件。连接成功加密后,从设备的主机将收到Encryption Change事件,通知其更新加密状态。
重启加密:主设备用到的加密事件和命令与启动加密时的完全一样,不过链路层需要发送更多的数据包:首先要停止加密,然后再重启加密。从设备用到的加密事件和命令也和初始加密时完全一样。在低功耗蓝牙中,不允许在停止加密后发送应用数据或主机数据 。
终止连接:如果不再需要发送数据,或者维持连接比断开后再重新连接需要更多的能量时,主机可以终止连接。主机向链路层发送Disconnect命令终止连接,随后链路层返回Command Status事件并尝试终止连接。如果连接成功终止,控制器将返回Disconnect Command事件。
当发生监控超时或者加密消息完整性检查失败导致了连接中断,主机也会收到Disconnect Complete事件。加密消息完整性检查失败通常不会发生,除非有攻击者试图接管连接,或者发生了某种特殊的情况,比如出现了较为罕见的位错误,导致报文通过了循环冗余校验而未通过消息完整性检查。
低功耗蓝牙HCI交互实例解析: https://blog.csdn.net/lewanhah01/article/details/104048913