在蓝牙系统中,为了支持不同应用,需要使用多个协议,这些协议按层次组合在一起,构成了蓝牙协议栈.蓝牙协议栈是蓝牙技术的核心组成部分,它能使设备之间互相定位并建立连接,通过这个连接,设备间能通过各种各样的应用程序进行交互和数据交换
蓝牙协议栈是蓝牙技术的核心部分,它能使设备之间相互定位并建立连接,通过这个连接,设备之间能够哦通过各种各样的应用程序进行交互和数据交换。
1. 蓝牙协议栈架构
图1
上面的图展示了蓝牙协议栈中的支持层,下面从上到下依次概要学习这些组成部分的功能。
1.1 OBEX
OBEX(Object Exchange,对象交换)是一种高效和紧凑的二进制通信协议,它使广泛的设备以一种简单、有效的方式自发地交换数据,其中支持的设备很广泛广泛,例如PC,PDA,电话,摄像头,自动答录机,计算器,数据采集器,手表等等。
OBEX协议构建在蓝牙和IrDA协议的上层,它最初作为a push or pull的应用。OBEX是由红外数据协会(IrDA)制定用于红外数据链路上数据对象交换的会话层协议.蓝牙SIG采纳了该协议,使得原来基于红外链路的OBEX应用有可能方便地移植到蓝牙上或在两者之间进行切换,广泛应用在蓝牙设备中,用来商业卡、数据甚至是应用程序,
OBEX是一种高效的二进制协议,采用简单和自发的方式来交换对象.它提供的功能类似于HTTP(超文本传输)协议,但它不需要HTTP服务器所需要的资源,这样使OBEX非常适用于资源有限的低端设备。在假定传输层可靠的基础上,采用客户机.服务器模式.它只定义传输对象,而不指定特定的传输数据类型,可以是从文件到商业电子贺卡、从命令到数据库等任何类型,从而具有很好的平台独立性。
OBEX客户端模块在WINCE6.0中是Obexapi.dll,OBEX服务器模块在WINCE6.0中是Obexsvr.dll,OBEX它在WinSock之上实现蓝牙和红外传输。
1.2 Winsock
Windows Sockets是Windows下得到广泛应用的、开放的、支持多种协议的网络编程接口。,WINCE的Windows Sockets(Winsock)基于大家熟悉的加州大学伯克利分校的套接字接口来规定了自己的可编程接口。它包含了一组扩展设计,以便充分利用WINCE的消息驱动机制进行编程,在WINCE4.1及之后版本的系统中支持Winsock2.2,它提供了更容易访问多个传输协议的办法。
WOSA(Windows Open System Architecture model,Windows开放式系统体系结构),是微软提出的一种在Windows操作系统下的软件架构,Winsock定义了一种标准服务提供接口(standard service provider interface,SPI),SPI接口介于应用程序编程接口(API)和协议堆栈之间。Winsock1.1只支持TCP/IP网络协议,而Winsock2.2增加了对更多协议的支持,它从Ws2.dll中导出它的功能。
1.3 COM Port Emulation
我们能够通过使用WINCE的COM端口模拟器(emulator facility)来在两个蓝牙设备之间创建一个连接,COM端口模拟器位于蓝牙协议栈最高层并提供了基于虚COM口来访问RFCOMM,它没有导出堆栈接口但提供一个API层来打开远程蓝牙设备的连接。当COM端口模拟器存出现在蓝牙协议栈中,可以创建一个虚拟服务器或是客户COM端来接收传入,或是创建传出的RFCOMM连接。
1.3.1 微微网
WINCE蓝牙允许我们创建一个微微网,根据蓝牙协议你,一个微微网中有一个主设备和最多7个从设备,下面引申对微微网概念的学习:
蓝牙系统采用一种灵活的无基站的组网方式,使得一个蓝牙设备可同时与7个其它的蓝牙设备相连接。基于蓝牙技术的无线接入简称为BLUEPAC(BluetoothPublic Access),蓝牙系统的网络结构的拓扑结构有两种形式:微微网(piconet)和分布式网络(Scatternet)。
一个蓝牙网络由一个主设备和一个或多个从属设备组成,它们都与这设备的时间和跳频模式同步(以主设备的时钟和蓝牙设备的地址为准)。每个独立的同步蓝牙网络就被称为一个微微网(piconet)。
在每个微微网中,一组伪随机调频序列被用来决定79个跳频信道,这个调频序列对于每个微微网来说是惟一的,由主设备地址和时钟决定。信道分成时隙,每个时隙相应有一个调频频率,通常调频速率为1600跳/s。
微微网是通过蓝牙技术以特定方式连接起来的一种微型网络,一个微微网可以只是两台相连的设备,比如一台便携式电脑和一部移动电话,也可以是8台连在一起的设备。在一个微微网中,所有设备的级别是相同的,具有相同的权限。蓝牙采用自组式组网方式(Ad-hoc),微微网由主设备(Master)单元(发起链接的设备)和从设备(Slave)单元构成,有一个主设备单元和最多7个从设备单元。主设备单元负责提供时钟同步信号和跳频序列,从设备单元一般是受控同步的设备单元,接受主设备单元的控制。
1.3.2 创建一个虚拟COM口
微软推荐我们使用Winsock API函数来创建连接,在两个蓝牙设备之前,我们必须要先有下面的信息:
⑴获取到要连接的蓝牙设备的地址,此地址保存在BT_ADDR类型的变量中。
⑵RFCOMM信道号(channel number,在1到31之前)。
⑶被指定为蓝牙操作的COM端口号(在0到9之间)
有了上面的信息,为了创建一个虚拟COM口,需要执行下面的动作:
⑴配置PORTEMUPortParams结构体
此指定要创建的虚拟COM端口的属性,并保存了蓝牙的特定信息,比如通道和蓝牙地址信息。
对于一个服务器端口,需要设置此结构体的flocal和channel,比如:
PORTEMUPortParamspp;
memset (&pp,0, sizeof(pp));
pp.flocal =TRUE;
pp.channel =channel & 0xff;
前面的例子通过设置flocal为TRUE来配置为一个服务器端口,这可使一个服务器COM端口在指定的通道能接收传入的连接。为避免冲突,当我们选择服务器通道时,推荐我们设置channel为RFCOMM_CHANNEL_MULTIPLE(0xfe),这可使RFCOMM使用下一个有效的通道。
对于一个客户端端口,需要设置此结构体的device、channel和uiportflags,如下:
PORTEMUPortParamspp;
memset (&pp,0, sizeof(pp));
pp.device = ba;
pp.channel =channel & 0xff;
前面的例子中,device赋值为一个保存了远程设备地址的BT_ADDR类型变量,用来发起一个RFCOMM层之上的远程连接。
如果服务器通道未知,客户端能够在uuidService指定客户端的UUID,在这种情况下根据此UUID,会自动执行一个SDP查询来获得远程设备使用的目标信道号。
⑵ 注册设备
通过调用RegisterDevice函数来注册设备,示范例子如下:
HANDLE h =RegisterDevice (L"COM", index, L"btd.dll", (DWORD)&pp);
此例子指定端口类型为COM、端口号和设备驱动dll的名称这些参数来注册设备,并且传递PORTEMUPortParams结构体变量。此函数用虚拟COM来注册蓝牙协议栈。
⑶创建一个以null结尾的字符串
创建一个以null结尾的字符串来保存COM端口的名称,此字符串必须在端口名包含一个冒号,比如:
WCHARszComPort[30];
wsprintf(szComPort, L"COM%d:", index);
⑷打开COM端口
HANDLE hCommPort= CreateFile (szComPort, GENERIC_READ | GENERIC_WRITE,
0, NULL,OPEN_EXISTING, 0, NULL);
对于客户端端口,只有在以可读或可写方式调用CreateFile来打开设备时,才能创建物理连接。当第一个可读或可写的handle关闭的时候,服务器和客户端的物理连接都会被终止。
每个被创建的虚拟端口最多可有多大4个打开句柄(open handle),每个句柄维护它自己的通信时间mask,如果以一个0访问掩码(accessmask)打开文件,它只能被用于WaitCommEvent函数,但不能用于ReadFile 和WriteFile函数。
一旦COM端口被创建,它的功能相当于一个串口,也就可以使用串口的API来串口此端口。
1.3.3 删除一个已存在的虚拟COM口
⑴用CreateFile返回的句柄调用CloseHandle函数来关闭虚拟的COM口
CloseHandle (hCommPort);
⑵使用RegisterDevice函数返回的句柄调用DeregisterDevice函数来注册设别。
DeregisterDevice (h);
1.3.4 端口模拟(port emulation)中使用自动绑定的通道
⑴设置PORTEMUPortParams结构体的channel成员变量为RFCOMM_CHANNEL_MULTIPLE:
PORTEMUPortParams pp;
memset (&pp, 0, sizeof(pp));
pp.channel = RFCOMM_CHANNEL_MULTIPLE;
⑵通过调用RegisterDevice 和CreateFile函数来创建虚拟COM口
⑶使用IOCTL_BLUETOOTH_GET_RFCOMM_CHANNEL来终止分配的RFCOMM通道。
DWORD port = 0;
DWORD dwSizeOut= 0;
HANDLE hFile;
if(!DeviceIoControl (hFile, IOCTL_BLUETOOTH_GET_RFCOMM_CHANNEL, NULL, 0,&port, sizeof(port), &dwSizeOut, NULL))
{
// Perform error handling
}
The following functions are supported:
ClearCommError
EscapeCommFunction
GetCommMask
GetCommModemStatus
GetCommProperties
GetCommState
GetCommTimeouts
SetCommMask
SetCommState
SetCommTimeouts
WaitCommEvent
WINCE为我们提供了便利的函数来建立蓝牙连接,其中就包括建立蓝牙的虚拟串口连接.利用虚拟串口连接,我们可以方便的把以前利用串口连接传输数据的程序改为蓝牙无线传输的程序.这个过程只需要一个步骤,就是在服务端和客户端注册虚拟串口. RegisterDevice这个函数就完成了这个功能.通过这个函数建立了串口之后,就可以像操控以往的串口一样用CreateFile,read,write这些系统API来读写
WINCE中的COM端口模拟器允许我们通过RFCOMM通道创建虚拟串口,它主管拨号和局域网连接,COM端口模拟功能包含在Btd.dll中。
1.4 TDI
在WINCE操作系统架构中,TDI(Transport Driver Interface,传输驱动接口)是作为一个基于Winsock用户API的适配层。TDI隔离了Winsock1.1中基于callback架构的堆栈的高度异步性。
1.5 SDP
SDP(Service Discovery Protocol)是一个蓝牙服务发现协议啊,它用来发布和发现运行于蓝牙协议栈顶部的服务,SDP客户端模块功能体现在Btdrt.dll中,SDP服务器模块功能体现在Btd.dll中。Btd.dll是蓝牙设备驱动包含所有的协议层,它被device.exe加载,被AFD使用,提供给TDI。执行在它自己的COM虚拟端口。通过IOCTLs来进行控制。
Btdrt.dll是一个实时的thunk DLL,提供标准应用程序接口(API)给用户编程。通过IOCTL访问驱动和提供回调函数。
提供应用程序在蓝牙环境中特定的涵义来发现哪个服务可用和决定那些可用服务的特征。 SDP 定义了一个蓝牙客户机是怎样可用蓝牙服务器服务和它们的特征的。这些协议定义了客户怎样能够寻找基于特定属性而不让客户知道可用服务的任何内容的服务。
http://www.rosoo.net/a/201109/15024.html
SDP
Service Discovery Protocol服务发现协议,蓝牙中定义的一个协议,主要用来提供一个方式,能够让应用程序发现和使用有关服务,并且能够知晓这些服务的特点。
SDP client
SDP 客户,从服务记录中获取信息的客户,通过发出SDP请求,由SDP 服务器进行维护。
SDP server
SDP服务器,用于维护服务记录清单,这些服务记录描述了同服务器相关联的服务的特性。
SDP Session
SDP对话,在SDP客户和SDP服务器之间的信息交换。信息的一次交换称为SDP transaction.
SDP Transaction
SDP事务处理,在SDP客户和SDP服务器之间交换请求和响应信息。
Security Mode 1
安全模式1,没有设置任何安全措施的设备,属于非安全模式。
Security Mode 2
安全模式2,在信道通信建立之前,不设定安全步骤,允许应用程序使用不同的和可改变的安全策略,尤其适用于同时运行不同的安全策略的应用程序的场合。属于服务级的安全模式。
Security Mode 3
安全模式3,在LMP level层的链路建立完成之前,就已经规划好安全步骤。属于链路级的安全模式。
1.6 RFCOMM
RFCOMM(SerialCable Emulation Protocol)一个基于欧洲电信标准协会ETSI07.10规程的串行线性仿真协议,它作为COM端口模拟器基础提供服务。此协议提供RS232控制和状态信号,如基带上的损坏,CTS以及数据信号等,为上层业务(如传统的串行线缆应用)提供了传送能力。
RFCOMM是一个简单传输协议,其目的为了解决如何在两个不同设备上的应用程序之间保证一条完整的通信路径,并在它们之间保持一通信段的问题。
RFCOMM是为了兼容传统的串口应用,同时取代有线的通信方式,蓝牙协议栈需要提供与有线串口一致的通信接口而开发出的协议。RFCOMM协议提供对基于L2CAP协议的串口仿真,基于ETSI07.10。可支持在两个BT设备之间同时保持高达60路的通信连接。
1.7 PAN
PAN(PersonalArea Network)个人局域网profile定义了使用规程,以支持基于IP的标准网络服务,此网络服务部署在蓝牙传输层,PAN在蓝牙的基础上实现了一个网络接口,下图是PB6.0下PAN部分包含的组件:
图2
1.8 L2CAP
L2CAP(LogicalLink Control and Adaptation Protocol)逻辑链路控制和适配协议是一种实现多路复用且基于连接的蓝牙通讯的低层协议,L2CAP没有实现流控,它依赖于蓝牙硬件提供的可靠的device-to-device基带连接。L2CAP建立在HCI之上,其功能是分发数据给更高层、数据包分段和重组等等,L2CAP层包含在Btd.dll中。
1.9 HID
图3
HID(HumanInterface Device,人机接口设备)profile(应理解为属性)定义了使用HID(比如基于蓝牙连接的键盘)的协议、步骤和应用范围。包含了蓝牙堆栈的设备使用SDP来发现HIDs,且此设备的蓝牙协议栈必须包含:
①HID class驱动发起、建立和结束和HID的连接。
②SDP解析器解析HID的记录(record)。
HID在被激活之前,它必须发送设备信息(描述符)给含有class驱动的宿主机(比如我们的WINCE蓝牙设备)。Class驱动使用这个设备描述符来确定设备特性,目的是为了使能HID对WINCE蓝牙设备的控制。在HID和WINCE蓝牙设备建立蓝牙连接之后,HID基于L2CAP连接,能够和calss驱动通信,实现了对WINCE蓝牙设备的控制。
虽然HID规范没有要求加密和身份验证,但微软强烈建议我们使用加密和身份验证来对HID发送过来的敏感数据进行保护,为了提高连接的安全性,需要合理配置HID的注册表信息,HKEY_LOCAL_MACHINE\Software\Microsoft\Bluetooth\Device\hid\<Addressof the Device>此键值下面的内容及含义如下:
①auth:决定了蓝牙连接是否进行身份验证,0表示disable身份认证,1表示enable身份验证(默认)。此项是可选择的,如果设置为0,可能导致潜在的安全风险。
② encryption:决定蓝牙连接是否加密,0表示不加密,1表示加密(默认)。此项是可选择的,如果设置为0,可能导致潜在的安全风险。
③active:决定HID当前是否激活和能能够接收HID传入的连接请求,0表示不激活HID,表示HID当前被激活(默认)。
在common.reg中有相关的信息:
Note that forhid netui overrides default for mice,
; setting NO security
;
[HKEY_LOCAL_MACHINE\Software\Microsoft\Bluetooth\Device\hid]
"DefaultMtu"=dword:0
"DefaultAuth"=dword:1
"DefaultEncrypt"=dword:1
WINCE还支持BTHHID_IOCTL_HIDConnect和BTHHID_IOCTL_HIDDisconnect,我们可以使用它们来连接和断开HID设备与宿主机蓝牙堆栈的联系。
1.10 Host ControllerInterface(HCI) Layer
HCI(HostController Interface,主控制器接口)提供了统一的接口用于访问蓝牙硬件,负责控制器管理、链路建立和维护,HCI层包含在Btd.dll中。在初始化序列中,HCI创建读和写线程,建立和蓝牙传输的连接,执行复位和读取蓝牙模块buffer的大小(read of device buffer size),然后进入初始化状态,并准备好接收客户端。
对于HCI接口,它向上提供了一个访问底层硬件的统一接口.比如提供给l2cap.其实不用关心HCI内部怎么实现的,只要懂得怎么使用就可以,更进一步,如果所有应用都是在l2cap上的,连HCI接口也没有必要知道.比如我们的应用只是基于winsock,rfcomm,或者obex,这些都是l2cap的上层,就不要关心HCI的上层接口.它是透明的,当它不存在好了。
如果好奇HCI的上层(比如l2cap)如何使用hci接口?其实是使用HCI_EstablishDeviceContext()这个函数来获得接口,并注册相关回调函数和事件响应函数.这些模块源代码都在WINCE600\PRIVATE\WINCEOS\COMM\BLUETOOTH\目录里面.
对于一些特殊的应用,比如你有一些蓝牙耳机这样的应用,就不是通过l2cap了,那么就要从hci层扩展.还是使用同样的接口方法,只是参数不同了.耳机这样的应用是要处理的是同步的连接SCO数据包,于是透过参数告知hci,将sco数据发给自己来处理.具体来说就是第2个参数BTH_CONTROL_ROUTE_BY_LINKTYPE,第5个参数BT_LINK_TYPE_SCO,以次来调用HCI_EstablishDeviceContext().
1.11 BluetoothUniversal Transport Manager
蓝牙统一传输管理器是一个介于HCI层和传输层的中间传输驱动,它监测Pnp(即插即用)设备和加载对应的传输层驱动,这部分对应的代码在\WINCE600\PUBLIC\COMMON\OAK\DRIVERS\BLUETOOTH\TRANSPORTS\UNIV下面
,在Bthuniv.dll中实现这部分功能。
为什么需要蓝牙统一传输管理器呢?是因为WinCE是一个开放的平台,它也不知道蓝牙究竟是连接串口、usb口、sdio甚至一些pcmcia等其他的pnp设备等等,而且作为HCI的上层也不想知道你用什么物理接口。于是它抽象出来这么一个东西来统一管理,简单说就是大一统所有的接口了,它先去扫描PCMCIA,USB和sdio等pnp设备,如果没有就根据注册表取默认的设备接口。最后被选定的接口会被安排到这里[HKEY_LOCAL_MACHINE\Software\Microsoft\Bluetooth\HCI],比如:
[HKEY_LOCAL_MACHINE\Software\Microsoft\Bluetooth\Transports\BuiltIn\1]
"driver"="bthuart.dll"
"flags"=dword:4
"name"="COM2:"
"baud"=dword:1c200
"resetdelay"=dword:1388
其中1表示优先级别,"name"="COM2:"和"baud"=dword:1c200表示以115200波特率打开COM2口。
1.12 HCI TransportLayer
HCI传输层传递HCI命令给蓝牙硬件(也就是蓝牙模块),HCI传输被设计用来抽象和简化蓝牙堆栈与控制器(这里指蓝牙模块)的物理通讯,WINCE的传输驱动支持使用UART、USB、SDIO和BCSP等接口的蓝牙模块。此层只需要实现少数几个函数,概括为open、read、write、close和两个用于bookkeeping(统计)的函数,其中read和write是阻塞的,蓝牙堆栈使用这些函数来发送蓝牙命令与数据包,和接收数据包与事件,这些函数在bt_hcip.h中定义,比如:
HCI_ReadHciParameters
HCI_SetCallback
HCI_StartHardware
HCI_StopHardware
HCI_OpenConnection
HCI_ReadPacket
HCI_WritePacket
HCI_CloseConnection
1.13 Link ManagerProtocol
链路管理协议处理蓝牙设备间建立的连接,主要包含身份认证和加密。
1.14 BaseBand
基带使能了蓝牙设备间的物理射频(RF)连接,这样可以形成微微网。
除了HCI传输层之外的其它层,都是以一个单独的整体实现的,它们通过表的回调的方式向上和下层导出它的接口,并且这些接口已经定义好。除了这些接口,蓝牙协议栈中的各个部分没有其他的联系,每一层是可替换的。
由此可见我们需要实现的就是HCI Transport Layer,也就是开发此层的那些函数