无连线的Linux——蓝牙的基础

时间:2024-03-23 22:54:46

Linux Without Wires The Basics of Bluetooth

蓝牙技术用于短距离(1米至100米)的通信。 它是最广泛的无线技术,根据功率和通信范围分为多个类别
蓝牙是基于分组的协议,并具有主从结构。 它工作在2400MHz和2483.5MHz频率范围之间,并使用跳频扩频。 蓝牙将数据分成数据包并在79个指定的频道之一上发送。 每个通道具有1MHz的带宽。 蓝牙4.0版本有40个通道,每个通道使用2MHz带宽。
无连线的Linux——蓝牙的基础
无连线的Linux——蓝牙的基础
蓝牙堆栈
蓝牙堆栈是用于实现蓝牙主机协议栈的软件。 蓝牙堆栈实现可以有两种类型:
第一种是通常用于台式计算机的通用实现,并且灵活,因为可以通过驱动程序添加额外的蓝牙配置文件。
第二个是嵌入式系统的实现,它是针对资源有限的设备,如外围设备。
蓝牙,Bluedroid,Widcomm,Bluecode +,BlueLet,BlueMagic等市场上有各种类型的蓝牙协议栈(见图2)。

无连线的Linux——蓝牙的基础
BlueZ
BlueZ是一个通用蓝牙堆栈,用于实现Linux的蓝牙主机协议栈。它是开源软件和官方的Linux蓝牙协议栈。您可以从开发部分的http://www.bluez.org/下载这个软件。
BlueZ将蓝牙协议层映射到内核模块,内核线程,用户空间守护进程,配置工具,实用程序和库(见图3)。 BlueZ的主要组件是:

bluetooth.ko,其中包含BlueZ的核心基础设施。它传播蓝牙sockets家族 AF_BLUETOOTH。所有BlueZ模块都利用其服务。
蓝牙HCI数据包通过UART或USB传输。相应的BlueZ HCI实现是hci_uart.ko和hci_usb.ko。
负责分割,重组和协议复用的蓝牙L2CAP层由l2cap.ko实现。
在bnep.ko的帮助下,TCP / IP应用程序可以通过蓝牙运行。这模拟L2CAP层上的以太网端口。名为kbnepd的内核线程负责BNEP连接。
rfcomm.ko负责运行终端等串口应用程序。这模拟了L2CAP层上的串行端口。名为krfcommd的内核线程负责RFCOMM连接。
hidp.ko实现HID(人机界面设备)层。用户模式守护进程隐藏允许BlueZ处理输入设备,如蓝牙鼠标。
sco.ko实现面向同步连接(SCO)层来处理音频。 SCO连接不指定连接到远程主机的通道;只有主机地址被指定。

蓝牙协议栈与OSI模型
将蓝牙协议栈与开放系统互连(OSI)模型进行比较的原因是为了更好地理解前者。假定了OSI模型的知识。
无线电,基带和链路管理器层是在蓝牙芯片组上实现的蓝牙架构的硬件部分。这些层大致映射到OSI模型中的物理层,数据链路层和网络层。主机控制接口(HCI)映射到传输层,并在L2CAP层和蓝牙芯片组之间传输数据。逻辑链路控制和适配协议(L2CAP)映射到会话层。使用射频通信(RFCOMM)的串行端口仿真,使用蓝牙网络封装协议(BNEP)的以太网仿真和服务发现协议(SDP)是功能丰富的表示层的一部分。位于堆栈顶部的各种应用程序环境称为配置文件。由于无线电,基带和链路管理器通常是蓝牙硬件的一部分,操作系统支持从HCI层开始(见图4)。
无连线的Linux——蓝牙的基础

仔细看看蓝牙协议栈
蓝牙协议栈分为两部分主机和控制器,并由主控制器接口(HCI)层接口(见图5)。
无连线的Linux——蓝牙的基础
蓝牙主机
主机由堆栈的软件部分组成。因此,它在操作系统级别上实现。它包括核心蓝牙协议的实现:蓝牙堆栈(或主机协议堆栈)和蓝牙架构的高级层(如API和配置文件)。它具有各种层次和协议,如L2CAP,RFCOMM,SDP等,下面将进行解释。

L2CAP(逻辑链路控制和适配协议)L2CAP具有以下特征:
在不同的高层协议之间复用数据的能力;因此,诸如RFCOM,SDP等的各种协议可以对其进行操作
分割和重组
服务质量
它支持一个组抽象,实现将一个组映射到微微网上
RFCOMM(射频通信):RFCOMM位于L2CAP层之上。这是一个串口仿真协议。它通过蓝牙基带仿真RS-232控制和数据信号。
它提供与TCP大致相同的服务和可靠性保证。从网络程序员的角度来看,端口号的选择是TCP和RFCOMM最大的区别。尽管TCP在一台机器上支持多达65,535个开放端口,但RFCOMM仅允许有30个。这对于如何选择服务器应用程序的端口号有重大影响。
SDP(服务发现协议):该协议是所有蓝牙应用程序所必需的,并且是蓝牙协议栈的重要组成部分。借助该协议,设备可以发现附近其他设备支持的服务以及与之连接所需的参数。每个服务都由一个通用唯一标识符(UUID)来标识。

蓝牙配置文件
蓝牙配置文件是描述设备如何使用蓝牙协议来实现特定任务的规范,这是蓝牙体系结构的理论部分。蓝牙服务是蓝牙协议栈的实际部分。有各种类型的配置文件可用。有些是:


GAP(通用访问配置文件):这定义了检测和建立到蓝牙设备的连接的通用要求。这是默认情况下可用的核心配置文件。
GOEP(通用对象交换配置文件):它定义了OBEX的要求,OBEX是一种用于在使用最少资源和使用模型的情况下在设备之间交换二进制对象的通信协议。
HSP(耳机配置文件):定义耳机和移动设备(如蜂窝电话)之间的互操作性的程序,头戴式耳机通过AT命令进行控制,并取决于串行端口配置文件。
SPP(串行端口配置文件):这定义了通过RFCOMM的虚拟串行端口连接的要求。
注意:要查看Linux系统中的可用蓝牙配置文件,请运行sdptool browse local。

HCI(主机控制器接口)
蓝牙主机和蓝牙控制器在HCI的帮助下进行通信。它包含在蓝牙主机和蓝牙控制器之间提取和传输数据的驱动程序。这些驱动程序使用一组发送和接收命令,数据包和事件的功能来实现蓝牙主机和蓝牙控制器之间的通信。
主机和控制器之间的通信是通过HCI包完成的,其中有四种类型。

命令数据包:这些数据包由主机生成并发送到控制器或蓝牙适配器来控制适配器。它们可以用来启动设备查询,连接到远程设备等。
事件数据包:这些数据包从蓝牙适配器发送到主机。无论何时发生事件,都会发送这些数据包,例如发送有关本地蓝牙适配器的信息,连接到远程设备等。
ACL数据包:ACL数据包由HCI层封装后传输到蓝牙适配器;然而,HCI头部被移除,并且裸ACL包在空中被传送并且在被接收之后被重新包装。
同步数据包:这些包也被封装在HCI层并传递给蓝牙适配器。然后,在解包HCI头部之后,它们被发送,但是在被接收之后也被重新包装。
蓝牙控制器
蓝牙控制器是蓝牙设备,如USB加密狗。它是蓝牙协议栈的硬件和固件部分。它实现了最低级别的蓝牙架构,包括物理层和无线电收发器。蓝牙控制器通过HCI从蓝牙主机接收命令。蓝牙控制器具有诸如链路管理器协议(LMP),基带和无线电的层。
链路管理协议(LMP):负责蓝牙设备之间的逻辑链路建立。它链接认证和加密,并创建,更新和删除逻辑链接和逻辑传输。它还更新到蓝牙设备的物理链路的参数。
基带:该层有一个链路控制器,负责蓝牙体系结构的物​​理层。它建立和管理形成微微网的蓝牙设备之间的物理射频链路。
无线电:这是一个蓝牙收发器,用于向基带和从基带传输数据。它是蓝牙架构的最底层。蓝牙协议栈的完整视图见图6。
无连线的Linux——蓝牙的基础
用蓝牙编码
编码之前,您需要开发文件链接到BlueZ库。为此,您必须安装libbluetooth-dev软件包:

sudo apt-get install libbluetooth-dev
现在,让我们做一些基本的蓝牙编程,如搜索蓝牙设备。

搜索蓝牙设备
要搜索蓝牙设备,您需要一些API。 BlueZ公开了类似于网络套接字编程的套接字API。如果您熟悉Linux中的网络编程,学习BlueZ API将会很简单。
用于指定6字节蓝牙设备地址的基本数据结构是bdaddr_t。在BlueZ中,每个蓝牙地址都被存储为bdaddr_t结构(见图7)。
无连线的Linux——蓝牙的基础
BlueZ提供了两个函数来在字符串和bdaddr_t结构之间进行转换:


int str2ba(const char * str,bdaddr_t * ba);
int ba2str(const bdaddr_t * ba,char * str);
头文件需要
在使用这些API之前,您需要一些头文件:


#include <bluetooth/bluetooth.h>
#include<Bluetooth/hci.h>
#include<Bluetooth/hci_lib.h>

注意:这些头文件在/ usr / include / blutooth目录中可用。
获取蓝牙适配器标识
您可以使用以下API获取蓝牙适配器资源编号:

int hci_get_route(bdaddr_t * bdaddr);
为本地蓝牙适配器分配从0开始的识别号码,并且程序必须指定在分配系统资源时使用哪个适配器。 通常,只有一个适配器或使用哪一个并不重要,所以将NULL传递给hci_get_route将检索第一个可用的蓝牙适配器的ID:

dev_id = hci_get_route(NULL);
注意:如果存在多个适配器,则通过将地址的char *表示形式传递给hci_devid来选择适配器:
dev_id = hci_devid( 01:43:24:9A:45:C3);

无连线的Linux——蓝牙的基础
打开HCI的文件句柄
获得蓝牙适配器ID后,可以打开主控制器接口的文件句柄,以便我们可以将命令传递给蓝牙控制器。

int fh = hci_open_dev(dev_id);
不要用0来硬编码设备ID,因为零并不总是第一个适配器的ID。如果系统有两个适配器并且第一个适配器(ID 0)被禁用,则第一个可用适配器是ID 1的适配器。
inquiry_info结构用于存储蓝牙设备类型的地址(见图8)。
该结构的第一个字段bdaddr_t给出了检测到的蓝牙设备的地址。 dev_class字段提供有关检测到的设备类型的信息,例如是电话,打印机还是计算机。

inquiry_info * ii = NULL;
你可以从http://www.wensley.org.uk/c/inq/reporter.c得到关于dev_class的更多信息

获取蓝牙地址和蓝牙设备的类型
选择本地蓝牙适配器和文件句柄到主控制器接口后,就可以在附近扫描蓝牙设备了。 hci_inquiry发现蓝牙设备并存储有关它们的基本信息,如蓝牙地址,
设备的类型等,在inquiry_info结构中。

int hci_inquiry(int dev_id,int time_len,int max_rsp,const uint8_t * lap,inquiry_info ** ii,long flags);
对设备的查询或发现持续时间最多为1.28 * time_len秒,并且至多max_rsp设备将在输出参数ii中返回。

注意:为了获得足够的查询时间,对time_len使用8,即10.24秒,对max_rsp使用255。如果要刷新先前检测到的设备的缓存,请将标志设置为IREQ_CACHE_FLUSH;否则,使用0,hci_inquiry也将返回超出范围,但仍在缓存中的蓝牙设备。

现在我们有所有可用设备的蓝牙地址。借助这些地址,您可以获取设备的名称。
int hci_read_remote_name(int fh,const bdaddr_t * ba,int len,char * name,int timeout);
hci_read_remote_name尝试超时毫秒,使用fh文件句柄在蓝牙地址ba的帮助下查询设备的用户友好名称,并将友好名称的len字节存储到名称中。
在此之后,不要忘记释放inquiry_info结构并关闭到主机控制器接口的文件句柄。
free( ii );
close ( fh );

如何运行程序并获得输出
首先,通过调用gcc和libbluetooth链接来编译这个程序:

#gcc scan_bt_devices.c -lbluetooth
输出见图9。虽然不需要提及,但在运行此程序之前,设备的蓝牙必须处于开启状态。您可以手动打开蓝牙,也可以使用rfkill命令将其打开:

无连线的Linux——蓝牙的基础
rfkill解锁蓝牙
您可以同样关闭它:

rfkill块蓝牙
RFCOMM和L2CAP套接字
L2CAP连接与UDP协议类似,都是基于数据包的协议,但它们之间有区别。在UDP中,从计算机发送的两个数据包可能是随机到达的,这意味着第二个数据包可能到达第一个数据包。但在L2CAP中,第一个数据包将始终达到第一个数据包。

RFCOMM连接就像TCP一样。但是RFCOMM只支持30个非保留信道(TCP中称为端口),在一台机器上没有保留信道,而TCP支持65,535个端口。

在L2CAP中,端口被称为协议服务多路复用器(PSM),取1到32,767之间的奇数值,其中从1到4095的奇数编号的PSM被保留,而从4097到32765的奇数编号的PSM未被保留。

L2CAP连接封装在ACL(异步面向连接的逻辑)连接中,并且RFCOMM连接在L2CAP连接内传输,所以RFCOMM连接也封装在ACL连接中。

RFCOMM和L2CAP服务器
RFCOMM和L2CAP具有套接字API来打开套接字描述符,如TCP / IP编程。但在这里我们有蓝牙家庭的AF_BLUETOOTH。

注意:对于RFCOMM套接字,包括bluetooth / rfcomm.h头文件和L2CAP的bluetooth / l2cap.h,以及蓝牙/ bluetooth.h和sys / socket.h。

RFCOMM具有SOCK_STREAM类型和BTPROTO_RFCOMM协议:

s = socket(AF_BLUETOOTH,SOCK_STREAM,BTPROTO_RFCOMM);
L2CAP具有SOCK_SEQPACKET类型和BTPROTO_L2CAP协议:

s = socket(AF_BLUETOOTH,SOCK_SEQPACKET,BTPROTO_L2CAP);
TCP / IP具有sockadd_in结构,类似地,L2CAP具有sockaddr_l2结构,而RFCOMM具有sockaddr_rc结构(参见图10和图11)。
无连线的Linux——蓝牙的基础无连线的Linux——蓝牙的基础
注意:内核2.4.37之后,sockaddr_l2结构有一些额外的字段,如l2_cid和l2_bdaddr_type。


使用BADDR_ANY宏,以便服务器可以侦听所有本地蓝牙适配器。蓝牙使用htobs和btohs,而不是使用TCP / IP中使用的hton和ntohs。
对于RFCOMM,输入:

loc_addr。 rc_family = AF_BLUETOOTH;
loc_addr。 rc_bdaddr = BDADDR ANY;
loc_addr。 rc_channel = 1;
对于L2CAP,输入:

loc_addr。 l2_family = AF_BLUETOOTH;
loc_addr。 l2_bdaddr = BDADDR ANY;
loc_addr。 l2_psm = htobs(0x1001);
既然你知道TCP / IP的概念,你可以很容易的理解下面提到的API,比如bind,listen,accept,send和recv。
对于RFCOMM和L2CAP,输入:

bind(s,(struct sockaddr *)&loc_addr,sizeof(loc_addr));
听(s,1); //为一个连接
client = accept(s,(struct sockaddr *)&rem_addr,&(sizeof(rem_addr)));
注意:打印接收到的蓝牙地址之前,请使用本文前面介绍的ba2str API将其转换为字符串。您也可以通过访问bdaddr_t结构来打印地址。


现在从客户端接收数据:

b = recv(client,buf,sizeof(buf),0);
如果(b> 0){
printf(??收到[%s] \ n ??,buf);
}

读完数据后,关闭socket。

close ( client );
close ( S );


RFCOMM和L2CAP客户端
对于客户端,首先打开socket。


对于RFCOMM,输入:

s = socket(AF_BLUETOOTH,SOCK_STREAM,BTPROTO_RFCOMM);
对于L2CAP,输入:

s = socket(AF_BLUETOOTH,SOCK_SEQPACKET,BTPROTO_L2CAP);
现在连接这个套接字与现有的服务器。
对于RFCOMM,输入:
struct sockaddr_rc addr;
char dest[18] = “<Bluetooth Address>” //putserver’s bluetooth adapter address
addr . rc_family = AF_BLUETOOTH ;
addr . rc_psm = 1 ;
str2ba ( dest , &addr . rc_bdaddr ) ;
check = connect ( s , ( struct sockaddr * )&addr , sizeof ( addr ) ) ;

对于L2CAP,输入:

struct sockaddr_l2 addr;
char dest[18] = “<Bluetooth Address>” //putserver’s bluetooth adapter address you can findaddress from commandhciconfig -a.
addr . l2_family = AF_BLUETOOTH ;
addr . l2_psm = htobs(0x1001 ) ;
str2ba ( dest , &addr . l2_bdaddr ) ;
connect ( s , ( struct sockaddr * )&addr , sizeof ( addr ) ) ;

现在你可以发送数据到服务器了:

if( check == 0 ) {
status = send ( s , “Hi Subodh “ , 10 , 0 ) ;
}

与具有蓝牙功能的移动设备配对
您的手机也有一个服务器,以便它可以连接到客户端,并发送和接收文件。 要将您的系统与移动设备配对,首先,您必须打开一个套接字:

s = socket(AF_BLUETOOTH,SOCK_STREAM,BTPROTO_RFCOMM);
现在连接到手机的蓝牙地址:
struct sockaddr_rc addr;
char dest[18] = “<Mobile Bluetooth Address>” ;
addr . rc_family = AF_BLUETOOTH ;
addr . rc_psm = 1 ;
str2ba ( dest , &addr . rc_bdaddr ) ;
connect ( s , ( struct sockaddr * )&addr , sizeof ( addr ) ) ;


当你运行这个程序时,你的移动服务器将收到一个绑定接受消息。
接受此连接并发送加***。
从手机发送**后,系统将显示一个配对确认窗口。 输入您在手机中输入的号码
你现在已经连接到移动的服务器。
作为练习,尝试实现蓝牙耳机配置文件(HSP)。

参考

[1] http://people.csail.mit.edu/albert/bluez-intro/c404.html#bzi-choosing
[2] http://en.wikipedia.org/wiki/Bluetooth_stack
[3] http://www.drdobbs.com/mobile/using-bluetooth/232500828?pgno=1
[4] http://en.wikipedia.org/wiki/List_of_Bluetooth_protocols
[5] http://www.eetimes.com/document.asp?doc_id=1275960
[6] http://en.wikipedia.org/wiki/Bluetooth
[7] https://wusewj-linux-books.googlecode.com/files/Essential%20Linux%20Device%20Drivers%5B2008,851p%5D.pdf