31.5. 蓝牙

Written by Pav Lucistnik.

Bluetooth (蓝牙) 是一项无线技术, 用于建立带宽为 2.4GHZ,波长为 10 米的私有网络。 网络一般是由便携式设备,比加手机 (cellular phone), 掌上电脑 (handhelds) 和膝上电脑 (laptops)) 以 ad-hoc 形式组成。不象其它流行的无线技术――Wi-Fi,Bluetooth 提供了更高级的服务层面,像类 FTP 的文件服务、文件推送 (file pushing)、语音传送、串行线模拟等等。

本章介绍如何在 FreeBSD 上使用 USB Bluetooth dongle。还将介绍蓝牙的协议和程序。

31.5.1. 加载蓝牙支持

在 FreeBSD 里,蓝牙栈 (Bluetooth stack) 通过使用 Netgraph 框架 (请看 netgraph(4)) 来的实现。 大量的"Bluetooth USB dongle"由 ng_ubt(4) 驱动程序支持。 基于 Broadcom BCM2033 芯片组的 Bluetooth 设备可以通过 ubtbcmfw(4)ng_ubt(4) 驱动程序支持。 3Com Bluetooth PC 卡 3CRWB60-A 由 ng_bt3c(4) 驱动程序支持。 基于 Serial 和 UART 的蓝牙设备由 sio(4)ng_h4(4)hcseriald(8)

在连接设备之前,查看要使用哪个驱动程序驱动程序,然后加载驱动程序。例如,如果设备使用 ng_ubt(4)驱动程序:

# kldload ng_ubt

如果系统启动时 Bluetooth 设备已经存在于系统里, 那么从 /boot/loader.conf 里加载这个模块:

ng_ubt_load="YES"

驱动加载后,插入 USB dongle。如果驱动工作正常,会在控制台和/var/log/messages出现下面这样的信息:

ubt0: vendor 0x0a12 product 0x0001, rev 1.10/5.25, addr 2
ubt0: Interface 0 endpoints: interrupt=0x81, bulk-in=0x82, bulk-out=0x2
ubt0: Interface 1 (alt.config 5) endpoints: isoc-in=0x83, isoc-out=0x3,
      wMaxPacketSize=49, nframes=6, buffer size=294

要启动和停止蓝牙堆栈,请使用其启动脚本。最好在拔下设备之前停止堆栈。启动蓝牙堆栈前需要先启动hcsecd(8)。启动堆栈时,将返回类似内容:

# service bluetooth start ubt0
BD_ADDR: 00:02:72:00:d4:1a
Features: 0xff 0xff 0xf 00 00 00 00 00
<3-Slot> <5-Slot> <Encryption> <Slot offset>
<Timing accuracy> <Switch> <Hold mode> <Sniff mode>
<Park mode> <RSSI> <Channel quality> <SCO link>
<HV2 packets> <HV3 packets> <u-law log> <A-law log> <CVSD>
<Paging scheme> <Power control> <Transparent SCO data>
Max. ACL packet size: 192 bytes
Number of ACL packets: 8
Max. SCO packet size: 64 bytes
Number of SCO packets: 8

31.5.2. 寻找其他蓝牙设备

主机控制器接口 (HCI) 提供了访问蓝牙基带功能的统一方法。在 FreeBSD 中,为每个蓝牙设备创建一个 netgraph HCI节点。详情请参阅ng_hci(4)

最常见的任务之一是通过RF发现附近的蓝牙设备。这个操作被称为查询(inquiry)。查询和其他与HCI相关的操作,请使用hccontrol(8)进行。下面的例子显示了如何找出哪些蓝牙设备在范围内。设备列表应该会在几秒钟内显示出来。请注意,只有当远程设备被设置为discoverable模式时,才会回应查询。

% hccontrol -n ubt0hci inquiry
Inquiry result, num_responses=1
Inquiry result #0
       BD_ADDR: 00:80:37:29:19:a4
       Page Scan Rep. Mode: 0x1
       Page Scan Period Mode: 00
       Page Scan Mode: 00
       Class: 52:02:04
       Clock offset: 0x78ef
Inquiry complete. Status: No error [00]

BD_ADDR是蓝牙设备的唯一地址,类似于网卡的MAC地址。与设备进行进一步通信时需要该地址,可以为BD_ADDR指定别名名。已知蓝牙主机的信息位于/etc/bluetooth/hosts。下面的例子显示了如何获得分配给远程设备的人可读名称:

% hccontrol -n ubt0hci remote_name_request 00:80:37:29:19:a4
BD_ADDR: 00:80:37:29:19:a4
Name: Pav's T39

如果在远程蓝牙设备上执行查询,它将发现计算机的名字为your.host.name (ubt0)。分配给本地设备的名称可以随时更改。

可以在 /etc/bluetooth/hosts 中设置远程设备的别名。更多关于 /etc/bluetooth/hosts 的信息可以在 bluetooth.hosts(5) 找到。

蓝牙系统提供了两个蓝牙设备之间的点对点连接,或者是点对多点连接,该连接在多个蓝牙设备之间共享。下面的例子显示了如何创建一个与远程设备的连接:

% hccontrol -n ubt0hci create_connection BT_ADDR

create_connection接受BT_ADDR以及/etc/bluetooth/hosts中的主机别名。

下面的示例演示如何获取本地设备的活动基带连接列表:

% hccontrol -n ubt0hci read_connection_list
Remote BD_ADDR    Handle Type Mode Role Encrypt Pending Queue State
00:80:37:29:19:a4     41  ACL    0 MAST    NONE       0     0 OPEN

当需要终止基带连接时,一个连接句柄很有用,尽管通常不需要手动终止基带连接。堆栈将自动终止非活动的基带连接。

# hccontrol -n ubt0hci disconnect 41
Connection handle: 41
Reason: Connection terminated by local host [0x16]

键入hccontrol help,以获得可用的HCI命令的完整列表。大多数 HCI命令不需要超级用户权限。

31.5.3. 设备配对

默认情况下,蓝牙通信不进行身份验证,任何设备都可以与任何其他设备进行通话。蓝牙设备,如手机等蓝牙设备,可以选择需要认证才能提供特定服务。蓝牙认证通常通过PIN代码来完成,该代码是一个长度不超过16个字符的ASCII字符串。用户必须在两个设备上输入相同的PIN代码。一旦用户输入了PIN代码,两个设备都将生成一个链接密钥。之后,这个链接密钥可以存储在设备中或持久化存储中。下一次,两个设备都将使用之前生成的链接密钥。这个过程被称为pairing。注意,如果链接密钥被任何一个设备丢失,必须重新配对。

hcsecd(8) 负责处理蓝牙身份验证请求。默认配置文件位于/etc/bluetooth/hcsecd.conf。本例中手机的 PIN1234,如下所示:

device {
        bdaddr  00:80:37:29:19:a4;
        name    "Pav's T39";
        key     nokey;
        pin     "1234";
      }

PIN代码的唯一限制是长度。某些设备,如蓝牙耳机,可能会内置固定的PIN代码。-d开关迫使hcsecd(8)保持在前台,这样很容易看到正在发生的事情。将远程设备设置为接收配对并启动与远程设备的蓝牙连接。远程设备应表示已接受配对并请求PIN代码。输入hcsecd.conf中列出的相同的PIN代码。现在,计算机和远程设备已经配对。另外,也可以在远程设备上启动配对。

可以在 /etc/rc.conf中添加以下一行来配置hcsecd(8),以便在系统启动时自动启动:

hcsecd_enable="YES"

以下是hcsecd(8)守护进程输出的示例:

hcsecd[16484]: Got Link_Key_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4
hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', link key doesn't exist
hcsecd[16484]: Sending Link_Key_Negative_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4
hcsecd[16484]: Got PIN_Code_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4
hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', PIN code exists
hcsecd[16484]: Sending PIN_Code_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4

31.5.4. 使用PPP Profile存取网络

拨号网络(DUN)配置文件可用于将蜂窝电话配置为无线调制解调器,以连接到Internet访问服务器。它还可以用于配置计算机以接收来自蜂窝电话的数据呼叫。

PPP配置文件的网络访问可以为单个蓝牙设备或多个蓝牙设备提供LAN访问。它还可以使用PPP通过串行电缆仿真联网提供PCPC的连接。

在FreeBSD中。这些配置文件使用 ppp(8)rfcomm_pppd(8)包装器,它可以将蓝牙连接转换为PPP可以使用的东西。在使用配置文件之前,必须在/etc/ppp/ppp.conf中创建一个新的PPP标签。相关示例请参阅 rfcomm_pppd(8)

在本例中,rfcomm_pppd(8)用于在 DUN RFCOMM通道上打开一个到远程设备的连接,其BD_ADDR00:80:37:29:19:19:a4

# rfcomm_pppd -a 00:80:37:29:19:a4 -c -C dun -l rfcomm-dialup

实际的通道号将使用SDP协议从远程设备上获得。可以手动指定RFCOMM通道,在这种情况下,rfcomm_pppd(8)将不执行SDP查询。使用sdpcontrol(8)查询远程设备上的RFCOMM通道。

为了使用PPP LAN服务提供网络访问,必须运行sdpddp(8),并在/etc/ppp/ppp.conf中为LAN客户端创建一个新的条目。请参考 rfcomm_pppd(8)以了解相关示例。最后,在有效的RFCOMM通道号上启动RFCOMM服务器。RFCOMM PPP服务器将自动向本地SDP守护进程注册蓝牙LAN服务。下面的示例显示了如何启动 RFCOMM PPP服务器。

# rfcomm_pppd -s -C 7 -l rfcomm-server

31.5.5. 蓝牙通信协议

本节概述了各种蓝牙协议、功能和相关程序。

31.5.5.1. 逻辑链路控制和适应协议 (L2CAP

逻辑链路控制和适应协议(L2CAP)为上层协议提供面向连接和无连接的数据服务。L2CAP允许上层协议和应用程序发送和接收L2CAP数据包,长度不超过64k字节。

L2CAP基于信道的概念。信道是建立在基带连接之上的逻辑连接,每个信道以多对一的方式绑定到一个协议上。多个信道可以绑定到同一个协议,但一个信道不能绑定到多个协议。在一个信道上接收到的每个L2CAP数据包都被指向相应的上一级协议。多个信道可以共享同一个基带连接。

在 FreeBSD 中,每个蓝牙设备都会创建一个 netgraph L2CAP 节点。这个节点通常连接到下游蓝牙 HCI 节点和上游蓝牙套接字节点。L2CAP节点的默认名称是devicel2cap。有关详细信息,请参阅ng_l2cap(4)

可以使用 l2ping(8) 检查设备间的联通性。某些蓝牙实现可能不会返回发送给它们的所有数据, 因此在下面的示例中 0 bytes是正常的。

# l2ping -a 00:80:37:29:19:a4
0 bytes from 0:80:37:29:19:a4 seq_no=0 time=48.633 ms result=0
0 bytes from 0:80:37:29:19:a4 seq_no=1 time=37.551 ms result=0
0 bytes from 0:80:37:29:19:a4 seq_no=2 time=28.324 ms result=0
0 bytes from 0:80:37:29:19:a4 seq_no=3 time=46.150 ms result=0

l2control(8)实用程序用于对L2CAP节点执行各种操作。这个例子显示了如何获得本地设备的逻辑连接(通道)列表和基带连接列表:

% l2control -a 00:02:72:00:d4:1a read_channel_list
L2CAP channels:
Remote BD_ADDR     SCID/ DCID   PSM  IMTU/ OMTU State
00:07:e0:00:0b:ca    66/   64     3   132/  672 OPEN
% l2control -a 00:02:72:00:d4:1a read_connection_list
L2CAP connections:
Remote BD_ADDR    Handle Flags Pending State
00:07:e0:00:0b:ca     41 O           0 OPEN

另一个诊断工具是btsockstat(1)。它类似于netstat(1),但用于蓝牙网络相关的数据结构。下面的例子显示了与上述l2control(8)相同的逻辑连接。

% btsockstat
Active L2CAP sockets
PCB      Recv-Q Send-Q Local address/PSM       Foreign address   CID   State
c2afe900      0      0 00:02:72:00:d4:1a/3     00:07:e0:00:0b:ca 66    OPEN
Active RFCOMM sessions
L2PCB    PCB      Flag MTU   Out-Q DLCs State
c2afe900 c2b53380 1    127   0     Yes  OPEN
Active RFCOMM sockets
PCB      Recv-Q Send-Q Local address     Foreign address   Chan DLCI State
c2e8bc80      0    250 00:02:72:00:d4:1a 00:07:e0:00:0b:ca 3    6    OPEN

31.5.5.2. 射频通信 (Radio Frequency Communication RFCOMM)

RFCOMM协议通过L2CAP协议提供串行端口的仿真。RFCOMM是一种简单的传输协议,为模拟 RS-232 (EIATIA-232-E) 串行端口的 9 个电路提供了额外规定。它支持两个蓝牙设备之间多达 60 个同时连接(RFCOMM通道)。

为了实现 RFCOMM, 运行于不同设备上的应用程序建立起一条关于它们之间通信段的通信路径。 RFCOMM 实际上适用于使用串行端口的应用软件。 通信段是一个设备到另一个设备的蓝牙连接 (直接连接)。

RFCOMM只关注直连箱中的设备之间的连接,或者设备与网络箱中的调制解调器之间的连接。RFCOMM可以支持其他配置,例如通过蓝牙无线技术进行通信的模块,在一侧通过蓝牙无线技术进行通信,在另一侧提供有线接口。

在 FreeBSD 中,RFCOMM在 Bluetooth sockets 层实现。

31.5.5.3. 服务发现协议(Service Discovery Protocol,SDP

服务发现协议(Service Discovery Protocol,SDP)为客户端应用程序提供了发现服务器应用程序提供的服务的存在以及这些服务的属性的方法。服务的属性包括所提供的服务的类型或类别,以及利用该服务所需的机制或协议信息。

SDP涉及SDP服务器和SDP客户端之间的通信。服务器维护着一个服务记录列表,这些记录描述了与服务器相关联的服务特征。每个服务记录包含有关单个服务信息。客户端可以通过发出 SDP请求,从 SDP服务器维护的服务记录中检索信息。如果客户机或与客户机相关联的应用程序决定使用服务,它必须打开与服务提供商的单独连接才能使用该服务。SDP提供了一种发现服务及其属性的机制,但它并没有提供利用这些服务的机制。

通常情况下,SDP客户端会根据服务的某些期望特征来搜索服务。然而,有时候,在没有任何关于服务的事先信息的情况下,发现SDP服务器的服务记录所描述的服务类型是可取的。这种寻找任何提供的服务的过程被称为浏览(browsing)

蓝牙 SDP 服务器, sdpddp(8) 和命令行客户端, sdpcontrol(8), 都包含在标准的 FreeBSD 安装中。下面的示例显示了如何执行 SDP 浏览查询。

% sdpcontrol -a 00:01:03:fc:6e:ec browse
Record Handle: 00000000
Service Class ID List:
        Service Discovery Server (0x1000)
Protocol Descriptor List:
        L2CAP (0x0100)
                Protocol specific parameter #1: u/int/uuid16 1
                Protocol specific parameter #2: u/int/uuid16 1

Record Handle: 0x00000001
Service Class ID List:
        Browse Group Descriptor (0x1001)

Record Handle: 0x00000002
Service Class ID List:
        LAN Access Using PPP (0x1102)
Protocol Descriptor List:
        L2CAP (0x0100)
        RFCOMM (0x0003)
                Protocol specific parameter #1: u/int8/bool 1
Bluetooth Profile Descriptor List:
        LAN Access Using PPP (0x1102) ver. 1.0

注意,每个服务都有一个属性列表,例如RFCOMM通道。根据服务的不同,用户可能需要注意到其中的一些属性。一些蓝牙实现不支持服务浏览,可能会返回一个空列表。在这种情况下,可以搜索特定的服务。下面的示例显示了如何搜索OBEX对象推送(OPUSH)服务:

% sdpcontrol -a 00:01:03:fc:6e:ec search OPUSH

要在 FreeBSD 里为蓝牙客户端提供服务,可以使用 sdpd(8) 服务。 您可以通过在 /etc/rc.conf 中加入下面的行:

sdpd_enable="YES"

然后,使用以下命令启动sdpdp(8)

# service sdpd start

想要向远程客户端提供蓝牙服务的本地服务器应用程序将向本地SDP守护进程注册该服务。一个例子是rfcomm_pppd(8)。一旦启动,它将向本地SDP守护进程注册蓝牙LAN服务。

通过本地控制通道发起SDP浏览查询,可以获得在本地SDP服务器上注册的服务列表:

# sdpcontrol -l browse

31.5.5.4. OBEX 对象推送 (OPUSH)

对象交换(OBEX)是一种广泛使用的协议,用于移动设备之间的简单文件传输。它主要用于红外通信,用于在笔记本和PDA之间传输文件,以及在手机和其他带有个人信息管理器(PIM)应用程序的设备之间发送名片或日历条目。

OBEX服务器和客户端由obexapp实现,可以从 ports 或 package 安装comms/obexapp

OBEX客户端用于从OBEX服务器推送和/或拉取对象。一个例子是一张名片或一个预约。OBEX客户端可以通过SDP从远程设备获取RFCOMM通道号。这可以通过指定服务名称而非 RFCOMM 通道号来实现。支持的服务名称有:IrMCFTRNOPUSH。也可以直接指定 RFCOMM 通道号(数字)。下面是一个OBEX会话的例子,在这个例子中,设备信息对象被从手机中拉出,一个新的对象,即名片,被推送到手机的目录中。

% obexapp -a 00:80:37:29:19:a4 -C IrMC
obex> get telecom/devinfo.txt devinfo-t39.txt
Success, response: OK, Success (0x20)
obex> put new.vcf
Success, response: OK, Success (0x20)
obex> di
Success, response: OK, Success (0x20)

为了提供OPUSH服务,需要运行sdpd(8),并且创建一个根文件夹,用于存储所有传入对象。根文件夹的默认路径是/var/spool/obex。最后,在有效的RFCOMM信道号上启动OBEX服务器。OBEX服务器将自动将OPUSH服务注册到本地SDP守护进程。下面的示例演示如何启动OBEX服务器。

# obexapp -s -C 10

31.5.5.5. 串口配置文件 (SPP

串行端口配置文件(SPP)允许蓝牙设备进行串行电缆仿真。该配置文件允许传统的应用程序通过虚拟的串行端口抽象,将蓝牙作为线缆的替代品。

在 FreeBSD 中,rfcomm_sppd(1)实现了 SPP,并使用伪 tty 作为虚拟串口抽象。下面的例子显示了如何连接到远程设备的串口服务。RFCOMM通道不必指定为rfcomm_sppd(1)可以通过SDP从远程设备获取。要覆盖这一点,请在命令行中指定一个RFCOMM通道。

# rfcomm_sppd -a 00:07:E0:00:0B:CA -t
rfcomm_sppd[94692]: Starting on /dev/pts/6...
/dev/pts/6

连接后,可以像使用串口一样使用虚拟终端(pseudo tty):

# cu -l /dev/pts/6

伪 tty 打印在 stdout 上,可以通过包装脚本读取:

PTS=`rfcomm_sppd -a 00:07:E0:00:0B:CA -t`
cu -l $PTS

31.5.6. 故障排除

默认情况下,当FreeBSD在接受一个新的连接时,会尝试执行角色切换并成为 master。一些不支持角色切换的老式蓝牙设备将无法连接。因为角色切换是在建立新连接时执行的, 所以无法询问远程设备是否支持角色切换。但是,有一个HCI选项可以在本地侧禁用角色切换:

# hccontrol -n ubt0hci write_node_role_switch 0

要显示蓝牙数据包,请使用第三方软件包hcidump,可以从 package 或 ports 安装 comms/hcidump。该实用程序类似于tcpdump(1),可用于在终端上显示蓝牙数据包的内容,并将蓝牙数据包转储到文件中。

本文档和其它文档可从这里下载: ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题,请先阅读 文档,如不能解决再联系 <questions@FreeBSD.org>.

关于本文档的问题请发信联系 <doc@FreeBSD.org>.