Linux 藍牙音頻軟件棧實現分析
- 藍牙協議棧簡介
- 藍牙控制器探測
- BlueZ 插件系統及音頻插件
藍牙協議棧簡介
藍牙協議棧是實現藍牙通信功能的軟件架構,它由多個層次組成,每一層負責特定的功能。藍牙協議棧的設計遵循藍牙標準 (由藍牙技術聯盟,Bluetooth SIG 定義),支持多種藍牙配置文件 (Profiles),以滿足不同的應用場景 (如音頻傳輸、文件傳輸、健康設備、鍵盤鼠標這樣的輸入輸出設備等)。
藍牙各個應用場景的實現,如音頻傳輸、文件傳輸和鍵盤鼠標這樣的輸入設備,與系統中常規的這些功能的實現大為不同。如對于音頻播放和錄制,通過 USB 連接的音頻設備,或通過 audio codec 實現的音頻播放和錄制,在 Linux 中,基于 ALSA 框架實現,內核通過導出設備文件向用戶空間暴露相應的硬件能力。USB 鍵盤鼠標,在 Linux 中,基于輸入設備框架實現,內核同樣通過導出設備文件向用戶空間暴露相應的硬件能力。
可與藍牙協議棧類比的不是系統中常規的各個功能的實現,而是 TCP/IP 網絡協議棧。在實現上,與 TCP/IP 網絡協議棧類似,藍牙協議棧不同功能的各個協議層次實現分布于硬件、Linux 操作系統內核、BlueZ 這樣的藍牙系統服務和 PulseAudio 這樣系統服務中。
Bluetooth SIG 官方的藍牙核心規范 (藍牙核心規范 6.0) 給出的藍牙核心系統架構如下圖所示:
藍牙協議棧的分層結構如下圖所示:
+--------------------------------------------------------------------------------------------+
| Application Layer |
| (Profiles: A2DP, HFP, HSP, AVRCP, HAP, BAP, ACS, ACAS, GAP, GATT, FTP, OPP, etc.) |
+--------------------------------------------------------------------------------------------+
| Middleware Layer |
| (Protocols: AVDTP, AVCTP, SDP, ATT, RFCOMM, OBEX, TCS, BNEP, etc.) |
+--------------------------------------------------------------------------------------------+
| Host Controller Interface (HCI) |
| (Protocols: HCI Commands, Events, and Data) |
+--------------------------------------------------------------------------------------------+
| Logical Link Control and |
| Adaptation Protocol (L2CAP) |
+--------------------------------------------------------------------------------------------+
| Baseband Layer |
| (Protocols: Link Manager Protocol (LMP), SCO, eSCO, ACL, ISOC, etc.) |
+--------------------------------------------------------------------------------------------+
| Radio Layer |
| (Physical Layer: Bluetooth Radio) |
+--------------------------------------------------------------------------------------------+
藍牙協議棧各層次簡單說明如下:
-
應用層:實現具體的藍牙應用功能 (如音頻傳輸、文件傳輸)。通過藍牙配置文件 (Profiles) 定義設備的行為。常見的應用層協議/配置文件有 A2DP (Advanced Audio Distribution Profile,高質量音頻傳輸),HFP (Hands-Free Profile,免提通話),HSP (Headset Profile,耳機通話),AVRCP (Audio/Video Remote Control Profile,遠程控制音頻/視頻設備),HAP (Hearing Aid Profile,用于助聽器設備),BAP (Bluetooth Audio Profile,用于通用音頻設備),ACS (Audio Control Service,提供音頻控制功能(如音量調節、播放控制),基于 GATT/ATT 實現,通過暴露特性(Characteristics)供應用程序使用),ACAS (Audio Stream Control Service,提供音頻流管理功能(如流的創建、配置、刪除),基于 GATT/ATT 實現,與 Isochronous Channels(ISOC) 協作,實現低延遲音頻流傳輸),GATT (Generic Attribute Profile,定義基于 ATT 的服務和特性,用于數據傳輸,提供邏輯信道管理,支持客戶端-服務器模型),GAP (Generic Access Profile,定義設備角色 (如廣播者、觀察者、中心設備、外圍設備) 和連接流程,負責設備發現、連接建立和安全控制),FTP (File Transfer Profile,文件傳輸),OPP (Object Push Profile,對象推送 (如聯系人、圖片)),PAN (Personal Area Network Profile,個人局域網),HID (Human Interface Device Profile,人機接口設備 (如鍵盤、鼠標) 等。
-
中間件層:提供高層協議和服務,支持應用層的功能實現。常見的中間件層協議有,SDP (Service Discovery Protocol,服務發現協議,用于查找設備支持的服務),RFCOMM (Radio Frequency Communication,串口仿真協議,用于模擬 RS-232 串口通信),OBEX (Object Exchange Protocol,對象交換協議,用于文件傳輸和數據同步),TCS (Telephony Control Protocol Specification,電話控制協議,用于語音通話),BNEP (Bluetooth Network Encapsulation Protocol,網絡封裝協議,用于藍牙網絡共享),AVDTP (Audio/Video Distribution Transport Protocol,音頻/視頻傳輸協議,負責音頻流的傳輸和控制,它定義了音頻流的建立、配置、啟動、暫停和停止等操作),AVCTP (Audio/Video Remote Control Profile,音頻/視頻遠程控制協議,藍牙協議棧中的控制傳輸協議,負責音頻/視頻控制命令的傳輸,它定義了控制命令的封裝和傳輸機制),ATT (Attribute Protocol,用于在 BLE 設備之間傳輸屬性數據,提供基于客戶端-服務器的數據訪問機制) 等。
-
HCI 層:提供主機和藍牙控制器之間的通信接口。負責傳輸命令、事件和數據。
-
L2CAP 層:提供多路復用、分段和重組功能,支持上層協議的數據傳輸。管理邏輯鏈路,提供可靠的數據傳輸服務。
-
基帶層:管理物理鏈路,處理藍牙設備的連接和通信。負責頻率跳變、數據包格式化和錯誤檢測。常見的基帶層協議有 LMP (Link Manager Protocol,鏈路管理協議,負責設備之間的連接建立和維護),SCO (Synchronous Connection-Oriented link,同步面向連接鏈路,用于語音傳輸),ACL (Asynchronous Connectionless link,異步無連接鏈路,用于數據傳輸),ISOC (Isochronous Channels,提供同步數據傳輸通道,支持低延遲的音頻流傳輸)。
-
射頻層:負責藍牙無線電信號的發送和接收。處理頻率跳變、調制和解調。使用藍牙無線電協議,主要工作在 2.4 GHz ISM 頻段。
藍牙協議棧相對于 TCP/IP 網絡協議棧,其各層之間并不是那么的各自獨立,而是緊密關聯的。藍牙協議棧中與音頻相關的有 4 個用于不同場景的子協議棧,它們分別是用于傳輸高質量音頻流的 A2DP,包括 A2DP -> AVDTP -> L2CAP -> ACL;用于通過藍牙遠程控制音頻/視頻設備的 AVRCP,包括 AVRCP -> AVCTP -> L2CAP -> ACL,A2DP 和 AVRCP 常協作實現藍牙音頻;用于語音通話的 HFP/HSP,包括 HFP/HSP -> SCO/eSCO;用于低功耗藍牙的 HAP/BAP,包括 HAP/BAP -> ACS/ACAS -> GATT -> ATT -> GAP -> ISOC。 除 HAP/BAP 協議棧外,其它的都是經典藍牙的協議棧。
類比于 TCP/IP 網絡協議棧中的 RTP/RTCP 協議,A2DP/AVDTP 協議類似于 RTP 協議,AVRCP/AVCTP 協議類似于 RTCP 協議,L2CAP/ACL 協議類似于 UDP 協議,只是它們是可靠傳輸協議。
在實現上,HCI 及更下層的協議無疑由 Linux 內核或硬件實現。應用層和中間件層協議的實現則常隨著時間的流逝而變化。低功耗藍牙是比較新的藍牙標準,對低功耗藍牙的支持是從 BlueZ 5.55 版本開始逐步添加的。對于 Linux 內核,則是從 Linux 5.13 版本開始,逐步支持 Isochronous Channels。
在早期的 BlueZ 版本中,PulseAudio 這樣的音頻服務需要將音頻流數據通過 Unix Domain Socket 發送給 BlueZ,再由 BlueZ 通過 A2DP 協議發送給藍牙硬件設備。從 BlueZ 5.0 開始,BlueZ 的音頻功能(如 A2DP)逐漸被移出 BlueZ 核心代碼庫,轉而由 PipeWire 或 PulseAudio 這樣的系統音頻服務器直接處理音頻流的傳輸。BlueZ 不再直接處理音頻流數據,而是通過 D-Bus 接口 與音頻后端(如 PipeWire 或 PulseAudio)交互。AVDTP 的實現由音頻后端負責,BlueZ 僅提供藍牙協議棧的核心功能(如設備管理、連接管理)。音頻后端負責音頻流的編碼、解碼和傳輸。音頻后端直接與藍牙硬件交互,處理音頻流數據。
藍牙控制器探測
BlueZ 是 Linux 官方藍牙協議棧,提供對藍牙無線通信標準的全面支持。核心協議方面,它支持 L2CAP(邏輯鏈路控制與適配協議),RFCOMM(串口仿真協議),SDP(服務發現協議),HCI(主機控制器接口)。配置文件方面,它支持 A2DP(高級音頻分發),AVRCP(音視頻遠程控制),HFP(免提協議),HID(人機接口設備),PAN(個人局域網)。硬件設備類型方面,它支持音頻設備,如藍牙耳機、音箱;輸入設備,如鍵盤、鼠標;網絡連接,如藍牙 PAN;物聯網,如智能家居設備。它還提供一系列與藍牙設備管理控制有關的工具程序,如 bluetoothd,藍牙守護進程,管理設備和服務;bluetoothctl,命令行工具,用于設備配對、連接等操作;hcitool,配置藍牙適配器及查詢設備信息;sdptool,瀏覽和發布 SDP 服務記錄。
BlueZ 整個項目的代碼豐富而復雜,這里主要關注與藍牙音頻有關的邏輯。
BlueZ 系統服務 bluetoothd 啟動時,執行 adapter_init()
函數初始化藍牙適配器,這個調用過程如下:
#0 adapter_init () at src/adapter.c:10337
#1 0x0000aaaaaaac3398 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:1216
adapter_init()
函數定義 (位于 src/adapter.c) 如下:
int adapter_init(void)
{dbus_conn = btd_get_dbus_connection();mgmt_primary = mgmt_new_default();if (!mgmt_primary) {error("Failed to access management interface");return -EIO;}if (getenv("MGMT_DEBUG"))mgmt_set_debug(mgmt_primary, mgmt_debug, "mgmt: ", NULL);DBG("sending read version command");if (mgmt_send(mgmt_primary, MGMT_OP_READ_VERSION,MGMT_INDEX_NONE, 0, NULL,read_version_complete, NULL, NULL) > 0)return 0;error("Failed to read management version information");return -EIO;
}
adapter_init()
函數訪問一下 DBus 連接,調用 mgmt_new_default()
函數創建并初始化 struct mgmt
對象,設置 struct mgmt
的調試配置,并調用 mgmt_send()
函數向 struct mgmt
發送一個讀取版本號的請求。
mgmt_new_default()
函數定義 (位于 src/shared/mgmt.c) 如下:
struct mgmt {int ref_count;int fd;bool close_on_unref;struct io *io;bool writer_active;struct queue *request_queue;struct queue *reply_queue;struct queue *pending_list;struct queue *notify_list;unsigned int next_request_id;unsigned int next_notify_id;bool need_notify_cleanup;bool in_notify;void *buf;uint16_t len;uint16_t mtu;mgmt_debug_func_t debug_callback;mgmt_destroy_func_t debug_destroy;void *debug_data;
};. . . . . .
static void mgmt_set_mtu(struct mgmt *mgmt)
{socklen_t len = 0;/* Check if kernel support BT_SNDMTU to read the current MTU set */if (getsockopt(mgmt->fd, SOL_BLUETOOTH, BT_SNDMTU, &mgmt->mtu,&len) < 0) {/* If BT_SNDMTU is not supported then MTU cannot be changed and* MTU is fixed to HCI_MAX_ACL_SIZE.*/mgmt->mtu = HCI_MAX_ACL_SIZE;return;}if (mgmt->mtu < UINT16_MAX) {uint16_t mtu = UINT16_MAX;/* Try increasing the MTU since some commands may go* over HCI_MAX_ACL_SIZE (1024)*/if (!setsockopt(mgmt->fd, SOL_BLUETOOTH, BT_SNDMTU, &mtu,sizeof(mtu)))mgmt->mtu = mtu;}
}struct mgmt *mgmt_new(int fd)
{struct mgmt *mgmt;if (fd < 0)return NULL;mgmt = new0(struct mgmt, 1);mgmt->fd = fd;mgmt->close_on_unref = false;mgmt->len = 512;mgmt->buf = malloc(mgmt->len);if (!mgmt->buf) {free(mgmt);return NULL;}mgmt->io = io_new(fd);if (!mgmt->io) {free(mgmt->buf);free(mgmt);return NULL;}mgmt->request_queue = queue_new();mgmt->reply_queue = queue_new();mgmt->pending_list = queue_new();mgmt->notify_list = queue_new();if (!io_set_read_handler(mgmt->io, can_read_data, mgmt, NULL)) {queue_destroy(mgmt->notify_list, NULL);queue_destroy(mgmt->pending_list, NULL);queue_destr