by fanxiushu 2017-10-27 转载或引用请注明原始作者。

做这个驱动,写这篇文章的目的就是因为macBook pro 2017版的触摸板在windows平台下难用,

于是决定重新开发macbook pro 2017触摸板的windows驱动。

已经开发好的驱动和源代码下载地址:

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
因为新近换的电脑是macbook pro 2017 13寸带bar的机器,其他还能将就,
就是触摸板难用,动不动就弹出右键菜单,经常误触,
而且尤其不爽的是拖动时候,必须按住边缘,另一只手指才能拖动,
触摸板这么大,基本无法单手进行拖动操作,需要另一个手的手指按住触摸板边缘,然后拖动。
虽然可以点击两次,第2次不离开触摸板来拖动,但是非常不习惯这种手势。
还是习惯传统的食指按住触摸板,中指来拖动(因为这种手势更加接近按住鼠标左键移动鼠标的效果)。

像我这种经常把电脑放到大腿上或者床上的人(反正就是不会老老实实的放到电脑桌上),
如果带个鼠标是很不方便的,只能依赖触摸板来控制电脑。
而且我的要求也不高,不需要什么多手势,只要触摸板能尽量模拟鼠标的效果就行了。
其实个人觉得windows本身的易操作性,使用鼠标的效果就能控制操作系统的所有的东西了,
手势多了我也记不住,每次切换不同手势还得思考一下也挺累(也许是还没习惯,也懒得去习惯了)。

基于以上各种原因,于是决定重新开发这款电脑的触摸板驱动。
首先解释一下“点按”和”轻点“:
按住或重压,就是按下去,能听到“哒”的一声响;
”点按“就是“哒”的一声按下去然后立马弹上来,
还有一个就是”轻点“,就是手指接触到触摸板然后迅速离开。

我需要达到的效果也非常简单明了,用两根手指模拟鼠标效果,操作整个系统。
1,首先一根手指按住触摸板的任意位置(是任意位置而不是触摸板边缘),另一根手指在触摸板上移动来达到拖动效果,
这就相当于按住鼠标左键,移动鼠标的效果一样。
2,一根手指轻点,相当于鼠标左键按下去然后立马弹上来。
     一根手指按住触摸板,相当于按下鼠标左键,从触摸板弹上来相当于弹出左键。
3,两根手指同时轻点,相当于鼠标右键按下去然后立马弹上来。
     一根手指按住触摸板右边四分之三到四分之四部分(是整个右边3/4,而不是右下边缘),相当于按住鼠标右键。
4,两根手指同时在触摸板移动,相当于滚轮滚动。
5,另外再附加实现一个功能:三指拖移。等于是第1个功能的效果:一个手指按住,另一个手指移动。
      这样不重压触摸板的也能操作整个系统。

不模拟鼠标的中间键按下效果,好像中间键没啥用处。

以上就是我的简单明了的要求,当然习惯各种手势的你可能并不赞同,甚至有点嗤之以鼻,然而这依然是我的简单明了的要求。
重新开发的触摸板驱动也只实现上边的功能而已。

要重新开发macbook pro 2017年 13寸带bar(以下简称mbp2017)的触摸板的windows驱动,
首先需要解决两件事:
一,采集mbp2017的触摸板数据,
二,模拟开发鼠标驱动。
第二个问题100%的确保能解决,使用HID模式的鼠标驱动就可以了。
关键是第一个问题,如果不能采集和解析触摸板的数据,基本就没戏了,只能老老实实的使用那个难用的原装驱动程序。
mbp2017的触摸板数据结构格式是没有公开的,而且未来也很可能被苹果公司改变,因此没有通用性,
我测试的对应bootcamp版本是 6.1.6813,对应的触摸板总线驱动的驱动日期是 2016/05/26, 版本 6.1.6500.0,
其他版本的没测试过,所以不知道的数据格式是不是不同。

既然这个数据没有公开,我们就必须要自己来采集和分析触摸板的数据,
好在触摸板这类设备本身的数据量不大,数据结构也应该不会多复杂,只要Apple公司没变态到做加密,估计是能解析出来的。
自己动手解析之前,先要搞清楚它的驱动运行流程。
苹果使用的SPI总线来传输触摸板和键盘的数据,SPI总线接口,也是我在接触mbp2017时候才发现还有这么一个玩意,真是孤陋寡闻了。
SPI 全称Serial Peripheral Interface--串行外设接口, 最初是Motorola提出和开发的,它使用主从模式通讯,这点跟USB有点像,
同时通讯也很简单,比RS232(串口)还简单,所以不论软件或硬件成本都比较低。
但是应付键盘和触摸板这类不需要大量通讯数据的器件完全足够了。更详细的关于SPI介绍,请查询其他资料。
mbp2017的电脑windows驱动中关于SPI的,首先有个SPI总线驱动,在SPI总线驱动下挂载两个位置,
位置1是键盘驱动,位置2才是触摸板驱动。详细可看下边的图示:

画红线的部分,在”系统设备“里边的 “Apple SPI Device ” 就是总线驱动,它负责给键盘和触摸板的功能驱动提供数据,
在“人体学输入设备”里边的“ Apple SPI Keyboard” 和 “ Apple SPI Trackpad” 对应的就是 键盘的功能驱动和触摸板的功能驱动,
再看“Apple SPI Trackpad” 的属性, 它在总线驱动的位置是 2, 我们要做的事情,就是替换这个功能驱动,使用我们自己开发的驱动来代替。

另外我们顺便看看这款电脑内置的USB接口的设备,
上边的蓝线部分,在 “通用串行总线控制器” 里边存在一个“Apple USB Composite Device”的复合设备,
这个复合设备管理着三个设备,“Apple Touch Bar”就是其中一个,这个就是multi-touch bar, 在windows平台下没啥用的鸡肋。
另外两个看下图所示:

Apple USB Composite Device一共包含三个设备:
FaceTime HD Camera,
Apple Touch Bar,
USB接口的光感氛围器,就是检测环境光线强弱,从而自动调节屏幕的亮度。
看图示的最下边, bNumConfigurations 是 3, 也就是三个配置描述符,分别对应这三种设备。

正如上篇文章所说的,在单纯只安装windows系统的时候,这个“Apple USB Composite Device” 设备不能被发现,
从而造成 摄像头,multi-touch bar,和光感器件找不到,也不知道苹果在设计硬件的时候搞了什么。
估计得保留安装苹果系统时候的那个EFI Parttion 分区,才能被识别,只是后来没再折腾了,保留了MacOS系统。

再回到触摸板驱动,我们打开注册表,在 如下的位置:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\SPI\VID_05ac&PID_0277&MI_02
能找到这个驱动的安装信息,
注意:mbp2017 对应的触摸板的PID是0277, VID是05ac, 不同型号的,可能PID会不同,这个得看具体机器。

如上图所示,在里边的LowerFilters字段里是 苹果的 AppleSPITrackpad驱动,
Service字段是 mshidkmdf ,从这信息,
我们立马就知道 AppleSPITrackpad 是一个KMDF模型的驱动程序,并且属于标准的HID的KMDF程序。
AppleSPITrackpad负责获取上边提到的 “Apple SPI Device" 总线驱动发上来的触摸板数据,
并且解析模拟成标准的兼容windows的 HID鼠标驱动数据。

AppleSPITrackpad在解析触摸板数据时候,提供了让人难以适应(至少我比较难适应)的鼠标动作模拟,
因此重新开发这个驱动来解决这个问题。
至此,我们大致知道他们的驱动的工作流程了。

现在的任务首先就是获取SPI总线驱动发上来的数据结构格式。
可以开发一个简单的WDM 过滤驱动,挂载到 LowerFilters字段里边的 AppleSPITrackpad 前边。
这样AppleSPITrackpad跟SPI总线驱动通讯的所有IRP请求都能被截获到,从而就能获取到触摸板数据。
WDM Filter的开发可以查看我很早前的一篇文章:
http://blog.csdn.net/fanxiushu/article/details/8834385
当然如果你能找到其他现成的工具来分析AppleSPITrackpad的通讯数据,会更快捷。

通过分析,AppleSPITrackpad发给SPI总线驱动四个请求:
IOCTL_HID_GET_DEVICE_DESCRIPTOR
IOCTL_HID_GET_DEVICE_ATTRIBUTES
IOCTL_HID_SET_FEATURE
IOCTL_HID_READ_REPORT
都是标准的HID请求命令,其中IOCTL_HID_SET_FEATURE 应该是用于告诉SPI总线驱动开启或者关闭触摸板功能的。
最主要的就是 IOCTL_HID_READ_REPORT命令,这个就是获取触摸板数据。

分析在 bootcamp版本是 6.1.6813,对应的触摸板SPI总线驱动的驱动日期是 2016/05/26, 版本 6.1.6500.0,
(这里再次提到版本和日期,因为不同版本很可能是不同的数据格式,由于本人就一台mac机器,无法测试其他情况)

数据格式结构大致如下:
前46个字节是格式头,接着每个手指占据30个字节。
比如 一个手指在触摸板上,IOCTL_HID_READ_REPORT获取到的数据长度是46 + 30 = 76个字节,
如果是两个手指在触摸板,IOCTL_HID_READ_REPORT获取的长度是 46 + 30*2 = 106字节,以此类推。

前46个字节描述成c语言数据结构大致如下:
typedef unsigned char              u8;
46 length
struct tp_protocol
{
    u8                  type;      // unknown type  =2
    u8                  clicked;   // 按住了触摸板, 不管几个按住,都是 1
    u8                  unknown1[5]; //
    u8                  is_finger;   // 触摸板有手指 1,当离开瞬间,出现 0
    u8                  unknown2[8]; //
    u8                  unknown3[8]; // 未知,固定 00-01-07-97-02-00-06-00
    u8                  finger_data_length; // 手指数据总长度, 手指个数*30
    u8                  unknown4[5]; //
    u8                  finger_number; //手指个数
    u8                  Clicked; // 同上边的clicked
    u8                  state;   // 手指在上边好像是 0x10, 手指离开瞬间最高设置 1,变成 0x80(0x90),最后离开后,还会出现 0x00
    u8                  state2;  // 手指在上边 0x20,离开瞬间 变 0
    u8                  state3;  // 平时0, Clicked为 0x10
    u8                  zero;    // 始终 0
    u8                  unknown5[10]; /
};

如上所示,其中unknown字段是 没能解析出来的,不过后来发现就已知的字段已经足够模拟鼠标动作了。

手指的30个字节的c语言结构如下:
/ 30 length
struct tp_finger
{
    short             org_x; //按下后,这个数字不变,
    short             org_y; //
    short             x;     //随着手指移动改变,
    short             y;     //
    short            unknown[11];
};

其中unknown未知,org_x,org_y好像没啥用,最有用的是 x,y。表示的是手指在触摸板的坐标位置。
整个触摸板(13寸机器)测试下来,触摸板范围,最左边大致是 -6300多, 左右边坐标 6800多,
最上边坐标7700左右,最下边-200左右。这些都是用手指移动到边界得出来的大致数据。

有了这些原始的触摸板的数据,我们基本就能确定能实现自己的mbp2017的触摸板驱动来模拟鼠标操作了。

/
再来看看windows平台下的HID驱动开发过程,
HID(Human Interface Device,人机接口设备)是一类设备总称,用于提供人和电脑进行交互的接口设备,
像最常用的鼠标,键盘等,触摸板,还有游戏使用的游戏杆等等。
像鼠标键盘都是windows提供的标准驱动,我们在开发HID驱动时候,填写适当的HID描述符,
告诉windows我们开发的是一个HID的鼠标或者HID键盘,windows自动就会给我们加载兼容的鼠标键盘驱动。
同时我们在自己的HID驱动开发中正确响应 IOCTL_HID_XXX事件(主要是IOCTL_HID_READ_REPORT)
这个鼠标键盘就能正常工作起来。看起来是非常简单明了的。确实也是如此。
比如我们要虚拟鼠标键盘,也使用HID来开发,比起挂钩什么PS/2或者HOOK之类的做法,也显得简单和稳定得多,
这个在以后开发的博客中会继续阐述。

我们先看看WDM模型的HID开发过程,
首先在 DriverEntry中注册
IRP_MJ_INTERNAL_DEVICE_CONTROL, IRP_MJ_POWER, IRP_MJ_PNP 三个派遣函数,
然后填写 HID_MINIDRIVER_REGISTRATION 结构的参数,调用HidRegisterMinidriver注册HID的小端口驱动,
这样总体框架就建立起来了。
然后在 IRP_MJ_POWER和IRP_MJ_PNP派遣函数中,按部就班的实现电源管理和即插即用事件,
如果是真实HID设备,则要认真实现这两个功能,如果是虚拟设备,则不用太在意,找个现成框架套上去就行,
接着就是核心处理事件 IRP_MJ_INTERNAL_DEVICE_CONTROL,
在这个派遣函数中,一般处理
IOCTL_HID_GET_DEVICE_DESCRIPTOR
IOCTL_HID_GET_DEVICE_ATTRIBUTES
IOCTL_HID_READ_REPORT
三个事件就能让鼠标键盘跑起来,当然处理的越多,功能越完善,但是HID的IOCTL命令也多不多到哪去。
是的,就是这么简单的框架,
但是在这里,我们不打算使用WDM的框架,而使用的是 KMDF(WDF的内核部分,就是对WDM的封装 )来开发。

KMDF就更加省事了,连 PNP和POWER也省略了,就只要关心 IRP_MJ_INTERNAL_DEVICE_CONTROL 就可以了。
但是正如微软自己所说,他们的WDF框架跟HID的minidriver小端口驱动在某些IO请求中存在冲突(IRP_MJ_POWER和IRP_MJ_PNP)
因此WDF框架不能直接桥接 HID的class driver和mini driver,他们使用了一个折中方案,
开发一个 mshidkmdf的驱动来桥接WDF框架和classdriver,在此驱动中调用HidRegisterMinidriver 来注册HID小端口驱动,
并且mshidkmdf作为服务安装,而 我们开发的HID驱动则作为LowerFilters来安装。详细可查看如下链接:
 https://msdn.microsoft.com/en-us/library/windows/hardware/ff540774
 这就是我们为何在上面的图中看到苹果的 AppleSPItrackpad触摸板驱动变成了 Lowerfilters 的底层过滤驱动了。

最后看看我们开发KMDF模型的这款触摸板驱动的流程:
首先在DriverEntry中,配置好参数,这里主要关心的是 EvtDeviceAdd 函数,
调用WdfDriverCreate 来初始化框架。
在EvtDeviceAdd 函数中,首先调用WdfFdoInitSetFilter 表明我们开发的是一个过滤驱动。
然后调用WdfDeviceInitAssignWdmIrpPreprocessCallback 注册IRP_MN_QUERY_ID这个特殊查询事件,
因为我们必须这么做,才能让windows识别到我们的驱动ID,
对应这块触摸板,我们还得注册两个电源事件,就是D0状态(加电和掉电)转换,
因为加电情况下,我们得通知SPI总线驱动,开启苹果的触摸板驱动,掉电情况做些停止操作处理,
大致如下注册电源事件:
//设置电源回调函数
    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
    //设备处于工作(供电D0状态)或者非工作状态
    pnpPowerCallbacks.EvtDeviceD0Entry = EvtDeviceD0Entry ;// 设备加电时候被调用
    pnpPowerCallbacks.EvtDeviceD0Exit = EvtDeviceD0Exit;      // 设备掉电时候被调用
    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); ///

然后就是创建过滤设备,创建IO队列,一共两个队列,一个是默认的IO队列,用于处理 InternalDeviceControl请求,
一个是手动队列,我们在处理 IOCTL_HID_READ_REPORT时候,需要入队等待处理,
然后就是初始化一些相关变量等数据。
这里我们读取SPI总线驱动的原始触摸板数据,使用的是串行读取,因此整个驱动创建一个全局的Request来重复使用,
在InternalDeviceControl 的 IOCTL_HID_GET_DEVICE_DESCRIPTOR,IOCTL_HID_GET_DEVICE_ATTRIBUTES
中我们把事先准备好的HID的鼠标描述符,属性等信息报告给classdriver, 
然后接收到 IOCTL_HID_READ_REPORT命令时候调用WdfRequestForwardToIoQueue 加到手动的IO队列。

当EvtDeviceD0Entry函数被调用(就是设备加电了),发起全局的Request对SPI总线驱动的读取操作,
同时设置这个Request的完成回调函数,在完成函数中分析处理读取到的触摸板数据,处理完成后接着继续发起对触摸板数据的读取。
 
更详细的请查看稍后发布到GITHUB和CSDN上的源代码和驱动程序。

驱动实现的功能一个5个(如上边所说)
1,一个手指按住触摸板任意位置,另一个手指移动来达到拖动效果
2,一个手指轻点或者一个手指按下触摸板,模拟鼠标左击
3,两个手指轻点,或者一个手指按住触摸板右边3/4-4/4位置,模拟鼠标右击
4,双指同时移动来模拟滚轮滚动
5,三指拖移。

如果你已经适应了苹果的触摸板windows的手势行为,则无需留意本文的内容。

MacBook Pro 2017 13寸版 触摸板windows驱动开发(开发HID鼠标键盘驱动之一)相关推荐

  1. MacBook Pro 2019 13寸 体验

    原因 团队成员几乎都用 mbp,很多项目配置和软件使用都是针对 MacOS. 研发方向转变:golang 和 python,语言跨平台,感觉 MacOS 研发效率更高. MacOS 自带原生类 uni ...

  2. macbook pro 2015 13寸装单系统win_MacBook 全系列选购指南丨学生党或者设计师,入手哪款最合适?...

    苹果 MacBook 笔记本电脑,被誉为"妈妈看了放心"的学习本,也是学生时代的"装X神器".比起几乎人手都有的iPhone,苹果的笔记本无论是入手门槛还是后续 ...

  3. macbook pro 2015 13寸装单系统win_该如何选择Mac os系统还是Win系统?

    现在工作.学习上很大部分都需要用到电脑,那么该如何选择电脑呢?是win系统比较好呢?还是mac os系统更安全稳定呢?现在macw和大家聊一聊,什么样的人群适合购买Mac. [设计师] Mac是设计师 ...

  4. macbook pro 2015 13寸装单系统win_我的MacBook|这六款实用软件让我一见倾心

    上一篇"我的MacBook"为主题的文章,小编对自己使用频率最高的软件做了一个简单的汇总,不知道大家有没有看到呢.其实软件在精不在多,即使只有两三款软件能打动你,为你带来效率上的显 ...

  5. macbook pro python开发_年轻人第一台 Mac,来自一个开发者的 Macbook Pro 2019 16寸简评...

    从工作开始,一直就想买个 Mac,但是一直没有买成,虽说有公司配发的 Mac(这也让我从 Macbook Pro 2015 13寸到 Macbook Pro 2017 15寸,到 Macbook Pr ...

  6. 手用计算机电池,MacBook 篇一:二手MacBook pro 2017上手+换电池体验

    MacBook 篇一:二手MacBook pro 2017上手+换电池体验 2020-12-14 17:49:25 36点赞 98收藏 55评论 前言 随着去年换了苹果手机,对于苹果的生态愈发心水,在 ...

  7. 浅谈MacBook Pro 2019 15寸

    MacBook Pro 是Apple在笔记本电脑方面的得意之作,它的特点就在于性能强悍,是设计人员.软件工作者的不二之选.下面就MacBook Pro 2019 15寸作简单分析. CPU 随着Mac ...

  8. MacBook Pro 2017外接显卡实战——打破Mac不能玩游戏的定律

    MacBook Pro 2017 外接显卡实战--打破 Mac 不能玩游戏的定律 懂电脑的人都知道,Mac 是非常不适合打游戏的.2017 年的 MacBook Pro 顶配自带独显性能也就 1050 ...

  9. java程序员买mac还是pc_Java程序猿,买macbook pro2020款13寸还是2019款16寸?

    Java程序猿,买macbook pro2020款13寸还是2019款16寸? 这个首先看你使用的场景是怎么样?长期作为办公室专用电脑,不需要携带着长期出差情况--建议用2019款的MacBook P ...

  10. MacBook Pro(13 英寸,2011 年末)A1278安装Windows11蓝屏代码WDF_VIOLATION问题解决

    MacBook Pro(13 英寸,2011 年末)A1278,扔了可惜,想让它重新焕发青春.京东上花171元购买了一个240G的京造SATA3 SSD,将原来的机械硬盘换掉,启动时按Command+ ...

最新文章

  1. 微生物生态学中的挑战:建立对于群落功能与动态的预测性认识
  2. c语言编程:输入一个数看它是不是素数
  3. java 中启动线程的正确方式
  4. c++内存对齐的规则
  5. 项目Alpha冲刺--5/10
  6. spring aop实现过程之三Spring AOP中Aspect编织的实现
  7. Services overview
  8. JRE和JDK 1.3、1.4、1.5(5.0)、6.0 各版本下载地址大全(J2SDK,JavaSE JavaEE)
  9. SQL存储过程的导入导出
  10. 浅谈asio中async_accept函数占用内存高的写法
  11. hive 如何将数组转成字符串_hive详细笔记(八)-Hive之列转行和行转列(附带讲解视频 )...
  12. 摩擦力特点用计算机绘制出,AGC液压缸模拟工况摩擦力特性测试方法研究
  13. 静电场求电场强度E和电势U的方法
  14. 每日一题--字符串数组重排(Google推荐面试书--Cracking the Coding Interview)
  15. Emacs-206-Windows上实现org-pomodoro的声音提示播放
  16. 平面设计中的抠图技法与修图思路
  17. 【react-hook】 useCallback
  18. Python爬虫——Requests 库基本使用
  19. etcd-集群部署,基于ssl认证的节点间通信,客户端基于ssl客户端证书访问。
  20. 按照老师的方式,将四分位距的统计学异常检测如法炮制

热门文章

  1. Android OpenCv实现拍照搜题功能实现步骤
  2. JSONObject.fromObject(obj) 报错
  3. 软件工程 -- 开发模型
  4. 苹果计算机 win10,苹果电脑怎么安装Win10系统?
  5. GIS软件开发入门需要学习哪些内容?
  6. vbs整人代码蓝屏_vbs恶作剧(整人代码)-英文报数 蓝屏 重启电脑等
  7. 空间相关分析(四) 空间相关分析实战——对比人均GDP与综合经济指数
  8. tomcat7 安装和环境变量配置
  9. CHIP-seq流程学习笔记(11)-使用GSEA软件进行GSEA分析
  10. LQR控制算法推导以及简单分析