MacBook Pro 2017 13寸版 触摸板windows驱动开发(开发HID鼠标键盘驱动之一)
生活随笔
收集整理的這篇文章主要介紹了
MacBook Pro 2017 13寸版 触摸板windows驱动开发(开发HID鼠标键盘驱动之一)
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
by fanxiushu 2017-10-27 轉(zhuǎn)載或引用請注明原始作者。
如下連接,
http://blog.csdn.net/fanxiushu/article/details/78186745
因?yàn)樾陆鼡Q的電腦是macbook pro 2017 13寸帶bar的機(jī)器,其他還能將就,
就是觸摸板難用,動不動就彈出右鍵菜單,經(jīng)常誤觸,
而且尤其不爽的是拖動時(shí)候,必須按住邊緣,另一只手指才能拖動,
觸摸板這么大,基本無法單手進(jìn)行拖動操作,需要另一個(gè)手的手指按住觸摸板邊緣,然后拖動。
雖然可以點(diǎn)擊兩次,第2次不離開觸摸板來拖動,但是非常不習(xí)慣這種手勢。
還是習(xí)慣傳統(tǒng)的食指按住觸摸板,中指來拖動(因?yàn)檫@種手勢更加接近按住鼠標(biāo)左鍵移動鼠標(biāo)的效果)。
像我這種經(jīng)常把電腦放到大腿上或者床上的人(反正就是不會老老實(shí)實(shí)的放到電腦桌上),
如果帶個(gè)鼠標(biāo)是很不方便的,只能依賴觸摸板來控制電腦。
而且我的要求也不高,不需要什么多手勢,只要觸摸板能盡量模擬鼠標(biāo)的效果就行了。
其實(shí)個(gè)人覺得windows本身的易操作性,使用鼠標(biāo)的效果就能控制操作系統(tǒng)的所有的東西了,
手勢多了我也記不住,每次切換不同手勢還得思考一下也挺累(也許是還沒習(xí)慣,也懶得去習(xí)慣了)。
基于以上各種原因,于是決定重新開發(fā)這款電腦的觸摸板驅(qū)動。
首先解釋一下“點(diǎn)按”和”輕點(diǎn)“:
按住或重壓,就是按下去,能聽到“噠”的一聲響;
”點(diǎn)按“就是“噠”的一聲按下去然后立馬彈上來,
還有一個(gè)就是”輕點(diǎn)“,就是手指接觸到觸摸板然后迅速離開。
我需要達(dá)到的效果也非常簡單明了,用兩根手指模擬鼠標(biāo)效果,操作整個(gè)系統(tǒng)。
1,首先一根手指按住觸摸板的任意位置(是任意位置而不是觸摸板邊緣),另一根手指在觸摸板上移動來達(dá)到拖動效果,
這就相當(dāng)于按住鼠標(biāo)左鍵,移動鼠標(biāo)的效果一樣。
2,一根手指輕點(diǎn),相當(dāng)于鼠標(biāo)左鍵按下去然后立馬彈上來。
???? 一根手指按住觸摸板,相當(dāng)于按下鼠標(biāo)左鍵,從觸摸板彈上來相當(dāng)于彈出左鍵。
3,兩根手指同時(shí)輕點(diǎn),相當(dāng)于鼠標(biāo)右鍵按下去然后立馬彈上來。
???? 一根手指按住觸摸板右邊四分之三到四分之四部分(是整個(gè)右邊3/4,而不是右下邊緣),相當(dāng)于按住鼠標(biāo)右鍵。
4,兩根手指同時(shí)在觸摸板移動,相當(dāng)于滾輪滾動。
5,另外再附加實(shí)現(xiàn)一個(gè)功能:三指拖移。等于是第1個(gè)功能的效果:一個(gè)手指按住,另一個(gè)手指移動。
????? 這樣不重壓觸摸板的也能操作整個(gè)系統(tǒng)。
不模擬鼠標(biāo)的中間鍵按下效果,好像中間鍵沒啥用處。
以上就是我的簡單明了的要求,當(dāng)然習(xí)慣各種手勢的你可能并不贊同,甚至有點(diǎn)嗤之以鼻,然而這依然是我的簡單明了的要求。
重新開發(fā)的觸摸板驅(qū)動也只實(shí)現(xiàn)上邊的功能而已。
要重新開發(fā)macbook pro 2017年 13寸帶bar(以下簡稱mbp2017)的觸摸板的windows驅(qū)動,
首先需要解決兩件事:
一,采集mbp2017的觸摸板數(shù)據(jù),
二,模擬開發(fā)鼠標(biāo)驅(qū)動。
第二個(gè)問題100%的確保能解決,使用HID模式的鼠標(biāo)驅(qū)動就可以了。
關(guān)鍵是第一個(gè)問題,如果不能采集和解析觸摸板的數(shù)據(jù),基本就沒戲了,只能老老實(shí)實(shí)的使用那個(gè)難用的原裝驅(qū)動程序。
mbp2017的觸摸板數(shù)據(jù)結(jié)構(gòu)格式是沒有公開的,而且未來也很可能被蘋果公司改變,因此沒有通用性,
我測試的對應(yīng)bootcamp版本是 6.1.6813,對應(yīng)的觸摸板總線驅(qū)動的驅(qū)動日期是 2016/05/26, 版本 6.1.6500.0,
其他版本的沒測試過,所以不知道的數(shù)據(jù)格式是不是不同。
既然這個(gè)數(shù)據(jù)沒有公開,我們就必須要自己來采集和分析觸摸板的數(shù)據(jù),
好在觸摸板這類設(shè)備本身的數(shù)據(jù)量不大,數(shù)據(jù)結(jié)構(gòu)也應(yīng)該不會多復(fù)雜,只要Apple公司沒變態(tài)到做加密,估計(jì)是能解析出來的。
自己動手解析之前,先要搞清楚它的驅(qū)動運(yùn)行流程。
蘋果使用的SPI總線來傳輸觸摸板和鍵盤的數(shù)據(jù),SPI總線接口,也是我在接觸mbp2017時(shí)候才發(fā)現(xiàn)還有這么一個(gè)玩意,真是孤陋寡聞了。
SPI 全稱Serial Peripheral Interface--串行外設(shè)接口, 最初是Motorola提出和開發(fā)的,它使用主從模式通訊,這點(diǎn)跟USB有點(diǎn)像,
同時(shí)通訊也很簡單,比RS232(串口)還簡單,所以不論軟件或硬件成本都比較低。
但是應(yīng)付鍵盤和觸摸板這類不需要大量通訊數(shù)據(jù)的器件完全足夠了。更詳細(xì)的關(guān)于SPI介紹,請查詢其他資料。
mbp2017的電腦windows驅(qū)動中關(guān)于SPI的,首先有個(gè)SPI總線驅(qū)動,在SPI總線驅(qū)動下掛載兩個(gè)位置,
位置1是鍵盤驅(qū)動,位置2才是觸摸板驅(qū)動。詳細(xì)可看下邊的圖示:
畫紅線的部分,在”系統(tǒng)設(shè)備“里邊的 “Apple SPI Device ” 就是總線驅(qū)動,它負(fù)責(zé)給鍵盤和觸摸板的功能驅(qū)動提供數(shù)據(jù),
在“人體學(xué)輸入設(shè)備”里邊的“ Apple SPI Keyboard” 和 “ Apple SPI Trackpad” 對應(yīng)的就是 鍵盤的功能驅(qū)動和觸摸板的功能驅(qū)動,
再看“Apple SPI Trackpad” 的屬性, 它在總線驅(qū)動的位置是 2, 我們要做的事情,就是替換這個(gè)功能驅(qū)動,使用我們自己開發(fā)的驅(qū)動來代替。
另外我們順便看看這款電腦內(nèi)置的USB接口的設(shè)備,
上邊的藍(lán)線部分,在 “通用串行總線控制器” 里邊存在一個(gè)“Apple USB Composite Device”的復(fù)合設(shè)備,
這個(gè)復(fù)合設(shè)備管理著三個(gè)設(shè)備,“Apple Touch Bar”就是其中一個(gè),這個(gè)就是multi-touch bar, 在windows平臺下沒啥用的雞肋。
另外兩個(gè)看下圖所示:
Apple USB Composite Device一共包含三個(gè)設(shè)備:
FaceTime HD Camera,
Apple Touch Bar,
USB接口的光感氛圍器,就是檢測環(huán)境光線強(qiáng)弱,從而自動調(diào)節(jié)屏幕的亮度。
看圖示的最下邊, bNumConfigurations 是 3, 也就是三個(gè)配置描述符,分別對應(yīng)這三種設(shè)備。
正如上篇文章所說的,在單純只安裝windows系統(tǒng)的時(shí)候,這個(gè)“Apple USB Composite Device” 設(shè)備不能被發(fā)現(xiàn),
從而造成 攝像頭,multi-touch bar,和光感器件找不到,也不知道蘋果在設(shè)計(jì)硬件的時(shí)候搞了什么。
估計(jì)得保留安裝蘋果系統(tǒng)時(shí)候的那個(gè)EFI Parttion 分區(qū),才能被識別,只是后來沒再折騰了,保留了MacOS系統(tǒng)。
再回到觸摸板驅(qū)動,我們打開注冊表,在 如下的位置:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\SPI\VID_05ac&PID_0277&MI_02
能找到這個(gè)驅(qū)動的安裝信息,
注意:mbp2017 對應(yīng)的觸摸板的PID是0277, VID是05ac, 不同型號的,可能PID會不同,這個(gè)得看具體機(jī)器。
如上圖所示,在里邊的LowerFilters字段里是 蘋果的 AppleSPITrackpad驅(qū)動,
Service字段是 mshidkmdf ,從這信息,
我們立馬就知道 AppleSPITrackpad 是一個(gè)KMDF模型的驅(qū)動程序,并且屬于標(biāo)準(zhǔn)的HID的KMDF程序。
AppleSPITrackpad負(fù)責(zé)獲取上邊提到的 “Apple SPI Device" 總線驅(qū)動發(fā)上來的觸摸板數(shù)據(jù),
并且解析模擬成標(biāo)準(zhǔn)的兼容windows的 HID鼠標(biāo)驅(qū)動數(shù)據(jù)。
AppleSPITrackpad在解析觸摸板數(shù)據(jù)時(shí)候,提供了讓人難以適應(yīng)(至少我比較難適應(yīng))的鼠標(biāo)動作模擬,
因此重新開發(fā)這個(gè)驅(qū)動來解決這個(gè)問題。
至此,我們大致知道他們的驅(qū)動的工作流程了。
現(xiàn)在的任務(wù)首先就是獲取SPI總線驅(qū)動發(fā)上來的數(shù)據(jù)結(jié)構(gòu)格式。
可以開發(fā)一個(gè)簡單的WDM 過濾驅(qū)動,掛載到 LowerFilters字段里邊的 AppleSPITrackpad 前邊。
這樣AppleSPITrackpad跟SPI總線驅(qū)動通訊的所有IRP請求都能被截獲到,從而就能獲取到觸摸板數(shù)據(jù)。
WDM Filter的開發(fā)可以查看我很早前的一篇文章:
http://blog.csdn.net/fanxiushu/article/details/8834385
當(dāng)然如果你能找到其他現(xiàn)成的工具來分析AppleSPITrackpad的通訊數(shù)據(jù),會更快捷。
通過分析,AppleSPITrackpad發(fā)給SPI總線驅(qū)動四個(gè)請求:
IOCTL_HID_GET_DEVICE_DESCRIPTOR
IOCTL_HID_GET_DEVICE_ATTRIBUTES
IOCTL_HID_SET_FEATURE
IOCTL_HID_READ_REPORT
都是標(biāo)準(zhǔn)的HID請求命令,其中IOCTL_HID_SET_FEATURE 應(yīng)該是用于告訴SPI總線驅(qū)動開啟或者關(guān)閉觸摸板功能的。
最主要的就是 IOCTL_HID_READ_REPORT命令,這個(gè)就是獲取觸摸板數(shù)據(jù)。
分析在 bootcamp版本是 6.1.6813,對應(yīng)的觸摸板SPI總線驅(qū)動的驅(qū)動日期是 2016/05/26, 版本 6.1.6500.0,
(這里再次提到版本和日期,因?yàn)椴煌姹竞芸赡苁遣煌臄?shù)據(jù)格式,由于本人就一臺mac機(jī)器,無法測試其他情況)
數(shù)據(jù)格式結(jié)構(gòu)大致如下:
前46個(gè)字節(jié)是格式頭,接著每個(gè)手指占據(jù)30個(gè)字節(jié)。
比如 一個(gè)手指在觸摸板上,IOCTL_HID_READ_REPORT獲取到的數(shù)據(jù)長度是46 + 30 = 76個(gè)字節(jié),
如果是兩個(gè)手指在觸摸板,IOCTL_HID_READ_REPORT獲取的長度是 46 + 30*2 = 106字節(jié),以此類推。
前46個(gè)字節(jié)描述成c語言數(shù)據(jù)結(jié)構(gòu)大致如下:
typedef unsigned char????????????? u8;
46 length
struct tp_protocol
{
??? u8????????????????? type;????? // unknown type? =2
??? u8????????????????? clicked;?? // 按住了觸摸板, 不管幾個(gè)按住,都是 1
??? u8????????????????? unknown1[5]; //
??? u8????????????????? is_finger;?? // 觸摸板有手指 1,當(dāng)離開瞬間,出現(xiàn) 0
??? u8????????????????? unknown2[8]; //
??? u8????????????????? unknown3[8]; // 未知,固定 00-01-07-97-02-00-06-00
??? u8????????????????? finger_data_length; // 手指數(shù)據(jù)總長度, 手指個(gè)數(shù)*30
??? u8????????????????? unknown4[5]; //
??? u8????????????????? finger_number; //手指個(gè)數(shù)
??? u8????????????????? Clicked; // 同上邊的clicked
??? u8????????????????? state;?? // 手指在上邊好像是 0x10, 手指離開瞬間最高設(shè)置 1,變成 0x80(0x90),最后離開后,還會出現(xiàn) 0x00
??? u8????????????????? state2;? // 手指在上邊 0x20,離開瞬間 變 0
??? u8????????????????? state3;? // 平時(shí)0, Clicked為 0x10
??? u8????????????????? zero;??? // 始終 0
??? u8????????????????? unknown5[10]; /
};
如上所示,其中unknown字段是 沒能解析出來的,不過后來發(fā)現(xiàn)就已知的字段已經(jīng)足夠模擬鼠標(biāo)動作了。
手指的30個(gè)字節(jié)的c語言結(jié)構(gòu)如下:
/ 30 length
struct tp_finger
{
??? short???????????? org_x; //按下后,這個(gè)數(shù)字不變,
??? short???????????? org_y; //
??? short???????????? x;???? //隨著手指移動改變,
??? short???????????? y;???? //
??? short ?????????? unknown[11];
};
其中unknown未知,org_x,org_y好像沒啥用,最有用的是 x,y。表示的是手指在觸摸板的坐標(biāo)位置。
整個(gè)觸摸板(13寸機(jī)器)測試下來,觸摸板范圍,最左邊大致是 -6300多, 左右邊坐標(biāo) 6800多,
最上邊坐標(biāo)7700左右,最下邊-200左右。這些都是用手指移動到邊界得出來的大致數(shù)據(jù)。
有了這些原始的觸摸板的數(shù)據(jù),我們基本就能確定能實(shí)現(xiàn)自己的mbp2017的觸摸板驅(qū)動來模擬鼠標(biāo)操作了。
/
再來看看windows平臺下的HID驅(qū)動開發(fā)過程,
HID(Human Interface Device,人機(jī)接口設(shè)備)是一類設(shè)備總稱,用于提供人和電腦進(jìn)行交互的接口設(shè)備,
像最常用的鼠標(biāo),鍵盤等,觸摸板,還有游戲使用的游戲桿等等。
像鼠標(biāo)鍵盤都是windows提供的標(biāo)準(zhǔn)驅(qū)動,我們在開發(fā)HID驅(qū)動時(shí)候,填寫適當(dāng)?shù)腍ID描述符,
告訴windows我們開發(fā)的是一個(gè)HID的鼠標(biāo)或者HID鍵盤,windows自動就會給我們加載兼容的鼠標(biāo)鍵盤驅(qū)動。
同時(shí)我們在自己的HID驅(qū)動開發(fā)中正確響應(yīng) IOCTL_HID_XXX事件(主要是IOCTL_HID_READ_REPORT)
這個(gè)鼠標(biāo)鍵盤就能正常工作起來。看起來是非常簡單明了的。確實(shí)也是如此。
比如我們要虛擬鼠標(biāo)鍵盤,也使用HID來開發(fā),比起掛鉤什么PS/2或者HOOK之類的做法,也顯得簡單和穩(wěn)定得多,
這個(gè)在以后開發(fā)的博客中會繼續(xù)闡述。
我們先看看WDM模型的HID開發(fā)過程,
首先在 DriverEntry中注冊
IRP_MJ_INTERNAL_DEVICE_CONTROL, IRP_MJ_POWER, IRP_MJ_PNP 三個(gè)派遣函數(shù),
然后填寫 HID_MINIDRIVER_REGISTRATION 結(jié)構(gòu)的參數(shù),調(diào)用HidRegisterMinidriver注冊HID的小端口驅(qū)動,
這樣總體框架就建立起來了。
然后在 IRP_MJ_POWER和IRP_MJ_PNP派遣函數(shù)中,按部就班的實(shí)現(xiàn)電源管理和即插即用事件,
如果是真實(shí)HID設(shè)備,則要認(rèn)真實(shí)現(xiàn)這兩個(gè)功能,如果是虛擬設(shè)備,則不用太在意,找個(gè)現(xiàn)成框架套上去就行,
接著就是核心處理事件 IRP_MJ_INTERNAL_DEVICE_CONTROL,
在這個(gè)派遣函數(shù)中,一般處理
IOCTL_HID_GET_DEVICE_DESCRIPTOR
IOCTL_HID_GET_DEVICE_ATTRIBUTES
IOCTL_HID_READ_REPORT
三個(gè)事件就能讓鼠標(biāo)鍵盤跑起來,當(dāng)然處理的越多,功能越完善,但是HID的IOCTL命令也多不多到哪去。
是的,就是這么簡單的框架,
但是在這里,我們不打算使用WDM的框架,而使用的是 KMDF(WDF的內(nèi)核部分,就是對WDM的封裝 )來開發(fā)。
KMDF就更加省事了,連 PNP和POWER也省略了,就只要關(guān)心 IRP_MJ_INTERNAL_DEVICE_CONTROL 就可以了。
但是正如微軟自己所說,他們的WDF框架跟HID的minidriver小端口驅(qū)動在某些IO請求中存在沖突(IRP_MJ_POWER和IRP_MJ_PNP)
因此WDF框架不能直接橋接 HID的class driver和mini driver,他們使用了一個(gè)折中方案,
開發(fā)一個(gè) mshidkmdf的驅(qū)動來橋接WDF框架和classdriver,在此驅(qū)動中調(diào)用HidRegisterMinidriver 來注冊HID小端口驅(qū)動,
并且mshidkmdf作為服務(wù)安裝,而 我們開發(fā)的HID驅(qū)動則作為LowerFilters來安裝。詳細(xì)可查看如下鏈接:
?https://msdn.microsoft.com/en-us/library/windows/hardware/ff540774
?這就是我們?yōu)楹卧谏厦娴膱D中看到蘋果的 AppleSPItrackpad觸摸板驅(qū)動變成了 Lowerfilters 的底層過濾驅(qū)動了。
最后看看我們開發(fā)KMDF模型的這款觸摸板驅(qū)動的流程:
首先在DriverEntry中,配置好參數(shù),這里主要關(guān)心的是 EvtDeviceAdd 函數(shù),
調(diào)用WdfDriverCreate 來初始化框架。
在EvtDeviceAdd 函數(shù)中,首先調(diào)用WdfFdoInitSetFilter 表明我們開發(fā)的是一個(gè)過濾驅(qū)動。
然后調(diào)用WdfDeviceInitAssignWdmIrpPreprocessCallback 注冊IRP_MN_QUERY_ID這個(gè)特殊查詢事件,
因?yàn)槲覀儽仨氝@么做,才能讓windows識別到我們的驅(qū)動ID,
對應(yīng)這塊觸摸板,我們還得注冊兩個(gè)電源事件,就是D0狀態(tài)(加電和掉電)轉(zhuǎn)換,
因?yàn)榧与娗闆r下,我們得通知SPI總線驅(qū)動,開啟蘋果的觸摸板驅(qū)動,掉電情況做些停止操作處理,
大致如下注冊電源事件:
//設(shè)置電源回調(diào)函數(shù)
??? WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
??? //設(shè)備處于工作(供電D0狀態(tài))或者非工作狀態(tài)
??? pnpPowerCallbacks.EvtDeviceD0Entry = EvtDeviceD0Entry ;// 設(shè)備加電時(shí)候被調(diào)用
??? pnpPowerCallbacks.EvtDeviceD0Exit = EvtDeviceD0Exit;????? // 設(shè)備掉電時(shí)候被調(diào)用
??? WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); ///
然后就是創(chuàng)建過濾設(shè)備,創(chuàng)建IO隊(duì)列,一共兩個(gè)隊(duì)列,一個(gè)是默認(rèn)的IO隊(duì)列,用于處理 InternalDeviceControl請求,
一個(gè)是手動隊(duì)列,我們在處理 IOCTL_HID_READ_REPORT時(shí)候,需要入隊(duì)等待處理,
然后就是初始化一些相關(guān)變量等數(shù)據(jù)。
這里我們讀取SPI總線驅(qū)動的原始觸摸板數(shù)據(jù),使用的是串行讀取,因此整個(gè)驅(qū)動創(chuàng)建一個(gè)全局的Request來重復(fù)使用,
在InternalDeviceControl 的 IOCTL_HID_GET_DEVICE_DESCRIPTOR,IOCTL_HID_GET_DEVICE_ATTRIBUTES
中我們把事先準(zhǔn)備好的HID的鼠標(biāo)描述符,屬性等信息報(bào)告給classdriver,?
然后接收到 IOCTL_HID_READ_REPORT命令時(shí)候調(diào)用WdfRequestForwardToIoQueue 加到手動的IO隊(duì)列。
當(dāng)EvtDeviceD0Entry函數(shù)被調(diào)用(就是設(shè)備加電了),發(fā)起全局的Request對SPI總線驅(qū)動的讀取操作,
同時(shí)設(shè)置這個(gè)Request的完成回調(diào)函數(shù),在完成函數(shù)中分析處理讀取到的觸摸板數(shù)據(jù),處理完成后接著繼續(xù)發(fā)起對觸摸板數(shù)據(jù)的讀取。
?
更詳細(xì)的請查看稍后發(fā)布到GITHUB和CSDN上的源代碼和驅(qū)動程序。
驅(qū)動實(shí)現(xiàn)的功能一個(gè)5個(gè)(如上邊所說)
1,一個(gè)手指按住觸摸板任意位置,另一個(gè)手指移動來達(dá)到拖動效果
2,一個(gè)手指輕點(diǎn)或者一個(gè)手指按下觸摸板,模擬鼠標(biāo)左擊
3,兩個(gè)手指輕點(diǎn),或者一個(gè)手指按住觸摸板右邊3/4-4/4位置,模擬鼠標(biāo)右擊
4,雙指同時(shí)移動來模擬滾輪滾動
5,三指拖移。
做這個(gè)驅(qū)動,寫這篇文章的目的就是因?yàn)閙acBook pro 2017版的觸摸板在windows平臺下難用,
于是決定重新開發(fā)macbook pro 2017觸摸板的windows驅(qū)動。已經(jīng)開發(fā)好的驅(qū)動和源代碼下載地址:
GITHUB:?? https://github.com/fanxiushu/kmouse_filter-AppleSPITrack-driver
CSDN:?? http://download.csdn.net/download/fanxiushu/10047600
如下連接,
http://blog.csdn.net/fanxiushu/article/details/78186745
因?yàn)樾陆鼡Q的電腦是macbook pro 2017 13寸帶bar的機(jī)器,其他還能將就,
就是觸摸板難用,動不動就彈出右鍵菜單,經(jīng)常誤觸,
而且尤其不爽的是拖動時(shí)候,必須按住邊緣,另一只手指才能拖動,
觸摸板這么大,基本無法單手進(jìn)行拖動操作,需要另一個(gè)手的手指按住觸摸板邊緣,然后拖動。
雖然可以點(diǎn)擊兩次,第2次不離開觸摸板來拖動,但是非常不習(xí)慣這種手勢。
還是習(xí)慣傳統(tǒng)的食指按住觸摸板,中指來拖動(因?yàn)檫@種手勢更加接近按住鼠標(biāo)左鍵移動鼠標(biāo)的效果)。
像我這種經(jīng)常把電腦放到大腿上或者床上的人(反正就是不會老老實(shí)實(shí)的放到電腦桌上),
如果帶個(gè)鼠標(biāo)是很不方便的,只能依賴觸摸板來控制電腦。
而且我的要求也不高,不需要什么多手勢,只要觸摸板能盡量模擬鼠標(biāo)的效果就行了。
其實(shí)個(gè)人覺得windows本身的易操作性,使用鼠標(biāo)的效果就能控制操作系統(tǒng)的所有的東西了,
手勢多了我也記不住,每次切換不同手勢還得思考一下也挺累(也許是還沒習(xí)慣,也懶得去習(xí)慣了)。
基于以上各種原因,于是決定重新開發(fā)這款電腦的觸摸板驅(qū)動。
首先解釋一下“點(diǎn)按”和”輕點(diǎn)“:
按住或重壓,就是按下去,能聽到“噠”的一聲響;
”點(diǎn)按“就是“噠”的一聲按下去然后立馬彈上來,
還有一個(gè)就是”輕點(diǎn)“,就是手指接觸到觸摸板然后迅速離開。
我需要達(dá)到的效果也非常簡單明了,用兩根手指模擬鼠標(biāo)效果,操作整個(gè)系統(tǒng)。
1,首先一根手指按住觸摸板的任意位置(是任意位置而不是觸摸板邊緣),另一根手指在觸摸板上移動來達(dá)到拖動效果,
這就相當(dāng)于按住鼠標(biāo)左鍵,移動鼠標(biāo)的效果一樣。
2,一根手指輕點(diǎn),相當(dāng)于鼠標(biāo)左鍵按下去然后立馬彈上來。
???? 一根手指按住觸摸板,相當(dāng)于按下鼠標(biāo)左鍵,從觸摸板彈上來相當(dāng)于彈出左鍵。
3,兩根手指同時(shí)輕點(diǎn),相當(dāng)于鼠標(biāo)右鍵按下去然后立馬彈上來。
???? 一根手指按住觸摸板右邊四分之三到四分之四部分(是整個(gè)右邊3/4,而不是右下邊緣),相當(dāng)于按住鼠標(biāo)右鍵。
4,兩根手指同時(shí)在觸摸板移動,相當(dāng)于滾輪滾動。
5,另外再附加實(shí)現(xiàn)一個(gè)功能:三指拖移。等于是第1個(gè)功能的效果:一個(gè)手指按住,另一個(gè)手指移動。
????? 這樣不重壓觸摸板的也能操作整個(gè)系統(tǒng)。
不模擬鼠標(biāo)的中間鍵按下效果,好像中間鍵沒啥用處。
以上就是我的簡單明了的要求,當(dāng)然習(xí)慣各種手勢的你可能并不贊同,甚至有點(diǎn)嗤之以鼻,然而這依然是我的簡單明了的要求。
重新開發(fā)的觸摸板驅(qū)動也只實(shí)現(xiàn)上邊的功能而已。
要重新開發(fā)macbook pro 2017年 13寸帶bar(以下簡稱mbp2017)的觸摸板的windows驅(qū)動,
首先需要解決兩件事:
一,采集mbp2017的觸摸板數(shù)據(jù),
二,模擬開發(fā)鼠標(biāo)驅(qū)動。
第二個(gè)問題100%的確保能解決,使用HID模式的鼠標(biāo)驅(qū)動就可以了。
關(guān)鍵是第一個(gè)問題,如果不能采集和解析觸摸板的數(shù)據(jù),基本就沒戲了,只能老老實(shí)實(shí)的使用那個(gè)難用的原裝驅(qū)動程序。
mbp2017的觸摸板數(shù)據(jù)結(jié)構(gòu)格式是沒有公開的,而且未來也很可能被蘋果公司改變,因此沒有通用性,
我測試的對應(yīng)bootcamp版本是 6.1.6813,對應(yīng)的觸摸板總線驅(qū)動的驅(qū)動日期是 2016/05/26, 版本 6.1.6500.0,
其他版本的沒測試過,所以不知道的數(shù)據(jù)格式是不是不同。
既然這個(gè)數(shù)據(jù)沒有公開,我們就必須要自己來采集和分析觸摸板的數(shù)據(jù),
好在觸摸板這類設(shè)備本身的數(shù)據(jù)量不大,數(shù)據(jù)結(jié)構(gòu)也應(yīng)該不會多復(fù)雜,只要Apple公司沒變態(tài)到做加密,估計(jì)是能解析出來的。
自己動手解析之前,先要搞清楚它的驅(qū)動運(yùn)行流程。
蘋果使用的SPI總線來傳輸觸摸板和鍵盤的數(shù)據(jù),SPI總線接口,也是我在接觸mbp2017時(shí)候才發(fā)現(xiàn)還有這么一個(gè)玩意,真是孤陋寡聞了。
SPI 全稱Serial Peripheral Interface--串行外設(shè)接口, 最初是Motorola提出和開發(fā)的,它使用主從模式通訊,這點(diǎn)跟USB有點(diǎn)像,
同時(shí)通訊也很簡單,比RS232(串口)還簡單,所以不論軟件或硬件成本都比較低。
但是應(yīng)付鍵盤和觸摸板這類不需要大量通訊數(shù)據(jù)的器件完全足夠了。更詳細(xì)的關(guān)于SPI介紹,請查詢其他資料。
mbp2017的電腦windows驅(qū)動中關(guān)于SPI的,首先有個(gè)SPI總線驅(qū)動,在SPI總線驅(qū)動下掛載兩個(gè)位置,
位置1是鍵盤驅(qū)動,位置2才是觸摸板驅(qū)動。詳細(xì)可看下邊的圖示:
畫紅線的部分,在”系統(tǒng)設(shè)備“里邊的 “Apple SPI Device ” 就是總線驅(qū)動,它負(fù)責(zé)給鍵盤和觸摸板的功能驅(qū)動提供數(shù)據(jù),
在“人體學(xué)輸入設(shè)備”里邊的“ Apple SPI Keyboard” 和 “ Apple SPI Trackpad” 對應(yīng)的就是 鍵盤的功能驅(qū)動和觸摸板的功能驅(qū)動,
再看“Apple SPI Trackpad” 的屬性, 它在總線驅(qū)動的位置是 2, 我們要做的事情,就是替換這個(gè)功能驅(qū)動,使用我們自己開發(fā)的驅(qū)動來代替。
另外我們順便看看這款電腦內(nèi)置的USB接口的設(shè)備,
上邊的藍(lán)線部分,在 “通用串行總線控制器” 里邊存在一個(gè)“Apple USB Composite Device”的復(fù)合設(shè)備,
這個(gè)復(fù)合設(shè)備管理著三個(gè)設(shè)備,“Apple Touch Bar”就是其中一個(gè),這個(gè)就是multi-touch bar, 在windows平臺下沒啥用的雞肋。
另外兩個(gè)看下圖所示:
Apple USB Composite Device一共包含三個(gè)設(shè)備:
FaceTime HD Camera,
Apple Touch Bar,
USB接口的光感氛圍器,就是檢測環(huán)境光線強(qiáng)弱,從而自動調(diào)節(jié)屏幕的亮度。
看圖示的最下邊, bNumConfigurations 是 3, 也就是三個(gè)配置描述符,分別對應(yīng)這三種設(shè)備。
正如上篇文章所說的,在單純只安裝windows系統(tǒng)的時(shí)候,這個(gè)“Apple USB Composite Device” 設(shè)備不能被發(fā)現(xiàn),
從而造成 攝像頭,multi-touch bar,和光感器件找不到,也不知道蘋果在設(shè)計(jì)硬件的時(shí)候搞了什么。
估計(jì)得保留安裝蘋果系統(tǒng)時(shí)候的那個(gè)EFI Parttion 分區(qū),才能被識別,只是后來沒再折騰了,保留了MacOS系統(tǒng)。
再回到觸摸板驅(qū)動,我們打開注冊表,在 如下的位置:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\SPI\VID_05ac&PID_0277&MI_02
能找到這個(gè)驅(qū)動的安裝信息,
注意:mbp2017 對應(yīng)的觸摸板的PID是0277, VID是05ac, 不同型號的,可能PID會不同,這個(gè)得看具體機(jī)器。
如上圖所示,在里邊的LowerFilters字段里是 蘋果的 AppleSPITrackpad驅(qū)動,
Service字段是 mshidkmdf ,從這信息,
我們立馬就知道 AppleSPITrackpad 是一個(gè)KMDF模型的驅(qū)動程序,并且屬于標(biāo)準(zhǔn)的HID的KMDF程序。
AppleSPITrackpad負(fù)責(zé)獲取上邊提到的 “Apple SPI Device" 總線驅(qū)動發(fā)上來的觸摸板數(shù)據(jù),
并且解析模擬成標(biāo)準(zhǔn)的兼容windows的 HID鼠標(biāo)驅(qū)動數(shù)據(jù)。
AppleSPITrackpad在解析觸摸板數(shù)據(jù)時(shí)候,提供了讓人難以適應(yīng)(至少我比較難適應(yīng))的鼠標(biāo)動作模擬,
因此重新開發(fā)這個(gè)驅(qū)動來解決這個(gè)問題。
至此,我們大致知道他們的驅(qū)動的工作流程了。
現(xiàn)在的任務(wù)首先就是獲取SPI總線驅(qū)動發(fā)上來的數(shù)據(jù)結(jié)構(gòu)格式。
可以開發(fā)一個(gè)簡單的WDM 過濾驅(qū)動,掛載到 LowerFilters字段里邊的 AppleSPITrackpad 前邊。
這樣AppleSPITrackpad跟SPI總線驅(qū)動通訊的所有IRP請求都能被截獲到,從而就能獲取到觸摸板數(shù)據(jù)。
WDM Filter的開發(fā)可以查看我很早前的一篇文章:
http://blog.csdn.net/fanxiushu/article/details/8834385
當(dāng)然如果你能找到其他現(xiàn)成的工具來分析AppleSPITrackpad的通訊數(shù)據(jù),會更快捷。
通過分析,AppleSPITrackpad發(fā)給SPI總線驅(qū)動四個(gè)請求:
IOCTL_HID_GET_DEVICE_DESCRIPTOR
IOCTL_HID_GET_DEVICE_ATTRIBUTES
IOCTL_HID_SET_FEATURE
IOCTL_HID_READ_REPORT
都是標(biāo)準(zhǔn)的HID請求命令,其中IOCTL_HID_SET_FEATURE 應(yīng)該是用于告訴SPI總線驅(qū)動開啟或者關(guān)閉觸摸板功能的。
最主要的就是 IOCTL_HID_READ_REPORT命令,這個(gè)就是獲取觸摸板數(shù)據(jù)。
分析在 bootcamp版本是 6.1.6813,對應(yīng)的觸摸板SPI總線驅(qū)動的驅(qū)動日期是 2016/05/26, 版本 6.1.6500.0,
(這里再次提到版本和日期,因?yàn)椴煌姹竞芸赡苁遣煌臄?shù)據(jù)格式,由于本人就一臺mac機(jī)器,無法測試其他情況)
數(shù)據(jù)格式結(jié)構(gòu)大致如下:
前46個(gè)字節(jié)是格式頭,接著每個(gè)手指占據(jù)30個(gè)字節(jié)。
比如 一個(gè)手指在觸摸板上,IOCTL_HID_READ_REPORT獲取到的數(shù)據(jù)長度是46 + 30 = 76個(gè)字節(jié),
如果是兩個(gè)手指在觸摸板,IOCTL_HID_READ_REPORT獲取的長度是 46 + 30*2 = 106字節(jié),以此類推。
前46個(gè)字節(jié)描述成c語言數(shù)據(jù)結(jié)構(gòu)大致如下:
typedef unsigned char????????????? u8;
46 length
struct tp_protocol
{
??? u8????????????????? type;????? // unknown type? =2
??? u8????????????????? clicked;?? // 按住了觸摸板, 不管幾個(gè)按住,都是 1
??? u8????????????????? unknown1[5]; //
??? u8????????????????? is_finger;?? // 觸摸板有手指 1,當(dāng)離開瞬間,出現(xiàn) 0
??? u8????????????????? unknown2[8]; //
??? u8????????????????? unknown3[8]; // 未知,固定 00-01-07-97-02-00-06-00
??? u8????????????????? finger_data_length; // 手指數(shù)據(jù)總長度, 手指個(gè)數(shù)*30
??? u8????????????????? unknown4[5]; //
??? u8????????????????? finger_number; //手指個(gè)數(shù)
??? u8????????????????? Clicked; // 同上邊的clicked
??? u8????????????????? state;?? // 手指在上邊好像是 0x10, 手指離開瞬間最高設(shè)置 1,變成 0x80(0x90),最后離開后,還會出現(xiàn) 0x00
??? u8????????????????? state2;? // 手指在上邊 0x20,離開瞬間 變 0
??? u8????????????????? state3;? // 平時(shí)0, Clicked為 0x10
??? u8????????????????? zero;??? // 始終 0
??? u8????????????????? unknown5[10]; /
};
如上所示,其中unknown字段是 沒能解析出來的,不過后來發(fā)現(xiàn)就已知的字段已經(jīng)足夠模擬鼠標(biāo)動作了。
手指的30個(gè)字節(jié)的c語言結(jié)構(gòu)如下:
/ 30 length
struct tp_finger
{
??? short???????????? org_x; //按下后,這個(gè)數(shù)字不變,
??? short???????????? org_y; //
??? short???????????? x;???? //隨著手指移動改變,
??? short???????????? y;???? //
??? short ?????????? unknown[11];
};
其中unknown未知,org_x,org_y好像沒啥用,最有用的是 x,y。表示的是手指在觸摸板的坐標(biāo)位置。
整個(gè)觸摸板(13寸機(jī)器)測試下來,觸摸板范圍,最左邊大致是 -6300多, 左右邊坐標(biāo) 6800多,
最上邊坐標(biāo)7700左右,最下邊-200左右。這些都是用手指移動到邊界得出來的大致數(shù)據(jù)。
有了這些原始的觸摸板的數(shù)據(jù),我們基本就能確定能實(shí)現(xiàn)自己的mbp2017的觸摸板驅(qū)動來模擬鼠標(biāo)操作了。
/
再來看看windows平臺下的HID驅(qū)動開發(fā)過程,
HID(Human Interface Device,人機(jī)接口設(shè)備)是一類設(shè)備總稱,用于提供人和電腦進(jìn)行交互的接口設(shè)備,
像最常用的鼠標(biāo),鍵盤等,觸摸板,還有游戲使用的游戲桿等等。
像鼠標(biāo)鍵盤都是windows提供的標(biāo)準(zhǔn)驅(qū)動,我們在開發(fā)HID驅(qū)動時(shí)候,填寫適當(dāng)?shù)腍ID描述符,
告訴windows我們開發(fā)的是一個(gè)HID的鼠標(biāo)或者HID鍵盤,windows自動就會給我們加載兼容的鼠標(biāo)鍵盤驅(qū)動。
同時(shí)我們在自己的HID驅(qū)動開發(fā)中正確響應(yīng) IOCTL_HID_XXX事件(主要是IOCTL_HID_READ_REPORT)
這個(gè)鼠標(biāo)鍵盤就能正常工作起來。看起來是非常簡單明了的。確實(shí)也是如此。
比如我們要虛擬鼠標(biāo)鍵盤,也使用HID來開發(fā),比起掛鉤什么PS/2或者HOOK之類的做法,也顯得簡單和穩(wěn)定得多,
這個(gè)在以后開發(fā)的博客中會繼續(xù)闡述。
我們先看看WDM模型的HID開發(fā)過程,
首先在 DriverEntry中注冊
IRP_MJ_INTERNAL_DEVICE_CONTROL, IRP_MJ_POWER, IRP_MJ_PNP 三個(gè)派遣函數(shù),
然后填寫 HID_MINIDRIVER_REGISTRATION 結(jié)構(gòu)的參數(shù),調(diào)用HidRegisterMinidriver注冊HID的小端口驅(qū)動,
這樣總體框架就建立起來了。
然后在 IRP_MJ_POWER和IRP_MJ_PNP派遣函數(shù)中,按部就班的實(shí)現(xiàn)電源管理和即插即用事件,
如果是真實(shí)HID設(shè)備,則要認(rèn)真實(shí)現(xiàn)這兩個(gè)功能,如果是虛擬設(shè)備,則不用太在意,找個(gè)現(xiàn)成框架套上去就行,
接著就是核心處理事件 IRP_MJ_INTERNAL_DEVICE_CONTROL,
在這個(gè)派遣函數(shù)中,一般處理
IOCTL_HID_GET_DEVICE_DESCRIPTOR
IOCTL_HID_GET_DEVICE_ATTRIBUTES
IOCTL_HID_READ_REPORT
三個(gè)事件就能讓鼠標(biāo)鍵盤跑起來,當(dāng)然處理的越多,功能越完善,但是HID的IOCTL命令也多不多到哪去。
是的,就是這么簡單的框架,
但是在這里,我們不打算使用WDM的框架,而使用的是 KMDF(WDF的內(nèi)核部分,就是對WDM的封裝 )來開發(fā)。
KMDF就更加省事了,連 PNP和POWER也省略了,就只要關(guān)心 IRP_MJ_INTERNAL_DEVICE_CONTROL 就可以了。
但是正如微軟自己所說,他們的WDF框架跟HID的minidriver小端口驅(qū)動在某些IO請求中存在沖突(IRP_MJ_POWER和IRP_MJ_PNP)
因此WDF框架不能直接橋接 HID的class driver和mini driver,他們使用了一個(gè)折中方案,
開發(fā)一個(gè) mshidkmdf的驅(qū)動來橋接WDF框架和classdriver,在此驅(qū)動中調(diào)用HidRegisterMinidriver 來注冊HID小端口驅(qū)動,
并且mshidkmdf作為服務(wù)安裝,而 我們開發(fā)的HID驅(qū)動則作為LowerFilters來安裝。詳細(xì)可查看如下鏈接:
?https://msdn.microsoft.com/en-us/library/windows/hardware/ff540774
?這就是我們?yōu)楹卧谏厦娴膱D中看到蘋果的 AppleSPItrackpad觸摸板驅(qū)動變成了 Lowerfilters 的底層過濾驅(qū)動了。
最后看看我們開發(fā)KMDF模型的這款觸摸板驅(qū)動的流程:
首先在DriverEntry中,配置好參數(shù),這里主要關(guān)心的是 EvtDeviceAdd 函數(shù),
調(diào)用WdfDriverCreate 來初始化框架。
在EvtDeviceAdd 函數(shù)中,首先調(diào)用WdfFdoInitSetFilter 表明我們開發(fā)的是一個(gè)過濾驅(qū)動。
然后調(diào)用WdfDeviceInitAssignWdmIrpPreprocessCallback 注冊IRP_MN_QUERY_ID這個(gè)特殊查詢事件,
因?yàn)槲覀儽仨氝@么做,才能讓windows識別到我們的驅(qū)動ID,
對應(yīng)這塊觸摸板,我們還得注冊兩個(gè)電源事件,就是D0狀態(tài)(加電和掉電)轉(zhuǎn)換,
因?yàn)榧与娗闆r下,我們得通知SPI總線驅(qū)動,開啟蘋果的觸摸板驅(qū)動,掉電情況做些停止操作處理,
大致如下注冊電源事件:
//設(shè)置電源回調(diào)函數(shù)
??? WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
??? //設(shè)備處于工作(供電D0狀態(tài))或者非工作狀態(tài)
??? pnpPowerCallbacks.EvtDeviceD0Entry = EvtDeviceD0Entry ;// 設(shè)備加電時(shí)候被調(diào)用
??? pnpPowerCallbacks.EvtDeviceD0Exit = EvtDeviceD0Exit;????? // 設(shè)備掉電時(shí)候被調(diào)用
??? WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); ///
然后就是創(chuàng)建過濾設(shè)備,創(chuàng)建IO隊(duì)列,一共兩個(gè)隊(duì)列,一個(gè)是默認(rèn)的IO隊(duì)列,用于處理 InternalDeviceControl請求,
一個(gè)是手動隊(duì)列,我們在處理 IOCTL_HID_READ_REPORT時(shí)候,需要入隊(duì)等待處理,
然后就是初始化一些相關(guān)變量等數(shù)據(jù)。
這里我們讀取SPI總線驅(qū)動的原始觸摸板數(shù)據(jù),使用的是串行讀取,因此整個(gè)驅(qū)動創(chuàng)建一個(gè)全局的Request來重復(fù)使用,
在InternalDeviceControl 的 IOCTL_HID_GET_DEVICE_DESCRIPTOR,IOCTL_HID_GET_DEVICE_ATTRIBUTES
中我們把事先準(zhǔn)備好的HID的鼠標(biāo)描述符,屬性等信息報(bào)告給classdriver,?
然后接收到 IOCTL_HID_READ_REPORT命令時(shí)候調(diào)用WdfRequestForwardToIoQueue 加到手動的IO隊(duì)列。
當(dāng)EvtDeviceD0Entry函數(shù)被調(diào)用(就是設(shè)備加電了),發(fā)起全局的Request對SPI總線驅(qū)動的讀取操作,
同時(shí)設(shè)置這個(gè)Request的完成回調(diào)函數(shù),在完成函數(shù)中分析處理讀取到的觸摸板數(shù)據(jù),處理完成后接著繼續(xù)發(fā)起對觸摸板數(shù)據(jù)的讀取。
?
更詳細(xì)的請查看稍后發(fā)布到GITHUB和CSDN上的源代碼和驅(qū)動程序。
驅(qū)動實(shí)現(xiàn)的功能一個(gè)5個(gè)(如上邊所說)
1,一個(gè)手指按住觸摸板任意位置,另一個(gè)手指移動來達(dá)到拖動效果
2,一個(gè)手指輕點(diǎn)或者一個(gè)手指按下觸摸板,模擬鼠標(biāo)左擊
3,兩個(gè)手指輕點(diǎn),或者一個(gè)手指按住觸摸板右邊3/4-4/4位置,模擬鼠標(biāo)右擊
4,雙指同時(shí)移動來模擬滾輪滾動
5,三指拖移。
如果你已經(jīng)適應(yīng)了蘋果的觸摸板windows的手勢行為,則無需留意本文的內(nèi)容。
總結(jié)
以上是生活随笔為你收集整理的MacBook Pro 2017 13寸版 触摸板windows驱动开发(开发HID鼠标键盘驱动之一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: showdialog 尝试读取或写入受保
- 下一篇: 打造前端MAC工作站(二)安装软件的两种