首页
社区
课程
招聘
[原创] 实战解析I2C HID协议----以I2C Touch Pad为例
发表于: 1天前 250

[原创] 实战解析I2C HID协议----以I2C Touch Pad为例

1天前
250

本文是<实战解析 USB HID 协议><实战解析 USB HID 协议 2>的扩展篇. 不过这次将目光转移到应用了I2C HID协议的设备.

1. 硬件部分

在单片机/嵌入式应用场景中, 设备间通信只需SCL/SDA 2根信号线; HID over I2C协议还额外需要一根INT中断信号. I2C Slaver有数据时, 如触摸Pad, 会触发INT信号 (Assert INT#). Host收到中断后才会发起I2C事务(直至此时Host才使能SCL信号, 再通过SDA信号读取数据).  由于这个特性, HID over I2C信号在逻辑分析仪中的输出会呈现一定的规律性: SCL/SDA仅在INT# Assert期间变化; INT# De-Assert期间无变化, 这种特性使得波形比较清澈(这样设计可能也是为了省电):


Acute解析的HID Over I2C波形


常见的I2C HID设备有I2C Touch PadI2C Touch Panel, 正好我手边有台带FocalTech I2C Touch Pad的二手笔记本且Touch Pad PCB上有INT/SCL/SDA这些信号的丝印, 方便接转接板. 本文的内容将基于此笔记本展开.


FocalTech I2C Pad PCB


2.固件部分

硬件部分的准备完成, 再看看固件/OS部分. HID over I2C协议获取HID Report Descriptor的过程有点绕. OS需要从ACPI中获得HID Descriptor Register. OS通过HID Descriptor RegisterTouch Pad中读取HID Descriptor. 注意, 目前读到的是HID Descriptor, 其结构如下:

typedef struct _HIDDescriptor
{
	uint16 wHIDDescLength;       
	uint16	bcdVersion;              
	uint16	wReportDescriptorLength;   
	uint16	wReportDescriptorRegister; 
	uint16	wInputRegister; 
	uint16	wMaxInputLength;
	uint16	wOutputRegister;
	uint16	wMaxOutputLength; 
	uint16	wCommandRegister; 
	uint16	wDataRegister; 
	uint16	wVendorID;
	uint16	wProductID;
	uint16	wVersionID;
	uint32	Reserved;
}HIDDescriptor;

OS再从HIDDescriptor->wReportDescriptorRegister获得Report Descriptor Register地址, 最终通过该地址获得HID Report Descriptor. 该过程的示意图如下:


另外OS从HIDDescriptor->wCommandRegister获得Command Register, 该寄存器用于Host向Slave发出如SetPower/RESET请求. 

2.1. SetPower/RESET

OS在读取HID Descriptor后, 在进一步读取HID Report Descriptor前, 还需要对HID Over I2C设备依次执行2个动作SetPower和RESET


2个命令都是OS通过向HIDDescriptor->wCommandRegister(在本文中是0x22)指定的寄存器写入2 BYTECommand实现.


命令            HOSTwCommandRegister写入的2 BYTE数值            备注
           
SET_POWER            BYTE0=0x08, BYTE1=0x00            SET_POWER=On, 让设备开始工作            
RESET            BYTE0=0x01, BYTE1=0x00            

1.Slave重置自身;                

2.重置完成后Assert INT#                

3.Host发现INT# Assert后发起I2CRead事务                

4.Slave持续向Host返回0x00 0x00作为重置成功的标志                

5.Host读取完毕后, Slave将De-Assert INT#            

附注, 如果RESET流程执行失败,  Windows设备管理器中的I2C 设备会出现黄色感叹号.


2.2. 获取I2C Slave Address和HID Descriptor Register Address

 

HID over I2C协议本质上依然是I2C协议和HID协议对于I2C协议, 固件需要向主机提供I2C Slave Address; 而对于HID协议, 固件需要向Host提供HID Descriptor Register. Host获得Descriptor Register, 再通过I2C 协议(Repeat Start事务)Pad固件中获得  HID Descriptor. 这种关系就像C语言里的指针: HID Descriptor Register就像HID Descriptor类型的指针通过该指针获取HID Descriptor结构体变量.




获取方式            备注            
I2C Slaver Address            OS enable ACPI以后, Method(_INI)设定, OS通过Method(_CRS)获取            字面意思, 就是Slave设备地址, 在本文中就是I2C Touch Pad Slave地址            
HID Descriptor Register            OS enable ACPI以后, Method(_INI)设定, OS通过Method(_DSW)获取            HID over I2C协议中最重要的部分. 由厂商提供, 固件设定的16bit地址.  Method(_DSW)中获得HID Descriptor Register,  再从Register中获得HID Descriptor.            


作为Pad厂商和主机厂商以外的读者, 可以通过RW dump整个ACPI Table获得这2个信息(本文使用的ACPI Dump文件参见附录):

a.       首先查看设备管理器, 定位I2C Touch PadBios Device Name:


b.       根据Bios Device NameI2C Touch PadACPI Device NameTPD0, 搜索整个ACPI Table. 可以看到在Device(TPD0) Scope中找到Method(_INI) :


Method(_INI)初始化I2C slave Address/HID Descriptor Register Address


Method(_CRS)Method(_DSW)返回I2C slave Address/HID Descriptor Register Address


2.3. 从逻辑分析仪观察获得HID Report Descriptor的过程

读者只能从ACPI Table中看到I2C Slave AddressHID Descriptor Register Address, 但是OSPad交互获得HID Report Descriptor的过程只能依赖逻辑分析仪读到. 我将通过2小节来演示该过程(演示中用到的逻辑分析仪文件见附件: HIDDescriptorAndHIDReportDescriptor.TBW).

2.3.1. I2C Host发起HID Descriptor RegisterHID Descriptor

逻辑分析仪上读到的第一段I2C事务如下:


对波形解读:


动作注释
StartI2C事务开始
Addr:0x2C+Write寻址Slave, 准备写入地址
Command Register:0x20写入0x20, 这是从ACPI中读到的HID   Descriptor Register
SR (Start Repeat)不释放总线切换模式
Addr:0x2C+Read再次寻址Slave, 开始从0x20处读取


Host Pad中读到的内容如上图中红框部分, 下表将数值还原为HID Descriptor:


OffsetHID Descriptor中的字段Value注释
0x00wHIDDescLength0x001EHID Over I2C Spec规定的固定内容指明整个HID   Descriptor长度为0x1E
0x02bcdVersion0x0100HID Over I2C Spec规定的固定内容, bcd格式的版本号
0x04wReportDescriptorLength0x02AEHID Report Descriptor长度
0x06wReportDescriptorRegister0x0021Host 会读取这个地址来获取 HID Report Descriptor
0x08wInputRegister0x0024读取输入报告(如Pad移动/按键)的地址
0x0AwMaxInputLength0x0020输入报告的最大长
0x0CwOutputRegister0x0025发送输出报告的地
0x0EwMaxOutputLength0x0000N/A
0x10wCommandRegister0x0022发送 Reset/SetPower 命令的地址
0x12wDataRegister0x0023进行数据传输时所依赖的寄存器地



表格中wInputRegister字段像在表达: 当有输入事件产生时(INT# Assert), OS会从wInputRegister所指向寄存器中读取Input Report. 然而, 在实际测试过程中, 我并没有从逻辑分析仪中看到这样的I2C事务. 不知道为啥Windows为啥没有完全按HID Over I2C协议实现(因为I2C事务是HOST发起的, 因此可以排除设备端不按标准实现的可能).


2.3.2. I2C Host再次发起读事务获取HID Report Descriptor

OS填充完HID Descriptor, 了解到HID Report Descriptor Register=0x21, 之后OS会从0x21(如下图红框处所示)获取HID Report Descriptor:


2.4. 解析HID Report Descriptor

我已将读到的HID Report Descriptor Hexdump保存到附件HidReportDescriptorptor.CSV. 我使用的的Touch Pad具有多点触摸功能, .csv文件中REPORT_ID(01)所描述的Usage_Page/Usage/Collection&EndCollection用于描述多点触摸功能的Input Report. 在之前的文章中已经有比较详细的解析HID Report Descriptor的步骤, 因此本文直接给出多点触摸功能的Input Report (Size = 0x20 Byte):


2.5. 解析Touch Pad 移动引起的Input Report

有了HID Report Descriptor这个模版, 现在可以解释逻辑分析仪上I2C Touch Pad移动产生的数据了(同样, 演示用的逻辑分析仪文件见附件: InputReportWhenFingerMove.TBW).


从逻辑分析仪窗口可以得到下列结论:

1.       INT# Assert(产生了手指移动/按压一类事件), Host端以”Start+I2CSlaveAddr + Rd”的时序, 直接读取Input Report; 而并非以”Start+I2CSlaveAddr + Wr + HIDDescriptor.wInputRegister + StartRepeat + Rd+ HIDDescriptor.wInputRegister”这种预想的时序读取Input Report.

2.       HostPad读到的Input Report具有固定的格式:  2字节InputReport长度+1字节ReportID+5*5字节多点触摸信息+1字节有效触摸点数+1字节按键状态.


3.      猜想

本系列完结, 实在不想写HID Over GPIO的文章了. 不知道有没有做个过滤驱动修改Input Report中位移的可行性. 我先研究研究, 如果可行, 再更新一篇.

 

4.      后记

写这篇文章, 始于一次Issue: I2C Touch Panel息屏回来后, Touch功能丢失并伴随设备管理器里I2C设备黄标(黄标原因是读不到_HID Descriptor). 由于复现步骤比较稳定, 抱着死马当活马医的心态, 我抓了一把HID over I2C协议, 结果发现主机发出SET_POWER Command, INT#再也没有Assert, INT#/SCL/SDA 3个信号平静的就像湖面. 咨询屏幕厂商后才发现是电源设计有问题.

复盘Issue, 我打算选用张银奎老师的幽兰笔记本来记录HID Over I2C协议的工作原理. 然而拆开后发现, 幽兰笔记本的TouchPad挂在USB Hub下面. 因此打消了写这篇文章的念头. 一次偶尔逛海鲜市场的时候发现居然有售卖公司的二手笔记本, 型号更是我首次Power On的型号. 交织不信任和收藏的复杂心态, 我收了这台机器, 到手后惊奇于居然真的能用!

但是要写这篇文章依然有限制: 1. 回路设计不能公开; 2. Bios代码不能公开;

比较巧的是, Touch Pad PCB上有信号丝印; 另外, HID Over I2C紧密相关的ACPI代码可以通过RW dump出来. 最终这篇文章还是艰难的诞生了~



[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 1天前 被hyjxiaobia编辑 ,原因:
上传的附件:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回