相关文章推荐
迷茫的红豆  ·  file handling - ...·  1 年前    · 
忧郁的麻辣香锅  ·  研究报告 | ...·  1 年前    · 

主机实现到 HCI 层,底层由蓝牙芯片实现。 HCI 层实现的是蓝牙芯片与主机通讯的方式。目前一般是 串口或者 USB 通讯 。所谓的 USB 也不是真正意义上的 USB 通讯,而是类似与 USB 转串口的方式,即通过驱动模拟 USB 设备实现串口通讯。目前 USB 蓝牙适配器 基本都是这种设备模式。 PC 端实现了 L2CAP, SDP, RFCOMM 协议,以及 USB 转串口的驱动。 Windows XP SP2 操作系统以上版本的都内置了这些协议栈,还有如 WIDCOMM 等公司提供的第三方协议栈。

实际中只需在市场上购买这种蓝牙适配器( USB 接口),然后通过配置内核蓝牙的接口驱动(即上图中的 HCI 层驱动),这样相应的蓝牙协议( linux 官方版本是 bluez )就已经在内核中了,这就相当于内核驱动中已经支持了相应的蓝牙协议( SDP,RFCOMM... ),有了驱动就需要接口库提供给应用程序使用,这里用到的接口库是开源的 bluez ,其实就是要在内核之上移植 bluez 及工具 bluez-utils

bluez 分为两部分:内核代码和用户态程序及工具集。

内核代码: bluez 核心协议和驱动程序等模块组成。自从 linux2.4.6 开始 linux 内核集成 bluez

这个是最底层的了,称为 Host Control Interface. 之所以称为 是源于蓝牙的应用模型的。蓝牙是连接智能外设的无线接口,接口的一侧是设备,另一侧就是主机 (Host) 了,采用类似记法的还有 IEEE1394 ,所以,从设计初衷来看,这几个东东都是针对差不多的市场的,当然,各有所长了。一个蓝牙适配器是否能被驱动起来,就看 的支持性了。最常见的蓝牙适配器就是笔者持有的这类 接口的了,对于大部分标准的蓝牙设备,它的驱动模块是 : hci-usb ,对于我们的 内核,插入这个适配器,该模块就被自动加载了。

L2CAP 之上有两个协议被较广地使用着: RFCOMM BNEP ,前者用于取代传统的串行口,包括串行口上的各种应用,比如,传真和拨号上网、打印机、文件图片等数据传输;后者则可以提供一个以太网接口,更适于计算机组网。自然地,对于手机和计算机之间, RFCOMM 总是更常被用到。

内核蓝牙配置:

[*] Networking support --->
<*> Bluetooth subsystem support ---> //蓝牙子系统必须选择
<*> L2CAP protocol suppor //逻辑链路控制和适配协议。
<*> SCO links support //蓝牙语音和耳机支持
<*> RFCOMM protocol suppor //面向流的传输协议,支持拨号网络等
[*] RFCOMM TTY support //
<*> BNEP protocol support //蓝牙网络封装协议,自组网支持
[*] Multicast filter support //蓝牙多播,支持BNEP
[*] Protocol filter support
<*> HIDP protocol support //基本支持协议
Bluetooth device drivers --->
<*> HCI USB driver //USB蓝牙模块支持
<M>HCI UART driver //基于串口,CF卡或PCMCIA的蓝牙
<*> HCI BlueFRITZ! USB driver
<*> HCI VHCI (Virtual HCI device) driver

此外,在 Bluetooth device drivers 里选上你所需要支持的 Bluetooth 设备。若使用 CSR chip ,通过串口和 cpu 通讯的,芯片默认使用 BCSP 作为通讯协议,所以选择 HCI UART driver BCSP protocol support

若是通过 usb 接口使用蓝牙适配器,需要选择 HCI USB driver。

用户态部分

用户态程序及工具集:应用程序接口和 bluez 工具集。

bluez 软件包名称: bluez ,提供 bluetoothd 守护进程。

bluez 工具集: bluez-utils ,提供 bluetoothctl 命令。

可用 bluetoothctl 完成蓝牙设备配对,步骤如下:

(optional) Select a default controller with select MAC_address .

Enter power on to turn the power to the controller on. It is off by default and will turn off again each reboot, see #Auto power-on after boot .

Enter devices to get the MAC Address of the device with which to pair.

Enter device discovery mode with scan on command if device is not yet on the list.

Turn the agent on with agent on or choose a specific agent: if you press tab twice after agent you should see a list of available agents, e.g. DisplayOnly KeyboardDisplay NoInputNoOutput DisplayYesNo KeyboardOnly off on.

Enter pair MAC_address to do the pairing (tab completion works).

If using a device without a PIN, one may need to manually trust the device before it can reconnect successfully. Enter trust MAC_address to do so.

Enter connect MAC_address to establish a connection.

一个操作示例如下,连接蓝牙音箱(bluetoothctl命令下):

select 48:51:B7:DE:56:DD //48:51:B7:DE:56:DD为网关蓝牙模块的地址
power off
power on
agent on
default-agent
scan on //搜索蓝牙设备,等待,直到待连接设备被搜索到
pair FC:58:FA:5B:7E:DD //配对
connect FC:58:FA:5B:7E:DD //连接,会提示连接成功
exit //退出bluetoothctl

蓝牙音频 Audio

蓝牙 profile

蓝牙 profile 协议概览 .pdf

Bluetooth 的一个很重要特性,就是所有的 Bluetooth 产品都无须实现全部的 Bluetooth 规范。为了更容易的保持 Bluetooth 设备之间的兼容, Bluetooth 规范中定义了 Profile Profile 定义了设备如何 实现一种连接或者应用 ,你可以把 Profile 理解为 连接层或者应用层协议

比如,如果一家公司希望它们的 Bluetooth 芯片支援所有的 Bluetooth 耳机,那么它只要支持 HeadSet Profile 即可,而无须考虑该芯片与其它 Bluetooth 设备的通讯与兼容性问题。如果你想购买 Bluetooth 产品,你应该了解你的应用需要哪些 Profile 来完成,并且确保你购买的 Bluetooth 产品支持这些 Profile

之所以把 Profile 翻译为配置文件 , 是为避免和 JavaME 中的简表混淆,配置文件也是蓝牙 SIG 官方网站给出的标准翻译。

想要使用蓝牙无线技术 , 设备必须能够翻译特定 蓝牙配置文件 , 配置文件 定义了可能的应用 。蓝牙配置文件 表达了一般行为 , 蓝牙设备可以 通过这些行为 与其他设备进行通信。

蓝牙技术定义了广泛的配置文件 , 描述了许多不同类型的使用安全。按蓝牙规格中提供的指导 , 开发商可创建应用程序以用来与其他符合蓝牙规格的设备协同工作。在最低限度下 , 各配置文件规格应包含下列主题的相关信息:

1 )与其他配置文件的相关性。

2 )建议的用户界面格式。

3 )配置文件使用的蓝牙协议堆栈的特定部分。

为执行其任务 , 每个配置文件都使用堆栈各层上的 特定选项和参数 。若需要 , 也可包括必需的服务记录概要。

在所有的 Profile 中,有四种是基本的 Profile ,这些 Profile 会被其它的 Profile 使用。它们是:

GAP Profile: Generic Access Profile ,该 Profile 保证不同的 Bluetooth 产品可以互相发现对方并建立连接。

SDAP Profile: Service Discovery Application Profile ,通过该 Profile ,一个 Bluetooth 设备可以找到其它 Bluetooth 设备提供的服务,以及查询相关的信息。

SPP Profile: Serial Port Profile ,模拟串口通讯。

GOEP Profile: Generic Object Exchange Profile ,通用对象交换。这个 Profile 的名字有些费解,它定义的是 数据的传输,包括同步,文件传输,或者推送其它的数据 。你可以把它理解为内容无关的传输层协议,可以被任何应用用来传输 自己定义的数据对象

Bluetooth 还定义了 9 种应用 (usage)Profile

CTP Profile: Cordless Telephone Profile ,无绳电话。

IP Profile: Intercom Profile ,这是在两个设备之间建立语音连接,换句话说,把两个昂贵的蓝牙设备变成廉价的对讲机。

HS Profile: HeadSet Profile ,用于连接耳机。

DNP Profile: Dial-up Networking Profile ,用于为 PC 提供拨号网络功能。

FP Profile: Fax Profile ,传真功能。

LAP Profile: LAN Access Profile ,使用 PPP 协议建立局域网。

OPP Profile: Object Push Profile ,用于设备之间传输数据对象。

FTP Profile: File Transfer Profile ,用于文件传输。

SP Profile: Synchronization Profile ,用于不同的 Bluetooth 设备同步,保持数据的一致性。

目前常用的配置蓝牙配置( profile )是 A2DP

A2DP 全名是 Advanced Audio Distribution Profile 高级音频分发配置文件,描述了立体声音频如何从媒体输出( source )传输至输入( sink )。 A2DP 是能够采用耳机内的芯片来堆栈数据,达到声音的高清晰度。然而并非支持 A2DP 的耳机就是 蓝牙立体声耳机 ,立体声实现的基本要求是双声道,所以单声道的蓝牙耳机是不能实现立体声的。

使用场景:简单来说,对于一个蓝牙音乐播放器( MP3 ),音频输出是音乐播放器,而音频输入是无线耳机或无线立体声音响。

此配置文件定义了音频设备的两个角色:输出和输入。

输出( SRC source ): 音频的输入端对音频数据进行编码,发送到 Sink 端。

输入( SNK sink ): 接收到音频数据后,进行解码操作还原出音频。

A2DP 定义了在 ACL 信道实现高品质音频内容的单声道或立体声分发协议和程序。 因此, 高级音频 蓝牙音频 应该区别开来,后者是指根据基带规格定义的 SCO 信道中分发窄幅波段的语音。

此配置文件建立在 GAVDP 基础上。它包括对复杂程度低的次频宽编解码技术 (SBC) 的必备支持和对 MPEG-1,2 音频、 MPEG-2,4 AAC 和自适应声学转换编码技术 (ATRAC) 的可选支持。

音频数据按适当的格式进行压缩后能在有限频宽中正常使用。环绕声的分发不在此配置文件的范围。

PulseAudio

https://www.freedesktop.org/wiki/Software/PulseAudio/

Bluetooth 的音频应使用软件 pulseaudio ,要使用蓝牙耳机或音响的话要先安装 pulseaudio-bluetooth pulseaudio-module-bluetooth PulseAudio 5.x 开始默认支持 A2DP

PulseAudio 是一个开源的、跨平台的、支持网络的 sound server 声音服务器基本上就是您的声音应用的代理者。 它可以支持从一个或多个 source (进程或音频采集设备)输入声音并重定向它到一个或多个 sink (声卡,远程网络 PulseAudio server 或其他进程)。 PulseAudio 的目的之一就是通过它来 reroute 所有的音频流。

为了支持 Bluetooth audio source PulseAuido 还实现了动态检测蓝牙音频设备的功能。这个功能和 BlueZ A2DP Sink 都是由 João Paulo 实现的,可以在它的 blog BlueZ now has A2DP Sink support 》中找到相关信息。

PulseAudio 如何从 BlueZ 得到音频数据

虽然 BlueZ 内部对 A2DP Sink 的实现较为复杂,但是暴露给外部的数据接口确非常简单。在 bluez/audio/ipc.c 中实现了三个 bt_audio_service 函数。 PulseAudio 使用 bt_audio_service_open() 打开一个 socket ,然后调用 bt_audio_service_get_data_fd() 得到音频数据文件描述符 fd 。这个 fd 是通过那个 socket BlueZ 的进程传递到 PulseAudio 的进程的。最后,使用完毕,调用 bt_audio_service_close() 来关闭 socket PulseAudio 通过 D-bus BlueZ 进行通信,进行参数的读取和设置,决定合适的读取时机,发送读取的状态。

PulseAudio fd 读出的音频数据流是经过 SBC 压缩编码的(对于采用其他编码,如 MPEG-1 ,的情况,本文不做讨论), PulseAudio 还需要对这些音频数据流进行解码。在 BlueZ 中已经实现了 SBC 编解码,源文件位于 bluez/sbc PluseAudio 直接使用了这些源代码,把它们放在 pulseadio/src/modules/bluetooth/sbc 中。

sudo apt install pulseaudio-module-bluetooth
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
libasound2-plugins libpulsedsp libspeexdsp1 pulseaudio pulseaudio-utils rtkit
Suggested packages:
pavumeter pavucontrol paman paprefs
The following NEW packages will be installed:
libasound2-plugins libpulsedsp libspeexdsp1 pulseaudio pulseaudio-module-bluetooth
pulseaudio-utils rtkit
0 upgraded, 7 newly installed, 0 to remove and 238 not upgraded.
Need to get 32.2 kB/1,387 kB of archives.
After this operation, 6,764 kB of additional disk space will be used.
Do you want to continue? [Y/n]
Get:1 http://mirrors.ustc.edu.cn/raspbian/raspbian stretch/main armhf rtkit armhf 0.11-4+deb9u1 [32.2 kB]
Fetched 32.2 kB in 0s (112 kB/s)
Selecting previously unselected package libspeexdsp1:armhf.
(Reading database ... 126798 files and directories currently installed.)
Preparing to unpack .../0-libspeexdsp1_1.2~rc1.2-1_armhf.deb ...
Unpacking libspeexdsp1:armhf (1.2~rc1.2-1) ....................................................]
Selecting previously unselected package libasound2-plugins:armhf...............................]
Preparing to unpack .../1-libasound2-plugins_1.1.1-1_armhf.deb ...
Unpacking libasound2-plugins:armhf (1.1.1-1) ..................................................]
Selecting previously unselected package libpulsedsp:armhf......................................]
Preparing to unpack .../2-libpulsedsp_10.0-1+deb9u1_armhf.deb ...
Unpacking libpulsedsp:armhf (10.0-1+deb9u1) ...................................................]
Selecting previously unselected package pulseaudio-utils.......................................]
Preparing to unpack .../3-pulseaudio-utils_10.0-1+deb9u1_armhf.deb ...
Unpacking pulseaudio-utils (10.0-1+deb9u1) ....................................................]
Selecting previously unselected package pulseaudio.............................................]
Preparing to unpack .../4-pulseaudio_10.0-1+deb9u1_armhf.deb ...
Unpacking pulseaudio (10.0-1+deb9u1) ...#####..................................................]
Selecting previously unselected package rtkit.####.............................................]
Preparing to unpack .../5-rtkit_0.11-4+deb9u1_armhf.deb ...
Unpacking rtkit (0.11-4+deb9u1) ...#################...........................................]
Selecting previously unselected package pulseaudio-module-bluetooth............................]
Preparing to unpack .../6-pulseaudio-module-bluetooth_10.0-1+deb9u1_armhf.deb ...
Unpacking pulseaudio-module-bluetooth (10.0-1+deb9u1) ...#.....................................]
Setting up libpulsedsp:armhf (10.0-1+deb9u1) ...##############.................................]
Setting up pulseaudio-utils (10.0-1+deb9u1) ...####################............................]
Setting up rtkit (0.11-4+deb9u1) ...###################################........................]
Created symlink /etc/systemd/system/graphical.target.wants/rtkit-daemon.service → /lib/systemd/system/rtkit-daemon.service.
Processing triggers for man-db (2.7.6.1-2) ...#############################....................]
Processing triggers for dbus (1.10.26-0+deb9u1) ...
Setting up libspeexdsp1:armhf (1.2~rc1.2-1) ...
Setting up libasound2-plugins:armhf (1.1.1-1) ...###############################...............]
Setting up pulseaudio (10.0-1+deb9u1) ...###########################################...........]
Adding user pulse to group audio######################################################.........]
Setting up pulseaudio-module-bluetooth (10.0-1+deb9u1) ...##############################.......]
Processing triggers for dbus (1.10.26-0+deb9u1) ...#########################################...]

启动pulseaudio:

/usr/bin/pulseaudio --start --log-target=syslog 

pulseaudio安装完成后,蓝牙音响设备pair/connect完成后,可以通过其提供的命令pactl查看蓝牙设备,pacmd设置profile、sink等。

#pactl list cards
Card #0
    Name: bluez_card.FC_58_FA_5B_7E_DD
    Driver: module-bluez5-device.c
    Owner Module: 20
    Properties:
        device.description = "A3"
        device.string = "FC:58:FA:5B:7E:DD"
        device.api = "bluez"
        device.class = "sound"
        device.bus = "bluetooth"
        device.form_factor = "headset"
        bluez.path = "/org/bluez/hci0/dev_FC_58_FA_5B_7E_DD"
        bluez.class = "0x260404"
        bluez.alias = "A3"
        device.icon_name = "audio-headset-bluetooth"
        device.intended_roles = "phone"
    Profiles:
        headset_head_unit: Headset Head Unit (HSP/HFP) (sinks: 1, sources: 1, priority: 20, available: yes)
        a2dp_sink: High Fidelity Playback (A2DP Sink) (sinks: 1, sources: 0, priority: 10, available: yes)
        off: Off (sinks: 0, sources: 0, priority: 0, available: yes)
    Active Profile: headset_head_unit
    Ports:
        headset-output: Headset (priority: 0, latency offset: 0 usec)
            Part of profile(s): headset_head_unit, a2dp_sink
        headset-input: Headset (priority: 0, latency offset: 0 usec)
            Part of profile(s): headset_head_unit
# pactl list sinks
Sink #1
    State: SUSPENDED
    Name: bluez_sink.FC_58_FA_5B_7E_DD
    Description: A3
    Driver: module-bluez5-device.c
    Sample Specification: s16le 1ch 8000Hz
    Channel Map: mono
    Owner Module: 20
    Mute: no
    Volume: mono: 65536 / 100%
            balance 0.00
    Base Volume: 65536 / 100%
    Monitor Source: bluez_sink.FC_58_FA_5B_7E_56.monitor
    Latency: 0 usec, configured 0 usec
    Flags: HARDWARE HW_VOLUME_CTRL LATENCY 
    Properties:
        bluetooth.protocol = "headset_head_unit"
        device.intended_roles = "phone"
        device.description = "A3"
        device.string = "FC:58:FA:5B:7E:DD"
        device.api = "bluez"
        device.class = "sound"
        device.bus = "bluetooth"
        device.form_factor = "headset"
        bluez.path = "/org/bluez/hci0/dev_FC_58_FA_5B_7E_DD"
        bluez.class = "0x260404"
        bluez.alias = "A3"
        device.icon_name = "audio-headset-bluetooth"
    Ports:
        headset-output: Headset (priority: 0)
    Active Port: headset-output
    Formats:

pacmd获取到音响card索引号和sink索引号后,可通过pactl设置profile和默认输出:

pacmd set-card-profile 0 a2dp_sink        // Card #0
pacmd set-default-sink 1                  // Sink #1

设置完成后,可用pulseaudio自带的paplay播放wav格式音频文件:

paplay test.wav

至此,蓝牙音响可播放出声音。

嵌入式linux开发板使用pulseaudio连接蓝牙耳机播放音频文件

bluetooth archlinux wiki

蓝牙核心技术概述(一):蓝牙概述

蓝牙设备开发的三种方式

对蓝牙profile的理解

ARM平台上蓝牙协议栈Bluez的移植使用和配置

UbuntuBluetooth A2DP receiver实现分析

Bluetooth headset (简体中文)

Turn Your Raspberry Pi Into a Wireless Portable Bluetooth Audio System A2DP

用树莓派玩转蓝牙

Playing Audio over Bluetooth on Rasbperry Pi (Using Bluealsa, Command Line)

树莓派连接天猫精灵蓝牙音箱-1