UEFI开发探索73- YIE002USB开发板(02 Windows编程)

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】

1 HID(人机接口设备)介绍

HID设备是USB规范中最早提出并支持的一种类设备,日常使用的键盘、鼠标等,都属于HID设备,是一种使用非常广泛的USB设备。

HID一开始为USB,但设计为与总线无关。它是为低延迟、低宽带的设备而设计的,在Windows 7以上,也支持蓝牙、I2C、GPIO等HID设备。

当然,我们主要还是研究跨平台的USB HID。

HID包含两个基本概念:报告描述符和报告。报告描述符描述设备支持的数据格式和含义,报告是在设备和软件客户端之间交换的实际数据。

上位机程序和HID设备通过报告来交换数据,上一篇中了解到,有三种报告类型:

输入报告(Input Report): 从HID设备发送到上位机程序的数据,通常在状态发生更改时发送;

输出报告(Output Report):从上位机程序发送到HID设备(比如键盘上的LED状态)数据;

功能报告(Feature Report):通常与配置信息相关。

在报告描述符中,可以定义每种类型的0个或多个报告,通常Input Report至少有一个。

不同的操作系统,对USB HID的支持方式不一样。Windows下有驱动Hidusb.sys支持HID传输,并提供了HID应用程序接口;UEFI的下使用EFI_USB_IO_PROTOCOL构建通信函数;而Linux下则可以使用libusb与HID设备通信。

2 Windows上位机程序UsbHID

为YIE002开发的Windows上位机,我命名为UsbHID。其界面图如图1所示。

图1 UsbHID界面

UsbHID工具运行于Windows系统下, 主要用来在与YIE002进行通信。它将系统中所有能够找到的HID设备以列表的形式显示,用户选择需要通信的设备,再选择三种通信方式中的一种,即可发送数据。

USB通信中,所有的命令都是由主机发出的。USB设备根据主机发出的命令,进行相应的处理。YIE002设计的固件中,对主机发出来的数据,进行了一定的处理并进行回传,以演示双方的通信过程。

YIE002的开发板上,提供了LED灯控制以及随机数生成的功能。上位机可以通过此数据通道,控制LED灯,并获取生成的随机数。

3 Windows系统的HID API

Windows系统上提供的HID应用程序编程接口(API),大致可以分为两类:

I 获取设备属性的函数

HidD _ GetAttributes()
HidD _ GetHidGuid()
HidD _ GetIndexedString()
HidD _ GetManufacturerString()
HidD _ GetPhysicalDescriptor()
HidD _ GetPreparsedData()
HidD _ GetProductString()
HidD _ GetSerialNumberString()
HidD _ GetNumInputBuffers()
HidD _ SetNumInputBuffers()

II 数据通信的函数

ReadFile()
WriteFile()
HidD _ GetInputReport()
HidD _ SetOutputReport()
HidD _ SetFeature()
HidD _ GetFeature()

获取设备属性函数的使用方法相对简单,直接查看MSDN上的函数描述就可以了。这里主要介绍下HID设备数据通讯函数的用法。

3.1 CreateFile()、ReadFile()和WriteFile()

这三个函数的函数原型为:

HANDLE CreateFile(
  LPCWSTR lpFileName,        //指向路径的指针
  DWORD dwDesiredAccess,     //访问模式(读/写)
  DWORD dwShareMode,         //共享模式
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针
  DWORD dwCreationDisposition,   //如何创建
  DWORD dwFlagsAndAttributes,    //文件属性(同步或异步)
  HANDLE hTemplateFile           //用于复制文件句柄
);
BOOL ReadFile(
  HANDLE hFile,        //设备句柄,可通过CreateFile得到
  LPVOID lpBuffer,     //存储数据的缓冲区
  DWORD nNumberOfBytesToRead,   //要读取的数据长度
  LPDWORD lpNumberOfBytesRead,  //实际收到的数据长度
  LPOVERLAPPED lpOverlapped      //OVERLAPPED结构体指针
);
BOOL WriteFile(
  HANDLE hFile,             //设备句柄,可通过CreateFile得到
  LPCVOID lpBuffer,         //要发送的buffer(指针)
  DWORD nNumberOfBytesToWrite,   //要发送数据的长度
  LPDWORD lpNumberOfBytesWritten,   //实际发送数据的长度
  LPOVERLAPPED lpOverlapped  //OVERLAPPED结构体指针,如果设备是以
//FILE_FLAG_OVERLAPPED方式打开的话,那么这个指针就不能为NULL
);

CreateFile()用于打开HID设备,设备路径可通过函数SetupDi系列函数获取。此函数有以下需要注意的地方:

● 访问模式。 系统独占设备,比如鼠标、键盘等,应将此参数设置为0,否则后续函数的操作将失败。也就是说,对独占设备只能进行查询操作,所以可使用的函数有限。

● 文件属性。此参数用来设置同步或异步模式,它主要是对后续的WriteFile()和ReadFile()有影响。此参数为0时,代表同步模式,也即数据处理完成之后才返回,否则阻塞在函数内部。

ReadFile()用于读取设备通过中断IN传输发来的输入报告,读取的数据从中断输入管道传入。其读取的数据是从HID设备驱动的缓冲区中得到的,缓冲区的大小,可通过HidD _ SetNumInputBuffers()来改变。

ReadFile()的入口参数nNumberOfBytesToRead代表要读取数据的长度(数据正文+1字节报告ID)。当然,在实际设计中,HID设备是由我们自己设计的,我们是知道设备固件的信息的。

上位机也可通过HidD _ GetPreparsedData()来取得报告的长度。此参数设置过大,不会有错误产生,上位机将受到实际读到的长度;此参数设置过小,也即小于数据长度,会返回错误。

WriteFile()通过中断OUT得到来自设备的数据,其入口参数lpBuffer的第一个元素为待发送的报告ID,而且此报告ID必须是Output Report,否则会返回错误。

3.2 HidD _ GetInputReport()和HidD _ SetOutputReport()

BOOLEAN HidD_GetInputReport(
  HANDLE HidDeviceObject,     //设备句柄
  PVOID  ReportBuffer,        //存储数据的缓冲区
  ULONG  ReportBufferLength  //缓冲区长度
);
BOOLEAN HidD_SetOutputReport(
  HANDLE HidDeviceObject,    //设备句柄
  PVOID  ReportBuffer,       //存有待发送数据的缓冲区
  ULONG  ReportBufferLength //缓冲区长度
);

这两个函数通过Input Report和Output Report进行数据传输。主要在其它们的入口参数中,缓冲区的数据,第一个元素为Report ID,后面跟着的才是数据正文。

在固件程序中,这两个函数对应的类命令为Get Report和Set Report。

3.3 HidD _ SetFeature()和HidD _ GetFeature()

BOOLEAN HidD_GetFeature(
  HANDLE HidDeviceObject,     //设备句柄
  PVOID  ReportBuffer,        //存储数据的缓冲区
  ULONG  ReportBufferLength  //缓冲区长度
);
BOOLEAN HidD_SetFeature(
  HANDLE HidDeviceObject,    //设备句柄
  PVOID  ReportBuffer,       //存有待发送数据的缓冲区
  ULONG  ReportBufferLength //缓冲区长度
);

与3.2介绍的两个函数类似,不过这两个函数是通过Feature Report进行数据传输的。在固件程序中,对应的类命令也是Get Report和Set Report。

了解了上述函数的基本用法后,就可以着手编写UsbHID工具了。具体的代码编写过程,在下一篇中进行介绍。

119 total views, 3 views today

发表评论

电子邮件地址不会被公开。