1. 程式人生 > 實用技巧 >Android平臺RTL藍芽適配偶現打不開問題除錯筆記

Android平臺RTL藍芽適配偶現打不開問題除錯筆記

2020-09-21

關鍵字:


這篇文章記錄一下筆者在RTL一款藍芽模組在整合到Android開發板上時遇到的一個奇怪的問題的除錯過程。

1、問題背景

所述是筆者這邊在一款自研Android開發板上整合RTL藍芽模組時遇到的一個偶現問題。

所用開發板執行Android4.4作業系統,開發板主晶片是海思平臺的,藍芽模組是RTL8761。

2、問題現象

其實海思的SDK中預設就有RTL藍芽模組的通用驅動。這份通用驅動所能支援的RTL系列藍芽、WLAN模組的型號還不少,這當然也包括了RTL8761。

在其它若干型號的RTL模組中,這份驅動程式工作良好。但在適配RTL8761時卻出現了偶發性的系統起來以後在應用層無法開啟藍芽功能的情況。在Android系統原生設定應用中藍芽功能頁面藍芽開關一開啟就立即自動關閉。

3、結論概要

這個問題的原因是因為驅動程式某些功能的啟動時序不對造成的。

解決的辦法有二:

1、將藍芽驅動編進核心映象中而不是以 ko 的形式由 Android 系統來載入;

2、延遲Android系統中藍芽服務的相關功能初始化。

4、分析過程

對Android系統整合而言,遇到諸如這種功能上的問題首先當然是從上層應用抓列印以期定位並解決掉問題的。

但這個問題筆者在抓了大量的Logcat以後絲毫無法在框架層,或者說絲毫無法在Android系統層面發現異常點。

那隻能去看驅動了。

海思SDK關於RTL的通用驅動位於以下目錄中:

./device/hisilicon/bigfish/bluetooth/realtek8xxx/driver

同樣,在筆者為這份驅動程式抓了大量的正常、異常情況的日誌對比以後也未能定位出確切的問題點。唯一的收穫就是發現在冷啟動過程中正常的情況下驅動先經歷了'COLD'模式啟動,然後又來了一次'WARM'模式啟動。而功能異常的情況下只有'COLD'模式啟動。

相關日誌片段如下所示:

rtk_btusb: download_patch: Check fw_info->fw_len:23818 max_patch_size 24576
rtk_btusb: check_fw_version: Controller lmp = 0x8761, patch lmp = 0xfcd2, default patch
lmp = 0x8761 rtk_btusb: check_fw_version: Cold BT controller startup rtk_btusb: download_patch: Cold reset bt chip only download rtk_btusb: download_data: start rtk_btusb: download_data: Send frag num 0 rtk_btusb: download_data: Receive acked frag num 0
rtk_btusb: download_patch: Check fw_info->fw_len:23818 max_patch_size 24576
rtk_btusb: check_fw_version: Controller lmp = 0xfcd2, patch lmp = 0xfcd2, default patch lmp = 0x8761
rtk_btusb: check_fw_version: Warm BT controller startup with same lmp
rtk_btusb: btusb_open: Start, PM usage count 0
rtk_btusb: btusb_submit_intr_urb: mMaxPacketSize 16, bEndpointAddress 0x81

同時,在這個過程中筆者還發現,如果將驅動程式中的“日誌開關”打開了的話,出現問題的概率會顯著提高。這個日誌開關是位於驅動原始碼 rtk_btusb.h 中的一個巨集定義,如下圖所示:

但這仍然不足以定位問題的根源。並且一時之間還讓筆者陷入了迷茫,不知接下來該怎樣分析了。

在這個迷茫的過程中,筆者開始反思自己懷疑原廠這種通用型的驅動程式程式碼有Bug是否是自己打從一開始方向就錯了。人類宗教,或者說人類的一系列“迷信文化”其實就是源於我們因為自己的“無知”而又恰好在某些機緣巧合的情況下觀測到了無法解釋的現象,在苦苦尋覓這些神祕現象的答案無果之後進而引發的一系列心理問題而產生的表現。但是筆者是堅定的無神論者與信奉科學的傑出青年,雖內心迷惑,但還不至於迷失自我與放棄底線。

在這些抓列印的日子中,筆者瞭解到原來所有的--至少筆者當前所在公司使用的RTL型號的--藍芽模組的驅動過程都是大致如下步驟所示:

1、上電;

2、核心與模組建立通訊;

3、根據通訊結果決定是否要下載firmware到模組;

4、下載完成後重新驅動模組;

5、驅動完成,可以正常提供服務。

而前文提到的'COLD'與'WARM'啟動差異中,'COLD'模式下正是核心向模組下載firmware的階段,'WARM'模式看起來像是上述步驟中的第4步重新驅動模組的過程。但筆者抓到的所有核心日誌中都沒有表現出firmware下載失敗的意思。

這麼看來問題似乎是出在firmware下載完成後的重驅動過程。

檢視這個驅動程式的 Makefile,發現它是以 ko 的形式單獨編譯並打包進Android系統中的,且在Android系統中的 init.rc 中載入。

這會有什麼問題嗎?

init.rc 是由Android系統執行的。這說明,當RTL8761驅動被載入的時候Android系統已經起來了。而Android系統在起來以後馬上就會去啟動各種系統服務,這其中就包括藍芽系統服務的初始化。而且藍芽驅動的載入與藍芽系統服務的初始化是非同步進行。

而且藍芽系統服務又是依賴於藍芽驅動所提供的基礎功能服務的。如果藍芽系統服務在初始化過程中要用到驅動中的某些功能或資訊,而此時若藍芽驅動尚未載入完成--這非常有可能,因為藍芽模組在正常工作之前需要先下載firmware,這是比較耗時的--這些功能剛好還未能提供,那Android層的藍芽系統服務是不是會初始化失敗?若所涉及的模組沒有足夠強大的容錯機制,那在後續Android應用層是否就會表現出無法正常使用藍芽的現象?聽起來非常合乎邏輯沒錯吧!

那接下來就去看看藍芽系統服務是如何初始化的。

Android系統藍芽服務的分析過程這裡就不詳細寫了,筆者這邊也並沒有多詳細的去研究它。總之,最終筆者通過延時了一個訊息事件的傳遞而“規避”掉了這個問題。這個訊息就是 BluetoothManagerService.java 中約 147 行的一個廣播接收器中的訊息轉發,具體延時傳遞的訊息修改如下圖所示:

打上這個補丁以後,問題就沒有再出現過了。這事實上就是因為藍芽模組的驅動時序與Android系統的藍芽服務初始化時序沒有匹配好導致的問題。解決它的辦法在本文第3節也貼出來了。

不過,這個問題雖然規避掉了,另外一個問題又出現了:為何同樣的軟體流程,這款通用驅動程式在其它型號的模組上沒有問題呢?

筆者猜測,可能是跟驅動程式中所做的“耗時操作”太多了導致的。換言之,這份驅動程式在邏輯上是沒問題的,但是存在隱患。隱患就是驅動過程所消耗的時間超過一定範圍,就會導致上層無法使用藍芽功能。

同時,筆者還發現 RTL8761 這款藍芽模組的firmware的尺寸達到了67KB,筆者公司專案使用的其它型號的firmware只有40KB左右,體積增加了50%。而且核心到模組之間下載firmware所使用的是序列通訊,速度是很慢的。這在無形之中又增加了驅動過程的時間消耗。各種“無心之舉”加持之下,問題終於暴發了。

另外,儘可能地關閉驅動程式中的“日誌列印”也是很有必要的。在嵌入式Linux核心中,訊息列印還是比較消耗資源的。

最後,這個問題的原因其實還是基於筆者個人的猜測,並沒有得到原廠的承認甚至於筆者本人也不敢保證自己的猜測就一定是其真實原因。但無論如何,強扭的瓜雖不甜,卻也能解渴啊不是!