1. RFCOMM
先来看看RFCOMM在协议栈层次体系中的位置。从下图可以看出RFCOMM处于传输层。与AVCTP,TCS-BIN处于同一层次。处于其上层的会话层中的OBEX,SPP等大部分协议通常都采用RFCOMM作为传输协议。因此RFCOMM传输协议在蓝牙协议栈中占据重要一席。
RFCOMM提供了基于L2CAP协议的串行(9针RS-232)模拟,支持在两个蓝牙设备间高达60路的通信连接。
1.1 RFCOMM使用示例——SPP
SPP(Serial Port Profile)定义了一系列协议和过程,蓝牙设备通过该协议实现RS232串行线缆的仿真。很多老式设备都工作在串行模块下面,使用SPP协议可以帮组连接这些老式设备。先来卡纳看SPP在蓝牙协议体系里面的位置:
从上图可以看出,SPP协议是大部分应用层协议都采用的会话层协议,常用的GOBEX以及HSP都采用了SPP协议。再来看看SPP协议所处的层次以及服务模型:
SPP按角色分为DevA和DevB,其中DevA是连接的发起者(initiator),DevB是等待连接的到来。从应用层角度看,SPP涉及到三个主要的过程:建立链路并设置虚拟串口连接、接收链路和建立虚拟串口连接、本地SDP数据库注册服务记录,其中第一项对DevA来说是强制实现的,后两项对DevB是强制实现的。
1.1.1 核心过程
1、建立链路并设置虚拟串口连接
该过程包括以下步骤:
1. Submit a query using SDP to find out the RFCOMM Server channel number of the desired application in the remote device. This might include a browsing capability to let the user select among available ports (or services) in the peer device. Or , if it is known exactly which service to contact, it is sufficient look up the necessary parameters using the Service Class ID associated with the desired service.
2. Optionally, require authentication of the remote device to be performed. Also optionally, require encryption to be turned on.
3. Request a new L2CAP channel to the remote RFCOMM entity.
4. Initiate an RFCOMM session on the L2CAP channel.
5. Start a new data link connection on the RFCOMM session, using the aforementioned server channel number.
After step 5, the virtual serial cable connection is ready to be used for communication between applications on both sides.
Note: If there already exists an RFCOMM session between the devices when setting up a new data link connection, the new connection must be established
on the existing RFCOMM.
2、接收链路和建立虚拟串口连接
该过程主要包括以下步骤:
1. If requested by the remote device, take part in authentication procedure and, upon further request, turn on encryption.
2. Accept a new channel establishment indication from L2CAP.
3. Accept an RFCOMM session establishment on that channel.
4. Accept a new data link connection on the RFCOMM session. This may trigger a local request to authenticate the remote device and turn on encryption, if the user has required that for the emulated serial port being connected to (and authentication/encryption procedures have not already been carried out ).
Note: steps 1 and 4 may be experienced as isolated events when there already exists an RFCOMM session to the remote device.
3、本地SDP数据库注册服务记录
该过程涉及到向本地SDP数据库为虚拟串口注册一条服务记录。这也意味着服务数据库的存在,以及对SDP查询的支持。
1.1.2 连接消息序列
先来看看DevB的初始化流程图。上层应用通过调用SppStartService()开始注册和初始化SPP服务,其中涉及到RFCOMM通道注册,SDP服务搜索等交互过程,在上层应用收到SPP_START_SERVICE_CFM表示服务初始化完成,下一步是等待DevA的连接到来。
再来看看典型的连接过程涉及到的消息交互流程图。DevA通过调用SppConnectRequest ()开启连接过程,DevB在接收到SPP_CONNECT_IND消息时决定是否接受该连接,并作出响应SppConnectResponse()。在DevA收到SPP_CLIENT_CONNECT_CFM,DevB收到SPP_SERVER_CONNECT_CFM后,表示SPP通信会话正式建立。
在走读ADK代码时候,你会发现一个奇怪的现象——通信模块(如L2CAP,RFCOMM,SPP等)里找不到数据收发的接口,传统的通信模块,比如SOCKET通信例程中,当通信链路建立后,会提供read/write,read_from/write_to等之类的显示读写函数帮助收发数据。这是因为ADK提供了流的机制,引入sink/source/transform概念。这样,当一条L2CAP/RFCOMM数据链路建立的同时会创建对应的sink实体,用于收发数据。Sink实体在有数据达到或者数据可以接受状态时向已注册任务(task)发送MESSAGE_MORE_DATA/MESSAGE_MORE_SPACE消息。以SPP为例,在DevA收到SPP_CLIENT_CONNECT_CFM,DevB收到SPP_SERVER_CONNECT_CFM后,例程将会保存已创建的sink实体,并进行配置。
Sink sink = (Sink) StreamRfcommSink(cfm->conn_id); /*从RFCOMM会话ID获取sink引用*/
SourceConfigure(StreamSourceFromSink(sink), VM_SOURCE_MESSAGES, VM_MESSAGES_ALL);
其实真正的sink实体创建于L2CAP链路创建成功的时候??这一点还只是猜测。
void connectionHandleL2capConnectCfm(const L2CA_AUTO_CONNECT_CFM_T *cfm){
Sink sink = StreamL2capSink(cfm->cid);
MessageSinkTask(sink, appTask); /* Associate the task with its sink */
}
目前还不清楚sink实体是何时创建的,总之,在SPP建立连接后,就拥有了一个数据收发的sink实体,通过该实体,配合BlueCore发送过来的MESSAGE_MORE_DATA和MESSAGE_MORE_SPACE等消息进行数据传输。
1.1.3 消息处理
SPP库例程中,DevA(Client)定义了一个回调函数——sppcConnectionHandler,用来接收下层的消息,并进行处理和转发。DevB(Server)定义了两个回调函数,一个用来处理连接时候的消息,一个用于处理服务相关(如SDP查询等)的消息。
spp->c.task.handler = sppcConnectionHandler;
spp->c.client_task = theAppTask;
TaskData sppsServiceTask = { sppServiceHandler };
spp->c.client_task = theAppTask;
SppConnectResponse()->
spp->c.task.handler= sppsConnectionHandler;
1.1.4 多串口仿真
两个使用RFCOMM通信的蓝牙设备可以同时打开多个串口仿真 ,RFCOMM支持多大60路,但是一个设备实际能打开的数据依实现而定。
ADK例程中怎么没有发现多串口复用相关的内容呢???