一:介绍
L2CAP -全称: Logical Link Control and Adaptation Protocol,L2CAP协议支持更高级别的协议复用和报文分片。它为RFCOMM和BNEP协议提供了基础。对于BTstack官方支持的所有配置文件,不需要直接使用L2CAP。但是,对于自定义协议的测试或开发,能够访问和提供L2CAP服务是很有帮助的。
二:访问远端设备上的L2CAP服务要求 L2CAP基于通道的概念。
信道是在基带连接之上的逻辑连接。每个通道以多对一的方式绑定到单个协议。一个通道可以绑定多个协议,但一个通道不能绑定多个协议。多个通道可以共享同一个基带连接。 为了与远端设备上的L2CAP服务通信,本地蓝牙设备上的应用程序使用l2cap_init函数初始化L2CAP层,然后使用l2cap_create_channel函数创建一个到远端设备PSM的出站L2CAP通道。l2cap_create_channel函数将初始化一个新的基带连接,如果它还不存在的话。作为L2CAP创建通道函数的输入参数给出的数据包处理程序将被分配给新的出站L2CAP通道。这个处理程序接收L2CAP_EVENT_CHANNEL_OPENED和L2CAP_EVENT_CHANNEL_CLOSED事件和L2CAP数据包,如下面的清单所示。
三:提供L2CAP服务要求
为了提供L2CAP服务,本地蓝牙设备上的应用程序必须初始化L2CAP层并通过l2cap_register_service注册服务。从那里开始,它可以等待进入的L2CAP连接。应用程序可以通过分别调用l2cap_accept_connection和l2cap_deny_connection函数来接受或拒绝传入的连接。 如果一个连接被接受并且进入的L2CAP通道成功打开,L2CAP服务可以使用l2cap_send向被连接的设备发送和接收L2CAP数据包。 下面的清单提供了L2CAP服务示例代码。
四:发送L2CAP数据要求
由于BTstack内部出包缓冲区已满,或者蓝牙模块中的ACL缓冲区已满,也就是说,如果应用程序发送的速度超过了可以通过空气传输的数据包,则L2CAP数据包的发送可能会失败。而不是直接调用l2cap_send,建议调用l2cap_request_can_send_now_event(cahnnel_id),它将尽快触发L2CAP_EVENT_CAN_SEND_NOW。在l2cap_request_can_send_now_event函数返回之前,可能会通过包处理程序接收到事件。L2CAP_EVENT_CAN_SEND_NOW表示可以发送的通道ID。
请注意,数据包可以发送的保证只有在事件被接收时才有效。从包处理程序返回后,BTstack可能需要发送自己。
五:LE数据通道
LE数据通道的全称实际上是具有LE基于信用的流量控制模式的面向连接的LE通道。在这种模式下,数据以sdu (Service data unit)的形式发送,sdu的大小可以大于单个HCI LE ACL报文。
LE数据通道类似于经典L2CAP通道,但也提供了类似于RFCOMM通道的基于信用的流量控制。除非使用蓝牙Core 4.2规范的LE数据包扩展,否则LE ACL报文的最大数据包大小为27字节。为了发送更大的报文,每个报文将被分成多个ACL LE报文,在接收端重新组合。 由于多个sdu可以同时传输,并且各个ACL LE数据包可以交错发送,因此BTstack需要每个通道都有一个专用的接收缓冲区,在创建或接受通道时必须通过该缓冲区。类似地,当发送sdu时,提供给l2cap_cbm_send_data的数据必须保持有效,直到收到L2CAP_EVENT_LE_PACKET_SENT。 在创建传出连接或接受传入连接时,initial_credits允许向远程端提供固定数量的信用。可以随时使用l2cap_cbm_provide_credits提供更多的积分。
如果使用L2CAP_LE_AUTOMATIC_CREDITS, BTstack会根据需要自动提供积分——为了方便,有效地交换了流量控制功能。 API的其余部分与L2CAP类似:
L2cap_cbm_register_service和l2cap_cbm_unregister_service用于管理本地服务。
L2cap_cbm_accept_connection和l2cap_cbm_decline_connection用于接受或拒绝传入的连接请求。
L2cap_cbm_create_channel创建一个outgoing连接。
L2cap_cbm_can_send_now检查一个数据包现在是否可以被调度传输。
l2cap_cbm_request_can_send_now_event请求L2CAP_EVENT_LE_CAN_SEND_NOW事件。
L2cap_cbm_disconnect关闭连接。
六:RFCOMM——无线电频率通信协议
射频通信(RFCOMM)协议通过L2CAP协议和重组提供了串行端口的仿真。它是串行端口配置文件和其他用于电信的配置文件的基础,如耳机配置文件、免提配置文件、对象交换(OBEX)等。
七:无RFCOMM报文边界
由于RFCOMM仿真了一个串行端口,它不保存包边界。在大多数操作系统中,RFCOMM / SPP将被建模为允许编写块字节的管道。操作系统和蓝牙堆栈可以自由地缓冲和屏蔽这些数据,以任何方式看起来都是合适的。在BTstack应用程序中,因此您将按照同样的顺序接收这些数据,但是没有任何保证可以将其分割成多个块。如果您需要保留发送一个特定大小的包的概念,最简单的方法是用2或4字节长的字段来前缀数据,然后在接收方上重建数据包。请注意,由于BTstack的“no buffer”策略,BTstack将立即发送退出的RFCOMM数据,并隐式地保存包边界,即。它将将数据作为单个RFCOMM包中的单个RFCOMM包发送到一个单一的L2CAP包中,它将会在一段时间内到达。虽然这将在两个BTstack实例之间保持,但依赖实现细节并不像描述的数据一样,这不是一个好想法。
八:RFCOMM流控制
RFCOMM具有强制性的基于信用的流量控制。这意味着建立RFCOMM连接的两个设备使用积分来跟踪可以向每个设备发送多少RFCOMM数据包。如果设备没有剩余的(传出)积分,则无法再发送RFCOMM报文,必须暂停传输。在建立连接期间,提供初始学分。BTstack跟踪两个方向的积分数量。如果没有可用的信用额度,RFCOMM发送函数将返回一个错误,您可以稍后再尝试。对于传入数据,BTstack通过不同的功能分别创建/注册具有和不具有自动信用管理的通道和服务。如果信用管理是自动的,则在需要时根据ACL流控制提供新的信用——这只在传输的数据不多和/或只使用一个物理连接时才有用。如果积分管理是手动的,则由应用程序提供积分,以便它可以显式地管理其接收缓冲区。
九:访问远端设备上的RFCOMM服务
为了与远程设备上的RFCOMM服务通信,本地蓝牙设备上的应用程序使用rfcomm_init函数初始化RFCOMM层,然后使用rfcomm_create_channel函数创建一个到远程设备上给定服务器通道的出站RFCOMM通道。如果RFCOMM多路复用器不存在,rfcomm_create_channel函数将为RFCOMM多路复用器发起一个新的L2CAP连接。信道将自动向远端提供足够的信用。要手动提供信用,您必须通过调用rfcomm_create_channel_with_initial_credits创建RFCOMM连接——参见手动信用分配一节。 作为RFCOMM创建通道函数的输入参数给出的数据包处理程序将被分配给新的传出通道。这个处理程序接收RFCOMM_EVENT_CHANNEL_OPENED和RFCOMM_EVENT_CHANNEL_CLOSED事件,以及RFCOMM数据包,如下面的清单所示。
十:提供RFCOMM服务
为了提供RFCOMM服务,本地蓝牙设备上的应用程序必须首先初始化L2CAP和RFCOMM层,然后在rfcomm_register_service中注册服务。从那里开始,它可以等待传入的RFCOMM连接。应用程序可以通过分别调用rfcomm_accept_connection和rfcomm_deny_connection函数来接受或拒绝传入的连接。如果一个连接被接受并且传入的RFCOMM通道成功打开,RFCOMM服务可以使用rfcomm_send向连接的设备发送RFCOMM数据包,并通过rfcomm_register_service调用提供的数据包处理程序接收数据包。 下面的清单提供了RFCOMM服务示例代码。