動(dòng)實(shí)踐指南)
簡(jiǎn)介面向 Linux 下 R7000 筆記本觸摸板異常問題的補(bǔ)丁源碼包適合具備基礎(chǔ)編譯能力的 Manjaro/Arch 系用戶也適合想了解 HID 驅(qū)動(dòng)模塊修復(fù)思路的開發(fā)者。資源圍繞 i2c-hid 驅(qū)動(dòng)獨(dú)立整理包含 2 個(gè) C 源文件、2 個(gè)頭文件和 1 個(gè) Makefile核心代碼集中在 C 文件中Makefile 用于快速編譯生成 i2c-hid.ko頭文件則保留驅(qū)動(dòng)所需的數(shù)據(jù)結(jié)構(gòu)與 DMI quirk 定義方便閱讀和對(duì)照修改。壓縮包僅 5 個(gè)文件、體積約 24KB結(jié)構(gòu)精簡(jiǎn)緊湊便于逐行核對(duì)補(bǔ)丁改動(dòng)。整體使用思路是自行編譯 i2c-hid.ko 并替換系統(tǒng)原有模塊再配合 i2c-hid.polling_mode1 內(nèi)核參數(shù)從而改善 R7000 在 Linux 下觸摸板識(shí)別或響應(yīng)異常這一過程對(duì)熟悉內(nèi)核模塊編譯與驅(qū)動(dòng)排障也有完整示例價(jià)值。目前已有 1461 人學(xué)習(xí)下載對(duì)遇到同型號(hào)或同類 HID 觸摸板故障的開發(fā)者來說具有直接參考價(jià)值。1. 先從文件名說起這個(gè)zip包里裝的到底是什么看到“i2c-hid_standalone.zip”這個(gè)名字第一反應(yīng)可能是一堆亂碼式的技術(shù)堆砌但如果你在嵌入式Linux或者Android底層驅(qū)動(dòng)這個(gè)圈子里待過一段時(shí)間就會(huì)明白這個(gè)文件名里藏著不少信息。它大概率是一個(gè)獨(dú)立編譯的I2C HID驅(qū)動(dòng)模塊被打包成zip方便分發(fā)主要面向那些沒法直接改內(nèi)核、或者內(nèi)核版本太老沒法直接啟用自帶i2c-hid驅(qū)動(dòng)的場(chǎng)景。1.1 i2c-hid到底是個(gè)什么東西I2C-HID全稱是I2C Human Interface Device是微軟推動(dòng)的一套規(guī)范目的是讓觸摸屏、觸摸板、指紋識(shí)別這類HID設(shè)備不通過USB而是走I2C總線來通信。這么做的好處很明顯I2C的功耗比USB低很多pin腳也少對(duì)平板、筆記本、手機(jī)這類對(duì)功耗敏感的設(shè)備特別友好。你去看Linux內(nèi)核drivers/hid/i2c-hid/目錄下就有對(duì)應(yīng)的驅(qū)動(dòng)實(shí)現(xiàn)。正常情況下內(nèi)核配置打開CONFIG_I2C_HID設(shè)備樹里聲明好I2C節(jié)點(diǎn)驅(qū)動(dòng)就能跑起來。但問題在于很多實(shí)際項(xiàng)目拿到的內(nèi)核版本比較老或者廠商BSP裁剪得比較狠把i2c-hid相關(guān)的配置給裁掉了這時(shí)候你沒法輕易要求廠商重新編內(nèi)核就得想別的辦法。1.2 為什么需要standalone的驅(qū)動(dòng)包“standalone”這個(gè)詞意味著這個(gè)驅(qū)動(dòng)模塊不依賴完整的內(nèi)核源碼樹而是以外部模塊out-of-tree module的方式編譯。你只需要有對(duì)應(yīng)的內(nèi)核頭文件就能編出.ko文件然后insmod加載進(jìn)去。這種做法的適用場(chǎng)景非常典型內(nèi)核源碼沒有完整放出廠商只提供了內(nèi)核頭文件和config文件內(nèi)核版本太老自帶的i2c-hid驅(qū)動(dòng)存在bug但你不想整體升級(jí)內(nèi)核平臺(tái)驗(yàn)證階段不想為了一個(gè)觸摸屏反復(fù)rebuild整個(gè)內(nèi)核鏡像。我之前在一個(gè)老舊ARM平板上就遇到過類似困境系統(tǒng)是供應(yīng)商定制的Android內(nèi)核源碼給得零零散散偏偏觸摸屏的I2C HID設(shè)備怎么都不出事件。折騰了一圈最后就是靠類似的standalone驅(qū)動(dòng)包繞開了整個(gè)內(nèi)核重編的問題。1.3 一個(gè)zip包在分發(fā)鏈路上的常見命運(yùn)先說一個(gè)比較扎心的事實(shí)很多這類zip包在網(wǎng)絡(luò)上轉(zhuǎn)來轉(zhuǎn)去到你手上時(shí)文件可能早就被各種網(wǎng)盤、聊天工具、郵件附件系統(tǒng)“處理”過一遍了。zip的二進(jìn)制結(jié)構(gòu)雖然設(shè)計(jì)得比較健壯但傳輸過程中被截?cái)?、被轉(zhuǎn)碼、被識(shí)別成文本文件的情況比比皆是。這也是為什么網(wǎng)上搜“i2c-hid_standalone.zip”的時(shí)候會(huì)連帶出大量zip解壓失敗的搜索詞。所以拿到zip包后的第一件事不是急著解壓而是先確認(rèn)這個(gè)包是不是完整、是不是真的zip文件。后面我會(huì)專門說這個(gè)。2. 解壓前先避坑zip文件從下載到落地的那些事故我見過太多人卡在解壓這一步源碼還沒看到就放棄了。其實(shí)zip解壓失敗大部分都是可以提前避免的。2.1 最容易踩的坑下載不完整導(dǎo)致“could not find EOCD”你如果搜過“導(dǎo)入資源包失敗caused by: invalid zip archive: could not find eocd”或者“導(dǎo)入失敗caused by: invalid zip archive: could not find eocd”就會(huì)知道這是個(gè)高頻問題。EOCD是End of Central Directory Record的縮寫是zip格式的中央目錄結(jié)尾標(biāo)記位于zip文件的最末尾。如果文件不完整、被截?cái)嗔诉@個(gè)標(biāo)記就沒了任何正規(guī)解壓工具都會(huì)拒絕處理。判斷方法很簡(jiǎn)單file i2c-hid_standalone.zip如果輸出顯示“Zip archive data, at least v2.0 to extract”說明文件頭沒問題。接著看文件大小如果你下載頁面標(biāo)注了具體大小可以對(duì)比一下。但更穩(wěn)妥的做法是看校驗(yàn)值md5sum i2c-hid_standalone.zip sha256sum i2c-hid_standalone.zip有些下載頁面會(huì)提供SHA256沒有的話至少也得確認(rèn)文件大小不是明顯偏小。我曾經(jīng)下載過一個(gè)大幾十MB的固件包實(shí)際下載完只有幾百KB一解壓就報(bào)“invalid zip archive”后來發(fā)現(xiàn)是公司代理緩存搞的鬼清掉代理重新下載就好了。2.2 確認(rèn)文件完整性之后再動(dòng)手如果你拿到的zip包來源是一個(gè)論壇帖、一個(gè)網(wǎng)盤鏈接或者一封郵件附件別指望對(duì)方會(huì)貼心地提供校驗(yàn)值。這時(shí)候可以用一個(gè)笨但有效的辦法用多個(gè)解壓工具交叉驗(yàn)證。比如先命令行試試unzip -t i2c-hid_standalone.zip-t參數(shù)是test integrity的意思只會(huì)檢查zip是否完整、各條目是否能正常讀出不會(huì)實(shí)際解壓。如果這一步報(bào)錯(cuò)后面就完全沒必要繼續(xù)了。再用圖形化的7-Zip或者Windows自帶資源管理器打開一次。如果兩個(gè)工具一個(gè)能打開一個(gè)不能那就值得警惕。7-Zip對(duì)zip容錯(cuò)性很高稍微有點(diǎn)損壞它也能硬著頭皮給你解出來而Windows自帶解壓對(duì)格式要求更嚴(yán)格。反過來如果7-Zip都打不開那基本可以斷定包有問題。2.3 解壓工具的選擇與中文亂碼問題很多人沒意識(shí)到zip解壓也有“兼容性”這一說。你搜“zip包用【306壓縮】軟件解壓后,里面以韓文命名的文件的文件名會(huì)顯示為亂碼”這類問題其實(shí)是因?yàn)閦ip文件信息里保存的編碼方式和解壓工具默認(rèn)使用的編碼方式不一致。傳統(tǒng)zip默認(rèn)用本地編碼現(xiàn)代工具普遍按UTF-8處理兩套體系混在一起就亂碼了。Linux環(huán)境下處理這種情況不推薦用什么國(guó)產(chǎn)壓縮軟件直接命令行最可靠unzip -O CP949 i2c-hid_standalone.zip-O參數(shù)指定解釋文件名時(shí)使用的字符集如果源zip是韓文系統(tǒng)打包的用CP949EUC-KR一般能正確顯示。如果是日文就試-O SHIFT_JIS。Windows下7-Zip也可以手動(dòng)指定編碼在解壓時(shí)選擇“文件名編碼”即可。不過對(duì)i2c-hid_standalone.zip這種包而言里面大概率全是英文文件名亂碼問題基本不會(huì)碰到但知道這個(gè)解法總沒壞處。2.4 密碼保護(hù)的zip包什么情況會(huì)碰到搜索詞里“zip壓縮包密碼破解工具”、“zip密碼移除”、“zip密碼忘記了怎么辦”這類占了很大比例說明不少人都收到過帶密碼的包。如果這個(gè)i2c-hid_standalone.zip是從某個(gè)付費(fèi)群、技術(shù)論壇或者硬件廠商的物料平臺(tái)上拿到的那帶密碼是常態(tài)。我先說個(gè)態(tài)度問題網(wǎng)上那些“zip密碼破解工具”、“zip密碼移除”的軟件絕大多數(shù)要么是捆綁流氓軟件要么只是暴力破解的上位機(jī)界面。zip的加密算法是AES-256或者ZipCrypto傳統(tǒng)算法除非密碼極短極簡(jiǎn)單否則暴力破解的性價(jià)比非常低。正規(guī)做法是查一下來源渠道密碼通常就在下載頁、論壇置頂帖或者隨包附帶的readme.txt里聯(lián)系給你發(fā)包的人直接問密碼有些硬件廠商的物料包密碼是統(tǒng)一的比如芯片型號(hào)加年份之類可以先推理試試。我在實(shí)際工作里就碰到過廠商把密碼寫在郵件正文底部小字里的情況不仔細(xì)看根本發(fā)現(xiàn)不了。3. 編譯一個(gè)外部驅(qū)動(dòng)模塊環(huán)境匹配比代碼本身更關(guān)鍵等你好不容易解壓出來了看到里面是一堆.c、.h、Makefile文件恭喜你已經(jīng)完成了最不容易出錯(cuò)的部分。接下來編譯才是真正的技術(shù)活。3.1 內(nèi)核頭文件版本必須對(duì)齊這句話我怎么說都不為過外部模塊編譯時(shí)依賴的內(nèi)核頭文件版本必須和目標(biāo)運(yùn)行內(nèi)核完全一致。版本不匹配編譯出來的.ko強(qiáng)行加載大概率直接報(bào)“invalid module format”或者“version magic mismatch”連insmod都過不去。先確認(rèn)你的目標(biāo)設(shè)備內(nèi)核版本uname -r然后確認(rèn)你的編譯環(huán)境里有沒有對(duì)應(yīng)的頭文件。Ubuntu/Debian系一般是apt search linux-headers-$(uname -r) apt install linux-headers-$(uname -r)如果你是交叉編譯那就要從設(shè)備廠商那里拿到對(duì)應(yīng)的內(nèi)核頭文件一般是某個(gè)目錄下面帶有include/generated/autoconf.h和include/config/auto.conf的那套東西。這兩個(gè)文件一個(gè)記錄了內(nèi)核編譯時(shí)的所有配置項(xiàng)一個(gè)包含了關(guān)鍵的自動(dòng)生成配置外部模塊編譯時(shí)Kbuild系統(tǒng)就是靠它們來確定配置上下文的。我之前就踩過一次坑設(shè)備的uname -r顯示某個(gè)版本號(hào)但實(shí)際上廠商BSP又打了一堆沒改版本號(hào)的補(bǔ)丁導(dǎo)致光看版本號(hào)根本看不出差異。最后是用/lib/modules/$(uname -r)/build這個(gè)軟鏈接是否存在來判斷頭文件是否安裝到位。如果軟鏈接是斷的那就說明頭文件沒裝全。3.2 makefile里最容易被忽略的三個(gè)設(shè)置解壓出來的Makefile一般長(zhǎng)這樣obj-m i2c-hid-standalone.o KDIR : /lib/modules/$(shell uname -r)/build PWD : $(shell pwd) all: make -C $(KDIR) M$(PWD) modules clean: make -C $(KDIR) M$(PWD) clean看起來很簡(jiǎn)單但實(shí)際項(xiàng)目里往往不是直接就能用的。最容易出問題的三個(gè)地方**第一KDIR路徑。**如果是交叉編譯KDIR不能指向本機(jī)的/lib/modules而是要指向你拿到的內(nèi)核源碼或者頭文件目錄。很多人上來就make報(bào)錯(cuò)說找不到include/generated/autoconf.h其實(shí)就是KDIR沒指對(duì)。**第二ARCH和CROSS_COMPILE。**交叉編譯的時(shí)候這兩個(gè)變量如果沒設(shè)編出來的模塊就跑不到目標(biāo)設(shè)備上。我建議不要臨時(shí)在命令行里加而是直接寫死在Makefile里比如ARCH ? arm CROSS_COMPILE ? arm-linux-gnueabihf-這里用?而不是是為了允許你在命令行用環(huán)境變量覆蓋。**第三模塊名和源文件名的對(duì)應(yīng)關(guān)系。**如果解壓出來的源碼里源文件名是i2c_hid_core.c但Makefile里寫的是obj-m i2c-hid-standalone.o i2c-hid-standalone-objs : i2c_hid_core.o這種情況下-objs這個(gè)變量名必須和模塊目標(biāo)名嚴(yán)格對(duì)應(yīng)多一個(gè)少一個(gè)字母都不行。如果不寫-objsKbuild會(huì)默認(rèn)去找與模塊同名的.c文件找不到就報(bào)“No rule to make target”之類的錯(cuò)誤。3.3 編譯報(bào)錯(cuò)的常見類型和排查思路編譯報(bào)錯(cuò)類型很多但只要你靜態(tài)分析起來其實(shí)是比較有規(guī)律的fatal error: linux/xxx.h: No such file or directory頭文件缺失先檢查KDIR指向的目錄下有沒有對(duì)應(yīng)的頭文件樹大概率是KDIR指錯(cuò)了要么是頭文件沒裝全。error: implicit declaration of function xxx內(nèi)核API版本差異。比如某些函數(shù)在新內(nèi)核里改了簽名或者干脆改名了。這時(shí)候需要去查一下目標(biāo)內(nèi)核提供的對(duì)應(yīng)函數(shù)原型在源碼里做兼容。老驅(qū)動(dòng)在新內(nèi)核上編譯這種情況特別常見。warning: _REENTRANT redefined或者一堆變量未使用的警告這個(gè)一般不影響使用但如果你有強(qiáng)迫癥可以檢查一下Makefile里是不是多定義了-D_REENTRANT之類的宏。編譯通過之后你會(huì)得到一個(gè).ko文件。這個(gè)文件要拷貝到目標(biāo)設(shè)備上建議路徑放到/lib/modules/$(uname -r)/extra/下這樣以后用modprobe也能找到。4. 加載與調(diào)試模塊裝上只是開始接下來就是最激動(dòng)人心的部分把模塊加載進(jìn)內(nèi)核看觸摸屏能不能動(dòng)。但實(shí)際上這一步往往才是調(diào)試時(shí)間的重頭戲。4.1 加載順序與參數(shù)在加載之前先檢查一下系統(tǒng)里是不是已經(jīng)有同類驅(qū)動(dòng)在占用設(shè)備了lsmod | grep hid如果內(nèi)核自帶的i2c-hid模塊已經(jīng)在運(yùn)行那你這個(gè)standalone模塊加載時(shí)會(huì)沖突。建議先rmmod i2c_hid或者把內(nèi)核自帶驅(qū)動(dòng)加入黑名單然后再insmod你的模塊insmod /lib/modules/$(uname -r)/extra/i2c-hid-standalone.ko加載時(shí)如果模塊支持參數(shù)比如有些驅(qū)動(dòng)允許指定I2C地址或者中斷GPIO號(hào)可以這樣insmod i2c-hid-standalone.ko irq_gpio123不過大部分情況下設(shè)備地址和中斷都是從設(shè)備樹或者ACPI表里讀的不太需要手動(dòng)指定。4.2 設(shè)備樹和設(shè)備ID匹配這是standalone模塊最核心的一塊也是很多人最容易懵的地方。i2c-hid驅(qū)動(dòng)本身是平臺(tái)驅(qū)動(dòng)或者I2C驅(qū)動(dòng)它需要知道你的設(shè)備掛在哪條I2C總線上、設(shè)備地址是多少、中斷腳是哪個(gè)。如果你的內(nèi)核里沒有設(shè)備樹節(jié)點(diǎn)那你最好在設(shè)備樹里增加類似這樣的節(jié)點(diǎn)i2c1 { status okay; touchscreen2c { compatible hid-over-i2c; reg 0x2c; interrupt-parent gpio1; interrupts 13 IRQ_TYPE_LEVEL_LOW; hid-descr-addr 0x0020; }; };compatible、reg、interrupt-parent、interrupts都好理解關(guān)鍵是hid-descr-addr這個(gè)屬性它是HID描述符的地址。如果你不確定這個(gè)值可以查閱你的觸摸屏/觸摸板的數(shù)據(jù)手冊(cè)或者從廠商提供的參考代碼里抄。這個(gè)值錯(cuò)了驅(qū)動(dòng)能加載但初始化階段會(huì)失敗報(bào)“unable to read HID descriptor”之類的錯(cuò)誤。有同行可能會(huì)問設(shè)備樹在外部模塊編譯的時(shí)候根本不參與編譯改了設(shè)備樹不還是要重編內(nèi)核答案是可以單獨(dú)編譯設(shè)備樹二進(jìn)制dtb很多平臺(tái)支持只更新dtb而不動(dòng)內(nèi)核鏡像。但如果你對(duì)平臺(tái)不熟悉這一步建議和硬件工程師一起做別一個(gè)人在那邊瞎試。4.3 驗(yàn)證驅(qū)動(dòng)是否生效從dmesg到input設(shè)備模塊加載成功不意味著設(shè)備就能用了。我習(xí)慣按以下順序一步步驗(yàn)證第一步看dmesg。dmesg | tail -50或者直接監(jiān)控內(nèi)核日志dmesg -w正常情況應(yīng)該能看到類似“i2c-hid-standalone: probe success”、“hid-generic: HID probe”之類的字樣。如果看到“probe failed”之類的錯(cuò)誤把報(bào)錯(cuò)信息完整記下來別只看最后幾行。第二步看input設(shè)備。I2C HID設(shè)備最終會(huì)通過hid-generic被注冊(cè)成一個(gè)input設(shè)備。檢查一下系統(tǒng)里有沒有新增的input節(jié)點(diǎn)cat /proc/bus/input/devices你應(yīng)該能看到一個(gè)名稱類似觸摸屏或者觸摸板的設(shè)備。如果你想測(cè)試這個(gè)設(shè)備的原始事件可以用hexdump直接讀它的設(shè)備節(jié)點(diǎn)比如hexdump /dev/input/event2然后手指在觸摸屏上滑動(dòng)如果終端里有數(shù)據(jù)溢出說明底層的input事件已經(jīng)產(chǎn)生了。第三步看中斷是否觸發(fā)。如果設(shè)備事件完全沒有那就要看中斷有沒有來。可以用cat /proc/interrupts找到給觸摸屏分配的中斷號(hào)檢查計(jì)數(shù)是否在觸摸時(shí)增加。如果計(jì)數(shù)不動(dòng)可能是中斷配置有問題或者設(shè)備根本沒起來。4.4 觸摸屏/觸摸板調(diào)試的常見疑難調(diào)試過程中有幾個(gè)現(xiàn)象特別值得注意**能識(shí)別設(shè)備但觸摸沒反應(yīng)。**這種情況大概率是中斷GPIO配置錯(cuò)了或者設(shè)備喚醒引腳的狀態(tài)不對(duì)。檢查一下設(shè)備樹里interrupts的觸發(fā)類型有些傳感器是低電平觸發(fā)你配置成上升沿觸發(fā)就永遠(yuǎn)等不到中斷。**坐標(biāo)亂跳、完全不受控。**如果設(shè)備能被識(shí)別坐標(biāo)值也有輸出但明顯不對(duì)那可能是I2C時(shí)鐘頻率太高導(dǎo)致信號(hào)質(zhì)量差或者上拉電阻沒焊。可以試著把I2C總線頻率降下來比如從400kHz降到100kHz看是否有改善。**probe階段直接卡死或者死循環(huán)。**這種情況往往是因?yàn)樵O(shè)備沒有正常響應(yīng)I2C讀操作。用i2cdetect檢查一下設(shè)備地址是否能探測(cè)到i2cdetect -y 11是I2C總線號(hào)具體按平臺(tái)來。如果設(shè)備地址不在列表里說明I2C通信都還沒建立起來先去查硬件連接別在軟件上繼續(xù)折騰。5. 換到Android或者其他平臺(tái)適配時(shí)最容易忽略的差異如果你不是純Linux環(huán)境而是在Android系統(tǒng)上做這件事那還有一些額外的問題需要考慮。5.1 Android系統(tǒng)里驅(qū)動(dòng)加載的特殊性Android的設(shè)備驅(qū)動(dòng)鏈路比桌面Linux多一層內(nèi)核層加載驅(qū)動(dòng)成功之后用戶空間的Android系統(tǒng)還要通過HAL層去訪問這個(gè)觸摸設(shè)備。如果你只是在內(nèi)核層insmod成功了但Android的輸入系統(tǒng)沒有識(shí)別到觸摸屏照樣是死的。一個(gè)常見做法是把你要加載的模塊放到/system/lib/modules/下然后在init.rc里增加一條insmod命令或者在init.${platform}.rc里加insmod /system/lib/modules/i2c-hid-standalone.ko但這里有個(gè)坑Android的SELinux策略可能會(huì)阻止insmod操作。如果你在日志里看到“avc: denied { module_load } for”之類的記錄就需要調(diào)整SELinux policy給init進(jìn)程增加module_load權(quán)限。這一塊經(jīng)常被忽略導(dǎo)致內(nèi)核層的模塊明明能加載但系統(tǒng)一啟動(dòng)就被攔截。另外Android的input設(shè)備采用EventHub監(jiān)聽新設(shè)備需要確認(rèn)/dev/input/下有對(duì)應(yīng)的event節(jié)點(diǎn)而且權(quán)限要正確。否則即使內(nèi)核事件產(chǎn)生了應(yīng)用層也拿不到。5.2 中斷與電源管理配置I2C HID設(shè)備在筆記本/平板/手機(jī)上往往需要和系統(tǒng)的電源管理協(xié)同。如果你的系統(tǒng)有自動(dòng)休眠功能觸摸屏在休眠喚醒后無響應(yīng)大概率是下面的問題驅(qū)動(dòng)在suspend時(shí)沒有正確執(zhí)行設(shè)備的休眠序列中斷喚醒沒有配置喚醒后設(shè)備處于未初始化狀態(tài)GPIO在休眠時(shí)被系統(tǒng)拉低導(dǎo)致設(shè)備斷電。遇到這個(gè)問題可以嘗試在驅(qū)動(dòng)源碼里找找power_manager相關(guān)的回調(diào)確認(rèn)它的i2c_hid_suspend和i2c_hid_resume函數(shù)是否被正常注冊(cè)。如果源碼里這兩個(gè)函數(shù)沒實(shí)現(xiàn)那就要手動(dòng)補(bǔ)上或者和廠商確認(rèn)設(shè)備的具體休眠喚醒時(shí)序。5.3 其他系統(tǒng)上的經(jīng)驗(yàn)我在一些非Linux、非Android的系統(tǒng)上也碰過類似的需求比如某些RTOS或者定制系統(tǒng)的觸摸屏驅(qū)動(dòng)。雖然文件名叫“i2c-hid_standalone”內(nèi)含的代碼邏輯大部分是Linux內(nèi)核那一套但如果你的目標(biāo)系統(tǒng)不是Linux那情況完全不一樣。一個(gè)值得借鑒的做法是不要試圖把Linux的驅(qū)動(dòng)原封不動(dòng)地搬到別的系統(tǒng)上而是把它的協(xié)議解析流程抽出來。I2C HID的核心邏輯其實(shí)有限讀取HID描述符、建立事件管道、處理Input Report。你只要搞清楚了這幾個(gè)流程在哪個(gè)系統(tǒng)里都能寫出對(duì)應(yīng)的實(shí)現(xiàn)。這也是為什么我建議在調(diào)試階段多花點(diǎn)時(shí)間去讀源碼里對(duì)HID Report Descriptor的解析過程而不是只關(guān)注probe和resume這些外圍邏輯。很多看似詭異的觸摸問題最后都能追溯到對(duì)報(bào)告描述符的解析偏差上。我在實(shí)際調(diào)試中就遇到過一次設(shè)備能上報(bào)事件但上報(bào)的坐標(biāo)范圍比屏幕實(shí)際尺寸大了一倍指針永遠(yuǎn)只在屏幕左上角轉(zhuǎn)悠。查到最后發(fā)現(xiàn)是報(bào)告描述符里L(fēng)ogical Max的解析有符號(hào)/無符號(hào)處理錯(cuò)誤導(dǎo)致坐標(biāo)被放大到65535。這種問題光靠調(diào)中斷、調(diào)時(shí)鐘是絕對(duì)查不出來的必須回到HID協(xié)議本身去找原因。所以說拿到i2c-hid_standalone.zip解壓、編譯、加載只是開始真正的考驗(yàn)在于你愿不愿意順著協(xié)議棧往里鉆。把I2C通信、HID描述符、input子系統(tǒng)這幾層之間的關(guān)系理清楚以后再碰到同類問題心里就有譜了。本文還有配套的精品資源點(diǎn)擊獲取