ThinkPad Hard Drive Active Protection System

  数年前,在我还没有买现在用的这台 T60 之前,曾经在网上看过一条技术新闻,说是国外 Linux 玩家给 ThinkPad 开发了一种软件,它可以响应对显示器拍打的动作,从而执行特定的操作,比如拍拍显示器的边框就运行 Vim 等等。据说这是因为 ThinkPad 内置了特殊的硬件,在其它品牌的本本上,这个软件是没有作用的。

  今天偶然想起这件事来就研究了一把,原来 ThinkPad 内置了一个陀螺仪,这个东西可以监测到笔记本的振动和倾斜,用来在笔记本跌落和受到撞击的时候避免硬盘磁头打到盘面从而使硬盘受伤。IBM 管这套东西叫 Hard Drive Active Protection System,简称 HDAPS。

  网上有大把关于如何在 Linux 下利用 APS 的信息,但是关于如何在 Windows 下利用 APS 的资源就比较少了。经过几次三番的 Google,终于搞明白了如何在 Windows 下获取 ThinkPad APS 信息的方法。

  首先,可以在 ftp://ftp.software.ibm.com/pc/pccbbs/mobiles_pdf/aps2mst.pdf 下载到 IBM Active Protection System Whitepaper,在其中的 User interface and task tray applet 小节下可以找到如下信息:
ShockPrf.sys: kernel mode device driver for prediction algorithm and hard disk drive control
Shockmgr.sys: kernel mode driver for miscellaneous operation
Sensor.dll: application interface dll

可见我们可以利用 C:\Windows\system32 下的 Sensor.dll 来获取 APS 数据,用 eXeScope 看了一下,Sensor.dll 导出了 21 个函数:
00000001:ShockproofCallSMAPIBIOS
00000002:ShockproofControl
00000003:ShockproofEnableDisableSnooze
00000004:ShockproofGetAccelerometerData
00000005:ShockproofGetAccelerometerDataEx
00000006:ShockproofGetAccelerometerMutex
00000007:ShockproofGetAutoDisable
00000008:ShockproofGetShockStatus
00000009:ShockproofGetSlaveCPUinfo
0000000A:ShockproofGetUnloadCnt
0000000B:ShockproofGetVersion
0000000C:ShockproofInformMouseDevChange
0000000D:ShockproofInformPENevent
0000000E:ShockproofInformPMevent
0000000F:ShockproofInformTabletLIDstate
00000010:ShockproofInvokeSnooze
00000011:ShockproofManualSensitivitySetting
00000012:ShockproofReleaseAccelerometerMutex
00000013:ShockproofSetAutoDisable
00000014:ShockproofSnoozeSetting
00000015:ShockproofTaskComplete

但是要利用其中的哪个函数呢?继续搜索,在 CodeProject 找到一篇有用的东西:http://www.codeproject.com/KB/system/LenovoAPS.aspx。不过里面的实现是 C# 的,被我改写成 C 代码如下:

  1. typedef struct
  2.  {
  3.      INT       PresentState;
  4.      USHORT    LatestRawAccelDataX;
  5.      USHORT    LatestRawAccelDataY;
  6.      USHORT    LatestAccelDataX;
  7.      USHORT    LatestAccelDataY;
  8.      CHAR      Temperature;
  9.      USHORT    LatestZeroG_X;
  10.      USHORT    LatestZeroG_Y;
  11.  } AccelerometerData;
  12.  
  13.  typedef void (__stdcall * funcShockproofGetAccelerometerData)( AccelerometerData * );

AccelerometerData 里除了倾斜数据之外还有单位是摄氏度的温度数据:)
最后两项 LatestZeroG_X & LatestZeroG_Y 是最后一次稳定时的数据,这两个值在把本本放在水平桌面上静止一会儿后取出来可以作为水平的参考值;
LatestRawAccelDataX & LatestRawAccelDataY 是陀螺仪输出的原始数据;
LatestAccelDataX & LatestAccelDataY 是 40ms 内由原始数据得出的平均数据;
PresentState 是内部状态值,具体的取值和本本的状态对应关系我没有仔细研究过,但基本上 0 是稳定状态,10 是禁用状态。

一开始应该把本本放在水平桌面上取水平参考值:

  1. USHORT cX = 0;
  2.  USHORT cY = 0;
  3.  
  4.  HMODULE hDll = LoadLibrary( _T("Sensor.dll") );
  5.  if( hDll )
  6.  {
  7.      ShockproofGetAccelerometerData = (funcShockproofGetAccelerometerData)GetProcAddress( hDll, "ShockproofGetAccelerometerData" );
  8.      if( ShockproofGetAccelerometerData )
  9.      {
  10.          AccelerometerData ad = { 0 };
  11.          ShockproofGetAccelerometerData( &ad );
  12.          cX = ad.LatestZeroG_X;
  13.          cY = ad.LatestZeroG_Y;
  14.      }
  15.  }

然后可以不断的调用(通过 timer 等)如下代码获取实时的本本倾斜状态:

  1. AccelerometerData ad = { 0 };
  2.  ShockproofGetAccelerometerData( &ad );
  3.  
  4.  int pitch = (int)( 90 * ( ad.LatestRawAccelDataX - cY ) / ( cX - cY ) );
  5.  int roll  = (int)( 90 * ( ad.LatestRawAccelDataY - cX ) / ( cY - cX ) );

pitch 是前后的倾斜角度,roll 是左右的倾斜角度。

  其实有了以上知识就足以开发出一些有意思的程序了,网上已经有网友利用 APS 写了震动监测(测地震?)、浏览 StreetView、模拟游戏摇杆、模拟鼠标移动、移动窗口等小玩意。所以完全有可能把 iPhone 上一些需要陀螺仪的游戏移植到 PC 上,当然,只能使用有类似 APS 的本本来玩:) 据说 Apple 的本本上就有类似的东西,叫做 Sudden Motion Sensor。

  不过,在利用 APS 特性来玩的时候会触发硬盘保护,所以你会听到硬盘磁头不断复位的咔咔声,所以,一定不要玩太过激烈的玩意:D

2 Responses to “ThinkPad Hard Drive Active Protection System”

  1. 王小力 Says:

    你好, 我刚刚email了你, 请求这段C源码. 谢谢你.

  2. 2ndboy Says:

    邮件已回,其实主要代码都已经在这篇 post 里啦:D

Leave a Reply